hyperbrane 0.1.2 → 0.2.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/README.md +67 -25
- package/dist/agent/agentClient.d.ts +5 -0
- package/dist/agent/agentClient.js +4 -0
- package/dist/agent/agentService.d.ts +9 -37
- package/dist/agent/agentService.js +62 -322
- package/dist/agent/agentStorage.d.ts +1 -20
- package/dist/agent/agentStorage.js +0 -30
- package/dist/agent/api.d.ts +80 -151
- package/dist/agent/api.js +1 -8
- package/dist/agent/client.d.ts +3 -1
- package/dist/agent/client.js +2 -3
- package/dist/agent/control.d.ts +23 -0
- package/dist/agent/control.js +479 -0
- package/dist/agent/hostTranscript.d.ts +0 -6
- package/dist/agent/hostTranscript.js +3 -14
- package/dist/agent/session/backend.d.ts +17 -32
- package/dist/agent/session/claude/home.d.ts +0 -1
- package/dist/agent/session/claude/home.js +0 -1
- package/dist/agent/session/claude/provision.d.ts +10 -0
- package/dist/agent/session/claude/provision.js +17 -0
- package/dist/agent/session/claude/transcript.d.ts +2 -7
- package/dist/agent/session/claude/transcript.js +68 -14
- package/dist/agent/session/client.d.ts +1 -10
- package/dist/agent/session/client.js +0 -3
- package/dist/agent/session/codex/provision.d.ts +14 -0
- package/dist/agent/session/codex/provision.js +25 -0
- package/dist/agent/session/fileTranscript.d.ts +6 -49
- package/dist/agent/session/fileTranscript.js +26 -82
- package/dist/agent/session/install.d.ts +5 -0
- package/dist/agent/session/install.js +35 -0
- package/dist/agent/session/sessionStorage.d.ts +4 -35
- package/dist/agent/session/sessionStorage.js +10 -16
- package/dist/agent/session/sharedConnection.d.ts +0 -8
- package/dist/agent/session/sharedConnection.js +0 -7
- package/dist/agent/session/storedTranscripts.d.ts +1 -6
- package/dist/agent/session/storedTranscripts.js +0 -3
- package/dist/agent/session/worker/jsonRpc.d.ts +2 -13
- package/dist/agent/session/worker/jsonRpc.js +2 -11
- package/dist/agent/session/worker/protocol.d.ts +26 -20
- package/dist/agent/session/worker/protocol.js +29 -18
- package/dist/agent/session/worker/workerParams.d.ts +0 -5
- package/dist/agent/session/worker/workerParams.js +14 -14
- package/dist/agent/session/workerConnection.d.ts +2 -13
- package/dist/agent/session/workerConnection.js +7 -12
- package/dist/agent/session/workerGatewayConnection.d.ts +18 -0
- package/dist/agent/session/workerGatewayConnection.js +86 -0
- package/dist/agent/vendor-types/manifest.json +87 -0
- package/dist/agent/workerBundle.d.ts +4 -19
- package/dist/agent/workerBundle.generated.js +1 -1
- package/dist/agent/workerBundle.js +3 -30
- package/dist/agent/workerReplacement.d.ts +3 -0
- package/dist/agent/workerReplacement.js +49 -0
- package/dist/cli/agent.js +31 -16
- package/dist/cli/command.d.ts +10 -11
- package/dist/cli/command.js +0 -4
- package/dist/cli/dimension.js +6 -6
- package/dist/cli/gateway.js +3 -6
- package/dist/cli/main.js +14 -31
- package/dist/cli/streamPrinter.d.ts +2 -10
- package/dist/cli/streamPrinter.js +16 -46
- package/dist/cli/table.d.ts +0 -3
- package/dist/cli/table.js +0 -4
- package/dist/dimension/api.d.ts +39 -27
- package/dist/dimension/api.js +28 -47
- package/dist/dimension/client.d.ts +5 -0
- package/dist/dimension/client.js +4 -0
- package/dist/dimension/control.d.ts +21 -0
- package/dist/dimension/control.js +328 -0
- package/dist/dimension/image.d.ts +0 -5
- package/dist/dimension/image.js +0 -8
- package/dist/dimension/imageFiles.generated.js +1 -1
- package/dist/dimension/paths.d.ts +5 -4
- package/dist/dimension/paths.js +15 -6
- package/dist/dimension/service.d.ts +8 -24
- package/dist/dimension/service.js +60 -228
- package/dist/dimension/transport-worker.d.ts +1 -0
- package/dist/dimension/transport-worker.js +151 -0
- package/dist/dimension/transportBundle.d.ts +2 -0
- package/dist/dimension/transportBundle.generated.d.ts +1 -0
- package/dist/dimension/transportBundle.generated.js +2 -0
- package/dist/dimension/transportBundle.js +4 -0
- package/dist/dimension/transportClient.d.ts +10 -0
- package/dist/dimension/transportClient.js +115 -0
- package/dist/dimension/transportProtocol.d.ts +53 -0
- package/dist/dimension/transportProtocol.js +8 -0
- package/dist/index.d.ts +5 -9
- package/dist/index.js +5 -19
- package/dist/lib/control.d.ts +8 -0
- package/dist/lib/control.js +62 -0
- package/dist/lib/docker.d.ts +0 -22
- package/dist/lib/docker.js +0 -10
- package/dist/lib/image.d.ts +0 -3
- package/dist/lib/image.js +0 -2
- package/dist/lib/jsonDirectory.d.ts +0 -13
- package/dist/lib/jsonDirectory.js +10 -15
- package/dist/lib/scriptBundle.d.ts +6 -0
- package/dist/lib/scriptBundle.js +28 -0
- package/dist/lib/sh.d.ts +2 -0
- package/dist/lib/sh.js +8 -0
- package/dist/lib/sleep.d.ts +0 -1
- package/dist/lib/sleep.js +0 -1
- package/dist/lib/streamFetch.d.ts +15 -0
- package/dist/lib/streamFetch.js +150 -0
- package/dist/lib/tar.d.ts +0 -1
- package/dist/lib/tar.js +5 -11
- package/dist/portal-gateway/api.d.ts +13 -44
- package/dist/portal-gateway/api.js +1 -3
- package/dist/portal-gateway/control.d.ts +42 -0
- package/dist/portal-gateway/control.js +657 -0
- package/dist/portal-gateway/gatewayClient.d.ts +5 -0
- package/dist/portal-gateway/gatewayClient.js +4 -0
- package/dist/portal-gateway/gatewayService.d.ts +8 -38
- package/dist/portal-gateway/gatewayService.js +68 -254
- package/dist/portal-gateway/image.d.ts +0 -1
- package/dist/portal-gateway/image.js +0 -2
- package/dist/portal-gateway/imageFiles.generated.js +2 -2
- package/dist/portal-gateway/portalClient.d.ts +5 -0
- package/dist/portal-gateway/portalClient.js +4 -0
- package/dist/portal-gateway/portalService.d.ts +5 -47
- package/dist/portal-gateway/portalService.js +19 -349
- package/dist/portal-gateway/transport-worker.d.ts +1 -0
- package/dist/portal-gateway/transport-worker.js +205 -0
- package/dist/portal-gateway/transportBundle.d.ts +2 -0
- package/dist/portal-gateway/transportBundle.generated.d.ts +1 -0
- package/dist/portal-gateway/transportBundle.generated.js +2 -0
- package/dist/portal-gateway/transportBundle.js +4 -0
- package/dist/portal-gateway/upstream.d.ts +0 -81
- package/dist/portal-gateway/upstream.js +0 -1
- package/dist/sandbox/api.d.ts +121 -187
- package/dist/sandbox/api.js +5 -7
- package/dist/sandbox/client.d.ts +2 -0
- package/dist/sandbox/client.js +4 -0
- package/dist/sandbox/data-endpoints/dataEndpoints.d.ts +13 -0
- package/dist/sandbox/data-endpoints/dataEndpoints.js +100 -0
- package/dist/sandbox/data-endpoints/firewalledPorts.d.ts +4 -0
- package/dist/sandbox/data-endpoints/firewalledPorts.js +28 -0
- package/dist/sandbox/lib/httpOverStream.d.ts +3 -0
- package/dist/sandbox/lib/httpOverStream.js +21 -0
- package/dist/sandbox/lib/remoteFileOperations.d.ts +10 -0
- package/dist/sandbox/lib/remoteFileOperations.js +70 -0
- package/dist/sandbox/lib/shellCommand.d.ts +3 -0
- package/dist/sandbox/lib/shellCommand.js +7 -0
- package/dist/sandbox/providers/boat/boatService.d.ts +2 -0
- package/dist/sandbox/providers/boat/boatService.js +321 -0
- package/dist/sandbox/providers/exedev/exeDevService.d.ts +2 -0
- package/dist/sandbox/providers/exedev/exeDevService.js +127 -0
- package/dist/sandbox/providers/exedev/exeDevToken.d.ts +1 -0
- package/dist/sandbox/providers/exedev/exeDevToken.js +54 -0
- package/dist/sandbox/providers/local/localService.d.ts +2 -0
- package/dist/sandbox/providers/local/localService.js +219 -0
- package/dist/sandbox/providers/ssh/sshService.d.ts +2 -0
- package/dist/sandbox/providers/ssh/sshService.js +82 -0
- package/dist/sandbox/providers/ssh/sshTransport.d.ts +12 -0
- package/dist/sandbox/providers/ssh/sshTransport.js +216 -0
- package/dist/sandbox/service.d.ts +2 -0
- package/dist/sandbox/service.js +18 -0
- package/dist/sandbox/storage/descriptors.d.ts +6 -0
- package/dist/sandbox/storage/descriptors.js +25 -0
- package/dist/sandbox/storage/sandboxStorage.d.ts +11 -0
- package/dist/sandbox/storage/sandboxStorage.js +42 -0
- package/dist/upstreams/anthropic.d.ts +0 -5
- package/dist/upstreams/anthropic.js +1 -8
- package/dist/upstreams/index.d.ts +2 -6
- package/dist/upstreams/index.js +0 -4
- package/dist/upstreams/openai.d.ts +0 -5
- package/dist/upstreams/openai.js +1 -13
- package/package.json +12 -4
- package/dist/agent/gatewaySignIns.d.ts +0 -5
- package/dist/agent/gatewaySignIns.js +0 -11
- package/dist/agent/session/codex/home.d.ts +0 -2
- package/dist/agent/session/codex/home.js +0 -4
- package/dist/agent/session/codex/messages.d.ts +0 -30
- package/dist/agent/session/codex/messages.js +0 -135
- package/dist/agent/session/codex/transcript.d.ts +0 -12
- package/dist/agent/session/codex/transcript.js +0 -22
- package/dist/agent/session/sdkMessages.d.ts +0 -15
- package/dist/agent/session/sdkMessages.js +0 -28
- package/dist/agent/session/workerSseConnection.d.ts +0 -31
- package/dist/agent/session/workerSseConnection.js +0 -76
- package/dist/agent/vendor-types/codex/app-server/AbsolutePathBuf.d.ts +0 -10
- package/dist/agent/vendor-types/codex/app-server/AbsolutePathBuf.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputBody.d.ts +0 -2
- package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputBody.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputContentItem.d.ts +0 -22
- package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputContentItem.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/ImageDetail.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/ImageDetail.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/ImageGenerationFailure.d.ts +0 -5
- package/dist/agent/vendor-types/codex/app-server/ImageGenerationFailure.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/ImageGenerationItem.d.ts +0 -11
- package/dist/agent/vendor-types/codex/app-server/ImageGenerationItem.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/LegacyAppPathString.d.ts +0 -23
- package/dist/agent/vendor-types/codex/app-server/LegacyAppPathString.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/MessagePhase.d.ts +0 -7
- package/dist/agent/vendor-types/codex/app-server/MessagePhase.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/ReasoningEffort.d.ts +0 -4
- package/dist/agent/vendor-types/codex/app-server/ReasoningEffort.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/SleepItem.d.ts +0 -7
- package/dist/agent/vendor-types/codex/app-server/SleepItem.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/WebSearchItem.d.ts +0 -14
- package/dist/agent/vendor-types/codex/app-server/WebSearchItem.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/serde_json/JsonValue.d.ts +0 -3
- package/dist/agent/vendor-types/codex/app-server/serde_json/JsonValue.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/AgentMessageDelivery.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/AgentMessageDelivery.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/AsyncUserInputQuestion.d.ts +0 -4
- package/dist/agent/vendor-types/codex/app-server/v2/AsyncUserInputQuestion.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/ByteRange.d.ts +0 -4
- package/dist/agent/vendor-types/codex/app-server/v2/ByteRange.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentState.d.ts +0 -5
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentState.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentStatus.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentStatus.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentTool.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentTool.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentToolCallStatus.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentToolCallStatus.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/CommandAction.d.ts +0 -19
- package/dist/agent/vendor-types/codex/app-server/v2/CommandAction.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionSource.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionSource.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionStatus.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionStatus.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallOutputContentItem.d.ts +0 -10
- package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallOutputContentItem.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallStatus.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallStatus.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/FileUpdateChange.d.ts +0 -6
- package/dist/agent/vendor-types/codex/app-server/v2/FileUpdateChange.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/HookPromptFragment.d.ts +0 -4
- package/dist/agent/vendor-types/codex/app-server/v2/HookPromptFragment.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/McpAppDisplayMode.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/McpAppDisplayMode.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/McpAppUi.d.ts +0 -8
- package/dist/agent/vendor-types/codex/app-server/v2/McpAppUi.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallAppContext.d.ts +0 -7
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallAppContext.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallError.d.ts +0 -3
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallError.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallResult.d.ts +0 -6
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallResult.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallStatus.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallStatus.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitation.d.ts +0 -5
- package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitation.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitationEntry.d.ts +0 -6
- package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitationEntry.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/PatchApplyStatus.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/PatchApplyStatus.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/PatchChangeKind.d.ts +0 -8
- package/dist/agent/vendor-types/codex/app-server/v2/PatchChangeKind.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/SubAgentActivityKind.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/SubAgentActivityKind.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/TextElement.d.ts +0 -11
- package/dist/agent/vendor-types/codex/app-server/v2/TextElement.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/ThreadItem.d.ts +0 -215
- package/dist/agent/vendor-types/codex/app-server/v2/ThreadItem.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/UserInput.d.ts +0 -35
- package/dist/agent/vendor-types/codex/app-server/v2/UserInput.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/WebSearchAction.d.ts +0 -14
- package/dist/agent/vendor-types/codex/app-server/v2/WebSearchAction.js +0 -2
- package/dist/cli/services.d.ts +0 -19
- package/dist/cli/services.js +0 -34
- package/dist/dimension/http.d.ts +0 -7
- package/dist/dimension/http.js +0 -77
- package/dist/dimension/storage.d.ts +0 -39
- package/dist/dimension/storage.js +0 -79
- package/dist/portal-gateway/fakes.d.ts +0 -3
- package/dist/portal-gateway/fakes.js +0 -11
- package/dist/portal-gateway/gatewayStorage.d.ts +0 -66
- package/dist/portal-gateway/gatewayStorage.js +0 -125
- package/dist/portal-gateway/portalStorage.d.ts +0 -34
- package/dist/portal-gateway/portalStorage.js +0 -44
- package/dist/sandbox/boatService.d.ts +0 -52
- package/dist/sandbox/boatService.js +0 -343
- package/dist/sandbox/containerService.d.ts +0 -15
- package/dist/sandbox/containerService.js +0 -184
- package/dist/sandbox/endpointProxy.d.ts +0 -90
- package/dist/sandbox/endpointProxy.js +0 -716
- package/dist/sandbox/exeDevAuth.d.ts +0 -16
- package/dist/sandbox/exeDevAuth.js +0 -49
- package/dist/sandbox/localService.d.ts +0 -19
- package/dist/sandbox/localService.js +0 -200
- package/dist/sandbox/sseConnection.d.ts +0 -41
- package/dist/sandbox/sseConnection.js +0 -166
- package/dist/sandbox/sshService.d.ts +0 -30
- package/dist/sandbox/sshService.js +0 -680
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
import { dimensionLayout } from "../dimension/api.js";
|
|
2
|
+
import { sh } from "../lib/sh.js";
|
|
3
|
+
import { AGENT_BACKENDS, AgentConflictError, AgentNotFoundError, } from "./api.js";
|
|
4
|
+
import { codexPackage } from "./session/codex/provision.js";
|
|
5
|
+
import { WORKER_PROTOCOL } from "./session/worker/protocol.js";
|
|
6
|
+
import { claudePackage } from "./session/claude/provision.js";
|
|
7
|
+
import { workerBundle } from "./workerBundle.js";
|
|
8
|
+
export const WORKER_DIR = `${dimensionLayout.statePath()}/worker`;
|
|
9
|
+
const OLD_WORKER_DIR = "/opt/hyperbrane/worker";
|
|
10
|
+
const SCRIPTS_DIR = "/opt/hyperbrane";
|
|
11
|
+
const START_TIMEOUT_S = 30;
|
|
12
|
+
const STOP_TIMEOUT_S = 10;
|
|
13
|
+
const EXIT = { noDimension: 40, notRunning: 41, oldDimension: 42, conflict: 43, noAgent: 44, thirdUser: 45, needBundle: 46, noSession: 47 };
|
|
14
|
+
const AUTH_UPSTREAMS = { claude: "anthropic", codex: "openai" };
|
|
15
|
+
function backendPackage(backend) {
|
|
16
|
+
return backend === "claude" ? claudePackage() : codexPackage();
|
|
17
|
+
}
|
|
18
|
+
export async function createAgentScript(host, agent, options = {}) {
|
|
19
|
+
const bundle = await workerBundle();
|
|
20
|
+
const env = {
|
|
21
|
+
...dimensionEnv(agent.dimensionName, options),
|
|
22
|
+
...(options.replaceBuild ? { REPLACE_BUILD: "1" } : {}),
|
|
23
|
+
AGENT: agent.name,
|
|
24
|
+
BUILD: bundle.build,
|
|
25
|
+
PROTOCOL: String(WORKER_PROTOCOL),
|
|
26
|
+
CA_BUNDLE: dimensionLayout.caBundlePath(),
|
|
27
|
+
START_TIMEOUT: String(START_TIMEOUT_S),
|
|
28
|
+
STOP_TIMEOUT: String(STOP_TIMEOUT_S),
|
|
29
|
+
};
|
|
30
|
+
let result = await run(host, CREATE_AGENT, env);
|
|
31
|
+
if (result.exitCode === EXIT.needBundle)
|
|
32
|
+
result = await run(host, CREATE_AGENT, { ...env, HAS_BUNDLE: "1" }, bundle.source);
|
|
33
|
+
check(result, `starting agent ${agent.name}`, agent);
|
|
34
|
+
}
|
|
35
|
+
export async function stopAgentScript(host, agentName, options = {}) {
|
|
36
|
+
const result = await run(host, STOP_AGENT, { ...dataEnv(options), AGENT: agentName, STOP_TIMEOUT: String(STOP_TIMEOUT_S) });
|
|
37
|
+
check(result, `stopping agent ${agentName}`);
|
|
38
|
+
}
|
|
39
|
+
export async function listAgentsScript(host, options = {}) {
|
|
40
|
+
const result = check(await run(host, LIST_AGENTS, dataEnv(options)), "listing agents");
|
|
41
|
+
return records(result.stdout).map(([, agent]) => agent);
|
|
42
|
+
}
|
|
43
|
+
export async function listSessionsScript(host, options = {}) {
|
|
44
|
+
const result = check(await run(host, LIST_SESSIONS, dataEnv(options)), "listing sessions");
|
|
45
|
+
const running = new Set(result.stdout
|
|
46
|
+
.split("\n")
|
|
47
|
+
.filter((line) => line.startsWith("running "))
|
|
48
|
+
.map((line) => line.slice("running ".length)));
|
|
49
|
+
return records(result.stdout).map(([header, record]) => {
|
|
50
|
+
const [agentName = "", saved = ""] = header.split("\t");
|
|
51
|
+
const status = !running.has(agentName) ? { state: "exited", code: null, signal: null } : (savedStatus(saved) ?? { state: "idle" });
|
|
52
|
+
return { ...record.spec, agentName, status, nativeId: record.nativeId, model: record.model, permissionMode: record.permissionMode ?? "ask" };
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
export async function installScript(host, agentName, backend, options = {}) {
|
|
56
|
+
const { pkg, version, executables } = backendPackage(backend);
|
|
57
|
+
const env = { ...dataEnv(options), AGENT: agentName, PKG: pkg, VERSION: version, EXECUTABLES: executables, CA_BUNDLE: dimensionLayout.caBundlePath() };
|
|
58
|
+
check(await run(host, INSTALL_BACKEND, env), `installing ${backend} for agent ${agentName}`);
|
|
59
|
+
}
|
|
60
|
+
export async function loginStateScript(host, ref, options = {}) {
|
|
61
|
+
const upstreams = Object.entries(AUTH_UPSTREAMS)
|
|
62
|
+
.map(([backend, upstream]) => `${backend}\t${upstream}`)
|
|
63
|
+
.join("\n");
|
|
64
|
+
const result = check(await run(host, LOGIN_STATE, { ...dataEnv(options), AGENT: ref.agentName, SESSION: ref.sessionName, UPSTREAMS: upstreams }), `signing in ${ref.agentName}/${ref.sessionName}`);
|
|
65
|
+
const [backend = "", signedIn] = result.stdout.trim().split("\t");
|
|
66
|
+
if (!AGENT_BACKENDS.includes(backend))
|
|
67
|
+
throw new Error(`session ${ref.sessionName} has no known backend (${backend})`);
|
|
68
|
+
return { backend: backend, gatewaySignedIn: signedIn === "1" };
|
|
69
|
+
}
|
|
70
|
+
export async function agentsDownScript(host, options = {}) {
|
|
71
|
+
const result = check(await run(host, AGENTS_DOWN, dataEnv(options)), "finding agents to restore");
|
|
72
|
+
return result.stdout
|
|
73
|
+
.split("\n")
|
|
74
|
+
.filter(Boolean)
|
|
75
|
+
.map((line) => {
|
|
76
|
+
const [name = "", dimensionName = ""] = line.split("\t");
|
|
77
|
+
return { name, dimensionName };
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
export async function workerSocketScript(host, agentName, options = {}) {
|
|
81
|
+
const result = check(await run(host, WORKER_SOCKET, { ...dataEnv(options), AGENT: agentName }), `finding agent ${agentName}'s worker`);
|
|
82
|
+
const [dimensionName = "", socket = ""] = result.stdout.split("\n");
|
|
83
|
+
return { dimensionName, socket };
|
|
84
|
+
}
|
|
85
|
+
export async function listBackendsScript(host, agentName, options = {}) {
|
|
86
|
+
const lines = AGENT_BACKENDS.map((backend) => {
|
|
87
|
+
const { pkg, version, executables } = backendPackage(backend);
|
|
88
|
+
return [backend, pkg, version, executables].join("\t");
|
|
89
|
+
});
|
|
90
|
+
const result = check(await run(host, LIST_BACKENDS, { ...dataEnv(options), AGENT: agentName, BACKENDS: lines.join("\n") }), "listing backends");
|
|
91
|
+
return result.stdout
|
|
92
|
+
.split("\n")
|
|
93
|
+
.filter(Boolean)
|
|
94
|
+
.map((line) => {
|
|
95
|
+
const [backend, installed = "", ready] = line.split("\t");
|
|
96
|
+
return { backend: backend, version: installed || null, ready: ready === "1", pinned: backendPackage(backend).version };
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function dataEnv({ dataDirectory }) {
|
|
100
|
+
return {
|
|
101
|
+
...(dataDirectory === undefined ? {} : { DATA: dataDirectory }),
|
|
102
|
+
CONTAINER_PREFIX: dimensionLayout.containerName(""),
|
|
103
|
+
RUN_PATH: dimensionLayout.runPath(),
|
|
104
|
+
STATE_PATH: dimensionLayout.statePath(),
|
|
105
|
+
WORKER_DIR,
|
|
106
|
+
SCRIPTS_DIR,
|
|
107
|
+
OLD_WORKER_DIR,
|
|
108
|
+
...Object.fromEntries(Object.entries(EXIT).map(([name, code]) => [`E_${name.replace(/[A-Z]/g, (c) => `_${c}`).toUpperCase()}`, String(code)])),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function dimensionEnv(dimensionName, options) {
|
|
112
|
+
return { ...dataEnv(options), DIMENSION: dimensionName };
|
|
113
|
+
}
|
|
114
|
+
function run(host, script, env, stdin) {
|
|
115
|
+
const inDimension = { KILL_AGENT, PREPARE_WORKER, PUT_SCRIPT, START_WORKER, REMOVE_OLD_SCRIPTS, INSTALL_IN_DIMENSION };
|
|
116
|
+
return host.runCommand(["sh", "-c", `${PRELUDE}\n${script}`], { env: { ...inDimension, ...env }, stdin });
|
|
117
|
+
}
|
|
118
|
+
function check(result, what, agent) {
|
|
119
|
+
const said = result.stderr.trim();
|
|
120
|
+
switch (result.exitCode) {
|
|
121
|
+
case 0:
|
|
122
|
+
return result;
|
|
123
|
+
case EXIT.noDimension:
|
|
124
|
+
throw new AgentNotFoundError(`dimension ${agent?.dimensionName ?? said} does not exist`);
|
|
125
|
+
case EXIT.notRunning:
|
|
126
|
+
throw new AgentConflictError(`dimension ${agent?.dimensionName ?? said} is not running`);
|
|
127
|
+
case EXIT.oldDimension:
|
|
128
|
+
throw new AgentConflictError(`dimension ${agent?.dimensionName ?? said} lacks the ${dimensionLayout.runPath()} and ${dimensionLayout.statePath()} mounts agents need (it predates them); recreate it`);
|
|
129
|
+
case EXIT.conflict:
|
|
130
|
+
throw new AgentConflictError(`agent ${agent?.name ?? ""} already exists in another dimension: ${said}`);
|
|
131
|
+
case EXIT.noAgent:
|
|
132
|
+
throw new AgentNotFoundError(`agent ${said} does not exist`);
|
|
133
|
+
case EXIT.noSession:
|
|
134
|
+
throw new AgentNotFoundError(`session ${said} does not exist`);
|
|
135
|
+
case EXIT.thirdUser:
|
|
136
|
+
throw new AgentConflictError(`${what}: ${said}`);
|
|
137
|
+
default:
|
|
138
|
+
throw new Error(`${what} failed (exit ${result.exitCode}): ${said || result.stdout.trim()}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function records(stdout) {
|
|
142
|
+
return stdout
|
|
143
|
+
.split(RECORD_END)
|
|
144
|
+
.map((block) => block.replace(/^(running [^\n]*\n)+/, ""))
|
|
145
|
+
.filter((block) => block.trim())
|
|
146
|
+
.flatMap((block) => {
|
|
147
|
+
const newline = block.indexOf("\n");
|
|
148
|
+
try {
|
|
149
|
+
return [[block.slice(0, newline), JSON.parse(block.slice(newline + 1))]];
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function savedStatus(file) {
|
|
157
|
+
try {
|
|
158
|
+
const { status } = JSON.parse(file);
|
|
159
|
+
if (!status)
|
|
160
|
+
return null;
|
|
161
|
+
if (status.state === "tool")
|
|
162
|
+
return Array.isArray(status.tools) ? status : null;
|
|
163
|
+
return SESSION_STATES.has(status.state) ? status : null;
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const SESSION_STATES = new Set(["exited", "starting", "waiting", "idle", "thinking"]);
|
|
170
|
+
const RECORD_END = "\n\x1e\n";
|
|
171
|
+
const PRELUDE = sh `
|
|
172
|
+
DATA="\${DATA:-$HOME/.local/share/hyperbrane}"
|
|
173
|
+
|
|
174
|
+
agent_dimension() {
|
|
175
|
+
record="$DATA/agents/$1.json"
|
|
176
|
+
if [ ! -f "$record" ]; then
|
|
177
|
+
echo "$1" >&2
|
|
178
|
+
exit "$E_NO_AGENT"
|
|
179
|
+
fi
|
|
180
|
+
DIMENSION="$(sed -n 's/^ *"dimensionName": *"\([^"]*\)".*/\1/p' "$record")"
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
# Exits unless dimension $DIMENSION exists and runs, with the mounts agents need. Sets CONTAINER,
|
|
184
|
+
# DIMENSION_USER (whom it runs as, uid[:gid]) and RUN (its run directory on the machine).
|
|
185
|
+
require_dimension() {
|
|
186
|
+
CONTAINER="$CONTAINER_PREFIX$DIMENSION"
|
|
187
|
+
RUN="$DATA/dimension-run/$DIMENSION"
|
|
188
|
+
format='{{index .Config.Labels "hyperbrane.dimension"}}|{{.State.Running}}|{{range .Mounts}}{{.Destination}},{{end}}|{{.Config.User}}'
|
|
189
|
+
if ! info="$(docker inspect -f "$format" "$CONTAINER" 2>/dev/null)"; then
|
|
190
|
+
echo "$DIMENSION" >&2
|
|
191
|
+
exit "$E_NO_DIMENSION"
|
|
192
|
+
fi
|
|
193
|
+
IFS='|' read -r label running mounts DIMENSION_USER <<INFO
|
|
194
|
+
$info
|
|
195
|
+
INFO
|
|
196
|
+
if [ "$label" != "$DIMENSION" ]; then
|
|
197
|
+
echo "$DIMENSION" >&2
|
|
198
|
+
exit "$E_NO_DIMENSION"
|
|
199
|
+
fi
|
|
200
|
+
if [ "$running" != true ]; then
|
|
201
|
+
echo "$DIMENSION" >&2
|
|
202
|
+
exit "$E_NOT_RUNNING"
|
|
203
|
+
fi
|
|
204
|
+
case "$mounts" in
|
|
205
|
+
*"$RUN_PATH,"*"$STATE_PATH,"* | *"$STATE_PATH,"*"$RUN_PATH,"*) ;;
|
|
206
|
+
*)
|
|
207
|
+
echo "$DIMENSION" >&2
|
|
208
|
+
exit "$E_OLD_DIMENSION"
|
|
209
|
+
;;
|
|
210
|
+
esac
|
|
211
|
+
DIMENSION_USER="\${DIMENSION_USER:-0:0}"
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
# Whether agent $1's worker runs, of any build, and can be reached: its ready file names a live
|
|
215
|
+
# shellcheck disable=SC2034
|
|
216
|
+
worker_running() {
|
|
217
|
+
WORKER_PID=""
|
|
218
|
+
WORKER_BUILD=""
|
|
219
|
+
WORKER_PROTOCOL=""
|
|
220
|
+
[ -f "$RUN/$1.ready" ] && [ -S "$RUN/$1.sock" ] || return 1
|
|
221
|
+
read -r WORKER_PID WORKER_BUILD WORKER_PROTOCOL < "$RUN/$1.ready" || return 1
|
|
222
|
+
docker exec -u 0 "$CONTAINER" grep -qs "hyperbrane/worker" "/proc/$WORKER_PID/cmdline"
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
stop_worker() {
|
|
226
|
+
if worker_running "$1"; then
|
|
227
|
+
docker exec -u 0 "$CONTAINER" kill "$WORKER_PID" 2>/dev/null || true
|
|
228
|
+
waited=0
|
|
229
|
+
while [ "$waited" -lt "$STOP_TIMEOUT" ] && docker exec -u 0 "$CONTAINER" kill -0 "$WORKER_PID" 2>/dev/null; do
|
|
230
|
+
sleep 1
|
|
231
|
+
waited=$((waited + 1))
|
|
232
|
+
done
|
|
233
|
+
fi
|
|
234
|
+
docker exec -u "$DIMENSION_USER" -e AGENT="$1" "$CONTAINER" sh -c "$KILL_AGENT"
|
|
235
|
+
rm -f "$RUN/$1.ready" 2>/dev/null || true
|
|
236
|
+
}
|
|
237
|
+
`;
|
|
238
|
+
const KILL_AGENT = sh `
|
|
239
|
+
for process in /proc/[0-9]*; do
|
|
240
|
+
if tr '\0' '\n' < "$process/environ" 2>/dev/null | grep -qx "HYB_AGENT=$AGENT"; then
|
|
241
|
+
kill -9 "\${process#/proc/}" 2>/dev/null || true
|
|
242
|
+
fi
|
|
243
|
+
done
|
|
244
|
+
`;
|
|
245
|
+
const PREPARE_WORKER = sh `
|
|
246
|
+
set -eu
|
|
247
|
+
mkdir -p "$WORKER_DIR" "$SCRIPTS_DIR"
|
|
248
|
+
chown "$OWNER" "$WORKER_DIR" "$SCRIPTS_DIR" "$RUN_PATH" "$STATE_PATH"
|
|
249
|
+
if [ ! -e "$WORKER_DIR/node_modules" ] && [ -d "$OLD_WORKER_DIR/node_modules" ]; then
|
|
250
|
+
cp -a "$OLD_WORKER_DIR/." "$WORKER_DIR/"
|
|
251
|
+
rm -rf "$OLD_WORKER_DIR"
|
|
252
|
+
fi
|
|
253
|
+
if [ ! -f "$WORKER_DIR/package.json" ]; then
|
|
254
|
+
printf '{\n "name": "hyperbrane-worker",\n "private": true\n}\n' > "$WORKER_DIR/package.json"
|
|
255
|
+
chown "$OWNER" "$WORKER_DIR/package.json"
|
|
256
|
+
fi
|
|
257
|
+
printf '[install]\ncafile = "%s"\n' "$CA_BUNDLE" > "$WORKER_DIR/bunfig.toml"
|
|
258
|
+
chown "$OWNER" "$WORKER_DIR/bunfig.toml"
|
|
259
|
+
`;
|
|
260
|
+
const PUT_SCRIPT = sh `
|
|
261
|
+
set -eu
|
|
262
|
+
cat > "$SCRIPT.part"
|
|
263
|
+
mv -f "$SCRIPT.part" "$SCRIPT"
|
|
264
|
+
`;
|
|
265
|
+
const START_WORKER = sh `
|
|
266
|
+
HOME="$(getent passwd "$(id -u)" | cut -d: -f6)"
|
|
267
|
+
export HOME
|
|
268
|
+
cd "$HOME" 2>/dev/null || cd /
|
|
269
|
+
exec bun "$HYB_WORKER_SCRIPT" >> "$HYB_LOG" 2>&1 < /dev/null
|
|
270
|
+
`;
|
|
271
|
+
const REMOVE_OLD_SCRIPTS = sh `
|
|
272
|
+
for old in "$SCRIPTS_DIR"/worker-*.js; do
|
|
273
|
+
[ "$old" = "$SCRIPTS_DIR/worker-$BUILD.js" ] || rm -f "$old"
|
|
274
|
+
done
|
|
275
|
+
`;
|
|
276
|
+
const CREATE_AGENT = sh `
|
|
277
|
+
set -eu
|
|
278
|
+
DIMENSION_WANTED="$DIMENSION"
|
|
279
|
+
require_dimension
|
|
280
|
+
|
|
281
|
+
mkdir -p "$DATA/agents"
|
|
282
|
+
record="$DATA/agents/$AGENT.json"
|
|
283
|
+
if [ ! -f "$record" ]; then
|
|
284
|
+
printf '{\n "name": "%s",\n "dimensionName": "%s"\n}\n' "$AGENT" "$DIMENSION" > "$record.$$"
|
|
285
|
+
ln "$record.$$" "$record" 2>/dev/null || true
|
|
286
|
+
rm -f "$record.$$"
|
|
287
|
+
fi
|
|
288
|
+
agent_dimension "$AGENT"
|
|
289
|
+
if [ "$DIMENSION" != "$DIMENSION_WANTED" ]; then
|
|
290
|
+
echo "$DIMENSION" >&2
|
|
291
|
+
exit "$E_CONFLICT"
|
|
292
|
+
fi
|
|
293
|
+
|
|
294
|
+
mkdir -p "$RUN"
|
|
295
|
+
exec 8> "$RUN/$AGENT.start.lock"
|
|
296
|
+
flock -w "$((START_TIMEOUT + STOP_TIMEOUT + 30))" 8
|
|
297
|
+
|
|
298
|
+
if worker_running "$AGENT" && [ "$WORKER_PROTOCOL" = "$PROTOCOL" ] && { [ -z "\${REPLACE_BUILD:-}" ] || [ "$WORKER_BUILD" = "$BUILD" ]; }; then exit 0; fi
|
|
299
|
+
|
|
300
|
+
runs_as="\${DIMENSION_USER%%:*}"
|
|
301
|
+
SOCKET_OWNER=""
|
|
302
|
+
if [ "$runs_as" = 0 ]; then
|
|
303
|
+
SOCKET_OWNER="$(id -u):$(id -g)"
|
|
304
|
+
elif [ "$runs_as" != "$(id -u)" ]; then
|
|
305
|
+
echo "agent $AGENT's worker would run as uid $runs_as, whose socket uid $(id -u) can't connect to" >&2
|
|
306
|
+
exit "$E_THIRD_USER"
|
|
307
|
+
fi
|
|
308
|
+
|
|
309
|
+
docker exec -u 0 -e OWNER="$DIMENSION_USER" -e WORKER_DIR -e SCRIPTS_DIR -e OLD_WORKER_DIR -e RUN_PATH -e STATE_PATH -e CA_BUNDLE \
|
|
310
|
+
"$CONTAINER" sh -c "$PREPARE_WORKER"
|
|
311
|
+
|
|
312
|
+
script="$SCRIPTS_DIR/worker-$BUILD.js"
|
|
313
|
+
if ! docker exec "$CONTAINER" test -f "$script"; then
|
|
314
|
+
[ "\${HAS_BUNDLE:-}" = 1 ] || exit "$E_NEED_BUNDLE"
|
|
315
|
+
docker exec -i -u "$DIMENSION_USER" -e SCRIPT="$script" "$CONTAINER" sh -c "$PUT_SCRIPT"
|
|
316
|
+
fi
|
|
317
|
+
|
|
318
|
+
stop_worker "$AGENT"
|
|
319
|
+
docker exec -d -u "$DIMENSION_USER" \
|
|
320
|
+
-e HYB_AGENT="$AGENT" -e HYB_LOG="$RUN_PATH/$AGENT.log" -e HYB_INSTALL_DIR="$WORKER_DIR" \
|
|
321
|
+
-e HYB_WORKER_SCRIPT="$script" -e HYB_WORKER_BUILD="$BUILD" -e HYB_SOCKET_OWNER="$SOCKET_OWNER" \
|
|
322
|
+
-e NODE_EXTRA_CA_CERTS="$CA_BUNDLE" -e SSL_CERT_FILE="$CA_BUNDLE" \
|
|
323
|
+
"$CONTAINER" sh -c "$START_WORKER"
|
|
324
|
+
|
|
325
|
+
waited=0
|
|
326
|
+
until worker_running "$AGENT" && [ "$WORKER_BUILD" = "$BUILD" ]; do
|
|
327
|
+
if [ "$waited" -ge "$((START_TIMEOUT * 10))" ]; then
|
|
328
|
+
echo "agent $AGENT's worker did not come up:" >&2
|
|
329
|
+
tail -c 2000 "$RUN/$AGENT.log" >&2 2>/dev/null || true
|
|
330
|
+
exit 1
|
|
331
|
+
fi
|
|
332
|
+
sleep 0.1
|
|
333
|
+
waited=$((waited + 1))
|
|
334
|
+
done
|
|
335
|
+
docker exec -u 0 -e SCRIPTS_DIR -e BUILD "$CONTAINER" sh -c "$REMOVE_OLD_SCRIPTS"
|
|
336
|
+
`;
|
|
337
|
+
const STOP_AGENT = sh `
|
|
338
|
+
set -eu
|
|
339
|
+
[ -f "$DATA/agents/$AGENT.json" ] || exit 0
|
|
340
|
+
agent_dimension "$AGENT"
|
|
341
|
+
require_dimension
|
|
342
|
+
stop_worker "$AGENT"
|
|
343
|
+
rm -f "$DATA/agents/$AGENT.json"
|
|
344
|
+
`;
|
|
345
|
+
const WORKER_SOCKET = sh `
|
|
346
|
+
set -eu
|
|
347
|
+
agent_dimension "$AGENT"
|
|
348
|
+
printf '%s\n%s\n' "$DIMENSION" "$DATA/dimension-run/$DIMENSION/$AGENT.sock"
|
|
349
|
+
`;
|
|
350
|
+
const LIST_AGENTS = sh `
|
|
351
|
+
for record in "$DATA"/agents/*.json; do
|
|
352
|
+
[ -f "$record" ] || continue
|
|
353
|
+
name="\${record##*/}"
|
|
354
|
+
printf '%s\n' "\${name%.json}"
|
|
355
|
+
cat "$record"
|
|
356
|
+
printf '\n\036\n'
|
|
357
|
+
done
|
|
358
|
+
`;
|
|
359
|
+
const LIST_SESSIONS = sh `
|
|
360
|
+
checks="$(mktemp -d)"
|
|
361
|
+
for record in "$DATA"/agents/*.json; do
|
|
362
|
+
[ -f "$record" ] || continue
|
|
363
|
+
agent="\${record##*/}"
|
|
364
|
+
agent="\${agent%.json}"
|
|
365
|
+
agent_dimension "$agent"
|
|
366
|
+
for session in "$DATA/dimension-state/$DIMENSION/agents/$agent/sessions"/*.json; do
|
|
367
|
+
[ -f "$session" ] || continue
|
|
368
|
+
status="$(tr -d '\n' < "\${session%.json}/status.json" 2>/dev/null || true)"
|
|
369
|
+
printf '%s\t%s\n' "$agent" "$status"
|
|
370
|
+
cat "$session"
|
|
371
|
+
printf '\n\036\n'
|
|
372
|
+
done
|
|
373
|
+
(
|
|
374
|
+
# shellcheck disable=SC2034
|
|
375
|
+
CONTAINER="$CONTAINER_PREFIX$DIMENSION"
|
|
376
|
+
# shellcheck disable=SC2034
|
|
377
|
+
RUN="$DATA/dimension-run/$DIMENSION"
|
|
378
|
+
if worker_running "$agent"; then echo "running $agent" > "$checks/$agent"; fi
|
|
379
|
+
) &
|
|
380
|
+
done
|
|
381
|
+
wait
|
|
382
|
+
cat "$checks"/* 2>/dev/null || true
|
|
383
|
+
rm -rf "$checks"
|
|
384
|
+
`;
|
|
385
|
+
const INSTALL_IN_DIMENSION = sh `
|
|
386
|
+
set -eu
|
|
387
|
+
cd "$WORKER_DIR"
|
|
388
|
+
HOME="$(getent passwd "$(id -u)" | cut -d: -f6)"
|
|
389
|
+
export HOME
|
|
390
|
+
exec 9> .install.lock
|
|
391
|
+
flock 9
|
|
392
|
+
installed() {
|
|
393
|
+
manifest="node_modules/$PKG/package.json"
|
|
394
|
+
[ -f "$manifest" ] || return 1
|
|
395
|
+
grep -q "\"version\": *\"$VERSION\"" "$manifest" || return 1
|
|
396
|
+
# shellcheck disable=SC2086
|
|
397
|
+
for executable in $EXECUTABLES; do
|
|
398
|
+
for found in node_modules/$executable; do
|
|
399
|
+
[ -x "$found" ] && return 0
|
|
400
|
+
done
|
|
401
|
+
done
|
|
402
|
+
return 1
|
|
403
|
+
}
|
|
404
|
+
installed && exit 0
|
|
405
|
+
force=""
|
|
406
|
+
if [ -f "node_modules/$PKG/package.json" ] && grep -q "\"version\": *\"$VERSION\"" "node_modules/$PKG/package.json"; then force=--force; fi
|
|
407
|
+
# shellcheck disable=SC2086
|
|
408
|
+
bun add --exact $force "$PKG@$VERSION"
|
|
409
|
+
installed || { echo "bun add $PKG@$VERSION left no executable ($EXECUTABLES)" >&2; exit 1; }
|
|
410
|
+
`;
|
|
411
|
+
const INSTALL_BACKEND = sh `
|
|
412
|
+
set -eu
|
|
413
|
+
agent_dimension "$AGENT"
|
|
414
|
+
require_dimension
|
|
415
|
+
docker exec -u "$DIMENSION_USER" -e WORKER_DIR -e PKG -e VERSION -e EXECUTABLES \
|
|
416
|
+
-e NODE_EXTRA_CA_CERTS="$CA_BUNDLE" -e SSL_CERT_FILE="$CA_BUNDLE" "$CONTAINER" sh -c "$INSTALL_IN_DIMENSION"
|
|
417
|
+
`;
|
|
418
|
+
const LIST_BACKENDS = sh `
|
|
419
|
+
set -eu
|
|
420
|
+
agent_dimension "$AGENT"
|
|
421
|
+
modules="$DATA/dimension-state/$DIMENSION/worker/node_modules"
|
|
422
|
+
tab="$(printf '\t')"
|
|
423
|
+
printf '%s\n' "$BACKENDS" | while IFS="$tab" read -r backend pkg _pinned executables; do
|
|
424
|
+
installed="$(sed -n 's/^ *"version": *"\([^"]*\)".*/\1/p' "$modules/$pkg/package.json" 2>/dev/null | head -n 1)"
|
|
425
|
+
ready=0
|
|
426
|
+
if [ -n "$installed" ]; then
|
|
427
|
+
# shellcheck disable=SC2086
|
|
428
|
+
for executable in $executables; do
|
|
429
|
+
for found in "$modules"/$executable; do
|
|
430
|
+
[ -x "$found" ] && ready=1
|
|
431
|
+
done
|
|
432
|
+
done
|
|
433
|
+
fi
|
|
434
|
+
printf '%s\t%s\t%s\n' "$backend" "$installed" "$ready"
|
|
435
|
+
done
|
|
436
|
+
`;
|
|
437
|
+
const LOGIN_STATE = sh `
|
|
438
|
+
set -eu
|
|
439
|
+
agent_dimension "$AGENT"
|
|
440
|
+
record="$DATA/dimension-state/$DIMENSION/agents/$AGENT/sessions/$SESSION.json"
|
|
441
|
+
if [ ! -f "$record" ]; then
|
|
442
|
+
echo "$SESSION" >&2
|
|
443
|
+
exit "$E_NO_SESSION"
|
|
444
|
+
fi
|
|
445
|
+
backend="$(sed -n 's/^ *"backend": *"\([^"]*\)".*/\1/p' "$record" | head -n 1)"
|
|
446
|
+
tab="$(printf '\t')"
|
|
447
|
+
upstream="$(printf '%s\n' "$UPSTREAMS" | while IFS="$tab" read -r name value; do
|
|
448
|
+
if [ "$name" = "$backend" ]; then echo "$value"; fi
|
|
449
|
+
done)"
|
|
450
|
+
signed=0
|
|
451
|
+
for portal in "$DATA"/portals/*.json; do
|
|
452
|
+
[ -f "$portal" ] || continue
|
|
453
|
+
grep -q "\"dimensionName\": *\"$DIMENSION\"" "$portal" || continue
|
|
454
|
+
gateway="$(sed -n 's/^ *"gatewayName": *"\([^"]*\)".*/\1/p' "$portal")"
|
|
455
|
+
state="$DATA/gateway-state/$gateway"
|
|
456
|
+
if [ -n "$upstream" ] && [ -f "$state/upstreams/$upstream.json" ] && [ -f "$state/credentials/$upstream.oauth.json" ]; then signed=1; fi
|
|
457
|
+
done
|
|
458
|
+
printf '%s\t%s\n' "$backend" "$signed"
|
|
459
|
+
`;
|
|
460
|
+
const AGENTS_DOWN = sh `
|
|
461
|
+
checks="$(mktemp -d)"
|
|
462
|
+
for record in "$DATA"/agents/*.json; do
|
|
463
|
+
[ -f "$record" ] || continue
|
|
464
|
+
agent="\${record##*/}"
|
|
465
|
+
agent="\${agent%.json}"
|
|
466
|
+
agent_dimension "$agent"
|
|
467
|
+
(
|
|
468
|
+
CONTAINER="$CONTAINER_PREFIX$DIMENSION"
|
|
469
|
+
# shellcheck disable=SC2034
|
|
470
|
+
RUN="$DATA/dimension-run/$DIMENSION"
|
|
471
|
+
[ "$(docker inspect -f '{{.State.Running}}' "$CONTAINER" 2>/dev/null)" = true ] || exit 0
|
|
472
|
+
worker_running "$agent" && exit 0
|
|
473
|
+
printf '%s\t%s\n' "$agent" "$DIMENSION" > "$checks/$agent"
|
|
474
|
+
) &
|
|
475
|
+
done
|
|
476
|
+
wait
|
|
477
|
+
cat "$checks"/* 2>/dev/null || true
|
|
478
|
+
rm -rf "$checks"
|
|
479
|
+
`;
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import type { Sandbox } from "../sandbox/api.ts";
|
|
2
2
|
import { type AgentTranscriptPage, type AgentTranscriptRequest } from "./api.ts";
|
|
3
|
-
/**
|
|
4
|
-
* A page of the session's transcript, read on `host` from the dimension's state directory (its host
|
|
5
|
-
* side) by the backend's own layout and parsing, so nothing has to run: one command, plus a read
|
|
6
|
-
* for each further chunk the rare page that needs more takes. Null for a session whose env moves the backend's home
|
|
7
|
-
* elsewhere (into the container, where only its worker reaches it).
|
|
8
|
-
*/
|
|
9
3
|
export declare function readHostTranscript(host: Sandbox, { stateDirectory, agentName, sessionName }: {
|
|
10
4
|
stateDirectory: string;
|
|
11
5
|
agentName: string;
|
|
@@ -2,12 +2,6 @@ import { AgentNotFoundError } from "./api.js";
|
|
|
2
2
|
import { CHUNK_BYTES, PROBE_BYTES, PROBE_LINES, TIME_SLACK_MS, createFileTranscript, prefetchedFiles, sandboxFiles, transcriptPage, transcriptTime, } from "./session/fileTranscript.js";
|
|
3
3
|
import { backendHomePath, sessionRecordPath } from "./session/sessionStorage.js";
|
|
4
4
|
import { STORED_TRANSCRIPTS } from "./session/storedTranscripts.js";
|
|
5
|
-
/**
|
|
6
|
-
* In one round trip: the session's record, its backend's file (found as the backend lays them out),
|
|
7
|
-
* the stretches written again in it, where the page starts (for a time, by bisecting the rest of the
|
|
8
|
-
* file as FileTranscript.page does) and what comes before that (PREFETCH_BYTES of it). The same
|
|
9
|
-
* searches as fileTranscript.ts, here where the file is. Args: a JSON object, see readHostTranscript.
|
|
10
|
-
*/
|
|
11
5
|
const READ_SCRIPT = String.raw `
|
|
12
6
|
import base64, datetime, fnmatch, glob, json, mmap, os, sys
|
|
13
7
|
args = json.loads(sys.argv[1])
|
|
@@ -146,16 +140,9 @@ if native_id and layout:
|
|
|
146
140
|
out.update(path=path, size=size, end=end, after=after, copies=[[a, b] for a, b in copies], pieces=pieces)
|
|
147
141
|
print(json.dumps(out))
|
|
148
142
|
`;
|
|
149
|
-
/** What's sent of the file with the record: a page and the slack around it, mostly, so it takes no more reads. */
|
|
150
143
|
const PREFETCH_BYTES = 8 * CHUNK_BYTES;
|
|
151
|
-
/**
|
|
152
|
-
* A page of the session's transcript, read on `host` from the dimension's state directory (its host
|
|
153
|
-
* side) by the backend's own layout and parsing, so nothing has to run: one command, plus a read
|
|
154
|
-
* for each further chunk the rare page that needs more takes. Null for a session whose env moves the backend's home
|
|
155
|
-
* elsewhere (into the container, where only its worker reaches it).
|
|
156
|
-
*/
|
|
157
144
|
export async function readHostTranscript(host, { stateDirectory, agentName, sessionName }, { limit, before, cursor }) {
|
|
158
|
-
before ??= cursor;
|
|
145
|
+
before ??= cursor;
|
|
159
146
|
const args = {
|
|
160
147
|
record: sessionRecordPath(stateDirectory, agentName, sessionName),
|
|
161
148
|
homes: Object.fromEntries(Object.keys(STORED_TRANSCRIPTS).map((backend) => [backend, backendHomePath(stateDirectory, backend)])),
|
|
@@ -174,6 +161,8 @@ export async function readHostTranscript(host, { stateDirectory, agentName, sess
|
|
|
174
161
|
if (!record)
|
|
175
162
|
throw new AgentNotFoundError(`session ${sessionName} does not exist`);
|
|
176
163
|
const stored = STORED_TRANSCRIPTS[record.spec.backend];
|
|
164
|
+
if (!stored)
|
|
165
|
+
return null;
|
|
177
166
|
if (Object.keys(stored.homeEnvironment("")).some((name) => record.spec.env?.[name] !== undefined))
|
|
178
167
|
return null;
|
|
179
168
|
if (!record.nativeId)
|
|
@@ -1,82 +1,67 @@
|
|
|
1
|
-
import type { AgentBackendInfo, AgentBackgroundTasks, AgentInput, AgentInterrupt, AgentMessageQueue, AgentModels, AgentPermissionRequest, AgentPermissionResult, AgentQuestionRequest, AgentQuestionResponse, AgentSession,
|
|
2
|
-
import type {
|
|
3
|
-
/** A capability's calls for one session: those of api.ts, with the session already chosen. */
|
|
1
|
+
import type { AgentBackendInfo, AgentBackgroundTasks, AgentInput, AgentInterrupt, AgentMessageQueue, AgentModels, AgentPermissionMode, AgentPermissionModes, AgentPermissionRequest, AgentPermissionResult, AgentQuestionRequest, AgentQuestionResponse, AgentSession, AgentStreamingMessage, SessionRef } from "../api.ts";
|
|
2
|
+
import type { Transcript, TranscriptFiles } from "./fileTranscript.ts";
|
|
4
3
|
export type ForSession<Capability> = {
|
|
5
4
|
[K in keyof Capability]: Capability[K] extends (ref: SessionRef, ...args: infer Args) => infer Result ? (...args: Args) => Result : never;
|
|
6
5
|
};
|
|
7
|
-
/** What one session's CLI runs with. */
|
|
8
6
|
export type SessionLaunch = {
|
|
9
7
|
spec: AgentSession;
|
|
10
|
-
/** The backend's id for the conversation, from an earlier run, to resume it; null for a new one. */
|
|
11
8
|
nativeId: string | null;
|
|
12
9
|
cwd: string;
|
|
13
|
-
/** The model chosen through AgentModels; null for the backend's default. */
|
|
14
10
|
model: string | null;
|
|
15
|
-
|
|
11
|
+
permissionMode: AgentPermissionMode;
|
|
16
12
|
installDir: string;
|
|
17
|
-
/** The worker's own environment, the CLI pointed at its home, and the session's. */
|
|
18
13
|
env: Record<string, string>;
|
|
19
14
|
};
|
|
20
|
-
/** What the CLI reports to, and asks of, the worker. */
|
|
21
15
|
export type SessionCallbacks = {
|
|
22
|
-
/** Appends to the session's stream. */
|
|
23
16
|
emit(message: AgentStreamingMessage): void;
|
|
24
|
-
/** Records the backend's conversation id, so a later run can resume it. */
|
|
25
17
|
setNativeId(id: string): void;
|
|
26
|
-
/** The decision on a tool call: someone's answer, or a denial once the turn is interrupted, the session stops, or `signal` aborts. */
|
|
27
18
|
requestPermission(request: Omit<AgentPermissionRequest, "id">, signal?: AbortSignal): Promise<AgentPermissionResult>;
|
|
28
|
-
/** The user's answers, or null once the turn is interrupted, the session stops, or `signal` aborts. */
|
|
29
19
|
askQuestions(request: Omit<AgentQuestionRequest, "id">, signal?: AbortSignal): Promise<AgentQuestionResponse | null>;
|
|
30
|
-
|
|
31
|
-
|
|
20
|
+
exited(exit: CliExit | null, error?: Error): void;
|
|
21
|
+
activeChanged(): void;
|
|
22
|
+
subagentStarted(toolUseId: string, subagentId: string): void;
|
|
23
|
+
};
|
|
24
|
+
export type CliExit = {
|
|
25
|
+
code: number | null;
|
|
26
|
+
signal: string | null;
|
|
32
27
|
};
|
|
33
28
|
export type SessionContext = SessionLaunch & SessionCallbacks;
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
export type RunningSession = ForSession<AgentInput & AgentInterrupt & AgentMessageQueue & AgentModels & AgentPermissionModes & AgentBackgroundTasks> & {
|
|
30
|
+
active(): boolean;
|
|
36
31
|
close(): Promise<void>;
|
|
37
32
|
};
|
|
38
|
-
/** What a CLI's sign-in commands run with. */
|
|
39
33
|
export type AuthEnvironment = {
|
|
40
34
|
installDir: string;
|
|
41
35
|
env: Record<string, string>;
|
|
42
36
|
cwd: string;
|
|
43
37
|
};
|
|
44
|
-
/** A sign-in in progress: the CLI's own login command. */
|
|
45
38
|
export interface LoginProcess {
|
|
46
|
-
/** Resolves once the CLI has shown where to sign in. */
|
|
47
39
|
prompt: Promise<{
|
|
48
40
|
url: string;
|
|
49
41
|
userCode?: string;
|
|
50
42
|
needsCode: boolean;
|
|
51
43
|
}>;
|
|
52
|
-
/** Resolves when the CLI signed in; rejects with why it didn't. */
|
|
53
44
|
done: Promise<void>;
|
|
54
|
-
/** Gives the CLI the code the sign-in page showed. */
|
|
55
45
|
submitCode(code: string): void;
|
|
56
46
|
cancel(): void;
|
|
57
47
|
}
|
|
58
|
-
/** A CLI's sign-in, through its own commands. The credentials it saves are fakes when a gateway holds the real ones. */
|
|
59
48
|
export type BackendAuth = {
|
|
60
49
|
signedIn(environment: AuthEnvironment): Promise<boolean>;
|
|
61
|
-
/** With `gatewaySignedIn` (the dimension's gateway holds the upstream's credential) it completes by itself: the gateway answers. */
|
|
62
50
|
login(environment: AuthEnvironment, gatewaySignedIn: boolean): LoginProcess;
|
|
63
51
|
};
|
|
64
|
-
/** What finds a session's conversation in its backend's storage, read through `files`. */
|
|
65
52
|
export type TranscriptSource = {
|
|
66
53
|
nativeId: string;
|
|
67
54
|
env: Record<string, string>;
|
|
68
55
|
files: TranscriptFiles;
|
|
56
|
+
installDir: string;
|
|
69
57
|
};
|
|
70
|
-
/** An agent CLI, wired in agent/session/<backend>/: one file per capability. */
|
|
71
58
|
export type SessionBackend = {
|
|
72
|
-
/** Installs the backend under `installDir`, or updates it to the pinned version. Runs in the dimension. */
|
|
73
59
|
install(installDir: string): Promise<void>;
|
|
74
|
-
|
|
75
|
-
installed(installDir: string): Promise<Pick<AgentBackendInfo, "version" | "ready">>;
|
|
60
|
+
installed(installDir: string): Promise<Omit<AgentBackendInfo, "backend">>;
|
|
76
61
|
start(context: SessionContext): Promise<RunningSession>;
|
|
77
62
|
auth: BackendAuth;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
63
|
+
transcript(source: TranscriptSource): Transcript;
|
|
64
|
+
findSubagent(source: TranscriptSource, toolUseId: string): Promise<string | null>;
|
|
65
|
+
subagentTranscript(source: TranscriptSource, subagentId: string, toolUseId: string): Transcript;
|
|
81
66
|
homeEnvironment(home: string): Record<string, string>;
|
|
82
67
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AgentBackendInfo } from "../../api.ts";
|
|
2
|
+
export declare const CLAUDE_SDK_PACKAGE = "../../../vendor-types/claude/agent-sdk/sdk.js";
|
|
3
|
+
export declare function claudePackage(): {
|
|
4
|
+
pkg: string;
|
|
5
|
+
version: string;
|
|
6
|
+
executables: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function installClaude(installDir: string): Promise<void>;
|
|
9
|
+
export declare function installedClaude(installDir: string): Promise<Omit<AgentBackendInfo, "backend">>;
|
|
10
|
+
export declare function claudeExecutable(installDir: string): string;
|