@myagentroam/node 0.1.3 → 0.1.6
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/README.md +9 -1
- package/dist/capabilities.d.ts +2 -0
- package/dist/capabilities.js +15 -4
- package/dist/claude-agent-sdk.d.ts +4 -16
- package/dist/claude-agent-sdk.js +18 -79
- package/dist/claude-channel.js +0 -1
- package/dist/codex-app-server.d.ts +5 -0
- package/dist/codex-app-server.js +26 -5
- package/dist/config.d.ts +3 -0
- package/dist/config.js +18 -2
- package/dist/connector/node-connector-options.d.ts +26 -0
- package/dist/connector/node-connector-options.js +1 -0
- package/dist/connector/node-operation-router.d.ts +9 -0
- package/dist/connector/node-operation-router.js +22 -0
- package/dist/connector.d.ts +62 -356
- package/dist/connector.js +714 -6176
- package/dist/control-outbound-scheduler.d.ts +26 -0
- package/dist/control-outbound-scheduler.js +121 -0
- package/dist/database.d.ts +4 -0
- package/dist/database.js +15 -10
- package/dist/event-buffer.js +0 -1
- package/dist/fake-runner.js +0 -1
- package/dist/health.js +0 -1
- package/dist/main.js +6 -1
- package/dist/migrations/v001.js +0 -1
- package/dist/migrations/v002.js +0 -1
- package/dist/migrations/v003.d.ts +4 -0
- package/dist/migrations/v003.js +44 -0
- package/dist/native-jsonl-reader.d.ts +14 -0
- package/dist/native-jsonl-reader.js +97 -0
- package/dist/native-session-history.d.ts +41 -4
- package/dist/native-session-history.js +340 -159
- package/dist/opencode-runtime.d.ts +33 -0
- package/dist/opencode-runtime.js +80 -0
- package/dist/opencode-server.d.ts +47 -0
- package/dist/opencode-server.js +250 -0
- package/dist/operational.js +10 -3
- package/dist/process-tree.js +0 -1
- package/dist/runner/abstract-runner.d.ts +153 -0
- package/dist/runner/abstract-runner.js +154 -0
- package/dist/runner/claude/managed-run-controller.d.ts +52 -0
- package/dist/runner/claude/managed-run-controller.js +326 -0
- package/dist/runner/claude-code-runner.d.ts +70 -0
- package/dist/runner/claude-code-runner.js +286 -0
- package/dist/runner/codex/conversation-parser.d.ts +59 -0
- package/dist/runner/codex/conversation-parser.js +816 -0
- package/dist/runner/codex/managed-run-controller.d.ts +80 -0
- package/dist/runner/codex/managed-run-controller.js +388 -0
- package/dist/runner/codex-runner.d.ts +85 -0
- package/dist/runner/codex-runner.js +365 -0
- package/dist/runner/fake-test-runner.d.ts +26 -0
- package/dist/runner/fake-test-runner.js +47 -0
- package/dist/runner/opencode/conversation-parser.d.ts +27 -0
- package/dist/runner/opencode/conversation-parser.js +217 -0
- package/dist/runner/opencode/managed-run-controller.d.ts +76 -0
- package/dist/runner/opencode/managed-run-controller.js +451 -0
- package/dist/runner/opencode-runner.d.ts +60 -0
- package/dist/runner/opencode-runner.js +330 -0
- package/dist/runner/runner-registry.d.ts +19 -0
- package/dist/runner/runner-registry.js +75 -0
- package/dist/runner-command-engine.js +0 -1
- package/dist/runner-profiles.d.ts +4 -0
- package/dist/runner-profiles.js +76 -3
- package/dist/runner-usage.d.ts +2 -2
- package/dist/runner-usage.js +3 -6
- package/dist/runtime-state.js +26 -14
- package/dist/service/attachment-domain-state.d.ts +5 -0
- package/dist/service/attachment-domain-state.js +5 -0
- package/dist/service/conversation-history-service.d.ts +34 -0
- package/dist/service/conversation-history-service.js +158 -0
- package/dist/service/external-session-resume-service.d.ts +32 -0
- package/dist/service/external-session-resume-service.js +98 -0
- package/dist/service/fake-managed-run-service.d.ts +29 -0
- package/dist/service/fake-managed-run-service.js +62 -0
- package/dist/service/managed-runner-session-service.d.ts +12 -0
- package/dist/service/managed-runner-session-service.js +35 -0
- package/dist/service/native-session-projection-service.d.ts +51 -0
- package/dist/service/native-session-projection-service.js +271 -0
- package/dist/service/native-session-watch-service.d.ts +37 -0
- package/dist/service/native-session-watch-service.js +150 -0
- package/dist/service/node-connection-lifecycle-service.d.ts +37 -0
- package/dist/service/node-connection-lifecycle-service.js +85 -0
- package/dist/service/node-control-channel.d.ts +32 -0
- package/dist/service/node-control-channel.js +85 -0
- package/dist/service/node-control-message-service.d.ts +28 -0
- package/dist/service/node-control-message-service.js +104 -0
- package/dist/service/node-request-service.d.ts +14 -0
- package/dist/service/node-request-service.js +50 -0
- package/dist/service/node-run-event-bridge.d.ts +40 -0
- package/dist/service/node-run-event-bridge.js +83 -0
- package/dist/service/node-terminal-channel.d.ts +29 -0
- package/dist/service/node-terminal-channel.js +182 -0
- package/dist/service/node-upgrade-coordinator.d.ts +29 -0
- package/dist/service/node-upgrade-coordinator.js +137 -0
- package/dist/service/node-workspace-coordinator.d.ts +32 -0
- package/dist/service/node-workspace-coordinator.js +80 -0
- package/dist/service/run-attachment-service.d.ts +37 -0
- package/dist/service/run-attachment-service.js +160 -0
- package/dist/service/run-domain-state.d.ts +4 -0
- package/dist/service/run-domain-state.js +4 -0
- package/dist/service/run-event-service.d.ts +31 -0
- package/dist/service/run-event-service.js +102 -0
- package/dist/service/run-workbench-service.d.ts +27 -0
- package/dist/service/run-workbench-service.js +113 -0
- package/dist/service/runner-channel-message-service.d.ts +13 -0
- package/dist/service/runner-channel-message-service.js +20 -0
- package/dist/service/runner-interaction-service.d.ts +40 -0
- package/dist/service/runner-interaction-service.js +246 -0
- package/dist/service/runner-service.d.ts +44 -0
- package/dist/service/runner-service.js +68 -0
- package/dist/service/session-catalog-service.d.ts +45 -0
- package/dist/service/session-catalog-service.js +224 -0
- package/dist/service/session-command-service.d.ts +23 -0
- package/dist/service/session-command-service.js +69 -0
- package/dist/service/session-context-service.d.ts +34 -0
- package/dist/service/session-context-service.js +87 -0
- package/dist/service/session-domain-state.d.ts +8 -0
- package/dist/service/session-domain-state.js +8 -0
- package/dist/service/session-identity-service.d.ts +24 -0
- package/dist/service/session-identity-service.js +86 -0
- package/dist/service/session-lifecycle-service.d.ts +31 -0
- package/dist/service/session-lifecycle-service.js +242 -0
- package/dist/service/session-message-service.d.ts +33 -0
- package/dist/service/session-message-service.js +157 -0
- package/dist/service/session-presentation-service.d.ts +33 -0
- package/dist/service/session-presentation-service.js +36 -0
- package/dist/service/session-query-service.d.ts +31 -0
- package/dist/service/session-query-service.js +77 -0
- package/dist/service/terminal-relay-state.d.ts +6 -0
- package/dist/service/terminal-relay-state.js +3 -0
- package/dist/service/terminal-service.d.ts +12 -0
- package/dist/service/terminal-service.js +52 -0
- package/dist/service/workbench-event-service.d.ts +21 -0
- package/dist/service/workbench-event-service.js +53 -0
- package/dist/service/workbench-manifest-service.d.ts +89 -0
- package/dist/service/workbench-manifest-service.js +70 -0
- package/dist/service/workspace-change-service.d.ts +9 -0
- package/dist/service/workspace-change-service.js +86 -0
- package/dist/service/workspace-content-search-service.d.ts +14 -0
- package/dist/service/workspace-content-search-service.js +477 -0
- package/dist/service/workspace-domain-state.d.ts +75 -0
- package/dist/service/workspace-domain-state.js +40 -0
- package/dist/service/workspace-file-service.d.ts +17 -0
- package/dist/service/workspace-file-service.js +109 -0
- package/dist/service/workspace-queue-workbench-service.d.ts +55 -0
- package/dist/service/workspace-queue-workbench-service.js +226 -0
- package/dist/service/workspace-service.d.ts +15 -0
- package/dist/service/workspace-service.js +67 -0
- package/dist/service/workspace-session-service.d.ts +23 -0
- package/dist/service/workspace-session-service.js +139 -0
- package/dist/service/workspace-upload-service.d.ts +37 -0
- package/dist/service/workspace-upload-service.js +240 -0
- package/dist/service/workspace-watch-service.d.ts +18 -0
- package/dist/service/workspace-watch-service.js +120 -0
- package/dist/service/workspace-workbench-service.d.ts +47 -0
- package/dist/service/workspace-workbench-service.js +223 -0
- package/dist/service.js +2 -3
- package/dist/storage.js +0 -1
- package/dist/supervisor.d.ts +1 -0
- package/dist/supervisor.js +154 -0
- package/dist/terminal.d.ts +1 -0
- package/dist/terminal.js +1 -2
- package/dist/util/node-operation-parsers.d.ts +92 -0
- package/dist/util/node-operation-parsers.js +382 -0
- package/dist/util/runner-native-session-parsers.d.ts +72 -0
- package/dist/util/runner-native-session-parsers.js +381 -0
- package/dist/util/secret-environment.d.ts +1 -0
- package/dist/util/secret-environment.js +25 -0
- package/dist/workspace.d.ts +62 -2
- package/dist/workspace.js +539 -57
- package/package.json +3 -2
- package/dist/capabilities.js.map +0 -1
- package/dist/claude-agent-sdk.js.map +0 -1
- package/dist/claude-channel.js.map +0 -1
- package/dist/codex-app-server.js.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/connector.js.map +0 -1
- package/dist/database.js.map +0 -1
- package/dist/event-buffer.js.map +0 -1
- package/dist/fake-runner.js.map +0 -1
- package/dist/health.js.map +0 -1
- package/dist/main.js.map +0 -1
- package/dist/migrations/v001.js.map +0 -1
- package/dist/migrations/v002.js.map +0 -1
- package/dist/native-session-history.js.map +0 -1
- package/dist/operational.js.map +0 -1
- package/dist/process-tree.js.map +0 -1
- package/dist/runner-command-engine.js.map +0 -1
- package/dist/runner-profiles.js.map +0 -1
- package/dist/runner-usage.js.map +0 -1
- package/dist/runtime-state.js.map +0 -1
- package/dist/service.js.map +0 -1
- package/dist/storage.js.map +0 -1
- package/dist/terminal.js.map +0 -1
- package/dist/workspace.js.map +0 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { minimalRunnerEnvironment, platformCliInvocation, runRunnerProbe } from './operational.js';
|
|
3
|
+
const PROBE_TIMEOUT_MS = 3_000;
|
|
4
|
+
export function probeOpenCode(command = 'opencode') {
|
|
5
|
+
const invocation = platformCliInvocation(command, ['--version']);
|
|
6
|
+
const result = spawnSync(invocation.command, invocation.args, {
|
|
7
|
+
windowsHide: true,
|
|
8
|
+
env: minimalRunnerEnvironment(),
|
|
9
|
+
timeout: PROBE_TIMEOUT_MS,
|
|
10
|
+
encoding: 'utf8'
|
|
11
|
+
});
|
|
12
|
+
if (result.status !== 0 || result.error !== undefined)
|
|
13
|
+
return { available: false, compatible: false };
|
|
14
|
+
return openCodeProbeResult(`${result.stdout}${result.stderr}`);
|
|
15
|
+
}
|
|
16
|
+
export async function probeOpenCodeAsync(command = 'opencode') {
|
|
17
|
+
const invocation = platformCliInvocation(command, ['--version']);
|
|
18
|
+
const result = await runRunnerProbe(invocation.command, invocation.args, {
|
|
19
|
+
env: minimalRunnerEnvironment(),
|
|
20
|
+
timeoutMs: PROBE_TIMEOUT_MS
|
|
21
|
+
});
|
|
22
|
+
return result.status === 0
|
|
23
|
+
? openCodeProbeResult(`${result.stdout}${result.stderr}`)
|
|
24
|
+
: { available: false, compatible: false };
|
|
25
|
+
}
|
|
26
|
+
function openCodeProbeResult(output) {
|
|
27
|
+
const version = output.trim().replace(/^v/i, '').slice(0, 128);
|
|
28
|
+
return {
|
|
29
|
+
available: version.length > 0,
|
|
30
|
+
compatible: /^1\.18\./.test(version),
|
|
31
|
+
...(version.length === 0 ? {} : { version })
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function openCodeProfileModels(catalog) {
|
|
35
|
+
const connected = new Set(catalog.connected ?? []);
|
|
36
|
+
return (catalog.all ?? [])
|
|
37
|
+
.filter((provider) => typeof provider.id === 'string' &&
|
|
38
|
+
connected.has(provider.id) &&
|
|
39
|
+
provider.source === 'config')
|
|
40
|
+
.flatMap((provider) => {
|
|
41
|
+
const providerId = provider.id;
|
|
42
|
+
return Object.entries(provider.models ?? {}).flatMap(([key, model]) => {
|
|
43
|
+
const modelId = boundedString(model.id) ?? boundedString(key);
|
|
44
|
+
if (modelId === undefined)
|
|
45
|
+
return [];
|
|
46
|
+
const variants = Object.entries(model.variants ?? {})
|
|
47
|
+
.filter(([, value]) => value.disabled !== true)
|
|
48
|
+
.map(([id]) => id)
|
|
49
|
+
.filter((id) => id.length > 0 && id.length <= 64)
|
|
50
|
+
.slice(0, 32);
|
|
51
|
+
return [
|
|
52
|
+
{
|
|
53
|
+
id: `${providerId}/${modelId}`.slice(0, 128),
|
|
54
|
+
label: (boundedString(model.name) ?? modelId).slice(0, 128),
|
|
55
|
+
supportedEfforts: variants
|
|
56
|
+
}
|
|
57
|
+
];
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
export function openCodeContextLimits(catalog) {
|
|
62
|
+
const connected = new Set(catalog.connected ?? []);
|
|
63
|
+
return new Map((catalog.all ?? [])
|
|
64
|
+
.filter((provider) => typeof provider.id === 'string' &&
|
|
65
|
+
connected.has(provider.id) &&
|
|
66
|
+
provider.source === 'config')
|
|
67
|
+
.flatMap((provider) => Object.entries(provider.models ?? {}).flatMap(([key, model]) => {
|
|
68
|
+
const modelId = boundedString(model.id) ?? boundedString(key);
|
|
69
|
+
const context = model.limit?.context;
|
|
70
|
+
return modelId !== undefined && Number.isSafeInteger(context) && (context ?? 0) > 0
|
|
71
|
+
? [[`${provider.id}/${modelId}`, context]]
|
|
72
|
+
: [];
|
|
73
|
+
})));
|
|
74
|
+
}
|
|
75
|
+
function boundedString(value) {
|
|
76
|
+
if (typeof value !== 'string')
|
|
77
|
+
return undefined;
|
|
78
|
+
const trimmed = value.trim();
|
|
79
|
+
return trimmed.length > 0 && trimmed.length <= 4096 ? trimmed : undefined;
|
|
80
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import type { OpenCodeProviderCatalog } from './opencode-runtime.js';
|
|
3
|
+
export interface OpenCodeServerOptions {
|
|
4
|
+
readonly command?: string;
|
|
5
|
+
readonly startTimeoutMs?: number;
|
|
6
|
+
readonly spawnProcess?: typeof spawn;
|
|
7
|
+
}
|
|
8
|
+
export declare class OpenCodeServerClient {
|
|
9
|
+
private readonly options;
|
|
10
|
+
private child;
|
|
11
|
+
private client;
|
|
12
|
+
private starting;
|
|
13
|
+
private environment;
|
|
14
|
+
constructor(options?: OpenCodeServerOptions);
|
|
15
|
+
configureEnvironment(environment: Readonly<Record<string, string>>): void;
|
|
16
|
+
start(): Promise<void>;
|
|
17
|
+
private startServer;
|
|
18
|
+
providers(directory: string): Promise<OpenCodeProviderCatalog>;
|
|
19
|
+
sessions(directory: string, limit?: number): Promise<readonly unknown[]>;
|
|
20
|
+
sessionStatus(directory: string): Promise<Readonly<Record<string, unknown>>>;
|
|
21
|
+
createSession(directory: string, title?: string): Promise<unknown>;
|
|
22
|
+
forkSession(directory: string, sessionID: string, messageID: string): Promise<unknown>;
|
|
23
|
+
messages(directory: string, sessionID: string, limit?: number): Promise<readonly unknown[]>;
|
|
24
|
+
updateSession(directory: string, sessionID: string, title: string): Promise<unknown>;
|
|
25
|
+
deleteSession(directory: string, sessionID: string): Promise<unknown>;
|
|
26
|
+
abort(directory: string, sessionID: string): Promise<unknown>;
|
|
27
|
+
summarize(directory: string, sessionID: string, model: string): Promise<unknown>;
|
|
28
|
+
replyPermission(directory: string, requestID: string, decision: 'APPROVED' | 'REJECTED'): Promise<unknown>;
|
|
29
|
+
replyQuestion(directory: string, requestID: string, answers: readonly (readonly string[])[]): Promise<unknown>;
|
|
30
|
+
rejectQuestion(directory: string, requestID: string): Promise<unknown>;
|
|
31
|
+
prompt(input: {
|
|
32
|
+
readonly directory: string;
|
|
33
|
+
readonly sessionID: string;
|
|
34
|
+
readonly text: string;
|
|
35
|
+
readonly model: string;
|
|
36
|
+
readonly variant?: string;
|
|
37
|
+
readonly agent?: 'plan' | 'build';
|
|
38
|
+
readonly attachments?: readonly {
|
|
39
|
+
readonly name: string;
|
|
40
|
+
readonly mime: 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp';
|
|
41
|
+
readonly dataBase64: string;
|
|
42
|
+
}[];
|
|
43
|
+
}): Promise<unknown>;
|
|
44
|
+
subscribe(directory: string, signal: AbortSignal, onEvent: (event: unknown) => void, onReady?: () => void): Promise<void>;
|
|
45
|
+
stop(): Promise<void>;
|
|
46
|
+
private requireClient;
|
|
47
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { randomBytes } from 'node:crypto';
|
|
3
|
+
import { createServer } from 'node:net';
|
|
4
|
+
import { createOpencodeClient } from '@opencode-ai/sdk/v2/client';
|
|
5
|
+
import { minimalRunnerEnvironment, platformCliInvocation } from './operational.js';
|
|
6
|
+
import { terminateProcessTree } from './process-tree.js';
|
|
7
|
+
const START_TIMEOUT_MS = 10_000;
|
|
8
|
+
export class OpenCodeServerClient {
|
|
9
|
+
options;
|
|
10
|
+
child;
|
|
11
|
+
client;
|
|
12
|
+
starting;
|
|
13
|
+
environment = {};
|
|
14
|
+
constructor(options = {}) {
|
|
15
|
+
this.options = options;
|
|
16
|
+
}
|
|
17
|
+
configureEnvironment(environment) {
|
|
18
|
+
if (this.child !== undefined)
|
|
19
|
+
throw new Error('OPENCODE_SERVER_ALREADY_STARTED');
|
|
20
|
+
this.environment = { ...environment };
|
|
21
|
+
}
|
|
22
|
+
async start() {
|
|
23
|
+
if (this.client !== undefined)
|
|
24
|
+
return;
|
|
25
|
+
if (this.starting !== undefined)
|
|
26
|
+
return this.starting;
|
|
27
|
+
const starting = this.startServer();
|
|
28
|
+
this.starting = starting;
|
|
29
|
+
try {
|
|
30
|
+
await starting;
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
if (this.starting === starting)
|
|
34
|
+
this.starting = undefined;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async startServer() {
|
|
38
|
+
const port = await availablePort();
|
|
39
|
+
const password = randomBytes(24).toString('base64url');
|
|
40
|
+
const invocation = platformCliInvocation(this.options.command ?? 'opencode', [
|
|
41
|
+
'serve',
|
|
42
|
+
'--hostname',
|
|
43
|
+
'127.0.0.1',
|
|
44
|
+
'--port',
|
|
45
|
+
String(port)
|
|
46
|
+
]);
|
|
47
|
+
const spawnProcess = this.options.spawnProcess ?? spawn;
|
|
48
|
+
const child = spawnProcess(invocation.command, invocation.args, {
|
|
49
|
+
windowsHide: true,
|
|
50
|
+
detached: process.platform !== 'win32',
|
|
51
|
+
env: {
|
|
52
|
+
...minimalRunnerEnvironment(),
|
|
53
|
+
...this.environment,
|
|
54
|
+
OPENCODE_SERVER_USERNAME: 'opencode',
|
|
55
|
+
OPENCODE_SERVER_PASSWORD: password,
|
|
56
|
+
OPENCODE_DISABLE_AUTOUPDATE: 'true'
|
|
57
|
+
},
|
|
58
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
59
|
+
});
|
|
60
|
+
this.child = child;
|
|
61
|
+
const authorization = `Basic ${Buffer.from(`opencode:${password}`).toString('base64')}`;
|
|
62
|
+
const client = createOpencodeClient({
|
|
63
|
+
baseUrl: `http://127.0.0.1:${port}`,
|
|
64
|
+
headers: { authorization },
|
|
65
|
+
throwOnError: true
|
|
66
|
+
});
|
|
67
|
+
try {
|
|
68
|
+
await waitUntilHealthy(`http://127.0.0.1:${port}/global/health`, authorization, child, this.options.startTimeoutMs ?? START_TIMEOUT_MS);
|
|
69
|
+
this.client = client;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
await this.stop();
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async providers(directory) {
|
|
77
|
+
const client = await this.requireClient();
|
|
78
|
+
return unwrap(await client.provider.list({ directory }));
|
|
79
|
+
}
|
|
80
|
+
async sessions(directory, limit = 100) {
|
|
81
|
+
const client = await this.requireClient();
|
|
82
|
+
return unwrap(await client.session.list({ directory, limit }));
|
|
83
|
+
}
|
|
84
|
+
async sessionStatus(directory) {
|
|
85
|
+
const client = await this.requireClient();
|
|
86
|
+
return unwrap(await client.session.status({ directory }));
|
|
87
|
+
}
|
|
88
|
+
async createSession(directory, title) {
|
|
89
|
+
const client = await this.requireClient();
|
|
90
|
+
return unwrap(await client.session.create({ directory, ...(title === undefined ? {} : { title }) }));
|
|
91
|
+
}
|
|
92
|
+
async forkSession(directory, sessionID, messageID) {
|
|
93
|
+
const client = await this.requireClient();
|
|
94
|
+
return unwrap(await client.session.fork({ directory, sessionID, messageID }));
|
|
95
|
+
}
|
|
96
|
+
async messages(directory, sessionID, limit = 100) {
|
|
97
|
+
const client = await this.requireClient();
|
|
98
|
+
return unwrap(await client.session.messages({ directory, sessionID, limit }));
|
|
99
|
+
}
|
|
100
|
+
async updateSession(directory, sessionID, title) {
|
|
101
|
+
const client = await this.requireClient();
|
|
102
|
+
return unwrap(await client.session.update({ directory, sessionID, title }));
|
|
103
|
+
}
|
|
104
|
+
async deleteSession(directory, sessionID) {
|
|
105
|
+
const client = await this.requireClient();
|
|
106
|
+
return unwrap(await client.session.delete({ directory, sessionID }));
|
|
107
|
+
}
|
|
108
|
+
async abort(directory, sessionID) {
|
|
109
|
+
const client = await this.requireClient();
|
|
110
|
+
return unwrap(await client.session.abort({ directory, sessionID }));
|
|
111
|
+
}
|
|
112
|
+
async summarize(directory, sessionID, model) {
|
|
113
|
+
const client = await this.requireClient();
|
|
114
|
+
const separator = model.indexOf('/');
|
|
115
|
+
if (separator <= 0 || separator === model.length - 1)
|
|
116
|
+
throw new Error('OPENCODE_MODEL_INVALID');
|
|
117
|
+
return unwrap(await client.session.summarize({
|
|
118
|
+
directory,
|
|
119
|
+
sessionID,
|
|
120
|
+
providerID: model.slice(0, separator),
|
|
121
|
+
modelID: model.slice(separator + 1)
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
async replyPermission(directory, requestID, decision) {
|
|
125
|
+
const client = await this.requireClient();
|
|
126
|
+
return unwrap(await client.permission.reply({
|
|
127
|
+
directory,
|
|
128
|
+
requestID,
|
|
129
|
+
reply: decision === 'APPROVED' ? 'once' : 'reject'
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
async replyQuestion(directory, requestID, answers) {
|
|
133
|
+
const client = await this.requireClient();
|
|
134
|
+
return unwrap(await client.question.reply({
|
|
135
|
+
directory,
|
|
136
|
+
requestID,
|
|
137
|
+
answers: answers.map((answer) => [...answer])
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
async rejectQuestion(directory, requestID) {
|
|
141
|
+
const client = await this.requireClient();
|
|
142
|
+
return unwrap(await client.question.reject({ directory, requestID }));
|
|
143
|
+
}
|
|
144
|
+
async prompt(input) {
|
|
145
|
+
const client = await this.requireClient();
|
|
146
|
+
const separator = input.model.indexOf('/');
|
|
147
|
+
if (separator <= 0 || separator === input.model.length - 1)
|
|
148
|
+
throw new Error('OPENCODE_MODEL_INVALID');
|
|
149
|
+
return unwrap(await client.session.promptAsync({
|
|
150
|
+
directory: input.directory,
|
|
151
|
+
sessionID: input.sessionID,
|
|
152
|
+
model: {
|
|
153
|
+
providerID: input.model.slice(0, separator),
|
|
154
|
+
modelID: input.model.slice(separator + 1)
|
|
155
|
+
},
|
|
156
|
+
...(input.variant === undefined || input.variant.length === 0
|
|
157
|
+
? {}
|
|
158
|
+
: { variant: input.variant }),
|
|
159
|
+
...(input.agent === undefined ? {} : { agent: input.agent }),
|
|
160
|
+
parts: [
|
|
161
|
+
{ type: 'text', text: input.text },
|
|
162
|
+
...(input.attachments ?? []).map((attachment) => ({
|
|
163
|
+
type: 'file',
|
|
164
|
+
mime: attachment.mime,
|
|
165
|
+
filename: attachment.name,
|
|
166
|
+
url: `data:${attachment.mime};base64,${attachment.dataBase64}`
|
|
167
|
+
}))
|
|
168
|
+
]
|
|
169
|
+
}));
|
|
170
|
+
}
|
|
171
|
+
async subscribe(directory, signal, onEvent, onReady) {
|
|
172
|
+
const client = await this.requireClient();
|
|
173
|
+
const stream = await client.event.subscribe({ directory }, { signal });
|
|
174
|
+
onReady?.();
|
|
175
|
+
for await (const event of stream.stream)
|
|
176
|
+
onEvent(event);
|
|
177
|
+
}
|
|
178
|
+
async stop() {
|
|
179
|
+
this.client = undefined;
|
|
180
|
+
const child = this.child;
|
|
181
|
+
this.child = undefined;
|
|
182
|
+
if (child === undefined)
|
|
183
|
+
return;
|
|
184
|
+
if (child.pid === undefined) {
|
|
185
|
+
child.kill('SIGTERM');
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
await terminateProcessTree({
|
|
190
|
+
pid: child.pid,
|
|
191
|
+
kill: (signal) => child.kill(signal)
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
child.kill('SIGTERM');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
async requireClient() {
|
|
199
|
+
await this.start();
|
|
200
|
+
return this.client;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async function availablePort() {
|
|
204
|
+
return new Promise((resolve, reject) => {
|
|
205
|
+
const server = createServer();
|
|
206
|
+
server.once('error', reject);
|
|
207
|
+
server.listen(0, '127.0.0.1', () => {
|
|
208
|
+
const address = server.address();
|
|
209
|
+
const port = typeof address === 'object' && address !== null ? address.port : undefined;
|
|
210
|
+
server.close((error) => {
|
|
211
|
+
if (error !== undefined)
|
|
212
|
+
reject(error);
|
|
213
|
+
else if (port === undefined)
|
|
214
|
+
reject(new Error('OPENCODE_PORT_UNAVAILABLE'));
|
|
215
|
+
else
|
|
216
|
+
resolve(port);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
async function waitUntilHealthy(url, authorization, child, timeoutMs) {
|
|
222
|
+
const deadline = Date.now() + timeoutMs;
|
|
223
|
+
while (Date.now() < deadline) {
|
|
224
|
+
if (child.exitCode !== null)
|
|
225
|
+
throw new Error('OPENCODE_SERVER_EXITED');
|
|
226
|
+
try {
|
|
227
|
+
const response = await fetch(url, {
|
|
228
|
+
headers: { authorization },
|
|
229
|
+
signal: AbortSignal.timeout(Math.min(1_000, Math.max(1, deadline - Date.now())))
|
|
230
|
+
});
|
|
231
|
+
if (response.ok)
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
// The child may still be binding its loopback socket.
|
|
236
|
+
}
|
|
237
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
238
|
+
}
|
|
239
|
+
throw new Error('OPENCODE_SERVER_START_TIMEOUT');
|
|
240
|
+
}
|
|
241
|
+
function unwrap(result) {
|
|
242
|
+
if (typeof result !== 'object' || result === null || !('data' in result))
|
|
243
|
+
return result;
|
|
244
|
+
const wrapped = result;
|
|
245
|
+
if (wrapped.error !== undefined)
|
|
246
|
+
throw new Error('OPENCODE_REQUEST_FAILED');
|
|
247
|
+
if (wrapped.data === undefined)
|
|
248
|
+
throw new Error('OPENCODE_RESPONSE_INVALID');
|
|
249
|
+
return wrapped.data;
|
|
250
|
+
}
|
package/dist/operational.js
CHANGED
|
@@ -101,12 +101,19 @@ export function platformCliInvocation(command, args, platform = process.platform
|
|
|
101
101
|
const cliCommand = platformCliCommand(command, platform);
|
|
102
102
|
if (platform !== 'win32' || !/\.(?:cmd|bat)$/i.test(cliCommand))
|
|
103
103
|
return { command: cliCommand, args };
|
|
104
|
-
if (![cliCommand, ...args].every((value) => /^[A-Za-z0-9_
|
|
104
|
+
if (![cliCommand, ...args].every((value) => /^[A-Za-z0-9_@./:\\ -]+$/.test(value)))
|
|
105
105
|
throw new Error('NODE_CLI_COMMAND_INVALID');
|
|
106
|
-
return {
|
|
106
|
+
return {
|
|
107
|
+
command: comspec,
|
|
108
|
+
args: [
|
|
109
|
+
'/d',
|
|
110
|
+
'/s',
|
|
111
|
+
'/c',
|
|
112
|
+
[cliCommand, ...args].map((value) => (value.includes(' ') ? `"${value}"` : value)).join(' ')
|
|
113
|
+
]
|
|
114
|
+
};
|
|
107
115
|
}
|
|
108
116
|
export function nodeLog(event, fields = {}) {
|
|
109
117
|
const safeFields = redactLogValue(fields);
|
|
110
118
|
process.stdout.write(`${JSON.stringify({ timestamp: new Date().toISOString(), level: 'info', component: 'mar-node', event, ...safeFields })}\n`);
|
|
111
119
|
}
|
|
112
|
-
//# sourceMappingURL=operational.js.map
|
package/dist/process-tree.js
CHANGED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { NodeCapabilities, RunnerDefaultConfiguration, RunnerCommandDescriptor, NodeEnvelope, RunnerName, RunnerProfile, RunnerCommandState, SessionActivityState } from '@myagentroam/protocol';
|
|
2
|
+
import type { NodeAgentSession, NodeConversationTurn, NodeDatabase, NodeWorkspace } from '../database.js';
|
|
3
|
+
import type { ChannelDelivery } from '../claude-channel.js';
|
|
4
|
+
import type { CodexSessionControl } from '../codex-app-server.js';
|
|
5
|
+
export type RegisteredRunnerName = RunnerName | 'fake';
|
|
6
|
+
/**
|
|
7
|
+
* Stable lifecycle boundary for every Node Runner implementation.
|
|
8
|
+
*
|
|
9
|
+
* Vendor clients and payload decoders stay behind subclasses. Domain services
|
|
10
|
+
* depend on this boundary (or a capability interface implemented by it), never
|
|
11
|
+
* on an SDK/App Server client directly.
|
|
12
|
+
*/
|
|
13
|
+
export declare abstract class AbstractRunner<TName extends RegisteredRunnerName = RegisteredRunnerName> {
|
|
14
|
+
private control;
|
|
15
|
+
private commandControl;
|
|
16
|
+
abstract readonly name: TName;
|
|
17
|
+
/** Test-only runners are registered explicitly but never advertised. */
|
|
18
|
+
readonly advertised: boolean;
|
|
19
|
+
readonly interceptsRuns: boolean;
|
|
20
|
+
abstract readonly discoveryScope: 'ALL_WORKSPACES' | 'WORKSPACE_REQUIRED';
|
|
21
|
+
abstract profile(capabilities: NodeCapabilities): RunnerProfile | undefined;
|
|
22
|
+
refreshProfile(capabilities: NodeCapabilities, workspace?: NodeWorkspace, environment?: Readonly<Record<string, string>>): Promise<RunnerProfile | undefined>;
|
|
23
|
+
abstract available(capabilities: NodeCapabilities): boolean;
|
|
24
|
+
abstract defaultConfiguration(workspace?: NodeWorkspace): RunnerDefaultConfiguration;
|
|
25
|
+
supportsConfiguration(configuration: RunnerDefaultConfiguration, workspace?: NodeWorkspace): boolean;
|
|
26
|
+
protected abstract profileForValidation(): RunnerProfile;
|
|
27
|
+
abstract resumeExternal(session: NodeAgentSession, context: ExternalResumeContext, force?: boolean): Promise<ExternalResumeResult>;
|
|
28
|
+
abstract discoverSessions(context: RunnerDiscoveryContext, workspace?: NodeWorkspace): Promise<readonly NodeAgentSession[]>;
|
|
29
|
+
abstract readContextUsage(session: NodeAgentSession): Promise<RunnerContextUsage | undefined>;
|
|
30
|
+
presentSession(session: NodeAgentSession, capabilities: NodeCapabilities, context: RunnerSessionPresentationContext): RunnerSessionPresentation;
|
|
31
|
+
prepareMessageInput(input: string, collaborationMode: 'default' | 'plan'): string;
|
|
32
|
+
channelToken(sessionId: string): string | undefined;
|
|
33
|
+
deliverChannel(sessionId: string, messageId: string, senderId: string, token: string, content: string, notify: (runId: string, messageId: string, delivery: ChannelDelivery) => void): ChannelDelivery;
|
|
34
|
+
executeGlobalCommand(commandId: string, input: string): unknown;
|
|
35
|
+
serviceTier(): 'fast' | undefined;
|
|
36
|
+
usesRunnerTitleForRename(): boolean;
|
|
37
|
+
renameNative(session: NodeAgentSession, input: {
|
|
38
|
+
readonly title?: string | null;
|
|
39
|
+
readonly pinned?: boolean;
|
|
40
|
+
}, database: NodeDatabase): Promise<void>;
|
|
41
|
+
removeNative(session: NodeAgentSession, nativeSessionId: string): Promise<boolean>;
|
|
42
|
+
forkNative(session: NodeAgentSession, boundary: string | null, title: string): Promise<string | null>;
|
|
43
|
+
projectedRunnerTitle(metadata: ReturnType<NodeDatabase['getNativeSessionMetadata']>, input: RunnerProjectionTitleInput, database: NodeDatabase | undefined): string | null;
|
|
44
|
+
managedAliasId(nativeSessionId: string): string | undefined;
|
|
45
|
+
nativeAliasesFor(managedSessionId: string): readonly string[];
|
|
46
|
+
forgetNativeAlias(nativeSessionId: string): void;
|
|
47
|
+
rememberManagedNative(session: NodeAgentSession, nativeSessionId: string): void;
|
|
48
|
+
readManagedRunnerTitle(session: NodeAgentSession, nativeSessionId: string): Promise<string | undefined>;
|
|
49
|
+
readOfficialConversation(session: NodeAgentSession, input: {
|
|
50
|
+
readonly cursor?: string;
|
|
51
|
+
readonly limit?: number;
|
|
52
|
+
}, managedTurn: (nativeTurnId: string) => NodeConversationTurn | undefined): Promise<RunnerOfficialConversationPage | undefined>;
|
|
53
|
+
readExternalActivity(session: NodeAgentSession): Promise<SessionActivityState | undefined>;
|
|
54
|
+
acceptsTruncatedNativeHistory(): boolean;
|
|
55
|
+
nativeImageRoot(session: NodeAgentSession): string | undefined;
|
|
56
|
+
managedRunForNativeTurn(nativeTurnId: string): string | undefined;
|
|
57
|
+
abstract stop(): void | Promise<void>;
|
|
58
|
+
bindControl(control: RunnerControl): void;
|
|
59
|
+
acceptsRun(runner: unknown): boolean;
|
|
60
|
+
startManagedRun(envelope: NodeEnvelope): void;
|
|
61
|
+
cancelManagedRun(runId: string, intent: RunnerCancellationIntent): boolean;
|
|
62
|
+
decideApproval(input: RunnerApprovalDecision): boolean;
|
|
63
|
+
respondUserInput(runId: string, requestId: string, answers: Record<string, unknown>): boolean;
|
|
64
|
+
bindCommandControl(control: RunnerCommandControl): void;
|
|
65
|
+
executeCommand(session: NodeAgentSession, command: RunnerCommandDescriptor, input: string, context: {
|
|
66
|
+
readonly secretEnvironment: Readonly<Record<string, string>>;
|
|
67
|
+
}): Promise<unknown>;
|
|
68
|
+
}
|
|
69
|
+
export type RunnerCancellationIntent = 'CANCEL' | 'INTERRUPT';
|
|
70
|
+
export interface RunnerControl {
|
|
71
|
+
start(envelope: NodeEnvelope): void;
|
|
72
|
+
cancel(runId: string, intent: RunnerCancellationIntent): boolean;
|
|
73
|
+
decideApproval?(input: RunnerApprovalDecision): boolean;
|
|
74
|
+
respondUserInput?(runId: string, requestId: string, answers: Record<string, unknown>): boolean;
|
|
75
|
+
}
|
|
76
|
+
export interface RunnerCommandControl {
|
|
77
|
+
execute(session: NodeAgentSession, command: RunnerCommandDescriptor, input: string, context: {
|
|
78
|
+
readonly secretEnvironment: Readonly<Record<string, string>>;
|
|
79
|
+
}): Promise<unknown>;
|
|
80
|
+
}
|
|
81
|
+
export interface RunnerApprovalDecision {
|
|
82
|
+
readonly runId: string;
|
|
83
|
+
readonly approvalId: string;
|
|
84
|
+
readonly decision: 'APPROVED' | 'REJECTED';
|
|
85
|
+
}
|
|
86
|
+
export interface RunnerDiscoveryContext {
|
|
87
|
+
readonly workspaces: readonly NodeWorkspace[];
|
|
88
|
+
createProjection(input: {
|
|
89
|
+
readonly workspace: NodeWorkspace;
|
|
90
|
+
readonly runner: RunnerName;
|
|
91
|
+
readonly externalSessionId: string;
|
|
92
|
+
readonly cwd: string;
|
|
93
|
+
readonly title?: string;
|
|
94
|
+
readonly titleOrigin?: 'OFFICIAL' | 'NATIVE';
|
|
95
|
+
readonly lastActivityAt?: number;
|
|
96
|
+
}): NodeAgentSession;
|
|
97
|
+
}
|
|
98
|
+
export interface ExternalResumeContext {
|
|
99
|
+
readonly available: boolean;
|
|
100
|
+
projectionFor(session: NodeAgentSession): NodeAgentSession | undefined;
|
|
101
|
+
managedAliasFor(session: NodeAgentSession): NodeAgentSession | undefined;
|
|
102
|
+
currentResumed(sessionId: string): NodeAgentSession | undefined;
|
|
103
|
+
promote(sessionId: string): NodeAgentSession | undefined;
|
|
104
|
+
ensureConfiguration(session: NodeAgentSession): NodeAgentSession;
|
|
105
|
+
create(session: NodeAgentSession): NodeAgentSession;
|
|
106
|
+
workspacePath(workspaceId: string): string | undefined;
|
|
107
|
+
rememberManagedNative(nativeSessionId: string, sessionId: string): void;
|
|
108
|
+
}
|
|
109
|
+
export interface ExternalResumeResult {
|
|
110
|
+
readonly session?: NodeAgentSession;
|
|
111
|
+
readonly activity?: 'IDLE' | 'EXTERNAL_ACTIVE' | 'UNAVAILABLE';
|
|
112
|
+
readonly failureCode?: string;
|
|
113
|
+
readonly failureDetail?: string;
|
|
114
|
+
readonly messageFailureCode?: string;
|
|
115
|
+
}
|
|
116
|
+
export interface RunnerContextUsage {
|
|
117
|
+
readonly model?: string | null;
|
|
118
|
+
readonly usedTokens: number;
|
|
119
|
+
readonly maxTokens: number;
|
|
120
|
+
}
|
|
121
|
+
export interface RunnerProjectionTitleInput {
|
|
122
|
+
readonly externalSessionId: string;
|
|
123
|
+
readonly workspaceId: string;
|
|
124
|
+
readonly title?: string;
|
|
125
|
+
readonly titleOrigin?: 'OFFICIAL' | 'NATIVE';
|
|
126
|
+
}
|
|
127
|
+
export interface RunnerOfficialConversationPage {
|
|
128
|
+
readonly turns: readonly NodeConversationTurn[];
|
|
129
|
+
readonly nextCursor: string | null;
|
|
130
|
+
}
|
|
131
|
+
export interface RunnerSessionPresentationContext {
|
|
132
|
+
readonly managedActive: boolean;
|
|
133
|
+
readonly externalActivity: SessionActivityState | undefined;
|
|
134
|
+
readonly commandStates: readonly RunnerCommandState[];
|
|
135
|
+
}
|
|
136
|
+
export interface RunnerSessionPresentation {
|
|
137
|
+
readonly activity: SessionActivityState;
|
|
138
|
+
readonly control: {
|
|
139
|
+
readonly level: CodexSessionControl['level'];
|
|
140
|
+
readonly capabilities: CodexSessionControl['capabilities'];
|
|
141
|
+
};
|
|
142
|
+
readonly runtime: {
|
|
143
|
+
readonly level: CodexSessionControl['level'];
|
|
144
|
+
readonly capabilities: readonly string[];
|
|
145
|
+
readonly reasonCode: string | null;
|
|
146
|
+
};
|
|
147
|
+
readonly commandStates: readonly RunnerCommandState[];
|
|
148
|
+
}
|
|
149
|
+
export declare class RunnerCapabilityError extends Error {
|
|
150
|
+
readonly runner: RegisteredRunnerName;
|
|
151
|
+
readonly capability: string;
|
|
152
|
+
constructor(runner: RegisteredRunnerName, capability: string);
|
|
153
|
+
}
|