@greppleai/grip 0.0.1 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +114 -0
- package/README.md +13 -7
- package/dist/bun/cli.d.ts.map +1 -1
- package/dist/bun/cli.js +4 -2
- package/dist/bun/cli.js.map +1 -1
- package/dist/bun/embedded-assets.d.ts +3 -0
- package/dist/bun/embedded-assets.d.ts.map +1 -0
- package/dist/bun/embedded-assets.js +52 -0
- package/dist/bun/embedded-assets.js.map +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +17 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/types.d.ts +5 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/remote-workspace-protocol/contract.d.ts +13 -4
- package/dist/core/remote-workspace-protocol/contract.d.ts.map +1 -1
- package/dist/core/remote-workspace-protocol/contract.js +17 -1
- package/dist/core/remote-workspace-protocol/contract.js.map +1 -1
- package/dist/core/remote-workspace-protocol/guest-shared-client.d.ts +16 -0
- package/dist/core/remote-workspace-protocol/guest-shared-client.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/guest-shared-client.js +54 -0
- package/dist/core/remote-workspace-protocol/guest-shared-client.js.map +1 -0
- package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.d.ts +23 -0
- package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.js +112 -0
- package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.js.map +1 -0
- package/dist/core/remote-workspace-protocol/guest-shared-uplink.d.ts +30 -0
- package/dist/core/remote-workspace-protocol/guest-shared-uplink.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/guest-shared-uplink.js +243 -0
- package/dist/core/remote-workspace-protocol/guest-shared-uplink.js.map +1 -0
- package/dist/core/remote-workspace-protocol/lima-owner-directory.d.ts +3 -0
- package/dist/core/remote-workspace-protocol/lima-owner-directory.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/lima-owner-directory.js +31 -0
- package/dist/core/remote-workspace-protocol/lima-owner-directory.js.map +1 -0
- package/dist/core/remote-workspace-protocol/lima-owner-lock.d.ts +9 -0
- package/dist/core/remote-workspace-protocol/lima-owner-lock.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/lima-owner-lock.js +89 -0
- package/dist/core/remote-workspace-protocol/lima-owner-lock.js.map +1 -0
- package/dist/core/remote-workspace-protocol/session.d.ts +2 -0
- package/dist/core/remote-workspace-protocol/session.d.ts.map +1 -1
- package/dist/core/remote-workspace-protocol/session.js +3 -0
- package/dist/core/remote-workspace-protocol/session.js.map +1 -1
- package/dist/core/remote-workspace-protocol/shared-transport.d.ts +71 -0
- package/dist/core/remote-workspace-protocol/shared-transport.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/shared-transport.js +68 -0
- package/dist/core/remote-workspace-protocol/shared-transport.js.map +1 -0
- package/dist/core/sandbox/command.d.ts +2 -4
- package/dist/core/sandbox/command.d.ts.map +1 -1
- package/dist/core/sandbox/command.js +24 -46
- package/dist/core/sandbox/command.js.map +1 -1
- package/dist/core/sandbox/config.d.ts +91 -0
- package/dist/core/sandbox/config.d.ts.map +1 -0
- package/dist/core/sandbox/config.js +88 -0
- package/dist/core/sandbox/config.js.map +1 -0
- package/dist/core/sandbox/lima-guest-binary.d.ts +11 -0
- package/dist/core/sandbox/lima-guest-binary.d.ts.map +1 -0
- package/dist/core/sandbox/lima-guest-binary.js +140 -0
- package/dist/core/sandbox/lima-guest-binary.js.map +1 -0
- package/dist/core/sandbox/lima.d.ts +22 -3
- package/dist/core/sandbox/lima.d.ts.map +1 -1
- package/dist/core/sandbox/lima.js +434 -42
- package/dist/core/sandbox/lima.js.map +1 -1
- package/dist/core/sandbox/shared-lima-roots.d.ts +11 -0
- package/dist/core/sandbox/shared-lima-roots.d.ts.map +1 -0
- package/dist/core/sandbox/shared-lima-roots.js +25 -0
- package/dist/core/sandbox/shared-lima-roots.js.map +1 -0
- package/dist/core/sandbox/update-shared-guest-binary.d.ts +8 -0
- package/dist/core/sandbox/update-shared-guest-binary.d.ts.map +1 -0
- package/dist/core/sandbox/update-shared-guest-binary.js +100 -0
- package/dist/core/sandbox/update-shared-guest-binary.js.map +1 -0
- package/dist/core/settings-manager.d.ts +1 -5
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +25 -14
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/file-mutation-queue.d.ts +2 -0
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/core/tools/file-mutation-queue.js +9 -0
- package/dist/core/tools/file-mutation-queue.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +2 -3
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +2 -3
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +2 -3
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/operations.d.ts +13 -2
- package/dist/core/tools/operations.d.ts.map +1 -1
- package/dist/core/tools/operations.js +132 -36
- package/dist/core/tools/operations.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +3 -3
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts +1 -0
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js +5 -0
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +2 -3
- package/dist/core/tools/write.js.map +1 -1
- package/dist/daemon/activity-budget.d.ts +10 -0
- package/dist/daemon/activity-budget.d.ts.map +1 -0
- package/dist/daemon/activity-budget.js +34 -0
- package/dist/daemon/activity-budget.js.map +1 -0
- package/dist/daemon/config.d.ts +2 -0
- package/dist/daemon/config.d.ts.map +1 -1
- package/dist/daemon/config.js +15 -0
- package/dist/daemon/config.js.map +1 -1
- package/dist/daemon/outbound-budget.d.ts +10 -0
- package/dist/daemon/outbound-budget.d.ts.map +1 -0
- package/dist/daemon/outbound-budget.js +30 -0
- package/dist/daemon/outbound-budget.js.map +1 -0
- package/dist/daemon/security.d.ts.map +1 -1
- package/dist/daemon/security.js +3 -1
- package/dist/daemon/security.js.map +1 -1
- package/dist/daemon/server.d.ts.map +1 -1
- package/dist/daemon/server.js +104 -24
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/shared-connection.d.ts +33 -0
- package/dist/daemon/shared-connection.d.ts.map +1 -0
- package/dist/daemon/shared-connection.js +271 -0
- package/dist/daemon/shared-connection.js.map +1 -0
- package/dist/daemon/shared-revision.d.ts +5 -0
- package/dist/daemon/shared-revision.d.ts.map +1 -0
- package/dist/daemon/shared-revision.js +61 -0
- package/dist/daemon/shared-revision.js.map +1 -0
- package/dist/daemon/shared-roots.d.ts +9 -0
- package/dist/daemon/shared-roots.d.ts.map +1 -0
- package/dist/daemon/shared-roots.js +33 -0
- package/dist/daemon/shared-roots.js.map +1 -0
- package/dist/daemon/workspace-runtime.d.ts +1 -1
- package/dist/daemon/workspace-runtime.d.ts.map +1 -1
- package/dist/daemon/workspace-runtime.js +42 -10
- package/dist/daemon/workspace-runtime.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +5 -3
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +1 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +13 -3
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts +4 -3
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +5 -4
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +4 -3
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +5 -4
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +4 -3
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/hook-execution.d.ts +3 -0
- package/dist/modes/interactive/components/hook-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/hook-execution.js +20 -2
- package/dist/modes/interactive/components/hook-execution.js.map +1 -1
- package/dist/modes/interactive/components/message-card.d.ts +9 -0
- package/dist/modes/interactive/components/message-card.d.ts.map +1 -0
- package/dist/modes/interactive/components/message-card.js +24 -0
- package/dist/modes/interactive/components/message-card.js.map +1 -0
- package/dist/modes/interactive/components/skill-invocation-message.d.ts +4 -3
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +5 -4
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +27 -21
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +3 -2
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -3
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +106 -47
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts +3 -0
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +10 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js +1 -1
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js +5 -0
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/photon.d.ts.map +1 -1
- package/dist/utils/photon.js +1 -0
- package/dist/utils/photon.js.map +1 -1
- package/docs/daemon.md +6 -4
- package/docs/docs.json +1 -1
- package/docs/extensions.md +3 -0
- package/docs/index.md +8 -12
- package/docs/packages.md +3 -3
- package/docs/quickstart.md +11 -19
- package/docs/sandbox.md +40 -117
- package/docs/settings.md +12 -11
- package/docs/termux.md +1 -18
- package/docs/usage.md +2 -2
- package/examples/README.md +0 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +5 -6
- package/dist/core/sandbox/docker.d.ts +0 -169
- package/dist/core/sandbox/docker.d.ts.map +0 -1
- package/dist/core/sandbox/docker.js +0 -633
- package/dist/core/sandbox/docker.js.map +0 -1
- package/examples/sandbox-docker/Dockerfile +0 -274
- package/examples/sandbox-docker/README.md +0 -88
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/** Opt-in multiplex transport. V1 messages are validated *inside* a bound channel. */
|
|
2
|
+
import { type Static, Type } from "typebox";
|
|
3
|
+
import { type RemoteWorkspaceClientMessage, type RemoteWorkspaceProtocolLimits, type RemoteWorkspaceServerMessage } from "./contract.ts";
|
|
4
|
+
export declare const SHARED_WORKSPACE_WEBSOCKET_PROTOCOL = "grip.workspace.v2";
|
|
5
|
+
declare const Channel: Type.TObject<{
|
|
6
|
+
type: Type.TLiteral<"channel">;
|
|
7
|
+
channelId: Type.TString;
|
|
8
|
+
payload: Type.TUnknown;
|
|
9
|
+
}>;
|
|
10
|
+
declare const ClientFrame: Type.TUnion<[Type.TObject<{
|
|
11
|
+
type: Type.TLiteral<"hello">;
|
|
12
|
+
id: Type.TString;
|
|
13
|
+
version: Type.TLiteral<2>;
|
|
14
|
+
}>, Type.TObject<{
|
|
15
|
+
type: Type.TLiteral<"open">;
|
|
16
|
+
id: Type.TString;
|
|
17
|
+
root: Type.TString;
|
|
18
|
+
}>, Type.TObject<{
|
|
19
|
+
type: Type.TLiteral<"close">;
|
|
20
|
+
channelId: Type.TString;
|
|
21
|
+
}>, Type.TObject<{
|
|
22
|
+
type: Type.TLiteral<"channel">;
|
|
23
|
+
channelId: Type.TString;
|
|
24
|
+
payload: Type.TUnknown;
|
|
25
|
+
}>]>;
|
|
26
|
+
declare const ServerFrame: Type.TUnion<[Type.TObject<{
|
|
27
|
+
type: Type.TLiteral<"hello_ack">;
|
|
28
|
+
id: Type.TString;
|
|
29
|
+
version: Type.TLiteral<2>;
|
|
30
|
+
epoch: Type.TString;
|
|
31
|
+
daemonVersion: Type.TString;
|
|
32
|
+
}>, Type.TObject<{
|
|
33
|
+
type: Type.TLiteral<"opened">;
|
|
34
|
+
id: Type.TString;
|
|
35
|
+
channelId: Type.TString;
|
|
36
|
+
}>, Type.TObject<{
|
|
37
|
+
type: Type.TLiteral<"closed">;
|
|
38
|
+
channelId: Type.TString;
|
|
39
|
+
reason: Type.TOptional<Type.TObject<{
|
|
40
|
+
code: Type.TUnion<[Type.TLiteral<"protocol_error">, Type.TLiteral<"invalid_payload">, Type.TLiteral<"message_too_large">, Type.TLiteral<"policy_violation">, Type.TLiteral<"normal">]>;
|
|
41
|
+
message: Type.TString;
|
|
42
|
+
}>>;
|
|
43
|
+
}>, Type.TObject<{
|
|
44
|
+
type: Type.TLiteral<"control_error">;
|
|
45
|
+
id: Type.TString;
|
|
46
|
+
code: Type.TUnion<[Type.TLiteral<"invalid_root">, Type.TLiteral<"limit_exceeded">, Type.TLiteral<"invalid_request">]>;
|
|
47
|
+
message: Type.TString;
|
|
48
|
+
}>, Type.TObject<{
|
|
49
|
+
type: Type.TLiteral<"channel">;
|
|
50
|
+
channelId: Type.TString;
|
|
51
|
+
payload: Type.TUnknown;
|
|
52
|
+
}>]>;
|
|
53
|
+
export type SharedWorkspaceClientChannelFrame = Omit<Static<typeof Channel>, "payload"> & {
|
|
54
|
+
payload: RemoteWorkspaceClientMessage;
|
|
55
|
+
};
|
|
56
|
+
export type SharedWorkspaceServerChannelFrame = Omit<Static<typeof Channel>, "payload"> & {
|
|
57
|
+
payload: RemoteWorkspaceServerMessage;
|
|
58
|
+
};
|
|
59
|
+
export type SharedWorkspaceClientFrame = Exclude<Static<typeof ClientFrame>, {
|
|
60
|
+
type: "channel";
|
|
61
|
+
}> | SharedWorkspaceClientChannelFrame;
|
|
62
|
+
/** The daemon dispatches a valid outer envelope before parsing the inner message on its bound channel. */
|
|
63
|
+
export type SharedWorkspaceClientEnvelope = Static<typeof ClientFrame>;
|
|
64
|
+
export type SharedWorkspaceServerFrame = Exclude<Static<typeof ServerFrame>, {
|
|
65
|
+
type: "channel";
|
|
66
|
+
}> | SharedWorkspaceServerChannelFrame;
|
|
67
|
+
export declare function parseSharedWorkspaceClientFrame(input: string | Uint8Array, limits: RemoteWorkspaceProtocolLimits): SharedWorkspaceClientFrame;
|
|
68
|
+
export declare function parseSharedWorkspaceClientEnvelope(input: string | Uint8Array, limits: RemoteWorkspaceProtocolLimits): SharedWorkspaceClientEnvelope;
|
|
69
|
+
export declare function parseSharedWorkspaceServerFrame(input: string | Uint8Array, limits: RemoteWorkspaceProtocolLimits): SharedWorkspaceServerFrame;
|
|
70
|
+
export {};
|
|
71
|
+
//# sourceMappingURL=shared-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-transport.d.ts","sourceRoot":"","sources":["../../../src/core/remote-workspace-protocol/shared-transport.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,EAIN,KAAK,4BAA4B,EACjC,KAAK,6BAA6B,EAClC,KAAK,4BAA4B,EAEjC,MAAM,eAAe,CAAC;AAEvB,eAAO,MAAM,mCAAmC,sBAAsB,CAAC;AAsDvE,QAAA,MAAM,OAAO;;;;EAAwG,CAAC;AAEtH,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;IAA4C,CAAC;AAC9D,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;IAAgE,CAAC;AAGlF,MAAM,MAAM,iCAAiC,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,OAAO,CAAC,EAAE,SAAS,CAAC,GAAG;IACzF,OAAO,EAAE,4BAA4B,CAAC;CACtC,CAAC;AACF,MAAM,MAAM,iCAAiC,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,OAAO,CAAC,EAAE,SAAS,CAAC,GAAG;IACzF,OAAO,EAAE,4BAA4B,CAAC;CACtC,CAAC;AACF,MAAM,MAAM,0BAA0B,GACnC,OAAO,CAAC,MAAM,CAAC,OAAO,WAAW,CAAC,EAAE;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC,GACxD,iCAAiC,CAAC;AACrC,0GAA0G;AAC1G,MAAM,MAAM,6BAA6B,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAC;AACvE,MAAM,MAAM,0BAA0B,GACnC,OAAO,CAAC,MAAM,CAAC,OAAO,WAAW,CAAC,EAAE;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC,GACxD,iCAAiC,CAAC;AAuBrC,wBAAgB,+BAA+B,CAC9C,KAAK,EAAE,MAAM,GAAG,UAAU,EAC1B,MAAM,EAAE,6BAA6B,GACnC,0BAA0B,CAE5B;AACD,wBAAgB,kCAAkC,CACjD,KAAK,EAAE,MAAM,GAAG,UAAU,EAC1B,MAAM,EAAE,6BAA6B,GACnC,6BAA6B,CAE/B;AAED,wBAAgB,+BAA+B,CAC9C,KAAK,EAAE,MAAM,GAAG,UAAU,EAC1B,MAAM,EAAE,6BAA6B,GACnC,0BAA0B,CAE5B","sourcesContent":["/** Opt-in multiplex transport. V1 messages are validated *inside* a bound channel. */\nimport { type Static, Type } from \"typebox\";\nimport { Compile, type Validator } from \"typebox/compile\";\nimport {\n\tdecodeRemoteWorkspaceMessage,\n\tparseRemoteWorkspaceClientMessage,\n\tparseRemoteWorkspaceServerMessage,\n\ttype RemoteWorkspaceClientMessage,\n\ttype RemoteWorkspaceProtocolLimits,\n\ttype RemoteWorkspaceServerMessage,\n\tRemoteWorkspaceValidationError,\n} from \"./contract.ts\";\n\nexport const SHARED_WORKSPACE_WEBSOCKET_PROTOCOL = \"grip.workspace.v2\";\nconst strict = { additionalProperties: false } as const;\nconst Id = Type.String({ minLength: 1, maxLength: 128, pattern: \"^[A-Za-z0-9._:-]+$\" });\nconst ChannelId = Type.String({ minLength: 24, maxLength: 128, pattern: \"^[A-Za-z0-9_-]+$\" });\n\nconst Hello = Type.Object({ type: Type.Literal(\"hello\"), id: Id, version: Type.Literal(2) }, strict);\nconst HelloAck = Type.Object(\n\t{\n\t\ttype: Type.Literal(\"hello_ack\"),\n\t\tid: Id,\n\t\tversion: Type.Literal(2),\n\t\tepoch: ChannelId,\n\t\tdaemonVersion: Type.String({ minLength: 1, maxLength: 128 }),\n\t},\n\tstrict,\n);\nconst Open = Type.Object(\n\t{ type: Type.Literal(\"open\"), id: Id, root: Type.String({ minLength: 1, maxLength: 32_768 }) },\n\tstrict,\n);\nconst Opened = Type.Object({ type: Type.Literal(\"opened\"), id: Id, channelId: ChannelId }, strict);\nconst Close = Type.Object({ type: Type.Literal(\"close\"), channelId: ChannelId }, strict);\nconst Closed = Type.Object(\n\t{\n\t\ttype: Type.Literal(\"closed\"),\n\t\tchannelId: ChannelId,\n\t\t// Absent for an acknowledged client detach; present for an unsolicited protocol failure.\n\t\treason: Type.Optional(\n\t\t\tType.Object(\n\t\t\t\t{\n\t\t\t\t\tcode: Type.Union([\n\t\t\t\t\t\tType.Literal(\"protocol_error\"),\n\t\t\t\t\t\tType.Literal(\"invalid_payload\"),\n\t\t\t\t\t\tType.Literal(\"message_too_large\"),\n\t\t\t\t\t\tType.Literal(\"policy_violation\"),\n\t\t\t\t\t\tType.Literal(\"normal\"),\n\t\t\t\t\t]),\n\t\t\t\t\tmessage: Type.String({ minLength: 1, maxLength: 120 }),\n\t\t\t\t},\n\t\t\t\tstrict,\n\t\t\t),\n\t\t),\n\t},\n\tstrict,\n);\nconst ControlError = Type.Object(\n\t{\n\t\ttype: Type.Literal(\"control_error\"),\n\t\tid: Id,\n\t\tcode: Type.Union([Type.Literal(\"invalid_root\"), Type.Literal(\"limit_exceeded\"), Type.Literal(\"invalid_request\")]),\n\t\tmessage: Type.String({ minLength: 1, maxLength: 1024 }),\n\t},\n\tstrict,\n);\nconst Channel = Type.Object({ type: Type.Literal(\"channel\"), channelId: ChannelId, payload: Type.Unknown() }, strict);\n\nconst ClientFrame = Type.Union([Hello, Open, Close, Channel]);\nconst ServerFrame = Type.Union([HelloAck, Opened, Closed, ControlError, Channel]);\nconst clientValidator = Compile(ClientFrame);\nconst serverValidator = Compile(ServerFrame);\nexport type SharedWorkspaceClientChannelFrame = Omit<Static<typeof Channel>, \"payload\"> & {\n\tpayload: RemoteWorkspaceClientMessage;\n};\nexport type SharedWorkspaceServerChannelFrame = Omit<Static<typeof Channel>, \"payload\"> & {\n\tpayload: RemoteWorkspaceServerMessage;\n};\nexport type SharedWorkspaceClientFrame =\n\t| Exclude<Static<typeof ClientFrame>, { type: \"channel\" }>\n\t| SharedWorkspaceClientChannelFrame;\n/** The daemon dispatches a valid outer envelope before parsing the inner message on its bound channel. */\nexport type SharedWorkspaceClientEnvelope = Static<typeof ClientFrame>;\nexport type SharedWorkspaceServerFrame =\n\t| Exclude<Static<typeof ServerFrame>, { type: \"channel\" }>\n\t| SharedWorkspaceServerChannelFrame;\n\nfunction validateFrame<T extends { type: string }>(\n\tinput: string | Uint8Array,\n\tlimits: RemoteWorkspaceProtocolLimits,\n\tvalidator: Validator,\n\tparsePayload?: (value: unknown, limits: RemoteWorkspaceProtocolLimits) => unknown,\n): T {\n\tconst value = decodeRemoteWorkspaceMessage(input, limits);\n\tif (!validator.Check(value)) {\n\t\tconst failure = validator.Errors(value)[0];\n\t\tthrow new RemoteWorkspaceValidationError(\n\t\t\t`Invalid v2 frame: ${failure?.instancePath || \"/\"}: ${failure?.message ?? \"schema mismatch\"}`,\n\t\t);\n\t}\n\tconst frame = value as T;\n\tif (frame.type === \"channel\" && parsePayload) {\n\t\tconst channel = frame as T & { payload: unknown };\n\t\tparsePayload(channel.payload, limits);\n\t}\n\treturn frame;\n}\n\nexport function parseSharedWorkspaceClientFrame(\n\tinput: string | Uint8Array,\n\tlimits: RemoteWorkspaceProtocolLimits,\n): SharedWorkspaceClientFrame {\n\treturn validateFrame(input, limits, clientValidator, parseRemoteWorkspaceClientMessage);\n}\nexport function parseSharedWorkspaceClientEnvelope(\n\tinput: string | Uint8Array,\n\tlimits: RemoteWorkspaceProtocolLimits,\n): SharedWorkspaceClientEnvelope {\n\treturn validateFrame(input, limits, clientValidator);\n}\n\nexport function parseSharedWorkspaceServerFrame(\n\tinput: string | Uint8Array,\n\tlimits: RemoteWorkspaceProtocolLimits,\n): SharedWorkspaceServerFrame {\n\treturn validateFrame(input, limits, serverValidator, parseRemoteWorkspaceServerMessage);\n}\n"]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** Opt-in multiplex transport. V1 messages are validated *inside* a bound channel. */
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
import { Compile } from "typebox/compile";
|
|
4
|
+
import { decodeRemoteWorkspaceMessage, parseRemoteWorkspaceClientMessage, parseRemoteWorkspaceServerMessage, RemoteWorkspaceValidationError, } from "./contract.js";
|
|
5
|
+
export const SHARED_WORKSPACE_WEBSOCKET_PROTOCOL = "grip.workspace.v2";
|
|
6
|
+
const strict = { additionalProperties: false };
|
|
7
|
+
const Id = Type.String({ minLength: 1, maxLength: 128, pattern: "^[A-Za-z0-9._:-]+$" });
|
|
8
|
+
const ChannelId = Type.String({ minLength: 24, maxLength: 128, pattern: "^[A-Za-z0-9_-]+$" });
|
|
9
|
+
const Hello = Type.Object({ type: Type.Literal("hello"), id: Id, version: Type.Literal(2) }, strict);
|
|
10
|
+
const HelloAck = Type.Object({
|
|
11
|
+
type: Type.Literal("hello_ack"),
|
|
12
|
+
id: Id,
|
|
13
|
+
version: Type.Literal(2),
|
|
14
|
+
epoch: ChannelId,
|
|
15
|
+
daemonVersion: Type.String({ minLength: 1, maxLength: 128 }),
|
|
16
|
+
}, strict);
|
|
17
|
+
const Open = Type.Object({ type: Type.Literal("open"), id: Id, root: Type.String({ minLength: 1, maxLength: 32_768 }) }, strict);
|
|
18
|
+
const Opened = Type.Object({ type: Type.Literal("opened"), id: Id, channelId: ChannelId }, strict);
|
|
19
|
+
const Close = Type.Object({ type: Type.Literal("close"), channelId: ChannelId }, strict);
|
|
20
|
+
const Closed = Type.Object({
|
|
21
|
+
type: Type.Literal("closed"),
|
|
22
|
+
channelId: ChannelId,
|
|
23
|
+
// Absent for an acknowledged client detach; present for an unsolicited protocol failure.
|
|
24
|
+
reason: Type.Optional(Type.Object({
|
|
25
|
+
code: Type.Union([
|
|
26
|
+
Type.Literal("protocol_error"),
|
|
27
|
+
Type.Literal("invalid_payload"),
|
|
28
|
+
Type.Literal("message_too_large"),
|
|
29
|
+
Type.Literal("policy_violation"),
|
|
30
|
+
Type.Literal("normal"),
|
|
31
|
+
]),
|
|
32
|
+
message: Type.String({ minLength: 1, maxLength: 120 }),
|
|
33
|
+
}, strict)),
|
|
34
|
+
}, strict);
|
|
35
|
+
const ControlError = Type.Object({
|
|
36
|
+
type: Type.Literal("control_error"),
|
|
37
|
+
id: Id,
|
|
38
|
+
code: Type.Union([Type.Literal("invalid_root"), Type.Literal("limit_exceeded"), Type.Literal("invalid_request")]),
|
|
39
|
+
message: Type.String({ minLength: 1, maxLength: 1024 }),
|
|
40
|
+
}, strict);
|
|
41
|
+
const Channel = Type.Object({ type: Type.Literal("channel"), channelId: ChannelId, payload: Type.Unknown() }, strict);
|
|
42
|
+
const ClientFrame = Type.Union([Hello, Open, Close, Channel]);
|
|
43
|
+
const ServerFrame = Type.Union([HelloAck, Opened, Closed, ControlError, Channel]);
|
|
44
|
+
const clientValidator = Compile(ClientFrame);
|
|
45
|
+
const serverValidator = Compile(ServerFrame);
|
|
46
|
+
function validateFrame(input, limits, validator, parsePayload) {
|
|
47
|
+
const value = decodeRemoteWorkspaceMessage(input, limits);
|
|
48
|
+
if (!validator.Check(value)) {
|
|
49
|
+
const failure = validator.Errors(value)[0];
|
|
50
|
+
throw new RemoteWorkspaceValidationError(`Invalid v2 frame: ${failure?.instancePath || "/"}: ${failure?.message ?? "schema mismatch"}`);
|
|
51
|
+
}
|
|
52
|
+
const frame = value;
|
|
53
|
+
if (frame.type === "channel" && parsePayload) {
|
|
54
|
+
const channel = frame;
|
|
55
|
+
parsePayload(channel.payload, limits);
|
|
56
|
+
}
|
|
57
|
+
return frame;
|
|
58
|
+
}
|
|
59
|
+
export function parseSharedWorkspaceClientFrame(input, limits) {
|
|
60
|
+
return validateFrame(input, limits, clientValidator, parseRemoteWorkspaceClientMessage);
|
|
61
|
+
}
|
|
62
|
+
export function parseSharedWorkspaceClientEnvelope(input, limits) {
|
|
63
|
+
return validateFrame(input, limits, clientValidator);
|
|
64
|
+
}
|
|
65
|
+
export function parseSharedWorkspaceServerFrame(input, limits) {
|
|
66
|
+
return validateFrame(input, limits, serverValidator, parseRemoteWorkspaceServerMessage);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=shared-transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-transport.js","sourceRoot":"","sources":["../../../src/core/remote-workspace-protocol/shared-transport.ts"],"names":[],"mappings":"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","sourcesContent":["/** Opt-in multiplex transport. V1 messages are validated *inside* a bound channel. */\nimport { type Static, Type } from \"typebox\";\nimport { Compile, type Validator } from \"typebox/compile\";\nimport {\n\tdecodeRemoteWorkspaceMessage,\n\tparseRemoteWorkspaceClientMessage,\n\tparseRemoteWorkspaceServerMessage,\n\ttype RemoteWorkspaceClientMessage,\n\ttype RemoteWorkspaceProtocolLimits,\n\ttype RemoteWorkspaceServerMessage,\n\tRemoteWorkspaceValidationError,\n} from \"./contract.ts\";\n\nexport const SHARED_WORKSPACE_WEBSOCKET_PROTOCOL = \"grip.workspace.v2\";\nconst strict = { additionalProperties: false } as const;\nconst Id = Type.String({ minLength: 1, maxLength: 128, pattern: \"^[A-Za-z0-9._:-]+$\" });\nconst ChannelId = Type.String({ minLength: 24, maxLength: 128, pattern: \"^[A-Za-z0-9_-]+$\" });\n\nconst Hello = Type.Object({ type: Type.Literal(\"hello\"), id: Id, version: Type.Literal(2) }, strict);\nconst HelloAck = Type.Object(\n\t{\n\t\ttype: Type.Literal(\"hello_ack\"),\n\t\tid: Id,\n\t\tversion: Type.Literal(2),\n\t\tepoch: ChannelId,\n\t\tdaemonVersion: Type.String({ minLength: 1, maxLength: 128 }),\n\t},\n\tstrict,\n);\nconst Open = Type.Object(\n\t{ type: Type.Literal(\"open\"), id: Id, root: Type.String({ minLength: 1, maxLength: 32_768 }) },\n\tstrict,\n);\nconst Opened = Type.Object({ type: Type.Literal(\"opened\"), id: Id, channelId: ChannelId }, strict);\nconst Close = Type.Object({ type: Type.Literal(\"close\"), channelId: ChannelId }, strict);\nconst Closed = Type.Object(\n\t{\n\t\ttype: Type.Literal(\"closed\"),\n\t\tchannelId: ChannelId,\n\t\t// Absent for an acknowledged client detach; present for an unsolicited protocol failure.\n\t\treason: Type.Optional(\n\t\t\tType.Object(\n\t\t\t\t{\n\t\t\t\t\tcode: Type.Union([\n\t\t\t\t\t\tType.Literal(\"protocol_error\"),\n\t\t\t\t\t\tType.Literal(\"invalid_payload\"),\n\t\t\t\t\t\tType.Literal(\"message_too_large\"),\n\t\t\t\t\t\tType.Literal(\"policy_violation\"),\n\t\t\t\t\t\tType.Literal(\"normal\"),\n\t\t\t\t\t]),\n\t\t\t\t\tmessage: Type.String({ minLength: 1, maxLength: 120 }),\n\t\t\t\t},\n\t\t\t\tstrict,\n\t\t\t),\n\t\t),\n\t},\n\tstrict,\n);\nconst ControlError = Type.Object(\n\t{\n\t\ttype: Type.Literal(\"control_error\"),\n\t\tid: Id,\n\t\tcode: Type.Union([Type.Literal(\"invalid_root\"), Type.Literal(\"limit_exceeded\"), Type.Literal(\"invalid_request\")]),\n\t\tmessage: Type.String({ minLength: 1, maxLength: 1024 }),\n\t},\n\tstrict,\n);\nconst Channel = Type.Object({ type: Type.Literal(\"channel\"), channelId: ChannelId, payload: Type.Unknown() }, strict);\n\nconst ClientFrame = Type.Union([Hello, Open, Close, Channel]);\nconst ServerFrame = Type.Union([HelloAck, Opened, Closed, ControlError, Channel]);\nconst clientValidator = Compile(ClientFrame);\nconst serverValidator = Compile(ServerFrame);\nexport type SharedWorkspaceClientChannelFrame = Omit<Static<typeof Channel>, \"payload\"> & {\n\tpayload: RemoteWorkspaceClientMessage;\n};\nexport type SharedWorkspaceServerChannelFrame = Omit<Static<typeof Channel>, \"payload\"> & {\n\tpayload: RemoteWorkspaceServerMessage;\n};\nexport type SharedWorkspaceClientFrame =\n\t| Exclude<Static<typeof ClientFrame>, { type: \"channel\" }>\n\t| SharedWorkspaceClientChannelFrame;\n/** The daemon dispatches a valid outer envelope before parsing the inner message on its bound channel. */\nexport type SharedWorkspaceClientEnvelope = Static<typeof ClientFrame>;\nexport type SharedWorkspaceServerFrame =\n\t| Exclude<Static<typeof ServerFrame>, { type: \"channel\" }>\n\t| SharedWorkspaceServerChannelFrame;\n\nfunction validateFrame<T extends { type: string }>(\n\tinput: string | Uint8Array,\n\tlimits: RemoteWorkspaceProtocolLimits,\n\tvalidator: Validator,\n\tparsePayload?: (value: unknown, limits: RemoteWorkspaceProtocolLimits) => unknown,\n): T {\n\tconst value = decodeRemoteWorkspaceMessage(input, limits);\n\tif (!validator.Check(value)) {\n\t\tconst failure = validator.Errors(value)[0];\n\t\tthrow new RemoteWorkspaceValidationError(\n\t\t\t`Invalid v2 frame: ${failure?.instancePath || \"/\"}: ${failure?.message ?? \"schema mismatch\"}`,\n\t\t);\n\t}\n\tconst frame = value as T;\n\tif (frame.type === \"channel\" && parsePayload) {\n\t\tconst channel = frame as T & { payload: unknown };\n\t\tparsePayload(channel.payload, limits);\n\t}\n\treturn frame;\n}\n\nexport function parseSharedWorkspaceClientFrame(\n\tinput: string | Uint8Array,\n\tlimits: RemoteWorkspaceProtocolLimits,\n): SharedWorkspaceClientFrame {\n\treturn validateFrame(input, limits, clientValidator, parseRemoteWorkspaceClientMessage);\n}\nexport function parseSharedWorkspaceClientEnvelope(\n\tinput: string | Uint8Array,\n\tlimits: RemoteWorkspaceProtocolLimits,\n): SharedWorkspaceClientEnvelope {\n\treturn validateFrame(input, limits, clientValidator);\n}\n\nexport function parseSharedWorkspaceServerFrame(\n\tinput: string | Uint8Array,\n\tlimits: RemoteWorkspaceProtocolLimits,\n): SharedWorkspaceServerFrame {\n\treturn validateFrame(input, limits, serverValidator, parseRemoteWorkspaceServerMessage);\n}\n"]}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import type { SandboxContainer,
|
|
1
|
+
import type { SandboxContainer, SandboxStartResult, SandboxStopResult } from "./config.ts";
|
|
2
2
|
export interface SandboxStartUserCommand {
|
|
3
3
|
subcommand: "start";
|
|
4
|
-
image?: string;
|
|
5
|
-
runtime?: SandboxRuntime;
|
|
6
4
|
name?: string;
|
|
7
5
|
template?: string;
|
|
6
|
+
update?: boolean;
|
|
8
7
|
}
|
|
9
8
|
export type SandboxUserCommand = {
|
|
10
9
|
subcommand: "status";
|
|
@@ -15,7 +14,6 @@ export type SandboxUserCommand = {
|
|
|
15
14
|
url: string;
|
|
16
15
|
} | SandboxStartUserCommand | {
|
|
17
16
|
subcommand: "list";
|
|
18
|
-
runtime?: SandboxRuntime;
|
|
19
17
|
} | {
|
|
20
18
|
subcommand: "stop";
|
|
21
19
|
target?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../src/core/sandbox/command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../src/core/sandbox/command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE3F,MAAM,WAAW,uBAAuB;IACvC,UAAU,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,MAAM,kBAAkB,GAC3B;IAAE,UAAU,EAAE,QAAQ,CAAA;CAAE,GACxB;IAAE,UAAU,EAAE,OAAO,CAAA;CAAE,GACvB;IAAE,UAAU,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACrC,uBAAuB,GACvB;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,GACtB;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AA+C3C,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,CA6BzE;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAO3E;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,gBAAgB,EAAE,GAAG,MAAM,CASxE;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAMzE","sourcesContent":["import type { SandboxContainer, SandboxStartResult, SandboxStopResult } from \"./config.ts\";\n\nexport interface SandboxStartUserCommand {\n\tsubcommand: \"start\";\n\tname?: string;\n\ttemplate?: string;\n\tupdate?: boolean;\n}\n\nexport type SandboxUserCommand =\n\t| { subcommand: \"status\" }\n\t| { subcommand: \"clear\" }\n\t| { subcommand: \"attach\"; url: string }\n\t| SandboxStartUserCommand\n\t| { subcommand: \"list\" }\n\t| { subcommand: \"stop\"; target?: string };\n\nfunction splitCommandLine(input: string): string[] {\n\tconst tokens = input.match(/(?:[^\\s\"']+|\"[^\"]*\"|'[^']*')+/g) ?? [];\n\treturn tokens.map((token) => {\n\t\tif ((token.startsWith('\"') && token.endsWith('\"')) || (token.startsWith(\"'\") && token.endsWith(\"'\"))) {\n\t\t\treturn token.slice(1, -1);\n\t\t}\n\t\treturn token;\n\t});\n}\n\nfunction parseSandboxStartValue(\n\toption: string | undefined,\n\tvalue: string | undefined,\n): Pick<SandboxStartUserCommand, \"name\" | \"template\"> {\n\tif (option !== \"--name\" && option !== \"--template\") {\n\t\tthrow new Error(`Unsupported /sandbox start argument: ${option}`);\n\t}\n\tif (!value || value.startsWith(\"--\")) {\n\t\tthrow new Error(\"Usage: /sandbox start [--name <lima-instance>] [--template <lima-template>] [--update]\");\n\t}\n\treturn option === \"--name\" ? { name: value } : { template: value };\n}\n\nfunction parseSandboxStartCommand(tokens: string[]): SandboxUserCommand {\n\tconst fields: Pick<SandboxStartUserCommand, \"name\" | \"template\"> = {};\n\tlet update = false;\n\tfor (let index = 2; index < tokens.length; index++) {\n\t\tconst option = tokens[index];\n\t\tif (option === \"--update\") {\n\t\t\tif (update) throw new Error(\"Duplicate /sandbox start --update\");\n\t\t\tupdate = true;\n\t\t\tcontinue;\n\t\t}\n\t\tObject.assign(fields, parseSandboxStartValue(option, tokens[++index]));\n\t}\n\tif (update && (!fields.name || fields.template)) {\n\t\tthrow new Error(\"/sandbox start --update requires --name and cannot be combined with --template\");\n\t}\n\treturn {\n\t\tsubcommand: \"start\",\n\t\t...fields,\n\t\t...(update ? { update: true } : {}),\n\t};\n}\n\nexport function parseSandboxUserCommand(input: string): SandboxUserCommand {\n\tconst tokens = splitCommandLine(input.trim());\n\tif (tokens[0] !== \"/sandbox\") throw new Error(\"Sandbox command must start with /sandbox\");\n\n\tconst subcommand = tokens[1];\n\tswitch (subcommand) {\n\t\tcase undefined:\n\t\tcase \"status\":\n\t\t\tif (tokens.length > 2) throw new Error(\"Usage: /sandbox status\");\n\t\t\treturn { subcommand: \"status\" };\n\t\tcase \"clear\":\n\t\t\tif (tokens.length > 2) throw new Error(\"Usage: /sandbox clear\");\n\t\t\treturn { subcommand: \"clear\" };\n\t\tcase \"--attach\":\n\t\t\tif (tokens.length !== 3) throw new Error(\"Usage: /sandbox --attach <ws://url>\");\n\t\t\treturn { subcommand: \"attach\", url: tokens[2]! };\n\t\tcase \"start\":\n\t\t\treturn parseSandboxStartCommand(tokens);\n\t\tcase \"list\":\n\t\t\tif (tokens.length !== 2) throw new Error(\"Usage: /sandbox list\");\n\t\t\treturn { subcommand: \"list\" };\n\t\tcase \"stop\":\n\t\t\tif (tokens.length > 3) throw new Error(\"Usage: /sandbox stop [vm]\");\n\t\t\treturn tokens[2] ? { subcommand: \"stop\", target: tokens[2] } : { subcommand: \"stop\" };\n\t\tdefault:\n\t\t\tthrow new Error(\n\t\t\t\t`Unsupported /sandbox subcommand: ${subcommand}. Use status, clear, --attach, start, list, or stop.`,\n\t\t\t);\n\t}\n}\n\nexport function formatSandboxStartResult(result: SandboxStartResult): string {\n\treturn [\n\t\t`Sandbox started: ${result.containerName} (${result.containerId})`,\n\t\t`Workspace: ${result.workspaceRoot} -> ${result.workspaceMountPath}`,\n\t\t`Daemon: ${result.daemonUrlRedacted}`,\n\t\t\"Sandbox mode active: workspace tools/resources now route through the sandbox daemon.\",\n\t].join(\"\\n\");\n}\n\nexport function formatSandboxList(containers: SandboxContainer[]): string {\n\tif (containers.length === 0) return \"No Grip sandboxes found for this workspace.\";\n\treturn containers\n\t\t.map((container) => {\n\t\t\tconst endpoint = container.daemonEndpoint ? ` daemon=${container.daemonEndpoint}` : \"\";\n\t\t\tconst mount = container.workspaceMountPath ? ` workspace=${container.workspaceMountPath}` : \"\";\n\t\t\treturn `${container.id}\\t${container.name}\\t${container.state ?? container.status}\\t${container.image}${mount}${endpoint}`;\n\t\t})\n\t\t.join(\"\\n\");\n}\n\nexport function formatSandboxStopResult(result: SandboxStopResult): string {\n\tif (result.status === \"not-found\") return result.message;\n\tif (result.status === \"already-stopped\")\n\t\treturn `Sandbox already stopped: ${result.container.name} (${result.container.id})`;\n\tif (result.status === \"removed\") return `Sandbox removed: ${result.container.name} (${result.container.id})`;\n\treturn `Sandbox stopped: ${result.container.name} (${result.container.id})`;\n}\n"]}
|
|
@@ -7,61 +7,37 @@ function splitCommandLine(input) {
|
|
|
7
7
|
return token;
|
|
8
8
|
});
|
|
9
9
|
}
|
|
10
|
-
function
|
|
11
|
-
if (
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
function parseSandboxStartValue(option, value) {
|
|
11
|
+
if (option !== "--name" && option !== "--template") {
|
|
12
|
+
throw new Error(`Unsupported /sandbox start argument: ${option}`);
|
|
13
|
+
}
|
|
14
|
+
if (!value || value.startsWith("--")) {
|
|
15
|
+
throw new Error("Usage: /sandbox start [--name <lima-instance>] [--template <lima-template>] [--update]");
|
|
16
|
+
}
|
|
17
|
+
return option === "--name" ? { name: value } : { template: value };
|
|
14
18
|
}
|
|
15
19
|
function parseSandboxStartCommand(tokens) {
|
|
16
|
-
|
|
17
|
-
let
|
|
18
|
-
let name;
|
|
19
|
-
let template;
|
|
20
|
+
const fields = {};
|
|
21
|
+
let update = false;
|
|
20
22
|
for (let index = 2; index < tokens.length; index++) {
|
|
21
23
|
const option = tokens[index];
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
image = value;
|
|
28
|
-
break;
|
|
29
|
-
case "--runtime":
|
|
30
|
-
runtime = parseRuntime(value, "Usage: /sandbox start --runtime docker|lima");
|
|
31
|
-
break;
|
|
32
|
-
case "--name":
|
|
33
|
-
name = value;
|
|
34
|
-
break;
|
|
35
|
-
case "--template":
|
|
36
|
-
template = value;
|
|
37
|
-
break;
|
|
38
|
-
default:
|
|
39
|
-
throw new Error(`Unsupported /sandbox start argument: ${option}`);
|
|
24
|
+
if (option === "--update") {
|
|
25
|
+
if (update)
|
|
26
|
+
throw new Error("Duplicate /sandbox start --update");
|
|
27
|
+
update = true;
|
|
28
|
+
continue;
|
|
40
29
|
}
|
|
30
|
+
Object.assign(fields, parseSandboxStartValue(option, tokens[++index]));
|
|
41
31
|
}
|
|
42
|
-
if (name)
|
|
43
|
-
|
|
44
|
-
if ((name || template) && runtime !== "lima") {
|
|
45
|
-
throw new Error("/sandbox start --name and --template require --runtime lima");
|
|
32
|
+
if (update && (!fields.name || fields.template)) {
|
|
33
|
+
throw new Error("/sandbox start --update requires --name and cannot be combined with --template");
|
|
46
34
|
}
|
|
47
|
-
if (image && runtime === "lima")
|
|
48
|
-
throw new Error("/sandbox start --image is only supported by the Docker runtime");
|
|
49
35
|
return {
|
|
50
36
|
subcommand: "start",
|
|
51
|
-
...
|
|
52
|
-
...(
|
|
53
|
-
...(name ? { name } : {}),
|
|
54
|
-
...(template ? { template } : {}),
|
|
37
|
+
...fields,
|
|
38
|
+
...(update ? { update: true } : {}),
|
|
55
39
|
};
|
|
56
40
|
}
|
|
57
|
-
function parseSandboxListCommand(tokens) {
|
|
58
|
-
if (tokens.length === 2)
|
|
59
|
-
return { subcommand: "list" };
|
|
60
|
-
if (tokens.length !== 4 || tokens[2] !== "--runtime") {
|
|
61
|
-
throw new Error("Usage: /sandbox list [--runtime docker|lima]");
|
|
62
|
-
}
|
|
63
|
-
return { subcommand: "list", runtime: parseRuntime(tokens[3], "Usage: /sandbox list --runtime docker|lima") };
|
|
64
|
-
}
|
|
65
41
|
export function parseSandboxUserCommand(input) {
|
|
66
42
|
const tokens = splitCommandLine(input.trim());
|
|
67
43
|
if (tokens[0] !== "/sandbox")
|
|
@@ -84,10 +60,12 @@ export function parseSandboxUserCommand(input) {
|
|
|
84
60
|
case "start":
|
|
85
61
|
return parseSandboxStartCommand(tokens);
|
|
86
62
|
case "list":
|
|
87
|
-
|
|
63
|
+
if (tokens.length !== 2)
|
|
64
|
+
throw new Error("Usage: /sandbox list");
|
|
65
|
+
return { subcommand: "list" };
|
|
88
66
|
case "stop":
|
|
89
67
|
if (tokens.length > 3)
|
|
90
|
-
throw new Error("Usage: /sandbox stop [
|
|
68
|
+
throw new Error("Usage: /sandbox stop [vm]");
|
|
91
69
|
return tokens[2] ? { subcommand: "stop", target: tokens[2] } : { subcommand: "stop" };
|
|
92
70
|
default:
|
|
93
71
|
throw new Error(`Unsupported /sandbox subcommand: ${subcommand}. Use status, clear, --attach, start, list, or stop.`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.js","sourceRoot":"","sources":["../../../src/core/sandbox/command.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SandboxContainer, SandboxRuntime, SandboxStartResult, SandboxStopResult } from \"./docker.ts\";\n\nexport interface SandboxStartUserCommand {\n\tsubcommand: \"start\";\n\timage?: string;\n\truntime?: SandboxRuntime;\n\tname?: string;\n\ttemplate?: string;\n}\n\nexport type SandboxUserCommand =\n\t| { subcommand: \"status\" }\n\t| { subcommand: \"clear\" }\n\t| { subcommand: \"attach\"; url: string }\n\t| SandboxStartUserCommand\n\t| { subcommand: \"list\"; runtime?: SandboxRuntime }\n\t| { subcommand: \"stop\"; target?: string };\n\nfunction splitCommandLine(input: string): string[] {\n\tconst tokens = input.match(/(?:[^\\s\"']+|\"[^\"]*\"|'[^']*')+/g) ?? [];\n\treturn tokens.map((token) => {\n\t\tif ((token.startsWith('\"') && token.endsWith('\"')) || (token.startsWith(\"'\") && token.endsWith(\"'\"))) {\n\t\t\treturn token.slice(1, -1);\n\t\t}\n\t\treturn token;\n\t});\n}\n\nfunction parseRuntime(value: string | undefined, usage: string): SandboxRuntime {\n\tif (value === \"docker\" || value === \"lima\") return value;\n\tthrow new Error(usage);\n}\n\nfunction parseSandboxStartCommand(tokens: string[]): SandboxUserCommand {\n\tlet image: string | undefined;\n\tlet runtime: SandboxRuntime | undefined;\n\tlet name: string | undefined;\n\tlet template: string | undefined;\n\tfor (let index = 2; index < tokens.length; index++) {\n\t\tconst option = tokens[index];\n\t\tconst value = tokens[++index];\n\t\tif (!value)\n\t\t\tthrow new Error(\n\t\t\t\t\"Usage: /sandbox start [--runtime docker|lima] [--image <image>] [--name <lima-instance>] [--template <lima-template>]\",\n\t\t\t);\n\t\tswitch (option) {\n\t\t\tcase \"--image\":\n\t\t\t\timage = value;\n\t\t\t\tbreak;\n\t\t\tcase \"--runtime\":\n\t\t\t\truntime = parseRuntime(value, \"Usage: /sandbox start --runtime docker|lima\");\n\t\t\t\tbreak;\n\t\t\tcase \"--name\":\n\t\t\t\tname = value;\n\t\t\t\tbreak;\n\t\t\tcase \"--template\":\n\t\t\t\ttemplate = value;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unsupported /sandbox start argument: ${option}`);\n\t\t}\n\t}\n\tif (name) runtime ??= \"lima\";\n\tif ((name || template) && runtime !== \"lima\") {\n\t\tthrow new Error(\"/sandbox start --name and --template require --runtime lima\");\n\t}\n\tif (image && runtime === \"lima\") throw new Error(\"/sandbox start --image is only supported by the Docker runtime\");\n\treturn {\n\t\tsubcommand: \"start\",\n\t\t...(image ? { image } : {}),\n\t\t...(runtime ? { runtime } : {}),\n\t\t...(name ? { name } : {}),\n\t\t...(template ? { template } : {}),\n\t};\n}\n\nfunction parseSandboxListCommand(tokens: string[]): SandboxUserCommand {\n\tif (tokens.length === 2) return { subcommand: \"list\" };\n\tif (tokens.length !== 4 || tokens[2] !== \"--runtime\") {\n\t\tthrow new Error(\"Usage: /sandbox list [--runtime docker|lima]\");\n\t}\n\treturn { subcommand: \"list\", runtime: parseRuntime(tokens[3], \"Usage: /sandbox list --runtime docker|lima\") };\n}\n\nexport function parseSandboxUserCommand(input: string): SandboxUserCommand {\n\tconst tokens = splitCommandLine(input.trim());\n\tif (tokens[0] !== \"/sandbox\") throw new Error(\"Sandbox command must start with /sandbox\");\n\n\tconst subcommand = tokens[1];\n\tswitch (subcommand) {\n\t\tcase undefined:\n\t\tcase \"status\":\n\t\t\tif (tokens.length > 2) throw new Error(\"Usage: /sandbox status\");\n\t\t\treturn { subcommand: \"status\" };\n\t\tcase \"clear\":\n\t\t\tif (tokens.length > 2) throw new Error(\"Usage: /sandbox clear\");\n\t\t\treturn { subcommand: \"clear\" };\n\t\tcase \"--attach\":\n\t\t\tif (tokens.length !== 3) throw new Error(\"Usage: /sandbox --attach <ws://url>\");\n\t\t\treturn { subcommand: \"attach\", url: tokens[2]! };\n\t\tcase \"start\":\n\t\t\treturn parseSandboxStartCommand(tokens);\n\t\tcase \"list\":\n\t\t\treturn parseSandboxListCommand(tokens);\n\t\tcase \"stop\":\n\t\t\tif (tokens.length > 3) throw new Error(\"Usage: /sandbox stop [container]\");\n\t\t\treturn tokens[2] ? { subcommand: \"stop\", target: tokens[2] } : { subcommand: \"stop\" };\n\t\tdefault:\n\t\t\tthrow new Error(\n\t\t\t\t`Unsupported /sandbox subcommand: ${subcommand}. Use status, clear, --attach, start, list, or stop.`,\n\t\t\t);\n\t}\n}\n\nexport function formatSandboxStartResult(result: SandboxStartResult): string {\n\treturn [\n\t\t`Sandbox started: ${result.containerName} (${result.containerId})`,\n\t\t`Workspace: ${result.workspaceRoot} -> ${result.workspaceMountPath}`,\n\t\t`Daemon: ${result.daemonUrlRedacted}`,\n\t\t\"Sandbox mode active: workspace tools/resources now route through the sandbox daemon.\",\n\t].join(\"\\n\");\n}\n\nexport function formatSandboxList(containers: SandboxContainer[]): string {\n\tif (containers.length === 0) return \"No Grip sandboxes found for this workspace.\";\n\treturn containers\n\t\t.map((container) => {\n\t\t\tconst endpoint = container.daemonEndpoint ? ` daemon=${container.daemonEndpoint}` : \"\";\n\t\t\tconst mount = container.workspaceMountPath ? ` workspace=${container.workspaceMountPath}` : \"\";\n\t\t\treturn `${container.id}\\t${container.name}\\t${container.state ?? container.status}\\t${container.image}${mount}${endpoint}`;\n\t\t})\n\t\t.join(\"\\n\");\n}\n\nexport function formatSandboxStopResult(result: SandboxStopResult): string {\n\tif (result.status === \"not-found\") return result.message;\n\tif (result.status === \"already-stopped\")\n\t\treturn `Sandbox already stopped: ${result.container.name} (${result.container.id})`;\n\tif (result.status === \"removed\") return `Sandbox removed: ${result.container.name} (${result.container.id})`;\n\treturn `Sandbox stopped: ${result.container.name} (${result.container.id})`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"command.js","sourceRoot":"","sources":["../../../src/core/sandbox/command.ts"],"names":[],"mappings":"AAiBA,SAAS,gBAAgB,CAAC,KAAa,EAAY;IAClD,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,gCAAgC,CAAC,IAAI,EAAE,CAAC;IACnE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACtG,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3B,CAAC;QACD,OAAO,KAAK,CAAC;IAAA,CACb,CAAC,CAAC;AAAA,CACH;AAED,SAAS,sBAAsB,CAC9B,MAA0B,EAC1B,KAAyB,EAC4B;IACrD,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,YAAY,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,wCAAwC,MAAM,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,wFAAwF,CAAC,CAAC;IAC3G,CAAC;IACD,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAAA,CACnE;AAED,SAAS,wBAAwB,CAAC,MAAgB,EAAsB;IACvE,MAAM,MAAM,GAAuD,EAAE,CAAC;IACtE,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;YAC3B,IAAI,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;YACjE,MAAM,GAAG,IAAI,CAAC;YACd,SAAS;QACV,CAAC;QACD,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,gFAAgF,CAAC,CAAC;IACnG,CAAC;IACD,OAAO;QACN,UAAU,EAAE,OAAO;QACnB,GAAG,MAAM;QACT,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnC,CAAC;AAAA,CACF;AAED,MAAM,UAAU,uBAAuB,CAAC,KAAa,EAAsB;IAC1E,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9C,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAE1F,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAC7B,QAAQ,UAAU,EAAE,CAAC;QACpB,KAAK,SAAS,CAAC;QACf,KAAK,QAAQ;YACZ,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;YACjE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;QACjC,KAAK,OAAO;YACX,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;YAChE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;QAChC,KAAK,UAAU;YACd,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;YAChF,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,CAAE,EAAE,CAAC;QAClD,KAAK,OAAO;YACX,OAAO,wBAAwB,CAAC,MAAM,CAAC,CAAC;QACzC,KAAK,MAAM;YACV,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;YACjE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;QAC/B,KAAK,MAAM;YACV,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;YACpE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;QACvF;YACC,MAAM,IAAI,KAAK,CACd,oCAAoC,UAAU,sDAAsD,CACpG,CAAC;IACJ,CAAC;AAAA,CACD;AAED,MAAM,UAAU,wBAAwB,CAAC,MAA0B,EAAU;IAC5E,OAAO;QACN,oBAAoB,MAAM,CAAC,aAAa,KAAK,MAAM,CAAC,WAAW,GAAG;QAClE,cAAc,MAAM,CAAC,aAAa,OAAO,MAAM,CAAC,kBAAkB,EAAE;QACpE,WAAW,MAAM,CAAC,iBAAiB,EAAE;QACrC,sFAAsF;KACtF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAAA,CACb;AAED,MAAM,UAAU,iBAAiB,CAAC,UAA8B,EAAU;IACzE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,6CAA6C,CAAC;IAClF,OAAO,UAAU;SACf,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC;QACnB,MAAM,QAAQ,GAAG,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,WAAW,SAAS,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvF,MAAM,KAAK,GAAG,SAAS,CAAC,kBAAkB,CAAC,CAAC,CAAC,cAAc,SAAS,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/F,OAAO,GAAG,SAAS,CAAC,EAAE,KAAK,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC,KAAK,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS,CAAC,KAAK,GAAG,KAAK,GAAG,QAAQ,EAAE,CAAC;IAAA,CAC3H,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;AAAA,CACb;AAED,MAAM,UAAU,uBAAuB,CAAC,MAAyB,EAAU;IAC1E,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC;IACzD,IAAI,MAAM,CAAC,MAAM,KAAK,iBAAiB;QACtC,OAAO,4BAA4B,MAAM,CAAC,SAAS,CAAC,IAAI,KAAK,MAAM,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC;IACrF,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,oBAAoB,MAAM,CAAC,SAAS,CAAC,IAAI,KAAK,MAAM,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC;IAC7G,OAAO,oBAAoB,MAAM,CAAC,SAAS,CAAC,IAAI,KAAK,MAAM,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC;AAAA,CAC5E","sourcesContent":["import type { SandboxContainer, SandboxStartResult, SandboxStopResult } from \"./config.ts\";\n\nexport interface SandboxStartUserCommand {\n\tsubcommand: \"start\";\n\tname?: string;\n\ttemplate?: string;\n\tupdate?: boolean;\n}\n\nexport type SandboxUserCommand =\n\t| { subcommand: \"status\" }\n\t| { subcommand: \"clear\" }\n\t| { subcommand: \"attach\"; url: string }\n\t| SandboxStartUserCommand\n\t| { subcommand: \"list\" }\n\t| { subcommand: \"stop\"; target?: string };\n\nfunction splitCommandLine(input: string): string[] {\n\tconst tokens = input.match(/(?:[^\\s\"']+|\"[^\"]*\"|'[^']*')+/g) ?? [];\n\treturn tokens.map((token) => {\n\t\tif ((token.startsWith('\"') && token.endsWith('\"')) || (token.startsWith(\"'\") && token.endsWith(\"'\"))) {\n\t\t\treturn token.slice(1, -1);\n\t\t}\n\t\treturn token;\n\t});\n}\n\nfunction parseSandboxStartValue(\n\toption: string | undefined,\n\tvalue: string | undefined,\n): Pick<SandboxStartUserCommand, \"name\" | \"template\"> {\n\tif (option !== \"--name\" && option !== \"--template\") {\n\t\tthrow new Error(`Unsupported /sandbox start argument: ${option}`);\n\t}\n\tif (!value || value.startsWith(\"--\")) {\n\t\tthrow new Error(\"Usage: /sandbox start [--name <lima-instance>] [--template <lima-template>] [--update]\");\n\t}\n\treturn option === \"--name\" ? { name: value } : { template: value };\n}\n\nfunction parseSandboxStartCommand(tokens: string[]): SandboxUserCommand {\n\tconst fields: Pick<SandboxStartUserCommand, \"name\" | \"template\"> = {};\n\tlet update = false;\n\tfor (let index = 2; index < tokens.length; index++) {\n\t\tconst option = tokens[index];\n\t\tif (option === \"--update\") {\n\t\t\tif (update) throw new Error(\"Duplicate /sandbox start --update\");\n\t\t\tupdate = true;\n\t\t\tcontinue;\n\t\t}\n\t\tObject.assign(fields, parseSandboxStartValue(option, tokens[++index]));\n\t}\n\tif (update && (!fields.name || fields.template)) {\n\t\tthrow new Error(\"/sandbox start --update requires --name and cannot be combined with --template\");\n\t}\n\treturn {\n\t\tsubcommand: \"start\",\n\t\t...fields,\n\t\t...(update ? { update: true } : {}),\n\t};\n}\n\nexport function parseSandboxUserCommand(input: string): SandboxUserCommand {\n\tconst tokens = splitCommandLine(input.trim());\n\tif (tokens[0] !== \"/sandbox\") throw new Error(\"Sandbox command must start with /sandbox\");\n\n\tconst subcommand = tokens[1];\n\tswitch (subcommand) {\n\t\tcase undefined:\n\t\tcase \"status\":\n\t\t\tif (tokens.length > 2) throw new Error(\"Usage: /sandbox status\");\n\t\t\treturn { subcommand: \"status\" };\n\t\tcase \"clear\":\n\t\t\tif (tokens.length > 2) throw new Error(\"Usage: /sandbox clear\");\n\t\t\treturn { subcommand: \"clear\" };\n\t\tcase \"--attach\":\n\t\t\tif (tokens.length !== 3) throw new Error(\"Usage: /sandbox --attach <ws://url>\");\n\t\t\treturn { subcommand: \"attach\", url: tokens[2]! };\n\t\tcase \"start\":\n\t\t\treturn parseSandboxStartCommand(tokens);\n\t\tcase \"list\":\n\t\t\tif (tokens.length !== 2) throw new Error(\"Usage: /sandbox list\");\n\t\t\treturn { subcommand: \"list\" };\n\t\tcase \"stop\":\n\t\t\tif (tokens.length > 3) throw new Error(\"Usage: /sandbox stop [vm]\");\n\t\t\treturn tokens[2] ? { subcommand: \"stop\", target: tokens[2] } : { subcommand: \"stop\" };\n\t\tdefault:\n\t\t\tthrow new Error(\n\t\t\t\t`Unsupported /sandbox subcommand: ${subcommand}. Use status, clear, --attach, start, list, or stop.`,\n\t\t\t);\n\t}\n}\n\nexport function formatSandboxStartResult(result: SandboxStartResult): string {\n\treturn [\n\t\t`Sandbox started: ${result.containerName} (${result.containerId})`,\n\t\t`Workspace: ${result.workspaceRoot} -> ${result.workspaceMountPath}`,\n\t\t`Daemon: ${result.daemonUrlRedacted}`,\n\t\t\"Sandbox mode active: workspace tools/resources now route through the sandbox daemon.\",\n\t].join(\"\\n\");\n}\n\nexport function formatSandboxList(containers: SandboxContainer[]): string {\n\tif (containers.length === 0) return \"No Grip sandboxes found for this workspace.\";\n\treturn containers\n\t\t.map((container) => {\n\t\t\tconst endpoint = container.daemonEndpoint ? ` daemon=${container.daemonEndpoint}` : \"\";\n\t\t\tconst mount = container.workspaceMountPath ? ` workspace=${container.workspaceMountPath}` : \"\";\n\t\t\treturn `${container.id}\\t${container.name}\\t${container.state ?? container.status}\\t${container.image}${mount}${endpoint}`;\n\t\t})\n\t\t.join(\"\\n\");\n}\n\nexport function formatSandboxStopResult(result: SandboxStopResult): string {\n\tif (result.status === \"not-found\") return result.message;\n\tif (result.status === \"already-stopped\")\n\t\treturn `Sandbox already stopped: ${result.container.name} (${result.container.id})`;\n\tif (result.status === \"removed\") return `Sandbox removed: ${result.container.name} (${result.container.id})`;\n\treturn `Sandbox stopped: ${result.container.name} (${result.container.id})`;\n}\n"]}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { SandboxSettings } from "../settings-manager.ts";
|
|
2
|
+
export declare const DEFAULT_SANDBOX_WORKSPACE_MOUNT = "/workspace";
|
|
3
|
+
export declare const DEFAULT_SANDBOX_DAEMON_PORT = 8787;
|
|
4
|
+
export declare const DEFAULT_SANDBOX_LIMA_BINARY = "limactl";
|
|
5
|
+
export declare const DEFAULT_SANDBOX_LIMA_TEMPLATE = "template:docker-rootful";
|
|
6
|
+
export declare const SANDBOX_LABEL_PREFIX = "ai.greppleai.grip";
|
|
7
|
+
export type SandboxCleanupBehavior = "stop" | "remove";
|
|
8
|
+
export type SandboxConfig = Required<SandboxSettings>;
|
|
9
|
+
export type SandboxConfigOverrides = SandboxSettings;
|
|
10
|
+
export interface SandboxEnvironment {
|
|
11
|
+
GRIP_SANDBOX_LIMA?: string;
|
|
12
|
+
GRIP_SANDBOX_GUEST_BINARY?: string;
|
|
13
|
+
GRIP_SANDBOX_LIMA_TEMPLATE?: string;
|
|
14
|
+
GRIP_SANDBOX_LIMA_INSTANCE_PREFIX?: string;
|
|
15
|
+
GRIP_SANDBOX_WORKSPACE_MOUNT?: string;
|
|
16
|
+
GRIP_SANDBOX_DAEMON_PORT?: string;
|
|
17
|
+
GRIP_SANDBOX_DAEMON_HOST_BIND?: string;
|
|
18
|
+
GRIP_SANDBOX_CLEANUP?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface SandboxStartOptions extends SandboxConfigOverrides {
|
|
21
|
+
workspaceRoot: string;
|
|
22
|
+
sessionId?: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
update?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface SandboxListOptions {
|
|
27
|
+
workspaceRoot: string;
|
|
28
|
+
allWorkspaces?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface SandboxStopOptions {
|
|
31
|
+
workspaceRoot: string;
|
|
32
|
+
target?: string;
|
|
33
|
+
currentContainerId?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface SandboxContainer {
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
image: string;
|
|
39
|
+
status: string;
|
|
40
|
+
state: string | undefined;
|
|
41
|
+
createdAt: string | undefined;
|
|
42
|
+
workspaceRoot: string | undefined;
|
|
43
|
+
workspaceMountPath: string | undefined;
|
|
44
|
+
daemonPort: number | undefined;
|
|
45
|
+
daemonEndpoint: string | undefined;
|
|
46
|
+
labels: Record<string, string>;
|
|
47
|
+
}
|
|
48
|
+
export interface SandboxStartResult {
|
|
49
|
+
containerId: string;
|
|
50
|
+
containerName: string;
|
|
51
|
+
workspaceRoot: string;
|
|
52
|
+
workspaceMountPath: string;
|
|
53
|
+
daemonUrl: string;
|
|
54
|
+
daemonUrlRedacted: string;
|
|
55
|
+
token: string;
|
|
56
|
+
labels: Record<string, string>;
|
|
57
|
+
}
|
|
58
|
+
export type SandboxStopResult = {
|
|
59
|
+
status: "stopped";
|
|
60
|
+
container: SandboxContainer;
|
|
61
|
+
} | {
|
|
62
|
+
status: "removed";
|
|
63
|
+
container: SandboxContainer;
|
|
64
|
+
} | {
|
|
65
|
+
status: "already-stopped";
|
|
66
|
+
container: SandboxContainer;
|
|
67
|
+
} | {
|
|
68
|
+
status: "not-found";
|
|
69
|
+
message: string;
|
|
70
|
+
};
|
|
71
|
+
export interface SandboxService {
|
|
72
|
+
resolveConfig(overrides?: SandboxConfigOverrides): SandboxConfig;
|
|
73
|
+
start(options: SandboxStartOptions): Promise<SandboxStartResult>;
|
|
74
|
+
list(options: SandboxListOptions): Promise<SandboxContainer[]>;
|
|
75
|
+
stop(options: SandboxStopOptions): Promise<SandboxStopResult>;
|
|
76
|
+
}
|
|
77
|
+
export interface SandboxCommandResult {
|
|
78
|
+
exitCode: number | null;
|
|
79
|
+
stdout: string;
|
|
80
|
+
stderr: string;
|
|
81
|
+
}
|
|
82
|
+
export interface SandboxCommandOptions {
|
|
83
|
+
env?: Record<string, string>;
|
|
84
|
+
}
|
|
85
|
+
export interface SandboxRunner {
|
|
86
|
+
run(command: string, args: string[], options?: SandboxCommandOptions): Promise<SandboxCommandResult>;
|
|
87
|
+
}
|
|
88
|
+
export declare function redactSecrets(value: string): string;
|
|
89
|
+
export declare function resolveSandboxConfig(settings: SandboxSettings | undefined, env?: SandboxEnvironment, overrides?: SandboxConfigOverrides): SandboxConfig;
|
|
90
|
+
export declare function allocateSandboxDaemonPort(host: string, preferredPort: number): Promise<number>;
|
|
91
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/core/sandbox/config.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer } from \"node:net\";\nimport type { SandboxSettings } from \"../settings-manager.ts\";\n\nexport const DEFAULT_SANDBOX_WORKSPACE_MOUNT = \"/workspace\";\nexport const DEFAULT_SANDBOX_DAEMON_PORT = 8787;\nexport const DEFAULT_SANDBOX_LIMA_BINARY = \"limactl\";\nexport const DEFAULT_SANDBOX_LIMA_TEMPLATE = \"template:docker-rootful\";\nexport const SANDBOX_LABEL_PREFIX = \"ai.greppleai.grip\";\n\nexport type SandboxCleanupBehavior = \"stop\" | \"remove\";\n\nexport type SandboxConfig = Required<SandboxSettings>;\n\nexport type SandboxConfigOverrides = SandboxSettings;\n\nexport interface SandboxEnvironment {\n\tGRIP_SANDBOX_LIMA?: string;\n\tGRIP_SANDBOX_GUEST_BINARY?: string;\n\tGRIP_SANDBOX_LIMA_TEMPLATE?: string;\n\tGRIP_SANDBOX_LIMA_INSTANCE_PREFIX?: string;\n\tGRIP_SANDBOX_WORKSPACE_MOUNT?: string;\n\tGRIP_SANDBOX_DAEMON_PORT?: string;\n\tGRIP_SANDBOX_DAEMON_HOST_BIND?: string;\n\tGRIP_SANDBOX_CLEANUP?: string;\n}\n\nexport interface SandboxStartOptions extends SandboxConfigOverrides {\n\tworkspaceRoot: string;\n\tsessionId?: string;\n\tname?: string;\n\tupdate?: boolean;\n}\n\nexport interface SandboxListOptions {\n\tworkspaceRoot: string;\n\tallWorkspaces?: boolean;\n}\n\nexport interface SandboxStopOptions {\n\tworkspaceRoot: string;\n\ttarget?: string;\n\tcurrentContainerId?: string;\n}\n\nexport interface SandboxContainer {\n\tid: string;\n\tname: string;\n\timage: string;\n\tstatus: string;\n\tstate: string | undefined;\n\tcreatedAt: string | undefined;\n\tworkspaceRoot: string | undefined;\n\tworkspaceMountPath: string | undefined;\n\tdaemonPort: number | undefined;\n\tdaemonEndpoint: string | undefined;\n\tlabels: Record<string, string>;\n}\n\nexport interface SandboxStartResult {\n\tcontainerId: string;\n\tcontainerName: string;\n\tworkspaceRoot: string;\n\tworkspaceMountPath: string;\n\tdaemonUrl: string;\n\tdaemonUrlRedacted: string;\n\ttoken: string;\n\tlabels: Record<string, string>;\n}\n\nexport type SandboxStopResult =\n\t| { status: \"stopped\"; container: SandboxContainer }\n\t| { status: \"removed\"; container: SandboxContainer }\n\t| { status: \"already-stopped\"; container: SandboxContainer }\n\t| { status: \"not-found\"; message: string };\n\nexport interface SandboxService {\n\tresolveConfig(overrides?: SandboxConfigOverrides): SandboxConfig;\n\tstart(options: SandboxStartOptions): Promise<SandboxStartResult>;\n\tlist(options: SandboxListOptions): Promise<SandboxContainer[]>;\n\tstop(options: SandboxStopOptions): Promise<SandboxStopResult>;\n}\n\nexport interface SandboxCommandResult {\n\texitCode: number | null;\n\tstdout: string;\n\tstderr: string;\n}\n\nexport interface SandboxCommandOptions {\n\tenv?: Record<string, string>;\n}\n\nexport interface SandboxRunner {\n\trun(command: string, args: string[], options?: SandboxCommandOptions): Promise<SandboxCommandResult>;\n}\n\nfunction nonEmpty(value: string | undefined): string | undefined {\n\tconst trimmed = value?.trim();\n\treturn trimmed ? trimmed : undefined;\n}\n\nfunction parsePositiveInteger(value: string | number | undefined, settingName: string): number | undefined {\n\tif (value === undefined) return undefined;\n\tconst text = String(value).trim();\n\tif (!/^\\d+$/.test(text)) throw new Error(`Invalid ${settingName}: expected a decimal integer`);\n\tconst parsed = Number(text);\n\tif (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > 65535) {\n\t\tthrow new Error(`Invalid ${settingName}: expected a port from 1 to 65535`);\n\t}\n\treturn parsed;\n}\n\nfunction parseCleanup(value: string | undefined, settingName: string): SandboxCleanupBehavior | undefined {\n\tif (value === undefined) return undefined;\n\tif (value === \"stop\" || value === \"remove\") return value;\n\tthrow new Error(`Invalid ${settingName}: expected stop or remove`);\n}\n\nexport function redactSecrets(value: string): string {\n\treturn value\n\t\t.replace(/(GRIP_DAEMON_TOKEN=)[^\\s,;]+/g, \"$1[REDACTED]\")\n\t\t.replace(/(PI_DAEMON_TOKEN=)[^\\s,;]+/g, \"$1[REDACTED]\")\n\t\t.replace(/(Authorization:\\s*Bearer\\s+)[^\\s,;]+/gi, \"$1[REDACTED]\")\n\t\t.replace(/([?&]token=)[^\\s&]+/gi, \"$1[REDACTED]\");\n}\n\nexport function resolveSandboxConfig(\n\tsettings: SandboxSettings | undefined,\n\tenv: SandboxEnvironment = {},\n\toverrides: SandboxConfigOverrides = {},\n): SandboxConfig {\n\treturn {\n\t\tlimaBinary:\n\t\t\tnonEmpty(overrides.limaBinary) ??\n\t\t\tnonEmpty(env.GRIP_SANDBOX_LIMA) ??\n\t\t\tnonEmpty(settings?.limaBinary) ??\n\t\t\tDEFAULT_SANDBOX_LIMA_BINARY,\n\t\tlimaTemplate:\n\t\t\tnonEmpty(overrides.limaTemplate) ??\n\t\t\tnonEmpty(env.GRIP_SANDBOX_LIMA_TEMPLATE) ??\n\t\t\tnonEmpty(settings?.limaTemplate) ??\n\t\t\tDEFAULT_SANDBOX_LIMA_TEMPLATE,\n\t\tlimaInstanceNamePrefix:\n\t\t\tnonEmpty(overrides.limaInstanceNamePrefix) ??\n\t\t\tnonEmpty(env.GRIP_SANDBOX_LIMA_INSTANCE_PREFIX) ??\n\t\t\tnonEmpty(settings?.limaInstanceNamePrefix) ??\n\t\t\t\"grip-sandbox\",\n\t\tworkspaceMountPath:\n\t\t\tnonEmpty(overrides.workspaceMountPath) ??\n\t\t\tnonEmpty(env.GRIP_SANDBOX_WORKSPACE_MOUNT) ??\n\t\t\tnonEmpty(settings?.workspaceMountPath) ??\n\t\t\tDEFAULT_SANDBOX_WORKSPACE_MOUNT,\n\t\tdaemonPort:\n\t\t\toverrides.daemonPort ??\n\t\t\tparsePositiveInteger(env.GRIP_SANDBOX_DAEMON_PORT, \"GRIP_SANDBOX_DAEMON_PORT\") ??\n\t\t\tparsePositiveInteger(settings?.daemonPort, \"sandbox.daemonPort\") ??\n\t\t\tDEFAULT_SANDBOX_DAEMON_PORT,\n\t\tdaemonHostBind:\n\t\t\tnonEmpty(overrides.daemonHostBind) ??\n\t\t\tnonEmpty(env.GRIP_SANDBOX_DAEMON_HOST_BIND) ??\n\t\t\tnonEmpty(settings?.daemonHostBind) ??\n\t\t\t\"127.0.0.1\",\n\t\tcleanup:\n\t\t\toverrides.cleanup ??\n\t\t\tparseCleanup(env.GRIP_SANDBOX_CLEANUP, \"GRIP_SANDBOX_CLEANUP\") ??\n\t\t\tparseCleanup(settings?.cleanup, \"sandbox.cleanup\") ??\n\t\t\t\"stop\",\n\t};\n}\n\nexport async function allocateSandboxDaemonPort(host: string, preferredPort: number): Promise<number> {\n\tconst reserve = (port: number): Promise<number | undefined> =>\n\t\tnew Promise((resolvePort) => {\n\t\t\tconst server = createServer();\n\t\t\tserver.unref();\n\t\t\tserver.once(\"error\", () => resolvePort(undefined));\n\t\t\tserver.listen({ host, port, exclusive: true }, () => {\n\t\t\t\tconst address = server.address();\n\t\t\t\tconst selectedPort = typeof address === \"object\" && address ? address.port : undefined;\n\t\t\t\tserver.close(() => resolvePort(selectedPort));\n\t\t\t});\n\t\t});\n\tconst preferred = await reserve(preferredPort);\n\tif (preferred !== undefined) return preferred;\n\tconst fallback = await reserve(0);\n\tif (fallback === undefined) throw new Error(`Unable to allocate a sandbox daemon port on ${host}`);\n\treturn fallback;\n}\n"]}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { createServer } from "node:net";
|
|
2
|
+
export const DEFAULT_SANDBOX_WORKSPACE_MOUNT = "/workspace";
|
|
3
|
+
export const DEFAULT_SANDBOX_DAEMON_PORT = 8787;
|
|
4
|
+
export const DEFAULT_SANDBOX_LIMA_BINARY = "limactl";
|
|
5
|
+
export const DEFAULT_SANDBOX_LIMA_TEMPLATE = "template:docker-rootful";
|
|
6
|
+
export const SANDBOX_LABEL_PREFIX = "ai.greppleai.grip";
|
|
7
|
+
function nonEmpty(value) {
|
|
8
|
+
const trimmed = value?.trim();
|
|
9
|
+
return trimmed ? trimmed : undefined;
|
|
10
|
+
}
|
|
11
|
+
function parsePositiveInteger(value, settingName) {
|
|
12
|
+
if (value === undefined)
|
|
13
|
+
return undefined;
|
|
14
|
+
const text = String(value).trim();
|
|
15
|
+
if (!/^\d+$/.test(text))
|
|
16
|
+
throw new Error(`Invalid ${settingName}: expected a decimal integer`);
|
|
17
|
+
const parsed = Number(text);
|
|
18
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > 65535) {
|
|
19
|
+
throw new Error(`Invalid ${settingName}: expected a port from 1 to 65535`);
|
|
20
|
+
}
|
|
21
|
+
return parsed;
|
|
22
|
+
}
|
|
23
|
+
function parseCleanup(value, settingName) {
|
|
24
|
+
if (value === undefined)
|
|
25
|
+
return undefined;
|
|
26
|
+
if (value === "stop" || value === "remove")
|
|
27
|
+
return value;
|
|
28
|
+
throw new Error(`Invalid ${settingName}: expected stop or remove`);
|
|
29
|
+
}
|
|
30
|
+
export function redactSecrets(value) {
|
|
31
|
+
return value
|
|
32
|
+
.replace(/(GRIP_DAEMON_TOKEN=)[^\s,;]+/g, "$1[REDACTED]")
|
|
33
|
+
.replace(/(PI_DAEMON_TOKEN=)[^\s,;]+/g, "$1[REDACTED]")
|
|
34
|
+
.replace(/(Authorization:\s*Bearer\s+)[^\s,;]+/gi, "$1[REDACTED]")
|
|
35
|
+
.replace(/([?&]token=)[^\s&]+/gi, "$1[REDACTED]");
|
|
36
|
+
}
|
|
37
|
+
export function resolveSandboxConfig(settings, env = {}, overrides = {}) {
|
|
38
|
+
return {
|
|
39
|
+
limaBinary: nonEmpty(overrides.limaBinary) ??
|
|
40
|
+
nonEmpty(env.GRIP_SANDBOX_LIMA) ??
|
|
41
|
+
nonEmpty(settings?.limaBinary) ??
|
|
42
|
+
DEFAULT_SANDBOX_LIMA_BINARY,
|
|
43
|
+
limaTemplate: nonEmpty(overrides.limaTemplate) ??
|
|
44
|
+
nonEmpty(env.GRIP_SANDBOX_LIMA_TEMPLATE) ??
|
|
45
|
+
nonEmpty(settings?.limaTemplate) ??
|
|
46
|
+
DEFAULT_SANDBOX_LIMA_TEMPLATE,
|
|
47
|
+
limaInstanceNamePrefix: nonEmpty(overrides.limaInstanceNamePrefix) ??
|
|
48
|
+
nonEmpty(env.GRIP_SANDBOX_LIMA_INSTANCE_PREFIX) ??
|
|
49
|
+
nonEmpty(settings?.limaInstanceNamePrefix) ??
|
|
50
|
+
"grip-sandbox",
|
|
51
|
+
workspaceMountPath: nonEmpty(overrides.workspaceMountPath) ??
|
|
52
|
+
nonEmpty(env.GRIP_SANDBOX_WORKSPACE_MOUNT) ??
|
|
53
|
+
nonEmpty(settings?.workspaceMountPath) ??
|
|
54
|
+
DEFAULT_SANDBOX_WORKSPACE_MOUNT,
|
|
55
|
+
daemonPort: overrides.daemonPort ??
|
|
56
|
+
parsePositiveInteger(env.GRIP_SANDBOX_DAEMON_PORT, "GRIP_SANDBOX_DAEMON_PORT") ??
|
|
57
|
+
parsePositiveInteger(settings?.daemonPort, "sandbox.daemonPort") ??
|
|
58
|
+
DEFAULT_SANDBOX_DAEMON_PORT,
|
|
59
|
+
daemonHostBind: nonEmpty(overrides.daemonHostBind) ??
|
|
60
|
+
nonEmpty(env.GRIP_SANDBOX_DAEMON_HOST_BIND) ??
|
|
61
|
+
nonEmpty(settings?.daemonHostBind) ??
|
|
62
|
+
"127.0.0.1",
|
|
63
|
+
cleanup: overrides.cleanup ??
|
|
64
|
+
parseCleanup(env.GRIP_SANDBOX_CLEANUP, "GRIP_SANDBOX_CLEANUP") ??
|
|
65
|
+
parseCleanup(settings?.cleanup, "sandbox.cleanup") ??
|
|
66
|
+
"stop",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export async function allocateSandboxDaemonPort(host, preferredPort) {
|
|
70
|
+
const reserve = (port) => new Promise((resolvePort) => {
|
|
71
|
+
const server = createServer();
|
|
72
|
+
server.unref();
|
|
73
|
+
server.once("error", () => resolvePort(undefined));
|
|
74
|
+
server.listen({ host, port, exclusive: true }, () => {
|
|
75
|
+
const address = server.address();
|
|
76
|
+
const selectedPort = typeof address === "object" && address ? address.port : undefined;
|
|
77
|
+
server.close(() => resolvePort(selectedPort));
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
const preferred = await reserve(preferredPort);
|
|
81
|
+
if (preferred !== undefined)
|
|
82
|
+
return preferred;
|
|
83
|
+
const fallback = await reserve(0);
|
|
84
|
+
if (fallback === undefined)
|
|
85
|
+
throw new Error(`Unable to allocate a sandbox daemon port on ${host}`);
|
|
86
|
+
return fallback;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/core/sandbox/config.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer } from \"node:net\";\nimport type { SandboxSettings } from \"../settings-manager.ts\";\n\nexport const DEFAULT_SANDBOX_WORKSPACE_MOUNT = \"/workspace\";\nexport const DEFAULT_SANDBOX_DAEMON_PORT = 8787;\nexport const DEFAULT_SANDBOX_LIMA_BINARY = \"limactl\";\nexport const DEFAULT_SANDBOX_LIMA_TEMPLATE = \"template:docker-rootful\";\nexport const SANDBOX_LABEL_PREFIX = \"ai.greppleai.grip\";\n\nexport type SandboxCleanupBehavior = \"stop\" | \"remove\";\n\nexport type SandboxConfig = Required<SandboxSettings>;\n\nexport type SandboxConfigOverrides = SandboxSettings;\n\nexport interface SandboxEnvironment {\n\tGRIP_SANDBOX_LIMA?: string;\n\tGRIP_SANDBOX_GUEST_BINARY?: string;\n\tGRIP_SANDBOX_LIMA_TEMPLATE?: string;\n\tGRIP_SANDBOX_LIMA_INSTANCE_PREFIX?: string;\n\tGRIP_SANDBOX_WORKSPACE_MOUNT?: string;\n\tGRIP_SANDBOX_DAEMON_PORT?: string;\n\tGRIP_SANDBOX_DAEMON_HOST_BIND?: string;\n\tGRIP_SANDBOX_CLEANUP?: string;\n}\n\nexport interface SandboxStartOptions extends SandboxConfigOverrides {\n\tworkspaceRoot: string;\n\tsessionId?: string;\n\tname?: string;\n\tupdate?: boolean;\n}\n\nexport interface SandboxListOptions {\n\tworkspaceRoot: string;\n\tallWorkspaces?: boolean;\n}\n\nexport interface SandboxStopOptions {\n\tworkspaceRoot: string;\n\ttarget?: string;\n\tcurrentContainerId?: string;\n}\n\nexport interface SandboxContainer {\n\tid: string;\n\tname: string;\n\timage: string;\n\tstatus: string;\n\tstate: string | undefined;\n\tcreatedAt: string | undefined;\n\tworkspaceRoot: string | undefined;\n\tworkspaceMountPath: string | undefined;\n\tdaemonPort: number | undefined;\n\tdaemonEndpoint: string | undefined;\n\tlabels: Record<string, string>;\n}\n\nexport interface SandboxStartResult {\n\tcontainerId: string;\n\tcontainerName: string;\n\tworkspaceRoot: string;\n\tworkspaceMountPath: string;\n\tdaemonUrl: string;\n\tdaemonUrlRedacted: string;\n\ttoken: string;\n\tlabels: Record<string, string>;\n}\n\nexport type SandboxStopResult =\n\t| { status: \"stopped\"; container: SandboxContainer }\n\t| { status: \"removed\"; container: SandboxContainer }\n\t| { status: \"already-stopped\"; container: SandboxContainer }\n\t| { status: \"not-found\"; message: string };\n\nexport interface SandboxService {\n\tresolveConfig(overrides?: SandboxConfigOverrides): SandboxConfig;\n\tstart(options: SandboxStartOptions): Promise<SandboxStartResult>;\n\tlist(options: SandboxListOptions): Promise<SandboxContainer[]>;\n\tstop(options: SandboxStopOptions): Promise<SandboxStopResult>;\n}\n\nexport interface SandboxCommandResult {\n\texitCode: number | null;\n\tstdout: string;\n\tstderr: string;\n}\n\nexport interface SandboxCommandOptions {\n\tenv?: Record<string, string>;\n}\n\nexport interface SandboxRunner {\n\trun(command: string, args: string[], options?: SandboxCommandOptions): Promise<SandboxCommandResult>;\n}\n\nfunction nonEmpty(value: string | undefined): string | undefined {\n\tconst trimmed = value?.trim();\n\treturn trimmed ? trimmed : undefined;\n}\n\nfunction parsePositiveInteger(value: string | number | undefined, settingName: string): number | undefined {\n\tif (value === undefined) return undefined;\n\tconst text = String(value).trim();\n\tif (!/^\\d+$/.test(text)) throw new Error(`Invalid ${settingName}: expected a decimal integer`);\n\tconst parsed = Number(text);\n\tif (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > 65535) {\n\t\tthrow new Error(`Invalid ${settingName}: expected a port from 1 to 65535`);\n\t}\n\treturn parsed;\n}\n\nfunction parseCleanup(value: string | undefined, settingName: string): SandboxCleanupBehavior | undefined {\n\tif (value === undefined) return undefined;\n\tif (value === \"stop\" || value === \"remove\") return value;\n\tthrow new Error(`Invalid ${settingName}: expected stop or remove`);\n}\n\nexport function redactSecrets(value: string): string {\n\treturn value\n\t\t.replace(/(GRIP_DAEMON_TOKEN=)[^\\s,;]+/g, \"$1[REDACTED]\")\n\t\t.replace(/(PI_DAEMON_TOKEN=)[^\\s,;]+/g, \"$1[REDACTED]\")\n\t\t.replace(/(Authorization:\\s*Bearer\\s+)[^\\s,;]+/gi, \"$1[REDACTED]\")\n\t\t.replace(/([?&]token=)[^\\s&]+/gi, \"$1[REDACTED]\");\n}\n\nexport function resolveSandboxConfig(\n\tsettings: SandboxSettings | undefined,\n\tenv: SandboxEnvironment = {},\n\toverrides: SandboxConfigOverrides = {},\n): SandboxConfig {\n\treturn {\n\t\tlimaBinary:\n\t\t\tnonEmpty(overrides.limaBinary) ??\n\t\t\tnonEmpty(env.GRIP_SANDBOX_LIMA) ??\n\t\t\tnonEmpty(settings?.limaBinary) ??\n\t\t\tDEFAULT_SANDBOX_LIMA_BINARY,\n\t\tlimaTemplate:\n\t\t\tnonEmpty(overrides.limaTemplate) ??\n\t\t\tnonEmpty(env.GRIP_SANDBOX_LIMA_TEMPLATE) ??\n\t\t\tnonEmpty(settings?.limaTemplate) ??\n\t\t\tDEFAULT_SANDBOX_LIMA_TEMPLATE,\n\t\tlimaInstanceNamePrefix:\n\t\t\tnonEmpty(overrides.limaInstanceNamePrefix) ??\n\t\t\tnonEmpty(env.GRIP_SANDBOX_LIMA_INSTANCE_PREFIX) ??\n\t\t\tnonEmpty(settings?.limaInstanceNamePrefix) ??\n\t\t\t\"grip-sandbox\",\n\t\tworkspaceMountPath:\n\t\t\tnonEmpty(overrides.workspaceMountPath) ??\n\t\t\tnonEmpty(env.GRIP_SANDBOX_WORKSPACE_MOUNT) ??\n\t\t\tnonEmpty(settings?.workspaceMountPath) ??\n\t\t\tDEFAULT_SANDBOX_WORKSPACE_MOUNT,\n\t\tdaemonPort:\n\t\t\toverrides.daemonPort ??\n\t\t\tparsePositiveInteger(env.GRIP_SANDBOX_DAEMON_PORT, \"GRIP_SANDBOX_DAEMON_PORT\") ??\n\t\t\tparsePositiveInteger(settings?.daemonPort, \"sandbox.daemonPort\") ??\n\t\t\tDEFAULT_SANDBOX_DAEMON_PORT,\n\t\tdaemonHostBind:\n\t\t\tnonEmpty(overrides.daemonHostBind) ??\n\t\t\tnonEmpty(env.GRIP_SANDBOX_DAEMON_HOST_BIND) ??\n\t\t\tnonEmpty(settings?.daemonHostBind) ??\n\t\t\t\"127.0.0.1\",\n\t\tcleanup:\n\t\t\toverrides.cleanup ??\n\t\t\tparseCleanup(env.GRIP_SANDBOX_CLEANUP, \"GRIP_SANDBOX_CLEANUP\") ??\n\t\t\tparseCleanup(settings?.cleanup, \"sandbox.cleanup\") ??\n\t\t\t\"stop\",\n\t};\n}\n\nexport async function allocateSandboxDaemonPort(host: string, preferredPort: number): Promise<number> {\n\tconst reserve = (port: number): Promise<number | undefined> =>\n\t\tnew Promise((resolvePort) => {\n\t\t\tconst server = createServer();\n\t\t\tserver.unref();\n\t\t\tserver.once(\"error\", () => resolvePort(undefined));\n\t\t\tserver.listen({ host, port, exclusive: true }, () => {\n\t\t\t\tconst address = server.address();\n\t\t\t\tconst selectedPort = typeof address === \"object\" && address ? address.port : undefined;\n\t\t\t\tserver.close(() => resolvePort(selectedPort));\n\t\t\t});\n\t\t});\n\tconst preferred = await reserve(preferredPort);\n\tif (preferred !== undefined) return preferred;\n\tconst fallback = await reserve(0);\n\tif (fallback === undefined) throw new Error(`Unable to allocate a sandbox daemon port on ${host}`);\n\treturn fallback;\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SandboxRunner } from "./config.ts";
|
|
2
|
+
export interface LocalGuestBinary {
|
|
3
|
+
path: string;
|
|
4
|
+
arch: string;
|
|
5
|
+
sha256: string;
|
|
6
|
+
}
|
|
7
|
+
/** Refuse macOS executables in Linux VMs, even when the host CPU architecture matches. */
|
|
8
|
+
export declare function inspectLocalGuestBinary(path: string): Promise<LocalGuestBinary>;
|
|
9
|
+
/** Called only for explicitly requested local-development installs. No remote download or sudo. */
|
|
10
|
+
export declare function installLocalGuestBinary(runner: SandboxRunner, limaBinary: string, name: string, local: LocalGuestBinary, version: string, assertOwner?: () => Promise<void>): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=lima-guest-binary.d.ts.map
|