@myagentroam/node 0.9.62 → 0.9.65
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/LICENSE +202 -0
- package/NOTICE +4 -0
- package/dist/capabilities.js +110 -2
- package/dist/claude-agent-sdk.js +330 -2
- package/dist/claude-channel.js +44 -2
- package/dist/codex-app-server.js +510 -2
- package/dist/config.js +106 -2
- package/dist/connector/node-connector-options.js +1 -2
- package/dist/connector/node-operation-router.js +22 -2
- package/dist/connector.js +1736 -2
- package/dist/control-outbound-scheduler.js +153 -2
- package/dist/database.js +639 -2
- package/dist/fake-runner.js +129 -2
- package/dist/git-askpass.js +12 -0
- package/dist/git-execution-helper.js +266 -0
- package/dist/health.js +3 -2
- package/dist/main.js +57 -2
- package/dist/mcp-installation-model.js +1 -2
- package/dist/migrations/v004.js +55 -3
- package/dist/migrations/v005.js +13 -3
- package/dist/migrations/v006.js +90 -3
- package/dist/migrations/v007.js +22 -3
- package/dist/migrations/v008.js +18 -3
- package/dist/migrations/v009.js +70 -4
- package/dist/migrations/v010.js +43 -3
- package/dist/migrations/v011.js +20 -3
- package/dist/migrations/v012.js +8 -0
- package/dist/native-jsonl-reader.js +186 -2
- package/dist/native-session-history.js +1143 -2
- package/dist/opencode-runtime.js +80 -2
- package/dist/opencode-server.js +259 -2
- package/dist/operational.js +122 -2
- package/dist/process-tree.js +18 -2
- package/dist/ripgrep-binary.js +31 -2
- package/dist/rotating-log.js +67 -2
- package/dist/runner/abstract-runner.js +260 -2
- package/dist/runner/claude/managed-run-controller.js +355 -2
- package/dist/runner/claude/tool-normalizer.js +120 -2
- package/dist/runner/claude-code-runner.js +303 -2
- package/dist/runner/codex/composer-image-markers.js +8 -2
- package/dist/runner/codex/conversation-parser.js +913 -2
- package/dist/runner/codex/managed-run-controller.js +494 -2
- package/dist/runner/codex/tool-normalizer.js +214 -2
- package/dist/runner/codex-runner.js +585 -2
- package/dist/runner/fake-test-runner.js +47 -2
- package/dist/runner/mar-agent/conversation-normalizer.js +644 -2
- package/dist/runner/mar-agent/executor-stdio-client-transport.js +139 -2
- package/dist/runner/mar-agent/generated-images.js +41 -2
- package/dist/runner/mar-agent/history-projection.js +106 -2
- package/dist/runner/mar-agent/hosted-conversation-runtime.js +446 -2
- package/dist/runner/mar-agent/managed-run-controller.js +785 -2
- package/dist/runner/mar-agent/mcp-client-pool.js +233 -2
- package/dist/runner/mar-agent/runtime-model-factory.js +326 -2
- package/dist/runner/mar-agent-runner.js +458 -2
- package/dist/runner/opencode/conversation-parser.js +221 -2
- package/dist/runner/opencode/managed-run-controller.js +588 -2
- package/dist/runner/opencode/tool-normalizer.js +80 -2
- package/dist/runner/opencode-runner.js +426 -2
- package/dist/runner/runner-registry.js +87 -2
- package/dist/runner-command-engine.js +71 -2
- package/dist/runner-profiles.js +323 -2
- package/dist/runner-usage.js +190 -2
- package/dist/runtime-command-detector.js +73 -2
- package/dist/runtime-model.js +1 -2
- package/dist/runtime-state.js +1509 -2
- package/dist/service/attachment-domain-state.js +7 -2
- package/dist/service/bubblewrap-sandbox-executor.js +543 -2
- package/dist/service/continuous-run-coordinator.js +425 -2
- package/dist/service/conversation-history-service.js +552 -2
- package/dist/service/conversation-hosting-capability-service.js +61 -2
- package/dist/service/conversation-hosting-enablement-service.js +91 -2
- package/dist/service/conversation-hosting-maintenance-service.js +174 -2
- package/dist/service/conversation-segment-service.js +162 -2
- package/dist/service/conversation-template-node-service.js +992 -2
- package/dist/service/external-session-resume-service.js +152 -2
- package/dist/service/fake-managed-run-service.js +60 -2
- package/dist/service/git-execution-service.js +418 -0
- package/dist/service/git-operation-service.js +57 -0
- package/dist/service/hosted-conversation-node-service.js +1982 -2
- package/dist/service/hosted-conversation-store.js +1405 -2
- package/dist/service/managed-runner-session-service.js +35 -2
- package/dist/service/mar-agent-model-transport-test-service.js +88 -2
- package/dist/service/mcp-installation-verifier.js +87 -2
- package/dist/service/mcp-node-operation-service.js +90 -2
- package/dist/service/mission-mcp-server.js +124 -2
- package/dist/service/mission-mcp-stdio.js +40 -2
- package/dist/service/mission-runtime.js +142 -2
- package/dist/service/native-session-projection-service.js +274 -2
- package/dist/service/native-session-watch-service.js +683 -2
- package/dist/service/node-connection-lifecycle-service.js +88 -2
- package/dist/service/node-control-channel.js +85 -2
- package/dist/service/node-control-message-service.js +109 -2
- package/dist/service/node-request-service.js +62 -2
- package/dist/service/node-run-event-bridge.js +88 -2
- package/dist/service/node-terminal-channel.js +202 -2
- package/dist/service/node-upgrade-coordinator.js +137 -2
- package/dist/service/node-workspace-coordinator.js +97 -2
- package/dist/service/recent-run-projection-service.js +99 -2
- package/dist/service/run-attachment-service.js +321 -2
- package/dist/service/run-domain-state.js +4 -2
- package/dist/service/run-event-service.js +246 -2
- package/dist/service/run-ownership-policy.js +22 -2
- package/dist/service/run-workbench-service.js +120 -2
- package/dist/service/runner-channel-message-service.js +20 -2
- package/dist/service/runner-interaction-service.js +243 -2
- package/dist/service/runner-service.js +69 -2
- package/dist/service/runtime-credential-control-service.js +95 -2
- package/dist/service/session-catalog-service.js +404 -2
- package/dist/service/session-command-service.js +125 -2
- package/dist/service/session-context-service.js +128 -2
- package/dist/service/session-domain-state.js +9 -2
- package/dist/service/session-identity-service.js +86 -2
- package/dist/service/session-lifecycle-service.js +248 -2
- package/dist/service/session-message-service.js +270 -2
- package/dist/service/session-presentation-service.js +43 -2
- package/dist/service/session-query-service.js +82 -2
- package/dist/service/session-watch-slot-coordinator.js +21 -2
- package/dist/service/skill-directory-service.js +238 -2
- package/dist/service/skill-install-service.js +434 -2
- package/dist/service/skill-node-operation-service.js +206 -2
- package/dist/service/terminal-relay-state.js +3 -2
- package/dist/service/terminal-service.js +90 -2
- package/dist/service/web-search-error.js +40 -2
- package/dist/service/workbench-event-service.js +21 -2
- package/dist/service/workbench-manifest-service.js +69 -2
- package/dist/service/workspace-change-service.js +290 -2
- package/dist/service/workspace-content-search-service.js +457 -2
- package/dist/service/workspace-domain-state.js +41 -2
- package/dist/service/workspace-file-service.js +150 -2
- package/dist/service/workspace-git-exclude-service.js +119 -2
- package/dist/service/workspace-queue-workbench-service.js +176 -2
- package/dist/service/workspace-service.js +92 -2
- package/dist/service/workspace-session-service.js +154 -2
- package/dist/service/workspace-upload-service.js +262 -2
- package/dist/service/workspace-watch-controller.js +68 -2
- package/dist/service/workspace-watch-service.js +170 -2
- package/dist/service/workspace-watch-snapshot-service.js +218 -2
- package/dist/service/workspace-workbench-service.js +239 -2
- package/dist/service.js +91 -2
- package/dist/storage.js +32 -2
- package/dist/supervisor.js +136 -2
- package/dist/terminal.js +496 -2
- package/dist/util/git-credential-client.js +32 -0
- package/dist/util/node-operation-data.js +9 -2
- package/dist/util/node-operation-parsers.js +382 -2
- package/dist/util/personal-instructions.js +34 -2
- package/dist/util/runner-error.js +70 -2
- package/dist/util/runner-native-session-parsers.js +510 -2
- package/dist/util/safe-error.js +8 -2
- package/dist/util/secret-environment.js +25 -2
- package/dist/workspace.js +1793 -2
- package/package.json +11 -8
- package/vendor/ripgrep/darwin-arm64/rg +0 -0
- package/vendor/ripgrep/darwin-x64/rg +0 -0
- package/vendor/ripgrep/manifest.json +12 -0
- package/README.md +0 -42
- package/dist/event-buffer.js +0 -2
- package/dist/migrations/v001.js +0 -107
- package/dist/migrations/v002.js +0 -2
- package/dist/migrations/v003.js +0 -2
- package/dist/service/mcp-package-installer.js +0 -2
package/dist/opencode-runtime.js
CHANGED
|
@@ -1,2 +1,80 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
+
}
|
package/dist/opencode-server.js
CHANGED
|
@@ -1,2 +1,259 @@
|
|
|
1
|
-
|
|
2
|
-
const _0x45dd41=_0x3577;(function(_0x53f654,_0x37a96e){const _0x2adc7e=_0x3577,_0x18bd37=_0x53f654();while(!![]){try{const _0x7ff7b7=parseInt(_0x2adc7e(0x1b0))/(-0x15f3+-0x29*0xda+0x38de)+-parseInt(_0x2adc7e(0x17a))/(-0x2634+-0x377*0x7+0x3e77*0x1)*(parseInt(_0x2adc7e(0x169))/(-0x259*-0xe+-0x176b+-0x970))+-parseInt(_0x2adc7e(0x1bd))/(-0x332*-0x2+0xb97+-0x11f7)*(parseInt(_0x2adc7e(0x1a3))/(-0x569+-0x16f5+0xa9*0x2b))+-parseInt(_0x2adc7e(0x16c))/(0x19*-0xa3+-0x35f*0x9+0x2e48)+-parseInt(_0x2adc7e(0x195))/(-0x2*0xee5+-0xf7e*0x1+0x2d4f)+-parseInt(_0x2adc7e(0x16d))/(-0x11*-0x1c1+0x2d7*-0x2+-0x181b)+parseInt(_0x2adc7e(0x1c2))/(-0xc7a*0x1+0x1*-0x20dd+0x2d60);if(_0x7ff7b7===_0x37a96e)break;else _0x18bd37['push'](_0x18bd37['shift']());}catch(_0x565e33){_0x18bd37['push'](_0x18bd37['shift']());}}}(_0x11e6,-0x3d*0x7160+0x1a76ed+0xfa383));import{spawn}from'node:child_process';function _0x11e6(){const _0xf53595=['nda4ntq0ndDVB29mCuG','y2XVC2u','BwnW','y3jLyxrL','Dg9tDhjPBMC','zgf0yq','yMfZzty0DxjS','CMvXDwLYzunSAwvUDa','Cg9YDa','zMLSzq','zw52AxjVBM1LBNq','BM93','l2DSB2jHBc9OzwfSDgG','CMvWBhLrDwvZDgLVBG','C3vIC2nYAwjL','y2XPzw50','C3rHCNq','BMfTzq','ChjVDMLKzxjZ','BwLU','CMvQzwn0','o2jHC2u2ncW','BgLZDa','DMfYAwfUDa','zgf0ytO','B3bLBMnVzgu','qMfZAwmG','mZi4mJG2mvHwu1vczW','CMvWBhLqzxjTAxnZAw9U','ywDLBNq','ndeZodi2mgPItvLgEa','mte2ntGZntjeC2n5z2y','Aw5KzxHpzG','qvbquK9wruq','ChjVBxb0','zxHPDenVzgu','C3LZDgvT','C2XPy2u','y2HPBgq','AxnbCNjHEq','zgvSzxrL','y29TBwfUza','ChjVBxb0qxn5BMm','zgf0yujHC2u2na','mKHequ51vq','B3bLBMnVzgu6','y29UzMLNDxjLrw52AxjVBM1LBNq','CgvYBwLZC2LVBG','mti3lJaUmc4X','C3vTBwfYAxPL','zgLYzwn0B3j5','B3b0Aw9UCW','Dhj1zq','zgvSzxrLu2vZC2LVBG','BgvUz3rO','C2vZC2LVBKLe','zxzLBNq','Bwf4','t1bftKnprevFu0vsvKvsx1nuqvjux1rjtuvpvvq','t1bftKnprevFuKvtue9ou0vFsu5wquXjra','yxjNCW','t1bftKnprevFu0vsvKvsx0fmuKvbrfLFu1rbuLrfra','zM9YA1nLC3nPB24','t1bftKnprevFu0vsvKvsx0vysvrfra','ywrKCMvZCW','Ahr0CdOVlZeYnY4WlJaUmtO','t1bftKnprevFuKvrvuvtvf9gquLmruq','ywrK','ls1OB3n0BMfTzq','DxbKyxrLu2vZC2LVBG','y29UzMLNDxjLtwnW','ntyYnZiYm1zVuwjtBW','C3rYzwfT','yxr0ywnOBwvUDhm','CgLK','C3bHD25qCM9JzxnZ','zM9YAW','C3rHCNrtzxj2zxi','C3rVCa','BwvZC2fNzxm','AwDUB3jL','BwLTzq','ls1WB3j0','DxbKyxrL','B25Jzq','mtK1nvHwr1fqsa','CgXHDgzVCM0','A2LSBa','ChjVDMLKzxi','zxjYB3i','u0Lhvevstq','zNjVBq','CgLWzq','ywjVCNq','t1bftKnprevFue9svf9vtKfwquLmqujmrq','C2vZC2LVBG','BgLZDgvU','BwfW','mtm0mdCZn01euezjtG','D2LUmZi','B2jQzwn0','zw50CMLLCW','CxvLC3rPB24','DgLTzw91Da','C3rHCNruAw1LB3v0txm','y3jLyxrLu2vZC2LVBG','CMvQzwn0uxvLC3rPB24','Bw9KzwW','t1bftKnprevFtu9eruXFsu5wquXjra','CMvWBhK','Dgv4Da','ody1nLPOvgrusG','C3rHCNrPBMC','C2vZC2LVBNm','yMfZzty0','C2vYDMu'];_0x11e6=function(){return _0xf53595;};return _0x11e6();}import{randomBytes}from'node:crypto';import{createServer}from'node:net';import{minimalRunnerEnvironment,platformCliInvocation}from'./operational.js';import{terminateProcessTree}from'./process-tree.js';const START_TIMEOUT_MS=-0x1*-0x49e7+-0x3e61+0x1b8a;export class OpenCodeServerClient{[_0x45dd41(0x181)];[_0x45dd41(0x174)];[_0x45dd41(0x1d1)];[_0x45dd41(0x1be)];[_0x45dd41(0x1cc)]={};constructor(_0x367bbd={}){const _0x531e8a=_0x45dd41;this[_0x531e8a(0x181)]=_0x367bbd;}[_0x45dd41(0x17c)](_0xd45fcc){const _0x1e18af=_0x45dd41;if(this[_0x1e18af(0x174)]!==undefined)throw new Error(_0x1e18af(0x18b));this[_0x1e18af(0x1cc)]={..._0xd45fcc};}async[_0x45dd41(0x1d2)](){const _0x29348d=_0x45dd41;if(this[_0x29348d(0x1d1)]!==undefined)return;if(this[_0x29348d(0x1be)]!==undefined)return this[_0x29348d(0x1be)];const _0x360e76=this[_0x29348d(0x19b)]();this[_0x29348d(0x1be)]=_0x360e76;try{await _0x360e76;}finally{if(this[_0x29348d(0x1be)]===_0x360e76)this[_0x29348d(0x1be)]=undefined;}}async[_0x45dd41(0x19b)](){const _0x168379=_0x45dd41,{createOpencodeClient:_0x2fa3e6}=await import('@opencode-ai/sdk/v2/client'),_0x3ad8e6=await availablePort(),_0x2cf6e8=randomBytes(-0x1682+0x17*0xf5+-0x97*-0x1)[_0x168379(0x1c6)](_0x168379(0x1c8)),_0x1bc4b5=platformCliInvocation(this[_0x168379(0x181)][_0x168379(0x177)]??_0x168379(0x167),[_0x168379(0x1c1),_0x168379(0x192),_0x168379(0x17e),_0x168379(0x1a0),String(_0x3ad8e6)]),_0x4c6ab2=this[_0x168379(0x181)][_0x168379(0x199)]??spawn,_0x1d6058=_0x4c6ab2(_0x1bc4b5[_0x168379(0x177)],_0x1bc4b5[_0x168379(0x18a)],{'windowsHide':!![],'detached':process[_0x168379(0x1a4)]!==_0x168379(0x1b1),'env':{...minimalRunnerEnvironment(),...this[_0x168379(0x1cc)],'OPENCODE_SERVER_USERNAME':_0x168379(0x167),'OPENCODE_SERVER_PASSWORD':_0x2cf6e8,'OPENCODE_DISABLE_AUTOUPDATE':_0x168379(0x182)},'stdio':[_0x168379(0x19e),_0x168379(0x1aa),_0x168379(0x1aa)]});this[_0x168379(0x174)]=_0x1d6058;const _0x40cda1=_0x168379(0x168)+Buffer[_0x168379(0x1a9)](_0x168379(0x17b)+_0x2cf6e8)[_0x168379(0x1c6)](_0x168379(0x1c0)),_0x5582f6=_0x2fa3e6({'baseUrl':_0x168379(0x18f)+_0x3ad8e6,'headers':{'authorization':_0x40cda1},'throwOnError':!![]});try{await waitUntilHealthy(_0x168379(0x18f)+_0x3ad8e6+_0x168379(0x1ce),_0x40cda1,_0x1d6058,this[_0x168379(0x181)][_0x168379(0x1b6)]??START_TIMEOUT_MS),this[_0x168379(0x1d1)]=_0x5582f6;}catch(_0x4c819d){await this[_0x168379(0x19c)]();throw _0x4c819d;}}async[_0x45dd41(0x1d4)](_0x4ac3e2){const _0x4474b6=_0x45dd41,_0x36f373=await this[_0x4474b6(0x1c9)]();return unwrap(await _0x36f373[_0x4474b6(0x1a6)][_0x4474b6(0x164)]({'directory':_0x4ac3e2}));}async[_0x45dd41(0x194)](_0x528e71,_0x3fbfb8){const _0x182c1e=_0x45dd41,_0x5b4f0c=await this[_0x182c1e(0x1c9)]();if(typeof _0x3fbfb8!==_0x182c1e(0x1b2)||_0x3fbfb8===null||Array[_0x182c1e(0x175)](_0x3fbfb8))return;for(const [_0x1b49a0,_0x416ea3]of Object[_0x182c1e(0x1b3)](_0x3fbfb8)){await _0x5b4f0c[_0x182c1e(0x1c4)][_0x182c1e(0x191)]({'directory':_0x528e71,'name':_0x1b49a0,'config':_0x416ea3});}}async[_0x45dd41(0x1bf)](_0x137836,_0x27e511=0xc1e+0x22f+-0xde9){const _0x1d7668=_0x45dd41,_0x4b282c=await this[_0x1d7668(0x1c9)]();return unwrap(await _0x4b282c[_0x1d7668(0x1ad)][_0x1d7668(0x164)]({'directory':_0x137836,'limit':_0x27e511}));}async[_0x45dd41(0x1b7)](_0x470e65,_0x2e54dd){const _0x40ea0c=_0x45dd41,_0x49dc90=await this[_0x40ea0c(0x1c9)]();return unwrap(await _0x49dc90[_0x40ea0c(0x1ad)][_0x40ea0c(0x1c5)]({'directory':_0x470e65,..._0x2e54dd===undefined?{}:{'title':_0x2e54dd}}));}async[_0x45dd41(0x18c)](_0x21b777,_0x2ea5ef,_0x3e97ca){const _0x2f1048=_0x45dd41,_0x249dd0=await this[_0x2f1048(0x1c9)]();return unwrap(await _0x249dd0[_0x2f1048(0x1ad)][_0x2f1048(0x19a)]({'directory':_0x21b777,'sessionID':_0x2ea5ef,'messageID':_0x3e97ca}));}async[_0x45dd41(0x19d)](_0x3ac036,_0x3dea82,_0x412ea=0x2*0xce3+-0x1f*0x1+-0x1943){const _0x5d7dfe=_0x45dd41,_0x3790c1=await this[_0x5d7dfe(0x1c9)]();return unwrap(await _0x3790c1[_0x5d7dfe(0x1ad)][_0x5d7dfe(0x19d)]({'directory':_0x3ac036,'sessionID':_0x3dea82,'limit':_0x412ea}));}async[_0x45dd41(0x193)](_0x383f98,_0x2fd80a,_0x5634c9){const _0xbfc8f7=_0x45dd41,_0x1d9c15=await this[_0xbfc8f7(0x1c9)]();return unwrap(await _0x1d9c15[_0xbfc8f7(0x1ad)][_0xbfc8f7(0x1a1)]({'directory':_0x383f98,'sessionID':_0x2fd80a,'title':_0x5634c9}));}async[_0x45dd41(0x183)](_0x3a34d0,_0x1d5e85){const _0x266bc4=_0x45dd41,_0x146660=await this[_0x266bc4(0x1c9)]();return unwrap(await _0x146660[_0x266bc4(0x1ad)][_0x266bc4(0x176)]({'directory':_0x3a34d0,'sessionID':_0x1d5e85}));}async[_0x45dd41(0x1ab)](_0x35b252,_0x5d13c9){const _0x438ac2=_0x45dd41,_0x3a7d7e=await this[_0x438ac2(0x1c9)]();return unwrap(await _0x3a7d7e[_0x438ac2(0x1ad)][_0x438ac2(0x1ab)]({'directory':_0x35b252,'sessionID':_0x5d13c9}));}async[_0x45dd41(0x17f)](_0x5a233b,_0x1e08f7,_0x227e01){const _0x12ee71=_0x45dd41,_0x4e843f=await this[_0x12ee71(0x1c9)](),_0x46d537=_0x227e01[_0x12ee71(0x16e)]('/');if(_0x46d537<=0x15bd*-0x1+0x188*-0x17+0x38f5||_0x46d537===_0x227e01[_0x12ee71(0x184)]-(-0x39e+0x1*-0xe7f+-0x305*-0x6))throw new Error(_0x12ee71(0x1ba));return unwrap(await _0x4e843f[_0x12ee71(0x1ad)][_0x12ee71(0x17f)]({'directory':_0x5a233b,'sessionID':_0x1e08f7,'providerID':_0x227e01[_0x12ee71(0x173)](0x189a+0x3f1+-0x1c8b,_0x46d537),'modelID':_0x227e01[_0x12ee71(0x173)](_0x46d537+(-0x13*0x1d+0x19b1+-0xf1*0x19))}));}async[_0x45dd41(0x16a)](_0x112437,_0x4c1cbe,_0x43801c){const _0xdc1ff1=_0x45dd41,_0x4e8883=await this[_0xdc1ff1(0x1c9)]();return unwrap(await _0x4e8883[_0xdc1ff1(0x17d)][_0xdc1ff1(0x1bb)]({'directory':_0x112437,'requestID':_0x4c1cbe,'reply':_0x43801c===_0xdc1ff1(0x16f)?_0xdc1ff1(0x1a2):_0xdc1ff1(0x1d6)}));}async[_0x45dd41(0x1cf)](_0x327c20,_0x4e4633,_0x14e067){const _0x172a5a=_0x45dd41,_0x2a68b3=await this[_0x172a5a(0x1c9)]();return unwrap(await _0x2a68b3[_0x172a5a(0x1b4)][_0x172a5a(0x1bb)]({'directory':_0x327c20,'requestID':_0x4e4633,'answers':_0x14e067[_0x172a5a(0x1af)](_0x472f6d=>[..._0x472f6d])}));}async[_0x45dd41(0x1b8)](_0x1d4e82,_0x3e08b9){const _0x3a638d=_0x45dd41,_0x12af90=await this[_0x3a638d(0x1c9)]();return unwrap(await _0x12af90[_0x3a638d(0x1b4)][_0x3a638d(0x1d6)]({'directory':_0x1d4e82,'requestID':_0x3e08b9}));}async[_0x45dd41(0x170)](_0x19d6ae){const _0x476991=_0x45dd41,_0x26b88a=await this[_0x476991(0x1c9)](),_0x2fdf94=_0x19d6ae[_0x476991(0x1b9)][_0x476991(0x16e)]('/');if(_0x2fdf94<=0x13*-0x14+-0x10b*0x1+0x287||_0x2fdf94===_0x19d6ae[_0x476991(0x1b9)][_0x476991(0x184)]-(-0x252d*0x1+-0x96c+-0xa*-0x4a9))throw new Error(_0x476991(0x1ba));return unwrap(await _0x26b88a[_0x476991(0x1ad)][_0x476991(0x178)]({'directory':_0x19d6ae[_0x476991(0x180)],'sessionID':_0x19d6ae[_0x476991(0x185)],'model':{'providerID':_0x19d6ae[_0x476991(0x1b9)][_0x476991(0x173)](0x1689+0x8a5*-0x4+0xc0b,_0x2fdf94),'modelID':_0x19d6ae[_0x476991(0x1b9)][_0x476991(0x173)](_0x2fdf94+(0x5*0xcd+-0x15*-0x11d+0x1*-0x1b61))},..._0x19d6ae[_0x476991(0x165)]===undefined||_0x19d6ae[_0x476991(0x165)][_0x476991(0x184)]===0xa86+0x1aa5+-0x252b?{}:{'variant':_0x19d6ae[_0x476991(0x165)]},..._0x19d6ae[_0x476991(0x16b)]===undefined?{}:{'agent':_0x19d6ae[_0x476991(0x16b)]},..._0x19d6ae[_0x476991(0x172)]===undefined?{}:{'system':_0x19d6ae[_0x476991(0x172)]},'parts':[{'type':_0x476991(0x1bc),'text':_0x19d6ae[_0x476991(0x1bc)]},...(_0x19d6ae[_0x476991(0x197)]??[])[_0x476991(0x1af)](_0x6869d2=>({'type':_0x476991(0x1cb),'mime':_0x6869d2[_0x476991(0x19f)],'filename':_0x6869d2[_0x476991(0x1d3)],'url':_0x476991(0x166)+_0x6869d2[_0x476991(0x19f)]+_0x476991(0x1d7)+_0x6869d2[_0x476991(0x179)]}))]}));}async[_0x45dd41(0x1d0)](_0x53b867,_0x3c2a0f,_0x36c54b,_0x342b4){const _0x1bd6a4=_0x45dd41,_0xa1ef0f=await this[_0x1bd6a4(0x1c9)](),_0x386d46=await _0xa1ef0f[_0x1bd6a4(0x186)][_0x1bd6a4(0x1d0)]({'directory':_0x53b867},{'signal':_0x3c2a0f});_0x342b4?.();for await(const _0x1db500 of _0x386d46[_0x1bd6a4(0x196)])_0x36c54b(_0x1db500);}async[_0x45dd41(0x19c)](){const _0x41cdf7=_0x45dd41;this[_0x41cdf7(0x1d1)]=undefined;const _0x4fab52=this[_0x41cdf7(0x174)];this[_0x41cdf7(0x174)]=undefined;if(_0x4fab52===undefined)return;if(_0x4fab52[_0x41cdf7(0x198)]===undefined){_0x4fab52[_0x41cdf7(0x1a5)](_0x41cdf7(0x1a8));return;}try{await terminateProcessTree({'pid':_0x4fab52[_0x41cdf7(0x198)],'kill':_0x42ed24=>_0x4fab52[_0x41cdf7(0x1a5)](_0x42ed24)});}catch{_0x4fab52[_0x41cdf7(0x1a5)](_0x41cdf7(0x1a8));}}async[_0x45dd41(0x1c9)](){const _0x14e733=_0x45dd41;return await this[_0x14e733(0x1d2)](),this[_0x14e733(0x1d1)];}}async function availablePort(){return new Promise((_0x5c8fbe,_0x79d0d6)=>{const _0x14ca1b=_0x3577,_0x407255=createServer();_0x407255[_0x14ca1b(0x1a2)](_0x14ca1b(0x1a7),_0x79d0d6),_0x407255[_0x14ca1b(0x1ae)](0x1bd+0x49b*-0x3+0xc14,_0x14ca1b(0x17e),()=>{const _0x43777a=_0x14ca1b,_0x23fff7=_0x407255[_0x43777a(0x18e)](),_0x3ecf0f=typeof _0x23fff7===_0x43777a(0x1b2)&&_0x23fff7!==null?_0x23fff7[_0x43777a(0x1ca)]:undefined;_0x407255[_0x43777a(0x1c3)](_0x48c2fd=>{const _0x46834f=_0x43777a;if(_0x48c2fd!==undefined)_0x79d0d6(_0x48c2fd);else{if(_0x3ecf0f===undefined)_0x79d0d6(new Error(_0x46834f(0x1ac)));else _0x5c8fbe(_0x3ecf0f);}});});});}function _0x3577(_0x3c88dc,_0x5034d6){_0x3c88dc=_0x3c88dc-(0xb6c+-0xef*0xf+0x3f9);const _0x22fbc2=_0x11e6();let _0x1a1275=_0x22fbc2[_0x3c88dc];if(_0x3577['goLXNT']===undefined){var _0x2949b0=function(_0x2c1bce){const _0x44e68c='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x568941='',_0x229006='';for(let _0x539050=0x24a4+0x1355+-0x7ff*0x7,_0x1d8226,_0xeae82a,_0x16187c=0xb7d+-0x1325+0x7a8;_0xeae82a=_0x2c1bce['charAt'](_0x16187c++);~_0xeae82a&&(_0x1d8226=_0x539050%(0x17*0xf5+-0x146*0xe+0x1*-0x42b)?_0x1d8226*(-0x17d6+-0x1*0x26ea+0x24*0x1c0)+_0xeae82a:_0xeae82a,_0x539050++%(0x2516+-0x2365+-0x1ad))?_0x568941+=String['fromCharCode'](0x9e*0x1f+0x24ba+-0x36dd&_0x1d8226>>(-(0x1*-0xe7f+-0x7f*0x2+0x1*0xf7f)*_0x539050&-0x17f5+-0xea0+0x1*0x269b)):-0x1df*0x8+-0x57f+0x1477){_0xeae82a=_0x44e68c['indexOf'](_0xeae82a);}for(let _0x4ae0d1=-0x373*-0x1+0x1209+-0x157c,_0xf4de38=_0x568941['length'];_0x4ae0d1<_0xf4de38;_0x4ae0d1++){_0x229006+='%'+('00'+_0x568941['charCodeAt'](_0x4ae0d1)['toString'](-0x96c+0x1287+-0x5*0x1cf))['slice'](-(0x1689+0x8a5*-0x4+0xc0d));}return decodeURIComponent(_0x229006);};_0x3577['HBmmDE']=_0x2949b0,_0x3577['jmNruO']={},_0x3577['goLXNT']=!![];}const _0x31cf75=_0x22fbc2[0x5*0xcd+-0x15*-0x11d+0x2*-0xdb1];_0x3577['pnZsAG']!==_0x31cf75&&(_0x3577['jmNruO']={},_0x3577['pnZsAG']=_0x31cf75);const _0x1ed367=_0x3577['jmNruO'][_0x3c88dc];return _0x1ed367===undefined?(_0x1a1275=_0x3577['HBmmDE'](_0x1a1275),_0x3577['jmNruO'][_0x3c88dc]=_0x1a1275):_0x1a1275=_0x1ed367,_0x1a1275;}async function waitUntilHealthy(_0xcefd54,_0x1be1a3,_0x53a5a7,_0x1ac140){const _0x211b98=_0x45dd41,_0x2e498e=Date[_0x211b98(0x1cd)]()+_0x1ac140;while(Date[_0x211b98(0x1cd)]()<_0x2e498e){if(_0x53a5a7[_0x211b98(0x171)]!==null)throw new Error(_0x211b98(0x18d));try{const _0x53f697=await fetch(_0xcefd54,{'headers':{'authorization':_0x1be1a3},'signal':AbortSignal[_0x211b98(0x1b5)](Math[_0x211b98(0x1d5)](0x1d65+-0xee5+-0x153*0x8,Math[_0x211b98(0x187)](-0x1f35+-0x74c+0x2682,_0x2e498e-Date[_0x211b98(0x1cd)]())))});if(_0x53f697['ok'])return;}catch{}await new Promise(_0x579984=>setTimeout(_0x579984,-0x3*0x52c+-0xc94*0x3+0x3572*0x1));}throw new Error(_0x211b98(0x188));}function unwrap(_0x45fba8){const _0x3ed548=_0x45dd41;if(typeof _0x45fba8!==_0x3ed548(0x1b2)||_0x45fba8===null||!(_0x3ed548(0x1c7)in _0x45fba8))return _0x45fba8;const _0x516297=_0x45fba8;if(_0x516297[_0x3ed548(0x1a7)]!==undefined)throw new Error(_0x3ed548(0x190));if(_0x516297[_0x3ed548(0x1c7)]===undefined)throw new Error(_0x3ed548(0x189));return _0x516297[_0x3ed548(0x1c7)];}
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { randomBytes } from 'node:crypto';
|
|
3
|
+
import { createServer } from 'node:net';
|
|
4
|
+
import { minimalRunnerEnvironment, platformCliInvocation } from './operational.js';
|
|
5
|
+
import { terminateProcessTree } from './process-tree.js';
|
|
6
|
+
const START_TIMEOUT_MS = 10_000;
|
|
7
|
+
export class OpenCodeServerClient {
|
|
8
|
+
options;
|
|
9
|
+
child;
|
|
10
|
+
client;
|
|
11
|
+
starting;
|
|
12
|
+
environment = {};
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
this.options = options;
|
|
15
|
+
}
|
|
16
|
+
configureEnvironment(environment) {
|
|
17
|
+
if (this.child !== undefined)
|
|
18
|
+
throw new Error('OPENCODE_SERVER_ALREADY_STARTED');
|
|
19
|
+
this.environment = { ...environment };
|
|
20
|
+
}
|
|
21
|
+
async start() {
|
|
22
|
+
if (this.client !== undefined)
|
|
23
|
+
return;
|
|
24
|
+
if (this.starting !== undefined)
|
|
25
|
+
return this.starting;
|
|
26
|
+
const starting = this.startServer();
|
|
27
|
+
this.starting = starting;
|
|
28
|
+
try {
|
|
29
|
+
await starting;
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
if (this.starting === starting)
|
|
33
|
+
this.starting = undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async startServer() {
|
|
37
|
+
const { createOpencodeClient } = await import('@opencode-ai/sdk/v2/client');
|
|
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 configureMcp(directory, mcp) {
|
|
81
|
+
const client = await this.requireClient();
|
|
82
|
+
if (typeof mcp !== 'object' || mcp === null || Array.isArray(mcp))
|
|
83
|
+
return;
|
|
84
|
+
for (const [name, config] of Object.entries(mcp)) {
|
|
85
|
+
await client.mcp.add({
|
|
86
|
+
directory,
|
|
87
|
+
name,
|
|
88
|
+
config: config
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async sessions(directory, limit = 100) {
|
|
93
|
+
const client = await this.requireClient();
|
|
94
|
+
return unwrap(await client.session.list({ directory, limit }));
|
|
95
|
+
}
|
|
96
|
+
async createSession(directory, title) {
|
|
97
|
+
const client = await this.requireClient();
|
|
98
|
+
return unwrap(await client.session.create({ directory, ...(title === undefined ? {} : { title }) }));
|
|
99
|
+
}
|
|
100
|
+
async forkSession(directory, sessionID, messageID) {
|
|
101
|
+
const client = await this.requireClient();
|
|
102
|
+
return unwrap(await client.session.fork({ directory, sessionID, messageID }));
|
|
103
|
+
}
|
|
104
|
+
async messages(directory, sessionID, limit = 100) {
|
|
105
|
+
const client = await this.requireClient();
|
|
106
|
+
return unwrap(await client.session.messages({ directory, sessionID, limit }));
|
|
107
|
+
}
|
|
108
|
+
async updateSession(directory, sessionID, title) {
|
|
109
|
+
const client = await this.requireClient();
|
|
110
|
+
return unwrap(await client.session.update({ directory, sessionID, title }));
|
|
111
|
+
}
|
|
112
|
+
async deleteSession(directory, sessionID) {
|
|
113
|
+
const client = await this.requireClient();
|
|
114
|
+
return unwrap(await client.session.delete({ directory, sessionID }));
|
|
115
|
+
}
|
|
116
|
+
async abort(directory, sessionID) {
|
|
117
|
+
const client = await this.requireClient();
|
|
118
|
+
return unwrap(await client.session.abort({ directory, sessionID }));
|
|
119
|
+
}
|
|
120
|
+
async summarize(directory, sessionID, model) {
|
|
121
|
+
const client = await this.requireClient();
|
|
122
|
+
const separator = model.indexOf('/');
|
|
123
|
+
if (separator <= 0 || separator === model.length - 1)
|
|
124
|
+
throw new Error('OPENCODE_MODEL_INVALID');
|
|
125
|
+
return unwrap(await client.session.summarize({
|
|
126
|
+
directory,
|
|
127
|
+
sessionID,
|
|
128
|
+
providerID: model.slice(0, separator),
|
|
129
|
+
modelID: model.slice(separator + 1)
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
async replyPermission(directory, requestID, decision) {
|
|
133
|
+
const client = await this.requireClient();
|
|
134
|
+
return unwrap(await client.permission.reply({
|
|
135
|
+
directory,
|
|
136
|
+
requestID,
|
|
137
|
+
reply: decision === 'APPROVED' ? 'once' : 'reject'
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
async replyQuestion(directory, requestID, answers) {
|
|
141
|
+
const client = await this.requireClient();
|
|
142
|
+
return unwrap(await client.question.reply({
|
|
143
|
+
directory,
|
|
144
|
+
requestID,
|
|
145
|
+
answers: answers.map((answer) => [...answer])
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
async rejectQuestion(directory, requestID) {
|
|
149
|
+
const client = await this.requireClient();
|
|
150
|
+
return unwrap(await client.question.reject({ directory, requestID }));
|
|
151
|
+
}
|
|
152
|
+
async prompt(input) {
|
|
153
|
+
const client = await this.requireClient();
|
|
154
|
+
const separator = input.model.indexOf('/');
|
|
155
|
+
if (separator <= 0 || separator === input.model.length - 1)
|
|
156
|
+
throw new Error('OPENCODE_MODEL_INVALID');
|
|
157
|
+
return unwrap(await client.session.promptAsync({
|
|
158
|
+
directory: input.directory,
|
|
159
|
+
sessionID: input.sessionID,
|
|
160
|
+
model: {
|
|
161
|
+
providerID: input.model.slice(0, separator),
|
|
162
|
+
modelID: input.model.slice(separator + 1)
|
|
163
|
+
},
|
|
164
|
+
...(input.variant === undefined || input.variant.length === 0
|
|
165
|
+
? {}
|
|
166
|
+
: { variant: input.variant }),
|
|
167
|
+
...(input.agent === undefined ? {} : { agent: input.agent }),
|
|
168
|
+
...(input.system === undefined ? {} : { system: input.system }),
|
|
169
|
+
parts: [
|
|
170
|
+
{ type: 'text', text: input.text },
|
|
171
|
+
...(input.attachments ?? []).map((attachment) => ({
|
|
172
|
+
type: 'file',
|
|
173
|
+
mime: attachment.mime,
|
|
174
|
+
filename: attachment.name,
|
|
175
|
+
url: `data:${attachment.mime};base64,${attachment.dataBase64}`
|
|
176
|
+
}))
|
|
177
|
+
]
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
async subscribe(directory, signal, onEvent, onReady) {
|
|
181
|
+
const client = await this.requireClient();
|
|
182
|
+
const stream = await client.event.subscribe({ directory }, { signal });
|
|
183
|
+
onReady?.();
|
|
184
|
+
for await (const event of stream.stream)
|
|
185
|
+
onEvent(event);
|
|
186
|
+
}
|
|
187
|
+
async stop() {
|
|
188
|
+
this.client = undefined;
|
|
189
|
+
const child = this.child;
|
|
190
|
+
this.child = undefined;
|
|
191
|
+
if (child === undefined)
|
|
192
|
+
return;
|
|
193
|
+
if (child.pid === undefined) {
|
|
194
|
+
child.kill('SIGTERM');
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
await terminateProcessTree({
|
|
199
|
+
pid: child.pid,
|
|
200
|
+
kill: (signal) => child.kill(signal)
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
child.kill('SIGTERM');
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async requireClient() {
|
|
208
|
+
await this.start();
|
|
209
|
+
return this.client;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async function availablePort() {
|
|
213
|
+
return new Promise((resolve, reject) => {
|
|
214
|
+
const server = createServer();
|
|
215
|
+
server.once('error', reject);
|
|
216
|
+
server.listen(0, '127.0.0.1', () => {
|
|
217
|
+
const address = server.address();
|
|
218
|
+
const port = typeof address === 'object' && address !== null ? address.port : undefined;
|
|
219
|
+
server.close((error) => {
|
|
220
|
+
if (error !== undefined)
|
|
221
|
+
reject(error);
|
|
222
|
+
else if (port === undefined)
|
|
223
|
+
reject(new Error('OPENCODE_PORT_UNAVAILABLE'));
|
|
224
|
+
else
|
|
225
|
+
resolve(port);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
async function waitUntilHealthy(url, authorization, child, timeoutMs) {
|
|
231
|
+
const deadline = Date.now() + timeoutMs;
|
|
232
|
+
while (Date.now() < deadline) {
|
|
233
|
+
if (child.exitCode !== null)
|
|
234
|
+
throw new Error('OPENCODE_SERVER_EXITED');
|
|
235
|
+
try {
|
|
236
|
+
const response = await fetch(url, {
|
|
237
|
+
headers: { authorization },
|
|
238
|
+
signal: AbortSignal.timeout(Math.min(1_000, Math.max(1, deadline - Date.now())))
|
|
239
|
+
});
|
|
240
|
+
if (response.ok)
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
// The child may still be binding its loopback socket.
|
|
245
|
+
}
|
|
246
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
247
|
+
}
|
|
248
|
+
throw new Error('OPENCODE_SERVER_START_TIMEOUT');
|
|
249
|
+
}
|
|
250
|
+
function unwrap(result) {
|
|
251
|
+
if (typeof result !== 'object' || result === null || !('data' in result))
|
|
252
|
+
return result;
|
|
253
|
+
const wrapped = result;
|
|
254
|
+
if (wrapped.error !== undefined)
|
|
255
|
+
throw new Error('OPENCODE_REQUEST_FAILED');
|
|
256
|
+
if (wrapped.data === undefined)
|
|
257
|
+
throw new Error('OPENCODE_RESPONSE_INVALID');
|
|
258
|
+
return wrapped.data;
|
|
259
|
+
}
|
package/dist/operational.js
CHANGED
|
@@ -1,2 +1,122 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { writeNodeLog } from './rotating-log.js';
|
|
3
|
+
const secretKey = /(?:password|credential|token|cookie|authorization|secret|api[_-]?key)/i;
|
|
4
|
+
export function runRunnerProbe(command, args, options) {
|
|
5
|
+
return new Promise((resolve) => {
|
|
6
|
+
const child = spawn(command, args, {
|
|
7
|
+
windowsHide: true,
|
|
8
|
+
env: options.env,
|
|
9
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
10
|
+
});
|
|
11
|
+
let stdout = '';
|
|
12
|
+
let stderr = '';
|
|
13
|
+
let settled = false;
|
|
14
|
+
const finish = (status) => {
|
|
15
|
+
if (settled)
|
|
16
|
+
return;
|
|
17
|
+
settled = true;
|
|
18
|
+
clearTimeout(timer);
|
|
19
|
+
resolve({ status, stdout, stderr });
|
|
20
|
+
};
|
|
21
|
+
const timer = setTimeout(() => {
|
|
22
|
+
finish(null);
|
|
23
|
+
child.kill();
|
|
24
|
+
}, options.timeoutMs);
|
|
25
|
+
child.stdout.setEncoding('utf8');
|
|
26
|
+
child.stderr.setEncoding('utf8');
|
|
27
|
+
child.stdout.on('data', (value) => (stdout += value));
|
|
28
|
+
child.stderr.on('data', (value) => (stderr += value));
|
|
29
|
+
child.once('error', () => finish(null));
|
|
30
|
+
child.once('close', (status) => finish(status));
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export class NodeMetrics {
|
|
34
|
+
connectionAttempts = 0;
|
|
35
|
+
reconnects = 0;
|
|
36
|
+
heartbeats = 0;
|
|
37
|
+
runnerFailures = 0;
|
|
38
|
+
connectedAttempt() {
|
|
39
|
+
this.connectionAttempts += 1;
|
|
40
|
+
}
|
|
41
|
+
reconnectScheduled() {
|
|
42
|
+
this.reconnects += 1;
|
|
43
|
+
}
|
|
44
|
+
heartbeatSent() {
|
|
45
|
+
this.heartbeats += 1;
|
|
46
|
+
}
|
|
47
|
+
runnerFailed() {
|
|
48
|
+
this.runnerFailures += 1;
|
|
49
|
+
}
|
|
50
|
+
snapshot() {
|
|
51
|
+
return {
|
|
52
|
+
connectionAttempts: this.connectionAttempts,
|
|
53
|
+
reconnects: this.reconnects,
|
|
54
|
+
heartbeats: this.heartbeats,
|
|
55
|
+
runnerFailures: this.runnerFailures
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export function redactLogValue(value) {
|
|
60
|
+
if (Array.isArray(value))
|
|
61
|
+
return value.map(redactLogValue);
|
|
62
|
+
if (value !== null && typeof value === 'object')
|
|
63
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
64
|
+
key,
|
|
65
|
+
secretKey.test(key) ? '[REDACTED]' : redactLogValue(item)
|
|
66
|
+
]));
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
export function minimalRunnerEnvironment(input = process.env) {
|
|
70
|
+
const allowed = new Set([
|
|
71
|
+
'PATH',
|
|
72
|
+
'Path',
|
|
73
|
+
'HOME',
|
|
74
|
+
'USERPROFILE',
|
|
75
|
+
'TMPDIR',
|
|
76
|
+
'TMP',
|
|
77
|
+
'TEMP',
|
|
78
|
+
'SystemRoot',
|
|
79
|
+
'COMSPEC',
|
|
80
|
+
'LANG',
|
|
81
|
+
'LC_ALL',
|
|
82
|
+
'TERM'
|
|
83
|
+
]);
|
|
84
|
+
const output = {};
|
|
85
|
+
for (const [key, value] of Object.entries(input)) {
|
|
86
|
+
if (allowed.has(key) && !secretKey.test(key) && typeof value === 'string')
|
|
87
|
+
output[key] = value;
|
|
88
|
+
}
|
|
89
|
+
return output;
|
|
90
|
+
}
|
|
91
|
+
/** Windows npm CLIs are `.cmd` shims and require the controlled command processor path. */
|
|
92
|
+
export function platformCliCommand(command, platform = process.platform) {
|
|
93
|
+
if (platform !== 'win32' || /\.(?:cmd|exe|bat)$/i.test(command))
|
|
94
|
+
return command;
|
|
95
|
+
return `${command}.cmd`;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Runs only Node-owned CLI arguments through Windows' command processor so npm
|
|
99
|
+
* `.cmd` shims work without enabling `shell: true` on the actual child process.
|
|
100
|
+
*/
|
|
101
|
+
export function platformCliInvocation(command, args, platform = process.platform, comspec = process.env.ComSpec ?? process.env.COMSPEC ?? 'cmd.exe') {
|
|
102
|
+
const cliCommand = platformCliCommand(command, platform);
|
|
103
|
+
if (platform !== 'win32' || !/\.(?:cmd|bat)$/i.test(cliCommand))
|
|
104
|
+
return { command: cliCommand, args };
|
|
105
|
+
if (![cliCommand, ...args].every((value) => /^[A-Za-z0-9_@./:\\ -]+$/.test(value)))
|
|
106
|
+
throw new Error('NODE_CLI_COMMAND_INVALID');
|
|
107
|
+
return {
|
|
108
|
+
command: comspec,
|
|
109
|
+
args: [
|
|
110
|
+
'/d',
|
|
111
|
+
'/s',
|
|
112
|
+
'/c',
|
|
113
|
+
[cliCommand, ...args].map((value) => (value.includes(' ') ? `"${value}"` : value)).join(' ')
|
|
114
|
+
]
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export function nodeLog(event, fields = {}) {
|
|
118
|
+
const safeFields = redactLogValue(fields);
|
|
119
|
+
const line = `${JSON.stringify({ timestamp: new Date().toISOString(), level: 'info', component: 'mar-node', event, ...safeFields })}\n`;
|
|
120
|
+
process.stdout.write(line);
|
|
121
|
+
writeNodeLog(line);
|
|
122
|
+
}
|
package/dist/process-tree.js
CHANGED
|
@@ -1,2 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
const executeFile = promisify(execFile);
|
|
4
|
+
/** Kills the process group on Unix and the complete task tree on Windows. */
|
|
5
|
+
export async function terminateProcessTree(child, platform = process.platform, runCommand = executeFile) {
|
|
6
|
+
if (child.pid === undefined)
|
|
7
|
+
return;
|
|
8
|
+
if (platform === 'win32') {
|
|
9
|
+
await runCommand('taskkill', ['/pid', String(child.pid), '/T', '/F'], { windowsHide: true });
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
process.kill(-child.pid, 'SIGTERM');
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
child.kill('SIGTERM');
|
|
17
|
+
}
|
|
18
|
+
}
|
package/dist/ripgrep-binary.js
CHANGED
|
@@ -1,2 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { dirname, resolve } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
export const BUNDLED_RIPGREP_VERSION = '15.1.0';
|
|
4
|
+
const TARGETS = {
|
|
5
|
+
'linux-x64': ['linux-x64', 'rg'],
|
|
6
|
+
'linux-arm64': ['linux-arm64', 'rg'],
|
|
7
|
+
'darwin-x64': ['darwin-x64', 'rg'],
|
|
8
|
+
'darwin-arm64': ['darwin-arm64', 'rg'],
|
|
9
|
+
'win32-x64': ['win32-x64', 'rg.exe'],
|
|
10
|
+
'win32-arm64': ['win32-arm64', 'rg.exe']
|
|
11
|
+
};
|
|
12
|
+
export function bundledRipgrepDirectory(packageRoot) {
|
|
13
|
+
return packageRoot === undefined
|
|
14
|
+
? resolve(dirname(fileURLToPath(import.meta.url)), '..', 'vendor/ripgrep')
|
|
15
|
+
: resolve(packageRoot, 'mar-node/vendor/ripgrep');
|
|
16
|
+
}
|
|
17
|
+
export function bundledRipgrepPath(platform = process.platform, architecture = process.arch, packageRoot) {
|
|
18
|
+
const target = TARGETS[`${platform}-${architecture}`];
|
|
19
|
+
if (target === undefined)
|
|
20
|
+
throw new Error('RIPGREP_PLATFORM_UNSUPPORTED');
|
|
21
|
+
return resolve(bundledRipgrepDirectory(packageRoot), ...target);
|
|
22
|
+
}
|
|
23
|
+
export function prependBundledRipgrepToPath(environment, executable, platform = process.platform) {
|
|
24
|
+
const key = platform === 'win32' ? 'Path' : 'PATH';
|
|
25
|
+
const alternate = platform === 'win32' ? environment['PATH'] : undefined;
|
|
26
|
+
const current = environment[key] ?? alternate ?? '';
|
|
27
|
+
return {
|
|
28
|
+
...environment,
|
|
29
|
+
[key]: [dirname(executable), current].filter(Boolean).join(platform === 'win32' ? ';' : ':')
|
|
30
|
+
};
|
|
31
|
+
}
|