@crosshands/cli 0.1.6 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +3 -3
- package/dist/bin.js.map +1 -1
- package/dist/broker-host.d.ts +2 -1
- package/dist/broker-host.d.ts.map +1 -1
- package/dist/broker-host.js +65 -40
- package/dist/broker-host.js.map +1 -1
- package/dist/broker-result.d.ts +7 -0
- package/dist/broker-result.d.ts.map +1 -0
- package/dist/broker-result.js +10 -0
- package/dist/broker-result.js.map +1 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +60 -32
- package/dist/index.js.map +1 -1
- package/dist/intent/dispatch.d.ts +13 -0
- package/dist/intent/dispatch.d.ts.map +1 -0
- package/dist/intent/dispatch.js +277 -0
- package/dist/intent/dispatch.js.map +1 -0
- package/dist/intent/env.d.ts +11 -0
- package/dist/intent/env.d.ts.map +1 -0
- package/dist/intent/env.js +10 -0
- package/dist/intent/env.js.map +1 -0
- package/dist/intent/evaluate.d.ts +31 -0
- package/dist/intent/evaluate.d.ts.map +1 -0
- package/dist/intent/evaluate.js +156 -0
- package/dist/intent/evaluate.js.map +1 -0
- package/dist/intent/http.d.ts +8 -0
- package/dist/intent/http.d.ts.map +1 -0
- package/dist/intent/http.js +4 -0
- package/dist/intent/http.js.map +1 -0
- package/dist/intent/log.d.ts +82 -0
- package/dist/intent/log.d.ts.map +1 -0
- package/dist/intent/log.js +124 -0
- package/dist/intent/log.js.map +1 -0
- package/dist/intent/tree.d.ts +13 -0
- package/dist/intent/tree.d.ts.map +1 -0
- package/dist/intent/tree.js +99 -0
- package/dist/intent/tree.js.map +1 -0
- package/dist/local-client.d.ts +1 -0
- package/dist/local-client.d.ts.map +1 -1
- package/dist/local-client.js +23 -15
- package/dist/local-client.js.map +1 -1
- package/package.json +6 -6
- package/src/bin.ts +4 -3
- package/src/broker-host.ts +68 -38
- package/src/broker-result.ts +14 -0
- package/src/index.ts +71 -35
- package/src/intent/dispatch.ts +373 -0
- package/src/intent/env.ts +13 -0
- package/src/intent/evaluate.ts +211 -0
- package/src/intent/http.ts +10 -0
- package/src/intent/log.ts +219 -0
- package/src/intent/tree.ts +108 -0
- package/src/local-client.ts +31 -15
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { JsonlFileWriter, diagnosticsDirectory } from '@crosshands/runtime';
|
|
3
|
+
import { localClientPaths } from '../local-client.js';
|
|
4
|
+
import { parseJevEnv } from './env.js';
|
|
5
|
+
import { elapsedMs } from './http.js';
|
|
6
|
+
export function hashGoal(goal) {
|
|
7
|
+
return createHash('sha256').update(goal).digest('hex');
|
|
8
|
+
}
|
|
9
|
+
const OMIT = new Set(['goal', 'apiKey', 'treeText', 'text', 'value', 'TYPESAFE_API_KEY']);
|
|
10
|
+
export function createJevCall(operation, goal) {
|
|
11
|
+
return {
|
|
12
|
+
callId: randomUUID(),
|
|
13
|
+
operation,
|
|
14
|
+
...(goal === undefined ? {} : { goal }),
|
|
15
|
+
ranks: 0,
|
|
16
|
+
httpMs: 0,
|
|
17
|
+
brokerCalls: 0
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function recordRank(log, call, phase, stats, suggestion) {
|
|
21
|
+
if (call !== undefined) {
|
|
22
|
+
call.phase = phase;
|
|
23
|
+
call.ranks += 1;
|
|
24
|
+
if (stats.http !== undefined)
|
|
25
|
+
call.httpMs += stats.http.durationMs;
|
|
26
|
+
}
|
|
27
|
+
if (log === undefined || call === undefined)
|
|
28
|
+
return;
|
|
29
|
+
const shared = {
|
|
30
|
+
callId: call.callId,
|
|
31
|
+
operation: call.operation,
|
|
32
|
+
snapshotId: stats.snapshotId,
|
|
33
|
+
phase,
|
|
34
|
+
...(call.goal === undefined ? {} : { goal: call.goal })
|
|
35
|
+
};
|
|
36
|
+
if (stats.http !== undefined) {
|
|
37
|
+
log.emit({
|
|
38
|
+
kind: 'jev.http',
|
|
39
|
+
...shared,
|
|
40
|
+
status: stats.http.status,
|
|
41
|
+
durationMs: stats.http.durationMs,
|
|
42
|
+
requestBytes: stats.http.requestBytes,
|
|
43
|
+
responseBytes: stats.http.responseBytes,
|
|
44
|
+
candidateCount: stats.candidateCount
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (suggestion === undefined)
|
|
48
|
+
return;
|
|
49
|
+
const move = suggestion.move;
|
|
50
|
+
log.emit({
|
|
51
|
+
kind: 'jev.decision',
|
|
52
|
+
...shared,
|
|
53
|
+
...(stats.app === undefined ? {} : { app: stats.app }),
|
|
54
|
+
move: move.kind,
|
|
55
|
+
...(move.kind === 'click' ||
|
|
56
|
+
move.kind === 'setValue' ||
|
|
57
|
+
move.kind === 'secondary' ||
|
|
58
|
+
move.kind === 'scroll'
|
|
59
|
+
? { elementIndex: move.elementIndex }
|
|
60
|
+
: {}),
|
|
61
|
+
...(move.kind === 'blocked' ? { reason: move.reason } : {}),
|
|
62
|
+
...(suggestion.confidence === undefined ? {} : { confidence: suggestion.confidence }),
|
|
63
|
+
goalChars: stats.goalChars,
|
|
64
|
+
treeChars: stats.treeChars,
|
|
65
|
+
elementCount: stats.elementCount,
|
|
66
|
+
candidateCount: stats.candidateCount,
|
|
67
|
+
parseMs: stats.parseMs,
|
|
68
|
+
...(stats.evaluateMs === undefined ? {} : { evaluateMs: stats.evaluateMs })
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
export function emitCall(log, call, started, extra = {}) {
|
|
72
|
+
if (log === undefined || call === undefined)
|
|
73
|
+
return;
|
|
74
|
+
log.emit({
|
|
75
|
+
kind: 'jev.call',
|
|
76
|
+
callId: call.callId,
|
|
77
|
+
operation: call.operation,
|
|
78
|
+
durationMs: elapsedMs(started),
|
|
79
|
+
httpMs: call.httpMs,
|
|
80
|
+
brokerCalls: call.brokerCalls,
|
|
81
|
+
ranks: call.ranks,
|
|
82
|
+
...(call.phase === undefined ? {} : { phase: call.phase }),
|
|
83
|
+
...(extra.bound === undefined ? {} : { bound: extra.bound }),
|
|
84
|
+
...(extra.error === undefined ? {} : { error: extra.error }),
|
|
85
|
+
...(call.goal === undefined ? {} : { goal: call.goal })
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
export function createJevLogger(graphicalSessionId, env = process.env) {
|
|
89
|
+
const writer = new JsonlFileWriter({
|
|
90
|
+
directory: diagnosticsDirectory(graphicalSessionId, { env }),
|
|
91
|
+
generation: `jev-${randomUUID()}`
|
|
92
|
+
});
|
|
93
|
+
writer.start();
|
|
94
|
+
return {
|
|
95
|
+
emit(record) {
|
|
96
|
+
try {
|
|
97
|
+
const raw = record;
|
|
98
|
+
const goal = raw.goal;
|
|
99
|
+
const rest = {
|
|
100
|
+
...raw,
|
|
101
|
+
kind: record.kind,
|
|
102
|
+
v: 1,
|
|
103
|
+
ts: new Date().toISOString()
|
|
104
|
+
};
|
|
105
|
+
for (const key of OMIT)
|
|
106
|
+
delete rest[key];
|
|
107
|
+
writer.emit({
|
|
108
|
+
...rest,
|
|
109
|
+
...(typeof goal === 'string' ? { goalSha256: hashGoal(goal) } : {})
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Jev logs must not fail computer-use.
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
close: () => writer.close()
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
export function createCliJevLogger(env = process.env) {
|
|
120
|
+
if (parseJevEnv(env).kind === 'off')
|
|
121
|
+
return undefined;
|
|
122
|
+
return createJevLogger(localClientPaths().identity.graphicalSessionId, env);
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/intent/log.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type TreeMove = {
|
|
2
|
+
elementIndex: number;
|
|
3
|
+
role: string;
|
|
4
|
+
label: string;
|
|
5
|
+
line: string;
|
|
6
|
+
disabled: boolean;
|
|
7
|
+
clickable: boolean;
|
|
8
|
+
settable: boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare function parseTreeMoves(treeText: string): TreeMove[];
|
|
11
|
+
export declare const JEV_CHOICE_CAP = 255;
|
|
12
|
+
export declare function clickableMoves(moves: readonly TreeMove[]): TreeMove[];
|
|
13
|
+
//# sourceMappingURL=tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../src/intent/tree.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;IACjB,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,OAAO,CAAA;CAClB,CAAA;AAgED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE,CA2B3D;AAED,eAAO,MAAM,cAAc,MAAM,CAAA;AAEjC,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAIrE"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const ROW_START = /^(\t*)(\d+) (.+)$/;
|
|
2
|
+
const MULTI_WORD_ROLES = [
|
|
3
|
+
'search text field',
|
|
4
|
+
'text field',
|
|
5
|
+
'text area',
|
|
6
|
+
'menu button',
|
|
7
|
+
'pop up button',
|
|
8
|
+
'popup button',
|
|
9
|
+
'radio button',
|
|
10
|
+
'check box',
|
|
11
|
+
'combo box',
|
|
12
|
+
'disclosure triangle',
|
|
13
|
+
'standard window',
|
|
14
|
+
'scroll area',
|
|
15
|
+
'split group',
|
|
16
|
+
'outline row',
|
|
17
|
+
'table row'
|
|
18
|
+
];
|
|
19
|
+
const CLICKABLE_ROLE = new Set([
|
|
20
|
+
'button',
|
|
21
|
+
'link',
|
|
22
|
+
'checkbox',
|
|
23
|
+
'check box',
|
|
24
|
+
'radio button',
|
|
25
|
+
'radio',
|
|
26
|
+
'tab',
|
|
27
|
+
'menu button',
|
|
28
|
+
'pop up button',
|
|
29
|
+
'popup button',
|
|
30
|
+
'combo box',
|
|
31
|
+
'disclosure triangle'
|
|
32
|
+
]);
|
|
33
|
+
const SETTABLE_ROLE = new Set(['text field', 'search text field', 'text area', 'combo box']);
|
|
34
|
+
function isHeaderOrFooter(line) {
|
|
35
|
+
if (line.length === 0)
|
|
36
|
+
return true;
|
|
37
|
+
if (line.startsWith('App='))
|
|
38
|
+
return true;
|
|
39
|
+
if (line.startsWith('Window:'))
|
|
40
|
+
return true;
|
|
41
|
+
if (line.startsWith('The focused UI element'))
|
|
42
|
+
return true;
|
|
43
|
+
if (line.startsWith('No UI element'))
|
|
44
|
+
return true;
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
function roleAndLabel(body) {
|
|
48
|
+
const lower = body.toLowerCase();
|
|
49
|
+
const multi = MULTI_WORD_ROLES.find((role) => lower.startsWith(role) && (body.length === role.length || /[\s,(]/.test(body[role.length])));
|
|
50
|
+
if (multi !== undefined) {
|
|
51
|
+
return {
|
|
52
|
+
role: body.slice(0, multi.length),
|
|
53
|
+
label: body.slice(multi.length).replace(/^[\s,]+/, '')
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const comma = body.indexOf(',');
|
|
57
|
+
const head = (comma === -1 ? body : body.slice(0, comma)).trim();
|
|
58
|
+
const space = head.indexOf(' ');
|
|
59
|
+
if (space === -1)
|
|
60
|
+
return { role: head, label: '' };
|
|
61
|
+
return { role: head.slice(0, space), label: head.slice(space + 1).trim() };
|
|
62
|
+
}
|
|
63
|
+
export function parseTreeMoves(treeText) {
|
|
64
|
+
const rows = [];
|
|
65
|
+
for (const line of treeText.split('\n')) {
|
|
66
|
+
const match = ROW_START.exec(line);
|
|
67
|
+
if (match !== null && match[2] !== undefined && match[3] !== undefined) {
|
|
68
|
+
rows.push({ elementIndex: Number(match[2]), body: match[3] });
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const previous = rows[rows.length - 1];
|
|
72
|
+
if (previous !== undefined && !isHeaderOrFooter(line))
|
|
73
|
+
previous.body += `\n${line}`;
|
|
74
|
+
}
|
|
75
|
+
return rows.map((row) => {
|
|
76
|
+
const { role, label } = roleAndLabel(row.body);
|
|
77
|
+
const disabled = /\(disabled\)/.test(row.body);
|
|
78
|
+
const roleKey = role.toLowerCase();
|
|
79
|
+
const settable = /\(settable\)/.test(row.body) || SETTABLE_ROLE.has(roleKey);
|
|
80
|
+
const clickable = !disabled && (CLICKABLE_ROLE.has(roleKey) || settable);
|
|
81
|
+
return {
|
|
82
|
+
elementIndex: row.elementIndex,
|
|
83
|
+
role,
|
|
84
|
+
label,
|
|
85
|
+
line: `${row.elementIndex} ${row.body}`,
|
|
86
|
+
disabled,
|
|
87
|
+
clickable,
|
|
88
|
+
settable
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
export const JEV_CHOICE_CAP = 255;
|
|
93
|
+
export function clickableMoves(moves) {
|
|
94
|
+
const selected = moves.filter((move) => move.clickable);
|
|
95
|
+
if (selected.length <= JEV_CHOICE_CAP)
|
|
96
|
+
return selected;
|
|
97
|
+
return selected.slice(0, JEV_CHOICE_CAP);
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree.js","sourceRoot":"","sources":["../../src/intent/tree.ts"],"names":[],"mappings":"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"}
|
package/dist/local-client.d.ts
CHANGED
|
@@ -13,5 +13,6 @@ export type ProductionClientOptions = {
|
|
|
13
13
|
spawnBroker?: (entrypoint: string) => void | Promise<void>;
|
|
14
14
|
paths?: LocalClientPaths;
|
|
15
15
|
};
|
|
16
|
+
export declare function brokerSpawnEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
16
17
|
export declare function createProductionBrokerClient(options?: ProductionClientOptions): Promise<CliBrokerClient>;
|
|
17
18
|
//# sourceMappingURL=local-client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-client.d.ts","sourceRoot":"","sources":["../src/local-client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"local-client.d.ts","sourceRoot":"","sources":["../src/local-client.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,cAAc,EAGd,KAAK,oBAAoB,EAC1B,MAAM,qBAAqB,CAAA;AAG5B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,oBAAoB,CAAA;IAC9B,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAA;CAC5C,CAAA;AAED,wBAAgB,gBAAgB,IAAI,gBAAgB,CA2BnD;AAwDD,MAAM,MAAM,uBAAuB,GAAG;IACpC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1D,KAAK,CAAC,EAAE,gBAAgB,CAAA;CACzB,CAAA;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAOxE;AAkBD,wBAAsB,4BAA4B,CAChD,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,eAAe,CAAC,CAqC1B"}
|
package/dist/local-client.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import {
|
|
3
|
-
import { chmod, lstat, mkdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import { lstat, readFile } from 'node:fs/promises';
|
|
4
3
|
import { homedir } from 'node:os';
|
|
5
4
|
import { isAbsolute, join, resolve } from 'node:path';
|
|
6
5
|
import { CONTRACT_VERSIONS, createComputerError } from '@crosshands/contract';
|
|
7
|
-
import { LocalControlClient, brokerEndpoint } from '@crosshands/runtime';
|
|
6
|
+
import { LocalControlClient, brokerEndpoint, ensurePrivateDirectory, graphicalSessionKey } from '@crosshands/runtime';
|
|
7
|
+
import { unwrapBrokerResult } from './broker-result.js';
|
|
8
8
|
export function localClientPaths() {
|
|
9
9
|
const uid = process.getuid?.();
|
|
10
10
|
const osIdentity = process.env.CROSSHANDS_OS_IDENTITY ??
|
|
@@ -16,7 +16,7 @@ export function localClientPaths() {
|
|
|
16
16
|
process.env.SECURITYSESSIONID ??
|
|
17
17
|
process.env.SESSIONNAME ??
|
|
18
18
|
`interactive:${osIdentity}`;
|
|
19
|
-
const sessionKey =
|
|
19
|
+
const sessionKey = graphicalSessionKey(graphicalSessionId);
|
|
20
20
|
const runtimeDirectory = process.env.CROSSHANDS_RUNTIME_DIR ?? defaultRuntimeDirectory(sessionKey);
|
|
21
21
|
const identity = { osIdentity, graphicalSessionId };
|
|
22
22
|
return {
|
|
@@ -48,14 +48,13 @@ function defaultRuntimeDirectory(sessionKey) {
|
|
|
48
48
|
}
|
|
49
49
|
return join(localAppData, 'CrossHands', 'runtime', sessionKey);
|
|
50
50
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
await chmod(path, 0o700);
|
|
51
|
+
function prepareRuntimeDirectory(path) {
|
|
52
|
+
try {
|
|
53
|
+
ensurePrivateDirectory(path);
|
|
54
|
+
}
|
|
55
|
+
catch (cause) {
|
|
56
|
+
throw createComputerError('provider_unavailable', cause instanceof Error ? cause.message : 'CrossHands directory is unsafe');
|
|
57
|
+
}
|
|
59
58
|
}
|
|
60
59
|
async function readSecureToken(path) {
|
|
61
60
|
const info = await lstat(path);
|
|
@@ -74,12 +73,21 @@ async function connect(paths) {
|
|
|
74
73
|
identity: paths.identity
|
|
75
74
|
});
|
|
76
75
|
}
|
|
76
|
+
export function brokerSpawnEnv(env) {
|
|
77
|
+
const next = { ...env };
|
|
78
|
+
delete next.CROSSHANDS_JEV;
|
|
79
|
+
for (const key of Object.keys(next)) {
|
|
80
|
+
if (key === 'TYPESAFE_API_KEY' || key.startsWith('TYPESAFE_'))
|
|
81
|
+
delete next[key];
|
|
82
|
+
}
|
|
83
|
+
return next;
|
|
84
|
+
}
|
|
77
85
|
function defaultSpawnBroker(entrypoint) {
|
|
78
86
|
const child = spawn(process.execPath, [entrypoint, 'broker'], {
|
|
79
87
|
detached: true,
|
|
80
88
|
stdio: 'ignore',
|
|
81
89
|
windowsHide: true,
|
|
82
|
-
env: process.env
|
|
90
|
+
env: brokerSpawnEnv(process.env)
|
|
83
91
|
});
|
|
84
92
|
child.unref();
|
|
85
93
|
}
|
|
@@ -91,7 +99,7 @@ function brokerIsAbsent(cause) {
|
|
|
91
99
|
}
|
|
92
100
|
export async function createProductionBrokerClient(options = {}) {
|
|
93
101
|
const paths = options.paths ?? localClientPaths();
|
|
94
|
-
|
|
102
|
+
prepareRuntimeDirectory(paths.runtimeDirectory);
|
|
95
103
|
try {
|
|
96
104
|
const control = await connect(paths);
|
|
97
105
|
return controlAdapter(control);
|
|
@@ -124,7 +132,7 @@ export async function createProductionBrokerClient(options = {}) {
|
|
|
124
132
|
}
|
|
125
133
|
function controlAdapter(control) {
|
|
126
134
|
return {
|
|
127
|
-
request: async (operation, input) => control.request({ operation, input }, { deadlineMs: 30_000 }),
|
|
135
|
+
request: async (operation, input) => unwrapBrokerResult(await control.request({ operation, input }, { deadlineMs: 30_000 })),
|
|
128
136
|
close: () => control.close()
|
|
129
137
|
};
|
|
130
138
|
}
|
package/dist/local-client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-client.js","sourceRoot":"","sources":["../src/local-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAC1C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"local-client.js","sourceRoot":"","sources":["../src/local-client.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crosshands/cli",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Agent-agnostic local computer-use CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"access": "public"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@crosshands/
|
|
27
|
-
"@crosshands/
|
|
26
|
+
"@crosshands/runtime": "0.2.1",
|
|
27
|
+
"@crosshands/contract": "0.2.1"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "24.10.1",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"vitest": "4.1.5"
|
|
33
33
|
},
|
|
34
34
|
"optionalDependencies": {
|
|
35
|
-
"@crosshands/platform-darwin": "0.1
|
|
36
|
-
"@crosshands/platform-windows": "0.1
|
|
37
|
-
"@crosshands/platform-linux": "0.1
|
|
35
|
+
"@crosshands/platform-darwin": "0.2.1",
|
|
36
|
+
"@crosshands/platform-windows": "0.2.1",
|
|
37
|
+
"@crosshands/platform-linux": "0.2.1"
|
|
38
38
|
},
|
|
39
39
|
"engines": {
|
|
40
40
|
"node": ">=22"
|
package/src/bin.ts
CHANGED
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
import { stdin, stderr, stdout } from 'node:process'
|
|
4
4
|
import { StringDecoder } from 'node:string_decoder'
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import { runCli, type CliIo } from './index.js'
|
|
8
|
-
import { createProductionBrokerClient } from './local-client.js'
|
|
6
|
+
import type { CliIo } from './index.js'
|
|
9
7
|
|
|
10
8
|
async function readStdin(): Promise<string> {
|
|
11
9
|
const decoder = new StringDecoder('utf8')
|
|
@@ -25,9 +23,12 @@ async function readStdin(): Promise<string> {
|
|
|
25
23
|
async function main(): Promise<number> {
|
|
26
24
|
const argv = process.argv.slice(2)
|
|
27
25
|
if (argv[0] === 'broker') {
|
|
26
|
+
const { runBrokerHost } = await import('./broker-host.js')
|
|
28
27
|
await runBrokerHost()
|
|
29
28
|
return 0
|
|
30
29
|
}
|
|
30
|
+
const { runCli } = await import('./index.js')
|
|
31
|
+
const { createProductionBrokerClient } = await import('./local-client.js')
|
|
31
32
|
const io: CliIo = {
|
|
32
33
|
stdin: readStdin,
|
|
33
34
|
stdout: (value) => stdout.write(value),
|
package/src/broker-host.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto'
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
CONTRACT_VERSIONS,
|
|
3
5
|
createComputerError,
|
|
@@ -7,9 +9,13 @@ import {
|
|
|
7
9
|
type ReferenceBindings
|
|
8
10
|
} from '@crosshands/contract'
|
|
9
11
|
import {
|
|
12
|
+
JsonlDiagnosticsWriter,
|
|
10
13
|
LocalBroker,
|
|
11
14
|
LocalControlServer,
|
|
15
|
+
diagnosticsDirectory,
|
|
16
|
+
emitDiagnostic,
|
|
12
17
|
type BrokerEndpoint,
|
|
18
|
+
type ControlHandshakeEvent,
|
|
13
19
|
type LocalControlIdentity,
|
|
14
20
|
type LocalControlRequest,
|
|
15
21
|
type StableAppIdentity,
|
|
@@ -25,6 +31,7 @@ type ProviderModule = {
|
|
|
25
31
|
endpoint: BrokerEndpoint
|
|
26
32
|
identity: LocalControlIdentity
|
|
27
33
|
handler: (request: LocalControlRequest) => Promise<unknown>
|
|
34
|
+
onHandshake?: (event: ControlHandshakeEvent) => void
|
|
28
35
|
}) => Promise<{ start(): Promise<void>; close(): Promise<void> }>
|
|
29
36
|
inspectTarget?: (
|
|
30
37
|
operation: ComputerOperationName,
|
|
@@ -76,9 +83,16 @@ export async function runBrokerHost(): Promise<void> {
|
|
|
76
83
|
const providerModule = await loadProviderModule()
|
|
77
84
|
const paths = localClientPaths()
|
|
78
85
|
const peer = { ...paths.identity, verified: true, local: true }
|
|
86
|
+
const generation = `broker-${randomUUID()}`
|
|
87
|
+
const diagnostics = new JsonlDiagnosticsWriter({
|
|
88
|
+
directory: diagnosticsDirectory(paths.identity.graphicalSessionId),
|
|
89
|
+
generation
|
|
90
|
+
})
|
|
79
91
|
const broker = new LocalBroker({
|
|
80
92
|
identity: peer,
|
|
93
|
+
generation,
|
|
81
94
|
providerFactory: () => providerModule.createProvider(),
|
|
95
|
+
diagnostics,
|
|
82
96
|
...(providerModule.inspectTarget === undefined
|
|
83
97
|
? {}
|
|
84
98
|
: {
|
|
@@ -88,45 +102,61 @@ export async function runBrokerHost(): Promise<void> {
|
|
|
88
102
|
): Promise<TargetInspection | null> => providerModule.inspectTarget!(operation, input)
|
|
89
103
|
})
|
|
90
104
|
})
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
105
|
+
try {
|
|
106
|
+
await broker.connect({ peer, versions: CONTRACT_VERSIONS })
|
|
107
|
+
const handler = async ({ payload, deadlineAt }: LocalControlRequest): Promise<unknown> => {
|
|
108
|
+
if (payload === null || typeof payload !== 'object') throw new Error('Invalid broker request')
|
|
109
|
+
const record = payload as Record<string, unknown>
|
|
110
|
+
if (typeof record.operation !== 'string') throw new Error('Missing operation')
|
|
111
|
+
const operation = record.operation as ComputerOperationName
|
|
112
|
+
let input: unknown
|
|
113
|
+
try {
|
|
114
|
+
input = parseOperationInput(operation, record.input)
|
|
115
|
+
} catch (cause) {
|
|
116
|
+
if (cause instanceof Error && cause.name === 'ZodError') {
|
|
117
|
+
throw createComputerError('invalid_argument', 'Invalid input for the selected operation')
|
|
118
|
+
}
|
|
119
|
+
throw cause
|
|
103
120
|
}
|
|
104
|
-
|
|
121
|
+
return broker.request({ operation, input, deadlineMs: Math.max(1, deadlineAt - Date.now()) })
|
|
105
122
|
}
|
|
106
|
-
|
|
123
|
+
const onHandshake = (event: ControlHandshakeEvent): void => {
|
|
124
|
+
emitDiagnostic(diagnostics, {
|
|
125
|
+
kind: 'client.handshake',
|
|
126
|
+
...broker.diagnosticEnvelope(),
|
|
127
|
+
accepted: event.accepted,
|
|
128
|
+
requestId: event.requestId,
|
|
129
|
+
...(event.code === undefined ? {} : { code: event.code })
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
const server =
|
|
133
|
+
paths.endpoint.transport === 'named-pipe'
|
|
134
|
+
? await (providerModule.createControlServer?.({
|
|
135
|
+
endpoint: paths.endpoint,
|
|
136
|
+
identity: paths.identity,
|
|
137
|
+
handler,
|
|
138
|
+
onHandshake
|
|
139
|
+
}) ??
|
|
140
|
+
Promise.reject(
|
|
141
|
+
new Error('The Windows payload has no native DACL and peer-token control relay')
|
|
142
|
+
))
|
|
143
|
+
: new LocalControlServer({
|
|
144
|
+
endpoint: paths.endpoint,
|
|
145
|
+
runtimeDirectory: paths.runtimeDirectory,
|
|
146
|
+
tokenFile: paths.tokenFile,
|
|
147
|
+
identity: paths.identity,
|
|
148
|
+
handler,
|
|
149
|
+
onHandshake
|
|
150
|
+
})
|
|
151
|
+
await server.start()
|
|
152
|
+
emitDiagnostic(diagnostics, { kind: 'broker.start', ...broker.diagnosticEnvelope() })
|
|
153
|
+
await new Promise<void>((resolve) => {
|
|
154
|
+
const stop = (): void => resolve()
|
|
155
|
+
process.once('SIGINT', stop)
|
|
156
|
+
process.once('SIGTERM', stop)
|
|
157
|
+
})
|
|
158
|
+
await server.close()
|
|
159
|
+
} finally {
|
|
160
|
+
await broker.close('signal')
|
|
107
161
|
}
|
|
108
|
-
const server =
|
|
109
|
-
paths.endpoint.transport === 'named-pipe'
|
|
110
|
-
? await (providerModule.createControlServer?.({
|
|
111
|
-
endpoint: paths.endpoint,
|
|
112
|
-
identity: paths.identity,
|
|
113
|
-
handler
|
|
114
|
-
}) ??
|
|
115
|
-
Promise.reject(
|
|
116
|
-
new Error('The Windows payload has no native DACL and peer-token control relay')
|
|
117
|
-
))
|
|
118
|
-
: new LocalControlServer({
|
|
119
|
-
endpoint: paths.endpoint,
|
|
120
|
-
runtimeDirectory: paths.runtimeDirectory,
|
|
121
|
-
tokenFile: paths.tokenFile,
|
|
122
|
-
identity: paths.identity,
|
|
123
|
-
handler
|
|
124
|
-
})
|
|
125
|
-
await server.start()
|
|
126
|
-
await new Promise<void>((resolve) => {
|
|
127
|
-
const stop = (): void => resolve()
|
|
128
|
-
process.once('SIGINT', stop)
|
|
129
|
-
process.once('SIGTERM', stop)
|
|
130
|
-
})
|
|
131
|
-
await server.close()
|
|
132
162
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type BrokerResultEnvelope = {
|
|
2
|
+
requestId: string
|
|
3
|
+
result: unknown
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function isBrokerResultEnvelope(value: unknown): value is BrokerResultEnvelope {
|
|
7
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) return false
|
|
8
|
+
const record = value as Record<string, unknown>
|
|
9
|
+
return typeof record.requestId === 'string' && 'result' in record
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function unwrapBrokerResult(value: unknown): unknown {
|
|
13
|
+
return isBrokerResultEnvelope(value) ? value.result : value
|
|
14
|
+
}
|