Skip to content

Commit f34c63b

Browse files
author
Zhongjin Lu
committed
feat: add version flags and unify version lookup
1 parent 6a064f2 commit f34c63b

7 files changed

Lines changed: 54 additions & 49 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ Notes:
9696
- If XCTest returns 0 nodes (e.g., foreground app changed), agent-device falls back to AX when available.
9797

9898
Flags:
99+
- `--version, -V` print version and exit
99100
- `--platform ios|android`
100101
- `--device <name>`
101102
- `--udid <udid>` (iOS)

src/cli.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { parseArgs, usage } from './utils/args.ts';
22
import { asAppError, AppError } from './utils/errors.ts';
33
import { formatSnapshotText, printHumanError, printJson } from './utils/output.ts';
4+
import { readVersion } from './utils/version.ts';
45
import { pathToFileURL } from 'node:url';
56
import { sendToDaemon } from './daemon-client.ts';
67
import fs from 'node:fs';
@@ -10,6 +11,11 @@ import path from 'node:path';
1011
export async function runCli(argv: string[]): Promise<void> {
1112
const parsed = parseArgs(argv);
1213

14+
if (parsed.flags.version) {
15+
process.stdout.write(`${readVersion()}\n`);
16+
process.exit(0);
17+
}
18+
1319
if (parsed.flags.help || !parsed.command) {
1420
process.stdout.write(`${usage()}\n`);
1521
process.exit(parsed.flags.help ? 0 : 1);

src/daemon-client.ts

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import net from 'node:net';
22
import fs from 'node:fs';
33
import os from 'node:os';
44
import path from 'node:path';
5-
import { fileURLToPath } from 'node:url';
65
import { AppError } from './utils/errors.ts';
76
import type { CommandFlags } from './core/dispatch.ts';
87
import { runCmdDetached } from './utils/exec.ts';
8+
import { findProjectRoot, readVersion } from './utils/version.ts';
99

1010
export type DaemonRequest = {
1111
token: string;
@@ -134,33 +134,10 @@ async function sendRequest(info: DaemonInfo, req: DaemonRequest): Promise<Daemon
134134
});
135135
}
136136

137-
function readVersion(): string {
138-
try {
139-
const root = findProjectRoot();
140-
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')) as {
141-
version?: string;
142-
};
143-
return pkg.version ?? '0.0.0';
144-
} catch {
145-
return '0.0.0';
146-
}
147-
}
148-
149137
function resolveRequestTimeoutMs(): number {
150138
const raw = process.env.AGENT_DEVICE_DAEMON_TIMEOUT_MS;
151139
if (!raw) return 60000;
152140
const parsed = Number(raw);
153141
if (!Number.isFinite(parsed)) return 60000;
154142
return Math.max(1000, Math.floor(parsed));
155143
}
156-
157-
function findProjectRoot(): string {
158-
const start = path.dirname(fileURLToPath(import.meta.url));
159-
let current = start;
160-
for (let i = 0; i < 6; i += 1) {
161-
const pkgPath = path.join(current, 'package.json');
162-
if (fs.existsSync(pkgPath)) return current;
163-
current = path.dirname(current);
164-
}
165-
return start;
166-
}

src/daemon.ts

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import fs from 'node:fs';
33
import os from 'node:os';
44
import path from 'node:path';
55
import crypto from 'node:crypto';
6-
import { fileURLToPath } from 'node:url';
76
import { dispatchCommand, type CommandFlags } from './core/dispatch.ts';
87
import { isCommandSupportedOnDevice } from './core/capabilities.ts';
98
import { asAppError, AppError } from './utils/errors.ts';
9+
import { readVersion } from './utils/version.ts';
1010
import { stopIosRunnerSession } from './platforms/ios/runner-client.ts';
1111
import type { DaemonRequest, DaemonResponse } from './daemon/types.ts';
1212
import { SessionStore } from './daemon/session-store.ts';
@@ -203,26 +203,3 @@ function start(): void {
203203
}
204204

205205
start();
206-
207-
function readVersion(): string {
208-
try {
209-
const root = findProjectRoot();
210-
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')) as {
211-
version?: string;
212-
};
213-
return pkg.version ?? '0.0.0';
214-
} catch {
215-
return '0.0.0';
216-
}
217-
}
218-
219-
function findProjectRoot(): string {
220-
const start = path.dirname(fileURLToPath(import.meta.url));
221-
let current = start;
222-
for (let i = 0; i < 6; i += 1) {
223-
const pkgPath = path.join(current, 'package.json');
224-
if (fs.existsSync(pkgPath)) return current;
225-
current = path.dirname(current);
226-
}
227-
return start;
228-
}

src/utils/args.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ export type ParsedArgs = {
2525
noRecord?: boolean;
2626
replayUpdate?: boolean;
2727
help: boolean;
28+
version: boolean;
2829
};
2930
};
3031

3132
export function parseArgs(argv: string[]): ParsedArgs {
32-
const flags: ParsedArgs['flags'] = { json: false, help: false };
33+
const flags: ParsedArgs['flags'] = { json: false, help: false, version: false };
3334
const positionals: string[] = [];
3435

3536
for (let i = 0; i < argv.length; i += 1) {
@@ -42,6 +43,10 @@ export function parseArgs(argv: string[]): ParsedArgs {
4243
flags.help = true;
4344
continue;
4445
}
46+
if (arg === '--version' || arg === '-V') {
47+
flags.version = true;
48+
continue;
49+
}
4550
if (arg === '--verbose' || arg === '-v') {
4651
flags.verbose = true;
4752
continue;
@@ -229,5 +234,6 @@ Flags:
229234
--update, -u Replay: update selectors and rewrite replay file in place
230235
--user-installed Apps: list user-installed packages (Android only)
231236
--all Apps: list all packages (Android only)
237+
--version, -V Print version and exit
232238
`;
233239
}

src/utils/version.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import fs from 'node:fs';
2+
import path from 'node:path';
3+
import { fileURLToPath } from 'node:url';
4+
5+
export function readVersion(): string {
6+
try {
7+
const root = findProjectRoot();
8+
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')) as {
9+
version?: string;
10+
};
11+
return pkg.version ?? '0.0.0';
12+
} catch {
13+
return '0.0.0';
14+
}
15+
}
16+
17+
export function findProjectRoot(): string {
18+
const start = path.dirname(fileURLToPath(import.meta.url));
19+
let current = start;
20+
for (let i = 0; i < 6; i += 1) {
21+
const pkgPath = path.join(current, 'package.json');
22+
if (fs.existsSync(pkgPath)) return current;
23+
current = path.dirname(current);
24+
}
25+
return start;
26+
}

test/integration/smoke-cli.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,18 @@ test('cli --help returns usage', () => {
1717
assert.match(result.stdout, /agent-device/i);
1818
});
1919

20+
test('cli --version prints semver and exits 0', () => {
21+
const result = runCli(['--version']);
22+
assert.equal(result.status, 0, result.stderr);
23+
assert.match(result.stdout, /^\d+\.\d+\.\d+/i);
24+
});
25+
26+
test('cli -V prints semver and exits 0', () => {
27+
const result = runCli(['-V']);
28+
assert.equal(result.status, 0, result.stderr);
29+
assert.match(result.stdout, /^\d+\.\d+\.\d+/i);
30+
});
31+
2032
test('cli without command prints usage and exits 1', () => {
2133
const result = runCli([]);
2234
assert.equal(result.status, 1, result.stderr);

0 commit comments

Comments
 (0)