@farmslot/agent-runtime 0.9.0 → 0.10.0
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/CHANGELOG.md +16 -1
- package/README.md +1 -1
- package/bin/farmslot-agent.mjs +1 -1
- package/dist/execution-template/create.d.ts.map +1 -1
- package/dist/execution-template/create.js +4 -9
- package/dist/execution-template/create.js.map +1 -1
- package/dist/execution-template/execution-template.test.js +85 -34
- package/dist/execution-template/execution-template.test.js.map +1 -1
- package/dist/execution-template/frontmatter.d.ts +6 -3
- package/dist/execution-template/frontmatter.d.ts.map +1 -1
- package/dist/execution-template/frontmatter.js +8 -10
- package/dist/execution-template/frontmatter.js.map +1 -1
- package/dist/execution-template/index.d.ts +2 -2
- package/dist/execution-template/index.d.ts.map +1 -1
- package/dist/execution-template/index.js +2 -2
- package/dist/execution-template/index.js.map +1 -1
- package/dist/execution-template/infer.d.ts +2 -3
- package/dist/execution-template/infer.d.ts.map +1 -1
- package/dist/execution-template/infer.js +2 -19
- package/dist/execution-template/infer.js.map +1 -1
- package/dist/execution-template/lint.d.ts.map +1 -1
- package/dist/execution-template/lint.js +12 -14
- package/dist/execution-template/lint.js.map +1 -1
- package/dist/execution-template/resolve.d.ts.map +1 -1
- package/dist/execution-template/resolve.js +0 -2
- package/dist/execution-template/resolve.js.map +1 -1
- package/dist/execution-template/select.d.ts.map +1 -1
- package/dist/execution-template/select.js +4 -8
- package/dist/execution-template/select.js.map +1 -1
- package/dist/execution-template/snapshot.d.ts.map +1 -1
- package/dist/execution-template/snapshot.js +0 -1
- package/dist/execution-template/snapshot.js.map +1 -1
- package/dist/execution-template/types.d.ts +10 -5
- package/dist/execution-template/types.d.ts.map +1 -1
- package/dist/execution-template/types.js.map +1 -1
- package/dist/native/account-claude.d.ts +5 -0
- package/dist/native/account-claude.d.ts.map +1 -0
- package/dist/native/account-claude.js +42 -0
- package/dist/native/account-claude.js.map +1 -0
- package/dist/native/account-codex.d.ts +4 -0
- package/dist/native/account-codex.d.ts.map +1 -0
- package/dist/native/account-codex.js +51 -0
- package/dist/native/account-codex.js.map +1 -0
- package/dist/native/account-common.d.ts +30 -0
- package/dist/native/account-common.d.ts.map +1 -0
- package/dist/native/account-common.js +155 -0
- package/dist/native/account-common.js.map +1 -0
- package/dist/native/account-cursor.d.ts +5 -0
- package/dist/native/account-cursor.d.ts.map +1 -0
- package/dist/native/account-cursor.js +48 -0
- package/dist/native/account-cursor.js.map +1 -0
- package/dist/native/account-grok.d.ts +4 -0
- package/dist/native/account-grok.d.ts.map +1 -0
- package/dist/native/account-grok.js +55 -0
- package/dist/native/account-grok.js.map +1 -0
- package/dist/native/account-observation.test.d.ts +2 -0
- package/dist/native/account-observation.test.d.ts.map +1 -0
- package/dist/native/account-observation.test.js +256 -0
- package/dist/native/account-observation.test.js.map +1 -0
- package/dist/native/account-profiles.d.ts +11 -0
- package/dist/native/account-profiles.d.ts.map +1 -0
- package/dist/native/account-profiles.js +172 -0
- package/dist/native/account-profiles.js.map +1 -0
- package/dist/native/account-profiles.test.d.ts +2 -0
- package/dist/native/account-profiles.test.d.ts.map +1 -0
- package/dist/native/account-profiles.test.js +172 -0
- package/dist/native/account-profiles.test.js.map +1 -0
- package/dist/native/account-types.d.ts +11 -0
- package/dist/native/account-types.d.ts.map +1 -0
- package/dist/native/account-types.js +2 -0
- package/dist/native/account-types.js.map +1 -0
- package/dist/native/acp-requests.d.ts +11 -0
- package/dist/native/acp-requests.d.ts.map +1 -0
- package/dist/native/acp-requests.js +109 -0
- package/dist/native/acp-requests.js.map +1 -0
- package/dist/native/acp-rpc.d.ts +15 -0
- package/dist/native/acp-rpc.d.ts.map +1 -0
- package/dist/native/acp-rpc.js +73 -0
- package/dist/native/acp-rpc.js.map +1 -0
- package/dist/native/acp.d.ts +15 -0
- package/dist/native/acp.d.ts.map +1 -0
- package/dist/native/acp.js +285 -0
- package/dist/native/acp.js.map +1 -0
- package/dist/native/acp.test.d.ts +2 -0
- package/dist/native/acp.test.d.ts.map +1 -0
- package/dist/native/acp.test.js +257 -0
- package/dist/native/acp.test.js.map +1 -0
- package/dist/native/claude.d.ts.map +1 -1
- package/dist/native/claude.js +14 -0
- package/dist/native/claude.js.map +1 -1
- package/dist/native/client.d.ts +13 -0
- package/dist/native/client.d.ts.map +1 -1
- package/dist/native/client.js +112 -1
- package/dist/native/client.js.map +1 -1
- package/dist/native/codex.d.ts.map +1 -1
- package/dist/native/codex.js +61 -3
- package/dist/native/codex.js.map +1 -1
- package/dist/native/cursor-model.d.ts +6 -0
- package/dist/native/cursor-model.d.ts.map +1 -0
- package/dist/native/cursor-model.js +74 -0
- package/dist/native/cursor-model.js.map +1 -0
- package/dist/native/cursor-model.test.d.ts +2 -0
- package/dist/native/cursor-model.test.d.ts.map +1 -0
- package/dist/native/cursor-model.test.js +40 -0
- package/dist/native/cursor-model.test.js.map +1 -0
- package/dist/native/cursor.d.ts +2 -0
- package/dist/native/cursor.d.ts.map +1 -0
- package/dist/native/cursor.js +30 -0
- package/dist/native/cursor.js.map +1 -0
- package/dist/native/durability.test.js +461 -1
- package/dist/native/durability.test.js.map +1 -1
- package/dist/native/filesystem-policy.test.d.ts +2 -0
- package/dist/native/filesystem-policy.test.d.ts.map +1 -0
- package/dist/native/filesystem-policy.test.js +136 -0
- package/dist/native/filesystem-policy.test.js.map +1 -0
- package/dist/native/grok.d.ts +2 -0
- package/dist/native/grok.d.ts.map +1 -0
- package/dist/native/grok.js +17 -0
- package/dist/native/grok.js.map +1 -0
- package/dist/native/host.js +39 -0
- package/dist/native/host.js.map +1 -1
- package/dist/native/index.d.ts +3 -0
- package/dist/native/index.d.ts.map +1 -1
- package/dist/native/index.js +3 -0
- package/dist/native/index.js.map +1 -1
- package/dist/native/ipc.d.ts +59 -0
- package/dist/native/ipc.d.ts.map +1 -1
- package/dist/native/ipc.js +76 -4
- package/dist/native/ipc.js.map +1 -1
- package/dist/native/manager.d.ts +22 -4
- package/dist/native/manager.d.ts.map +1 -1
- package/dist/native/manager.js +386 -46
- package/dist/native/manager.js.map +1 -1
- package/dist/native/process-tree.d.ts +11 -0
- package/dist/native/process-tree.d.ts.map +1 -1
- package/dist/native/process-tree.js +123 -20
- package/dist/native/process-tree.js.map +1 -1
- package/dist/native/process-tree.test.js +126 -1
- package/dist/native/process-tree.test.js.map +1 -1
- package/dist/native/process.d.ts +5 -1
- package/dist/native/process.d.ts.map +1 -1
- package/dist/native/process.js +36 -13
- package/dist/native/process.js.map +1 -1
- package/dist/native/process.test.d.ts +2 -0
- package/dist/native/process.test.d.ts.map +1 -0
- package/dist/native/process.test.js +56 -0
- package/dist/native/process.test.js.map +1 -0
- package/dist/native/profile-service.d.ts +5 -0
- package/dist/native/profile-service.d.ts.map +1 -0
- package/dist/native/profile-service.js +76 -0
- package/dist/native/profile-service.js.map +1 -0
- package/dist/native/registry.d.ts +20 -0
- package/dist/native/registry.d.ts.map +1 -0
- package/dist/native/registry.js +89 -0
- package/dist/native/registry.js.map +1 -0
- package/dist/native/registry.test.d.ts +2 -0
- package/dist/native/registry.test.d.ts.map +1 -0
- package/dist/native/registry.test.js +13 -0
- package/dist/native/registry.test.js.map +1 -0
- package/dist/native/review-sandbox.d.ts +12 -0
- package/dist/native/review-sandbox.d.ts.map +1 -0
- package/dist/native/review-sandbox.js +6 -0
- package/dist/native/review-sandbox.js.map +1 -0
- package/dist/native/review-sandbox.test.d.ts +2 -0
- package/dist/native/review-sandbox.test.d.ts.map +1 -0
- package/dist/native/review-sandbox.test.js +40 -0
- package/dist/native/review-sandbox.test.js.map +1 -0
- package/dist/native/service.d.ts.map +1 -1
- package/dist/native/service.js +42 -8
- package/dist/native/service.js.map +1 -1
- package/dist/native/supervisor.js +121 -33
- package/dist/native/supervisor.js.map +1 -1
- package/dist/native/types.d.ts +11 -1
- package/dist/native/types.d.ts.map +1 -1
- package/dist/native/worker-history.d.ts +16 -0
- package/dist/native/worker-history.d.ts.map +1 -0
- package/dist/native/worker-history.js +77 -0
- package/dist/native/worker-history.js.map +1 -0
- package/dist/native/worker-history.test.d.ts +2 -0
- package/dist/native/worker-history.test.d.ts.map +1 -0
- package/dist/native/worker-history.test.js +195 -0
- package/dist/native/worker-history.test.js.map +1 -0
- package/dist/native/worker-launch.d.ts +72 -0
- package/dist/native/worker-launch.d.ts.map +1 -0
- package/dist/native/worker-launch.js +134 -0
- package/dist/native/worker-launch.js.map +1 -0
- package/dist/native/worker-launch.test.d.ts +2 -0
- package/dist/native/worker-launch.test.d.ts.map +1 -0
- package/dist/native/worker-launch.test.js +61 -0
- package/dist/native/worker-launch.test.js.map +1 -0
- package/dist/native/worker-relocation.test.d.ts +2 -0
- package/dist/native/worker-relocation.test.d.ts.map +1 -0
- package/dist/native/worker-relocation.test.js +110 -0
- package/dist/native/worker-relocation.test.js.map +1 -0
- package/dist/native/worker-service.d.ts +4 -0
- package/dist/native/worker-service.d.ts.map +1 -0
- package/dist/native/worker-service.js +85 -0
- package/dist/native/worker-service.js.map +1 -0
- package/dist/task-init/index.d.ts +5 -1
- package/dist/task-init/index.d.ts.map +1 -1
- package/dist/task-init/index.js +7 -3
- package/dist/task-init/index.js.map +1 -1
- package/dist/task-init/task-document.d.ts.map +1 -1
- package/dist/task-init/task-document.js +2 -2
- package/dist/task-init/task-document.js.map +1 -1
- package/dist/task-init/write.d.ts +0 -6
- package/dist/task-init/write.d.ts.map +1 -1
- package/dist/task-init/write.js +1 -8
- package/dist/task-init/write.js.map +1 -1
- package/package.json +2 -2
- package/scripts/cursor-session-discovery.cjs +48 -0
- package/scripts/execution-template-cli.mjs +8 -11
- package/scripts/review-filesystem.cjs +39 -0
- package/scripts/review-session.cjs +45 -0
- package/scripts/review-terminal.cjs +134 -0
- package/scripts/task-init-cli.mjs +3 -3
package/dist/native/process.js
CHANGED
|
@@ -29,7 +29,17 @@ export class JsonLineProcess {
|
|
|
29
29
|
this.closing = false;
|
|
30
30
|
this.pending = new Map();
|
|
31
31
|
const identity = `farmslot-native-${randomUUID()}`;
|
|
32
|
-
this.child = spawn(process.execPath, [
|
|
32
|
+
this.child = spawn(process.execPath, [
|
|
33
|
+
'-e',
|
|
34
|
+
registeredLaunch,
|
|
35
|
+
'--',
|
|
36
|
+
identity,
|
|
37
|
+
...(options.processSandbox
|
|
38
|
+
? [options.processSandbox.executable, ...options.processSandbox.args]
|
|
39
|
+
: []),
|
|
40
|
+
executable,
|
|
41
|
+
...args,
|
|
42
|
+
], {
|
|
33
43
|
cwd: options.cwd,
|
|
34
44
|
env: options.env,
|
|
35
45
|
stdio: 'pipe',
|
|
@@ -96,19 +106,20 @@ export class JsonLineProcess {
|
|
|
96
106
|
try {
|
|
97
107
|
if (this.child.pid) {
|
|
98
108
|
this.tree = new NativeProcessTree(this.child.pid);
|
|
99
|
-
this.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
catch (error) {
|
|
104
|
-
// Loss of process ownership evidence fails this session and its cleanup claim.
|
|
105
|
-
this.beginCleanup(new Error('Native process census failed', { cause: error }));
|
|
106
|
-
}
|
|
107
|
-
}, 100);
|
|
108
|
-
this.treeTimer.unref();
|
|
109
|
+
this.stopObservingTree = this.tree.observe((error) => {
|
|
110
|
+
// Loss of process ownership evidence fails this session and its cleanup claim.
|
|
111
|
+
this.beginCleanup(new Error('Native process census failed', { cause: error }));
|
|
112
|
+
});
|
|
109
113
|
options.onSpawn?.(this.child.pid, identity);
|
|
110
114
|
}
|
|
111
115
|
this.child.stdin.write('\n');
|
|
116
|
+
if (options.signal) {
|
|
117
|
+
const abort = () => this.beginStop();
|
|
118
|
+
options.signal.addEventListener('abort', abort, { once: true });
|
|
119
|
+
this.stopObservingAbort = () => options.signal.removeEventListener('abort', abort);
|
|
120
|
+
if (options.signal.aborted)
|
|
121
|
+
abort();
|
|
122
|
+
}
|
|
112
123
|
}
|
|
113
124
|
catch (error) {
|
|
114
125
|
this.fail(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -177,7 +188,7 @@ export class JsonLineProcess {
|
|
|
177
188
|
this.cleanupStarted = true;
|
|
178
189
|
this.cleanupError = error;
|
|
179
190
|
this.fail(error);
|
|
180
|
-
|
|
191
|
+
this.stopObservingTree?.();
|
|
181
192
|
clearTimeout(this.stopTimer);
|
|
182
193
|
void this.cleanGroup().then(() => this.settle(), (cleanupError) => {
|
|
183
194
|
this.cleanupError = new Error(`Native child cleanup failed: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`, { cause: cleanupError });
|
|
@@ -185,6 +196,7 @@ export class JsonLineProcess {
|
|
|
185
196
|
});
|
|
186
197
|
}
|
|
187
198
|
settle() {
|
|
199
|
+
this.stopObservingAbort?.();
|
|
188
200
|
// Detach host pipe handles even when cleanup failed and a descendant retained them.
|
|
189
201
|
this.child.stdin.destroy();
|
|
190
202
|
this.child.stdout.destroy();
|
|
@@ -210,13 +222,24 @@ export class JsonLineProcess {
|
|
|
210
222
|
this.child.kill('SIGKILL');
|
|
211
223
|
throw new Error('Native process ownership could not be established');
|
|
212
224
|
}
|
|
213
|
-
|
|
225
|
+
let stopError;
|
|
226
|
+
try {
|
|
227
|
+
this.tree.stop();
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
// A group can disappear while shutdown snapshots are being taken. Accept
|
|
231
|
+
// cleanup only after fresh OS evidence proves every tracked process and
|
|
232
|
+
// the original group are absent; otherwise retain the stop failure.
|
|
233
|
+
stopError = error;
|
|
234
|
+
}
|
|
214
235
|
const deadline = Date.now() + 3_000;
|
|
215
236
|
while (Date.now() < deadline) {
|
|
216
237
|
if (this.tree.empty() && (this.child.exitCode !== null || this.child.signalCode !== null))
|
|
217
238
|
return;
|
|
218
239
|
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
219
240
|
}
|
|
241
|
+
if (stopError)
|
|
242
|
+
throw stopError;
|
|
220
243
|
throw new Error('Native child processes did not stop');
|
|
221
244
|
}
|
|
222
245
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/native/process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuC,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/native/process.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process.test.d.ts","sourceRoot":"","sources":["../../src/native/process.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { JsonLineProcess } from './process.js';
|
|
4
|
+
import { NativeProcessTree } from './process-tree.js';
|
|
5
|
+
test('aborting startup rejects pending protocol work and confirms process cleanup', async () => {
|
|
6
|
+
const abort = new AbortController();
|
|
7
|
+
let stopped = false;
|
|
8
|
+
const child = new JsonLineProcess(process.execPath, ['-e', 'setInterval(()=>{},1000)'], { cwd: process.cwd(), signal: abort.signal }, () => { }, (_error, confirmed) => {
|
|
9
|
+
stopped = confirmed;
|
|
10
|
+
});
|
|
11
|
+
const pending = assert.rejects(child.request('initialize', {}), /closed/);
|
|
12
|
+
abort.abort();
|
|
13
|
+
await pending;
|
|
14
|
+
await child.close();
|
|
15
|
+
assert.equal(stopped, true);
|
|
16
|
+
});
|
|
17
|
+
test('a shutdown race is resolved only by fresh process absence after the child exits', async (t) => {
|
|
18
|
+
let ready;
|
|
19
|
+
const started = new Promise((resolve) => {
|
|
20
|
+
ready = resolve;
|
|
21
|
+
});
|
|
22
|
+
let exited;
|
|
23
|
+
const original = NativeProcessTree.prototype.stop;
|
|
24
|
+
t.mock.method(NativeProcessTree.prototype, 'stop', function () {
|
|
25
|
+
original.call(this);
|
|
26
|
+
throw new Error('Shutdown observation raced process exit');
|
|
27
|
+
});
|
|
28
|
+
const child = new JsonLineProcess(process.execPath, ['-e', 'process.stdout.write(JSON.stringify({ready:true})+"\\n");setInterval(()=>{},1000);'], { cwd: process.cwd() }, () => ready(), (error, stopped) => {
|
|
29
|
+
exited = { error, stopped };
|
|
30
|
+
});
|
|
31
|
+
await started;
|
|
32
|
+
await child.close();
|
|
33
|
+
assert.equal(exited?.stopped, true);
|
|
34
|
+
assert.equal(exited?.error, undefined);
|
|
35
|
+
});
|
|
36
|
+
test('a stop error cannot become successful cleanup while process absence remains unverified', async (t) => {
|
|
37
|
+
let ready;
|
|
38
|
+
const started = new Promise((resolve) => {
|
|
39
|
+
ready = resolve;
|
|
40
|
+
});
|
|
41
|
+
let stopped;
|
|
42
|
+
const original = NativeProcessTree.prototype.stop;
|
|
43
|
+
t.mock.method(NativeProcessTree.prototype, 'stop', function () {
|
|
44
|
+
original.call(this);
|
|
45
|
+
throw new Error('Owned process cleanup unconfirmed');
|
|
46
|
+
});
|
|
47
|
+
// The actual child is stopped, but this negative withholds the OS absence proof.
|
|
48
|
+
t.mock.method(NativeProcessTree.prototype, 'empty', () => false);
|
|
49
|
+
const child = new JsonLineProcess(process.execPath, ['-e', 'process.stdout.write(JSON.stringify({ready:true})+"\\n");setInterval(()=>{},1000);'], { cwd: process.cwd() }, () => ready(), (_error, confirmed) => {
|
|
50
|
+
stopped = confirmed;
|
|
51
|
+
});
|
|
52
|
+
await started;
|
|
53
|
+
await assert.rejects(child.close(), /Owned process cleanup unconfirmed/);
|
|
54
|
+
assert.equal(stopped, false);
|
|
55
|
+
});
|
|
56
|
+
//# sourceMappingURL=process.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process.test.js","sourceRoot":"","sources":["../../src/native/process.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { NativeSessionClient } from './client.js';
|
|
2
|
+
export declare const NATIVE_PROFILE_METHODS: readonly string[];
|
|
3
|
+
/** Called only after execution-node ownership is authorized, using that node's environment. */
|
|
4
|
+
export declare function routeNativeProfile(client: NativeSessionClient, owner: string, method: string, params: Record<string, unknown>): Promise<unknown>;
|
|
5
|
+
//# sourceMappingURL=profile-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-service.d.ts","sourceRoot":"","sources":["../../src/native/profile-service.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGvD,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAKnD,CAAC;AAgBF,+FAA+F;AAC/F,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,mBAAmB,EAC3B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,OAAO,CAAC,CA6DlB"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Methods, } from '@farmslot/protocol';
|
|
2
|
+
import { addNativeProfile, inspectNativeProfile, listNativeProfiles, nativeProfileEnvironment, removeNativeProfile, } from './account-profiles.js';
|
|
3
|
+
import { nativeRunnerDefinitions } from './registry.js';
|
|
4
|
+
export const NATIVE_PROFILE_METHODS = [
|
|
5
|
+
Methods.NATIVE_PROFILE_LIST,
|
|
6
|
+
Methods.NATIVE_PROFILE_ADD,
|
|
7
|
+
Methods.NATIVE_PROFILE_STATUS,
|
|
8
|
+
Methods.NATIVE_PROFILE_REMOVE,
|
|
9
|
+
];
|
|
10
|
+
function string(params, name) {
|
|
11
|
+
if (typeof params[name] !== 'string' || !params[name].trim())
|
|
12
|
+
throw new Error(`${name} must be a nonempty string`);
|
|
13
|
+
return params[name];
|
|
14
|
+
}
|
|
15
|
+
function quote(value) {
|
|
16
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
17
|
+
}
|
|
18
|
+
function sessionStopped(session) {
|
|
19
|
+
return (['closed', 'failed'].includes(session.state) &&
|
|
20
|
+
(!session.processPid || session.processStopped === true));
|
|
21
|
+
}
|
|
22
|
+
/** Called only after execution-node ownership is authorized, using that node's environment. */
|
|
23
|
+
export async function routeNativeProfile(client, owner, method, params) {
|
|
24
|
+
if (Object.keys(params).some((key) => !['executionNodeId', 'profileId', 'runner', 'directory', 'accountContextId'].includes(key)))
|
|
25
|
+
throw new Error('Native profiles accept native directories, not credentials or environment overrides');
|
|
26
|
+
if (method === Methods.NATIVE_PROFILE_LIST)
|
|
27
|
+
return { profiles: listNativeProfiles() };
|
|
28
|
+
const id = string(params, 'profileId');
|
|
29
|
+
if (method === Methods.NATIVE_PROFILE_ADD)
|
|
30
|
+
return {
|
|
31
|
+
profile: await addNativeProfile({
|
|
32
|
+
profileId: id,
|
|
33
|
+
runner: string(params, 'runner'),
|
|
34
|
+
...(params.directory !== undefined ? { directory: string(params, 'directory') } : {}),
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
if (method === Methods.NATIVE_PROFILE_STATUS)
|
|
38
|
+
return inspectNativeProfile(id, async (profile) => {
|
|
39
|
+
const definition = nativeRunnerDefinitions[profile.runner];
|
|
40
|
+
const account = await definition.account.observe({
|
|
41
|
+
cwd: profile.directory,
|
|
42
|
+
executable: definition.binary,
|
|
43
|
+
env: nativeProfileEnvironment(profile),
|
|
44
|
+
});
|
|
45
|
+
const loginCommand = [
|
|
46
|
+
'env',
|
|
47
|
+
...definition.account.unset.flatMap((name) => ['-u', name]),
|
|
48
|
+
...Object.entries(definition.account.environment(profile.directory)).map(([key, value]) => `${key}=${value}`),
|
|
49
|
+
account.executable ?? definition.binary,
|
|
50
|
+
...definition.account.loginArgs,
|
|
51
|
+
]
|
|
52
|
+
.map(quote)
|
|
53
|
+
.join(' ');
|
|
54
|
+
return { profile, account, loginCommand };
|
|
55
|
+
});
|
|
56
|
+
if (method === Methods.NATIVE_PROFILE_REMOVE) {
|
|
57
|
+
await removeNativeProfile(id, string(params, 'accountContextId'), async (profile) => {
|
|
58
|
+
const sessions = (await client.list(owner)).filter((session) => session.profileId === profile.id);
|
|
59
|
+
if (sessions.some((session) => session.workerManaged && !sessionStopped(session)))
|
|
60
|
+
throw new Error('Stop this profile’s worker runs before removing the profile');
|
|
61
|
+
for (const session of sessions) {
|
|
62
|
+
// Retirement blocks new launches. A stopped worker needs no standalone
|
|
63
|
+
// close request, which correctly requires its separate task controls.
|
|
64
|
+
if (session.workerManaged)
|
|
65
|
+
continue;
|
|
66
|
+
await client.close(owner, session.id);
|
|
67
|
+
const stopped = (await client.read(owner, session.id, undefined, 1)).session;
|
|
68
|
+
if (!sessionStopped(stopped))
|
|
69
|
+
throw new Error('Native profile process cleanup is not confirmed');
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return { removed: true };
|
|
73
|
+
}
|
|
74
|
+
throw new Error('Unknown native profile method');
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=profile-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-service.js","sourceRoot":"","sources":["../../src/native/profile-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { NativeAccountObservation, NativeAccountProbeOptions } from './account-types.js';
|
|
2
|
+
import type { NativeAdapter } from './types.js';
|
|
3
|
+
export interface NativeRunnerDefinition {
|
|
4
|
+
adapter: NativeAdapter;
|
|
5
|
+
binary: string;
|
|
6
|
+
supportsWorkers?: boolean;
|
|
7
|
+
supportsReadOnlyWorkspace?: boolean;
|
|
8
|
+
reviewRuntimeRoots?: (environment: NodeJS.ProcessEnv) => string[];
|
|
9
|
+
/** Default for new standalone conversations; worker launch settings stay authoritative. */
|
|
10
|
+
defaultEffort?: string;
|
|
11
|
+
account: {
|
|
12
|
+
environment(directory: string, base?: NodeJS.ProcessEnv): Record<string, string>;
|
|
13
|
+
unset: readonly string[];
|
|
14
|
+
loginArgs: readonly string[];
|
|
15
|
+
observe(options: NativeAccountProbeOptions): Promise<NativeAccountObservation>;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** Native profile mechanics stay with the runner definition, not client workflows. */
|
|
19
|
+
export declare const nativeRunnerDefinitions: Record<string, NativeRunnerDefinition>;
|
|
20
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/native/registry.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAM9F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,kBAAkB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,UAAU,KAAK,MAAM,EAAE,CAAC;IAClE,2FAA2F;IAC3F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE;QACP,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjF,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;QACzB,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;QAC7B,OAAO,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;KAChF,CAAC;CACH;AAED,sFAAsF;AACtF,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CA2E1E,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { claudeProfileEnvironment, observeClaudeAccount } from './account-claude.js';
|
|
4
|
+
import { observeCodexAccount } from './account-codex.js';
|
|
5
|
+
import { cursorProfileEnvironment, observeCursorAccount } from './account-cursor.js';
|
|
6
|
+
import { observeGrokAccount } from './account-grok.js';
|
|
7
|
+
import { claudeNativeAdapter } from './claude.js';
|
|
8
|
+
import { codexNativeAdapter } from './codex.js';
|
|
9
|
+
import { cursorNativeAdapter } from './cursor.js';
|
|
10
|
+
import { grokNativeAdapter } from './grok.js';
|
|
11
|
+
import { hostReviewSandboxAvailable } from './review-sandbox.js';
|
|
12
|
+
/** Native profile mechanics stay with the runner definition, not client workflows. */
|
|
13
|
+
export const nativeRunnerDefinitions = {
|
|
14
|
+
codex: {
|
|
15
|
+
adapter: codexNativeAdapter,
|
|
16
|
+
binary: 'codex',
|
|
17
|
+
supportsWorkers: true,
|
|
18
|
+
supportsReadOnlyWorkspace: true,
|
|
19
|
+
defaultEffort: 'low',
|
|
20
|
+
account: {
|
|
21
|
+
environment: (directory) => ({ CODEX_HOME: directory }),
|
|
22
|
+
unset: ['OPENAI_API_KEY', 'CODEX_API_KEY'],
|
|
23
|
+
loginArgs: ['login'],
|
|
24
|
+
observe: observeCodexAccount,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
claude: {
|
|
28
|
+
supportsReadOnlyWorkspace: hostReviewSandboxAvailable(),
|
|
29
|
+
reviewRuntimeRoots: (env) => [env.CLAUDE_CONFIG_DIR ?? join(env.HOME ?? homedir(), '.claude')],
|
|
30
|
+
adapter: claudeNativeAdapter,
|
|
31
|
+
binary: 'claude',
|
|
32
|
+
supportsWorkers: true,
|
|
33
|
+
account: {
|
|
34
|
+
environment: claudeProfileEnvironment,
|
|
35
|
+
unset: [
|
|
36
|
+
'ANTHROPIC_API_KEY',
|
|
37
|
+
'ANTHROPIC_AUTH_TOKEN',
|
|
38
|
+
'CLAUDE_CODE_OAUTH_TOKEN',
|
|
39
|
+
'CLAUDE_CONFIG_DIR',
|
|
40
|
+
'CLAUDE_SECURESTORAGE_CONFIG_DIR',
|
|
41
|
+
],
|
|
42
|
+
loginArgs: ['auth', 'login'],
|
|
43
|
+
observe: observeClaudeAccount,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
cursor: {
|
|
47
|
+
supportsReadOnlyWorkspace: hostReviewSandboxAvailable(),
|
|
48
|
+
reviewRuntimeRoots: (env) => [
|
|
49
|
+
join(env.HOME ?? homedir(), '.cursor'),
|
|
50
|
+
join(env.HOME ?? homedir(), 'Library/Caches/cursor-compile-cache'),
|
|
51
|
+
join(env.HOME ?? homedir(), 'Library/Application Support/Cursor'),
|
|
52
|
+
// cursor-agent locks chats under /tmp/cursor-agent-persist-<uid> (realpath
|
|
53
|
+
// /private/tmp/...). Denying that mkdir kills the tmux reviewer in seconds.
|
|
54
|
+
...(typeof process.getuid === 'function'
|
|
55
|
+
? [join('/tmp', `cursor-agent-persist-${process.getuid()}`)]
|
|
56
|
+
: []),
|
|
57
|
+
],
|
|
58
|
+
adapter: cursorNativeAdapter,
|
|
59
|
+
binary: 'cursor-agent',
|
|
60
|
+
supportsWorkers: true,
|
|
61
|
+
account: {
|
|
62
|
+
environment: cursorProfileEnvironment,
|
|
63
|
+
unset: [
|
|
64
|
+
'CURSOR_API_KEY',
|
|
65
|
+
'CURSOR_AUTH_TOKEN',
|
|
66
|
+
'CURSOR_CONFIG_DIR',
|
|
67
|
+
'CURSOR_DATA_DIR',
|
|
68
|
+
'XDG_CONFIG_HOME',
|
|
69
|
+
'XDG_DATA_HOME',
|
|
70
|
+
],
|
|
71
|
+
loginArgs: ['login'],
|
|
72
|
+
observe: observeCursorAccount,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
grok: {
|
|
76
|
+
supportsReadOnlyWorkspace: hostReviewSandboxAvailable(),
|
|
77
|
+
reviewRuntimeRoots: (env) => [env.GROK_HOME ?? join(env.HOME ?? homedir(), '.grok')],
|
|
78
|
+
adapter: grokNativeAdapter,
|
|
79
|
+
binary: 'grok',
|
|
80
|
+
supportsWorkers: true,
|
|
81
|
+
account: {
|
|
82
|
+
environment: (directory) => ({ GROK_HOME: directory }),
|
|
83
|
+
unset: ['GROK_API_KEY', 'XAI_API_KEY'],
|
|
84
|
+
loginArgs: ['login'],
|
|
85
|
+
observe: observeGrokAccount,
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/native/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.test.d.ts","sourceRoot":"","sources":["../../src/native/registry.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import test from 'node:test';
|
|
4
|
+
import { nativeRunnerDefinitions } from './registry.js';
|
|
5
|
+
test('cursor review sandbox includes the persist lock directory', () => {
|
|
6
|
+
const roots = nativeRunnerDefinitions.cursor.reviewRuntimeRoots?.({ HOME: '/Users/x' }) ?? [];
|
|
7
|
+
assert.ok(roots.some((root) => root.endsWith('/.cursor')));
|
|
8
|
+
assert.ok(roots.some((root) => root.includes('Application Support/Cursor')));
|
|
9
|
+
if (typeof process.getuid === 'function') {
|
|
10
|
+
assert.ok(roots.includes(join('/tmp', `cursor-agent-persist-${process.getuid()}`)));
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=registry.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.test.js","sourceRoot":"","sources":["../../src/native/registry.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAExD,IAAI,CAAC,2DAA2D,EAAE,GAAG,EAAE;IACrE,MAAM,KAAK,GAAG,uBAAuB,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC;IAC9F,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC3D,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC;IAC7E,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QACzC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,wBAAwB,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IACtF,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NativeWorkerFilesystemPolicy } from './worker-launch.js';
|
|
2
|
+
export interface NativeProcessSandbox {
|
|
3
|
+
executable: string;
|
|
4
|
+
args: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare const hostReviewSandboxAvailable: () => boolean;
|
|
7
|
+
/** Shared with terminal review launches; trust settings cannot weaken source protection. */
|
|
8
|
+
export declare const reviewProcessSandbox: (policy: NativeWorkerFilesystemPolicy, runtimeRoots: string[]) => Promise<{
|
|
9
|
+
sandbox: NativeProcessSandbox;
|
|
10
|
+
temporaryDirectory: string;
|
|
11
|
+
}>;
|
|
12
|
+
//# sourceMappingURL=review-sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-sandbox.d.ts","sourceRoot":"","sources":["../../src/native/review-sandbox.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAC;AAEvE,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAQD,eAAO,MAAM,0BAA0B,QANxB,OAMmD,CAAC;AACnE,4FAA4F;AAC5F,eAAO,MAAM,oBAAoB,WANrB,4BAA4B,gBACtB,MAAM,EAAE,KACrB,OAAO,CAAC;IAAE,OAAO,EAAE,oBAAoB,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,CAIhB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
const implementation = createRequire(import.meta.url)('../../scripts/review-filesystem.cjs');
|
|
3
|
+
export const hostReviewSandboxAvailable = implementation.available;
|
|
4
|
+
/** Shared with terminal review launches; trust settings cannot weaken source protection. */
|
|
5
|
+
export const reviewProcessSandbox = implementation.sandbox;
|
|
6
|
+
//# sourceMappingURL=review-sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-sandbox.js","sourceRoot":"","sources":["../../src/native/review-sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAQ5C,MAAM,cAAc,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,qCAAqC,CAM1F,CAAC;AACF,MAAM,CAAC,MAAM,0BAA0B,GAAG,cAAc,CAAC,SAAS,CAAC;AACnE,4FAA4F;AAC5F,MAAM,CAAC,MAAM,oBAAoB,GAAG,cAAc,CAAC,OAAO,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-sandbox.test.d.ts","sourceRoot":"","sources":["../../src/native/review-sandbox.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { execFile } from 'node:child_process';
|
|
3
|
+
import { mkdir, mkdtemp, readFile, realpath, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import test from 'node:test';
|
|
7
|
+
import { promisify } from 'node:util';
|
|
8
|
+
import { hostReviewSandboxAvailable, reviewProcessSandbox } from './review-sandbox.js';
|
|
9
|
+
const exec = promisify(execFile);
|
|
10
|
+
test('host review sandbox protects source, shared git metadata and unrelated files while reports stay writable', { skip: !hostReviewSandboxAvailable() }, async () => {
|
|
11
|
+
const root = await realpath(await mkdtemp(join(tmpdir(), 'review-permissions-')));
|
|
12
|
+
const source = join(root, 'source'), output = join(root, 'reports'), metadata = join(root, 'git'), other = join(root, 'other');
|
|
13
|
+
try {
|
|
14
|
+
for (const dir of [source, output, metadata, other])
|
|
15
|
+
await mkdir(dir);
|
|
16
|
+
for (const dir of [source, metadata, other])
|
|
17
|
+
await writeFile(join(dir, 'file'), 'original');
|
|
18
|
+
const { sandbox } = await reviewProcessSandbox({ readOnlyRoots: [source, metadata], writableRoots: [output] }, []);
|
|
19
|
+
const program = `const fs=require('node:fs');const roots=JSON.parse(process.argv[1]);const results={};for(const [name,root]of Object.entries(roots)){try{fs.writeFileSync(root+'/file','changed');results[name]=true;}catch(e){if(!['EPERM','EACCES'].includes(e.code))throw e;results[name]=false;}}console.log(JSON.stringify(results));`;
|
|
20
|
+
const result = await exec(sandbox.executable, [
|
|
21
|
+
...sandbox.args,
|
|
22
|
+
process.execPath,
|
|
23
|
+
'-e',
|
|
24
|
+
program,
|
|
25
|
+
JSON.stringify({ source, metadata, other, output }),
|
|
26
|
+
]);
|
|
27
|
+
assert.deepEqual(JSON.parse(result.stdout), {
|
|
28
|
+
source: false,
|
|
29
|
+
metadata: false,
|
|
30
|
+
other: false,
|
|
31
|
+
output: true,
|
|
32
|
+
});
|
|
33
|
+
assert.equal(await readFile(join(source, 'file'), 'utf8'), 'original');
|
|
34
|
+
assert.equal(await readFile(join(output, 'file'), 'utf8'), 'changed');
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
await rm(root, { recursive: true, force: true });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=review-sandbox.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-sandbox.test.js","sourceRoot":"","sources":["../../src/native/review-sandbox.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEvF,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AACjC,IAAI,CACF,0GAA0G,EAC1G,EAAE,IAAI,EAAE,CAAC,0BAA0B,EAAE,EAAE,EACvC,KAAK,IAAI,EAAE;IACT,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAClF,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,EACjC,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,EAC9B,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,EAC5B,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9B,IAAI,CAAC;QACH,KAAK,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC;YAAE,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QACtE,KAAK,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC;YAAE,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC;QAC5F,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,oBAAoB,CAC5C,EAAE,aAAa,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC,MAAM,CAAC,EAAE,EAC9D,EAAE,CACH,CAAC;QACF,MAAM,OAAO,GAAG,2TAA2T,CAAC;QAC5U,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE;YAC5C,GAAG,OAAO,CAAC,IAAI;YACf,OAAO,CAAC,QAAQ;YAChB,IAAI;YACJ,OAAO;YACP,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;SACpD,CAAC,CAAC;QACH,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;YAC1C,MAAM,EAAE,KAAK;YACb,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC;QACvE,MAAM,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC;IACxE,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC,CACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/native/service.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/native/service.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAKvD,qBAAa,wBAAyB,SAAQ,KAAK;IAE/C,QAAQ,CAAC,IAAI,EAAE,gBAAgB,GAAG,sBAAsB;gBAA/C,IAAI,EAAE,gBAAgB,GAAG,sBAAsB,EACxD,OAAO,EAAE,MAAM;CAKlB;AASD,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,mBAAmB,EAC3B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,OAAO,CAAC,CAyJlB"}
|
package/dist/native/service.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Methods, } from '@farmslot/protocol';
|
|
2
|
+
import { NATIVE_PROFILE_METHODS, routeNativeProfile } from './profile-service.js';
|
|
2
3
|
import { nativeWorkspaceChanges, nativeWorkspaceDiff, nativeWorkspaceList } from './workspace.js';
|
|
3
4
|
import { readWorkspaceText } from './workspace-files.js';
|
|
4
5
|
export class NativeSessionMethodError extends Error {
|
|
@@ -18,23 +19,52 @@ export async function routeNativeSession(client, principal, method, value) {
|
|
|
18
19
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
19
20
|
throw new NativeSessionMethodError('INVALID_PARAMS', 'Expected native session parameters');
|
|
20
21
|
const p = value;
|
|
22
|
+
if ('launch' in p)
|
|
23
|
+
throw new NativeSessionMethodError('INVALID_PARAMS', 'Worker launch settings are supplied by run dispatch');
|
|
21
24
|
if (p.executionNodeId !== undefined && p.executionNodeId !== client.executionNodeId)
|
|
22
25
|
throw new NativeSessionMethodError('INVALID_PARAMS', 'Native session targets another execution node');
|
|
23
26
|
try {
|
|
27
|
+
if (NATIVE_PROFILE_METHODS.includes(method))
|
|
28
|
+
return await routeNativeProfile(client, principal, method, p);
|
|
24
29
|
switch (method) {
|
|
25
30
|
case Methods.NATIVE_SESSION_WORKSPACE_LIST:
|
|
26
31
|
case Methods.NATIVE_SESSION_WORKSPACE_READ:
|
|
27
32
|
case Methods.NATIVE_SESSION_WORKSPACE_CHANGES:
|
|
28
33
|
case Methods.NATIVE_SESSION_WORKSPACE_DIFF: {
|
|
29
|
-
const
|
|
34
|
+
const sessionId = string(p, 'sessionId');
|
|
35
|
+
const inspect = async () => {
|
|
36
|
+
const { session } = await client.read(principal, sessionId, undefined, 1);
|
|
37
|
+
if (session.workerManaged) {
|
|
38
|
+
const worker = p.worker;
|
|
39
|
+
if (!worker || typeof worker !== 'object' || Array.isArray(worker))
|
|
40
|
+
throw new Error('Worker workspace requires its pinned run context');
|
|
41
|
+
const target = worker;
|
|
42
|
+
if (session.workerLeaseId !== target.leaseId ||
|
|
43
|
+
session.generation !== target.generation ||
|
|
44
|
+
['closed', 'failed', 'closing'].includes(session.state))
|
|
45
|
+
throw new Error('Current workspace is unavailable for a closed or transferred worker');
|
|
46
|
+
}
|
|
47
|
+
else if (p.worker !== undefined)
|
|
48
|
+
throw new Error('Worker workspace target is not a worker');
|
|
49
|
+
return session;
|
|
50
|
+
};
|
|
51
|
+
const session = await inspect();
|
|
52
|
+
let result;
|
|
30
53
|
if (method === Methods.NATIVE_SESSION_WORKSPACE_CHANGES)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
54
|
+
result = await nativeWorkspaceChanges(session.cwd);
|
|
55
|
+
else {
|
|
56
|
+
const path = string(p, 'path');
|
|
57
|
+
if (method === Methods.NATIVE_SESSION_WORKSPACE_LIST)
|
|
58
|
+
result = await nativeWorkspaceList(session.cwd, path);
|
|
59
|
+
else if (method === Methods.NATIVE_SESSION_WORKSPACE_DIFF)
|
|
60
|
+
result = { path, diff: await nativeWorkspaceDiff(session.cwd, path) };
|
|
61
|
+
else
|
|
62
|
+
result = { path, content: await readWorkspaceText(session.cwd, path) };
|
|
63
|
+
}
|
|
64
|
+
// File reads await the OS. Refuse their result if a handoff won during that await.
|
|
65
|
+
if (session.workerManaged)
|
|
66
|
+
await inspect();
|
|
67
|
+
return result;
|
|
38
68
|
}
|
|
39
69
|
case Methods.NATIVE_SESSION_CREATE:
|
|
40
70
|
case Methods.NATIVE_SESSION_ENSURE: {
|
|
@@ -44,6 +74,10 @@ export async function routeNativeSession(client, principal, method, value) {
|
|
|
44
74
|
};
|
|
45
75
|
if (p.model !== undefined)
|
|
46
76
|
params.model = string(p, 'model');
|
|
77
|
+
if (p.profileId !== undefined)
|
|
78
|
+
params.profileId = string(p, 'profileId');
|
|
79
|
+
if (p.accountContextId !== undefined)
|
|
80
|
+
params.accountContextId = string(p, 'accountContextId');
|
|
47
81
|
if (p.mode !== undefined) {
|
|
48
82
|
if (p.mode !== 'default' && p.mode !== 'plan')
|
|
49
83
|
throw new NativeSessionMethodError('INVALID_PARAMS', 'Unknown native interaction mode');
|