Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@
"@agent-device/xml": "workspace:*",
"@arethetypeswrong/cli": "^0.18.5",
"@chenglou/freerange": "^0.0.4",
"@limrun/api": "^0.60.0",
"@limrun/api": "^0.64.1",
"@nkzw/eslint-plugin": "^2.0.0",
"@nkzw/oxlint-config": "^1.3.0",
"@statelyai/graph": "2.4.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/provider-limrun/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@
}
},
"devDependencies": {
"@limrun/api": "^0.60.0"
"@limrun/api": "^0.64.1"
}
}
27 changes: 27 additions & 0 deletions packages/provider-limrun/src/client-options.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Limrun from '@limrun/api';
import { expect, test } from 'vitest';
import { limrunUserAgent } from './client-options.ts';

test('the Limrun SDK sends the agent-device prefix ahead of its own User-Agent', async () => {
const userAgents: Array<string | null> = [];
const limrun = new Limrun({
apiKey: 'lim_test_key',
baseURL: 'https://limrun.example',
maxRetries: 0,
userAgent: limrunUserAgent('1.2.3'),
fetch: async (_url, init) => {
userAgents.push(new Headers(init?.headers).get('user-agent'));
return new Response(JSON.stringify({ metadata: { id: 'instance-1' }, status: {} }), {
headers: { 'content-type': 'application/json' },
});
},
});

await limrun.iosInstances.create({});
await limrun.androidInstances.delete('instance-1');

expect(userAgents).toHaveLength(2);
for (const userAgent of userAgents) {
expect(userAgent).toMatch(/^agent-device-cli\/1\.2\.3 Limrun\/JS /);
}
});
5 changes: 5 additions & 0 deletions packages/provider-limrun/src/client-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { AppError } from '@agent-device/kernel/errors';

export const LIMRUN_CLIENT_ID = 'agent-device-cli';

/** Prefixes the SDK User-Agent so Limrun attributes API-key traffic, including instance creation, to agent-device. */
export function limrunUserAgent(clientVersion: string): string {
return `${LIMRUN_CLIENT_ID}/${clientVersion}`;
}

/** The organization client, or a typed refusal for an operation that needs the API key. */
export function requireLimrunOrgClient(
limrun: Limrun | undefined,
Expand Down
19 changes: 13 additions & 6 deletions packages/provider-limrun/src/connection-verification.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ afterEach(() => {
test('Limrun verification reads the selected instance service without creating an instance', async () => {
const result = await verifyLimrunConnection({
apiKey: 'lim_test_key',
clientVersion: '1.2.3',
platform: 'android',
});

Expand All @@ -63,6 +64,7 @@ test('Limrun verification reads the selected instance service without creating a
assert.equal(mockState.iosList.mock.calls.length, 0);
assert.equal(mockState.constructorOptions[0]?.apiKey, 'lim_test_key');
assert.equal(mockState.constructorOptions[0]?.defaultHeaders, undefined);
assert.equal(mockState.constructorOptions[0]?.userAgent, 'agent-device-cli/1.2.3');
});

test('Limrun verification classifies authentication failures', async () => {
Expand All @@ -71,6 +73,7 @@ test('Limrun verification classifies authentication failures', async () => {
await assert.rejects(
verifyLimrunConnection({
apiKey: 'lim_bad_key',
clientVersion: '1.2.3',
platform: 'ios',
}),
(error: unknown) => {
Expand All @@ -85,6 +88,7 @@ test('Limrun verification connects to an attached instance instead of the contro
const android = { apiUrl: 'https://attached.example/api', token: 'tok', adbUrl: 'wss://adb' };
const result = await verifyLimrunConnection({
apiKey: 'lim_test_key',
clientVersion: '1.2.3',
instances: { android },
platform: 'android',
});
Expand Down Expand Up @@ -117,11 +121,14 @@ test('Limrun verification names the variables to check when an instance rejects
] as const;
for (const { platform, instances, create, variables } of cases) {
create.mockRejectedValueOnce(new Error('Unexpected server response: 401'));
await assert.rejects(verifyLimrunConnection({ instances, platform }), (error: unknown) => {
assert.equal((error as { code?: string }).code, 'COMMAND_FAILED');
assert.match(JSON.stringify(error), variables);
assert.doesNotMatch(JSON.stringify(error), /lim_st_secret/);
return true;
});
await assert.rejects(
verifyLimrunConnection({ clientVersion: '1.2.3', instances, platform }),
(error: unknown) => {
assert.equal((error as { code?: string }).code, 'COMMAND_FAILED');
assert.match(JSON.stringify(error), variables);
assert.doesNotMatch(JSON.stringify(error), /lim_st_secret/);
return true;
},
);
}
});
3 changes: 3 additions & 0 deletions packages/provider-limrun/src/connection-verification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Limrun from '@limrun/api';
import { AppError } from '@agent-device/kernel/errors';
import type { ProviderConnectionVerification } from '@agent-device/contracts/remote';
import type { LimrunInstanceAccess } from './instance-access.ts';
import { limrunUserAgent } from './client-options.ts';

export type LimrunConnectionVerification = ProviderConnectionVerification & {
provider: 'limrun';
Expand All @@ -19,6 +20,7 @@ export type LimrunConnectionVerification = ProviderConnectionVerification & {

export type LimrunConnectionVerificationOptions = {
apiKey?: string;
clientVersion: string;
instances?: LimrunInstanceAccess;
platform: 'android' | 'ios';
region?: string;
Expand All @@ -38,6 +40,7 @@ export async function verifyLimrunConnection(
}
const client = new Limrun({
apiKey: options.apiKey,
userAgent: limrunUserAgent(options.clientVersion),
timeout: 15_000,
maxRetries: 0,
});
Expand Down
9 changes: 7 additions & 2 deletions packages/provider-limrun/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import type {
import { providerRuntimeOwner } from '@agent-device/contracts/platform-runtime';
import type { LimrunAppLogDescriptor } from './app-log-descriptor.ts';
import type { LimrunAppLogReader } from './app-log-runtime.ts';
import { LIMRUN_CLIENT_ID, requireLimrunOrgClient } from './client-options.ts';
import { LIMRUN_CLIENT_ID, limrunUserAgent, requireLimrunOrgClient } from './client-options.ts';
import { resolveLimrunRuntimeInstance } from './runtime-instance.ts';
import type { LimrunRequestOperationDrain } from './request-cancellation.ts';
import type { LimrunAppAsset } from './app-catalog.ts';
Expand Down Expand Up @@ -156,7 +156,12 @@ class LimrunRuntimeImplementation implements ProviderDeviceRuntime {
}
this.options = options;
this.dependencies = dependencies;
this.limrun = options.apiKey ? new Limrun({ apiKey: options.apiKey }) : undefined;
this.limrun = options.apiKey
? new Limrun({
apiKey: options.apiKey,
userAgent: limrunUserAgent(dependencies.clientVersion),
})
: undefined;
}

ownsDevice(device: DeviceInfo): boolean {
Expand Down
83 changes: 0 additions & 83 deletions packages/provider-limrun/src/session-allocation.test.ts

This file was deleted.

81 changes: 35 additions & 46 deletions packages/provider-limrun/src/session-allocation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
} from './app-catalog.ts';
import { createLimrunDeviceSession } from './device-session.ts';
import type { LimrunRuntimeDependencies } from './runtime-dependencies.ts';
import { LIMRUN_CLIENT_ID } from './client-options.ts';

type LimrunInstance = {
metadata: { id: string };
Expand Down Expand Up @@ -69,30 +68,25 @@ export async function resolvePreinstalledAppId(
export async function allocateLimrunIosSession(
params: SessionAllocationParams,
): Promise<LimrunIosSession> {
const instance = (await params.limrun.iosInstances.create(
{
wait: true,
metadata: params.metadata,
spec: {
...(params.region ? { region: params.region } : {}),
...(params.app
? {
initialAssets: [
{
kind: 'App' as const,
source: 'AssetID' as const,
assetId: params.app.id,
launchMode: 'RelaunchIfRunning' as const,
},
],
}
: {}),
},
},
{
headers: { 'User-Agent': `${LIMRUN_CLIENT_ID}/${params.dependencies.clientVersion}` },
const instance = (await params.limrun.iosInstances.create({
wait: true,
metadata: params.metadata,
spec: {
...(params.region ? { region: params.region } : {}),
...(params.app
? {
initialAssets: [
{
kind: 'App' as const,
source: 'AssetID' as const,
assetId: params.app.id,
launchMode: 'RelaunchIfRunning' as const,
},
],
}
: {}),
},
)) as LimrunInstance;
})) as LimrunInstance;
try {
if (!instance.status.apiUrl) {
throw new AppError('COMMAND_FAILED', 'Limrun iOS instance did not expose apiUrl');
Expand All @@ -117,29 +111,24 @@ export async function allocateLimrunIosSession(
export async function allocateLimrunAndroidSession(
params: SessionAllocationParams,
): Promise<LimrunAndroidSession> {
const instance = (await params.limrun.androidInstances.create(
{
wait: true,
metadata: params.metadata,
spec: {
...(params.region ? { region: params.region } : {}),
...(params.app
? {
initialAssets: [
{
kind: 'App' as const,
source: 'AssetIDs' as const,
assetIds: [params.app.id],
},
],
}
: {}),
},
},
{
headers: { 'User-Agent': `${LIMRUN_CLIENT_ID}/${params.dependencies.clientVersion}` },
const instance = (await params.limrun.androidInstances.create({
wait: true,
metadata: params.metadata,
spec: {
...(params.region ? { region: params.region } : {}),
...(params.app
? {
initialAssets: [
{
kind: 'App' as const,
source: 'AssetIDs' as const,
assetIds: [params.app.id],
},
],
}
: {}),
},
)) as LimrunInstance;
})) as LimrunInstance;
try {
if (!instance.status.apiUrl || !instance.status.adbWebSocketUrl) {
throw new AppError(
Expand Down
14 changes: 7 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ allowBuilds:
fallow: true
minimumReleaseAgeExclude:
- '@chenglou/freerange@0.0.4'
- '@limrun/api@0.64.1'
overrides:
brace-expansion@5: '^5.0.12'
fast-uri: 3.1.7
Expand Down
Loading
Loading