@opengeni/runtime 3.0.1 → 4.0.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/dist/assets/codemode-client.json +1 -0
- package/dist/chunk-4A4XTKKX.js +1 -0
- package/dist/{chunk-RIJ6DCYS.js → chunk-5HJEVOMJ.js} +9817 -9494
- package/dist/chunk-5HJEVOMJ.js.map +1 -0
- package/dist/{chunk-FHGLYLBL.js → chunk-SPT3PLMX.js} +1 -87
- package/dist/chunk-SPT3PLMX.js.map +1 -0
- package/dist/{chunk-GB763TSV.js → chunk-TE27J65Q.js} +1 -1
- package/dist/chunk-TE27J65Q.js.map +1 -0
- package/dist/{chunk-46BFUGFL.js → chunk-WIHICNWS.js} +1660 -388
- package/dist/chunk-WIHICNWS.js.map +1 -0
- package/dist/{chunk-UU6EJR52.js → chunk-YU4VRTR4.js} +2 -2
- package/dist/chunk-YU4VRTR4.js.map +1 -0
- package/dist/chunk-ZG2YH25H.js +94 -0
- package/dist/chunk-ZG2YH25H.js.map +1 -0
- package/dist/codex-tool-search.d.ts +5 -4
- package/dist/context-compaction.d.ts +19 -0
- package/dist/gmail-rest-mcp.js +1 -1
- package/dist/index.d.ts +30 -22
- package/dist/index.js +25 -5
- package/dist/lazy-manifest-OEYEHNVY.js +45 -0
- package/dist/lazy-manifest-OEYEHNVY.js.map +1 -0
- package/dist/lazy-manifest.d.ts +6 -0
- package/dist/lazy-tool-transport.d.ts +3 -2
- package/dist/{localSources-PVFCAPN2.js → localSources-TWHTEKGO.js} +3 -2
- package/dist/localSources-TWHTEKGO.js.map +1 -0
- package/dist/metrics.d.ts +6 -0
- package/dist/operational-instructions.d.ts +1 -1
- package/dist/prepared-compaction-request.d.ts +8 -0
- package/dist/provider-history-adapter.d.ts +2 -2
- package/dist/runtime-skills.d.ts +3 -10
- package/dist/sandbox/codemode-client.d.ts +17 -0
- package/dist/sandbox/codemode-token.d.ts +3 -3
- package/dist/sandbox/index.d.ts +2 -1
- package/dist/sandbox/index.js +8 -2
- package/dist/sandbox/materialization-verification-error.d.ts +27 -0
- package/dist/sandbox/provider-command-session.d.ts +44 -2
- package/dist/sandbox/providers/modal-command-control.d.ts +32 -29
- package/dist/sandbox/providers/modal-command-router-wire.d.ts +49 -0
- package/dist/sandbox/providers/modal-command-session.d.ts +2 -1
- package/dist/sandbox/providers/modal-legacy-command-control.d.ts +50 -0
- package/dist/sandbox/providers/modal-materialization-verification.d.ts +7 -0
- package/dist/sandbox/providers/modal.d.ts +0 -8
- package/dist/sandbox/routing/routing-session.d.ts +21 -0
- package/dist/sandbox/turn-tool-cancellation.d.ts +1 -0
- package/dist/skill-library.d.ts +0 -6
- package/dist/skill-library.js +1 -1
- package/dist/workspace-tool-gateway.js +7 -5
- package/package.json +9 -7
- package/src/bundled_artifact_skills/opengeni-documents/SKILL.md +19 -0
- package/src/bundled_artifact_skills/opengeni-documents/references/api.md +22 -0
- package/src/bundled_default_skills/opengeni-client/SKILL.md +349 -0
- package/src/bundled_default_skills/opengeni-client/agents/openai.yaml +4 -0
- package/src/bundled_default_skills/opengeni-client/references/api-workflows.md +256 -0
- package/src/bundled_default_skills/opengeni-client/references/compatibility-and-troubleshooting.md +84 -0
- package/src/bundled_default_skills/opengeni-client/references/customer-skill-template.md +96 -0
- package/src/bundled_default_skills/opengeni-client/references/data-tools-and-credentials.md +99 -0
- package/src/bundled_default_skills/opengeni-client/references/discovery-and-autonomy.md +81 -0
- package/src/bundled_default_skills/opengeni-client/references/external-users-and-connect.md +457 -0
- package/src/bundled_default_skills/opengeni-client/references/isolation-and-authorization.md +77 -0
- package/src/bundled_default_skills/opengeni-client/references/product-integration-shapes.md +232 -0
- package/src/bundled_default_skills/opengeni-client/references/product-shapes-and-ui.md +116 -0
- package/src/bundled_default_skills/opengeni-client/references/runtime-profile-and-verification.md +157 -0
- package/src/bundled_default_skills/opengeni-help/SKILL.md +101 -0
- package/src/bundled_management_skills/opengeni-skills/SKILL.md +33 -3
- package/src/codex-tool-search.ts +13 -5
- package/src/context-compaction.ts +87 -4
- package/src/gmail-rest-mcp.ts +1 -2
- package/src/index.ts +363 -131
- package/src/lazy-manifest.ts +55 -0
- package/src/lazy-tool-transport.ts +15 -2
- package/src/metrics.ts +6 -0
- package/src/model-request-capture.ts +3 -0
- package/src/operational-instructions.ts +11 -2
- package/src/prepared-compaction-request.ts +51 -0
- package/src/provider-history-adapter.ts +12 -3
- package/src/runtime-skills.ts +3 -17
- package/src/sandbox/codemode-client.ts +204 -0
- package/src/sandbox/codemode-token.ts +7 -2
- package/src/sandbox/index.ts +7 -0
- package/src/sandbox/materialization-verification-error.ts +65 -0
- package/src/sandbox/provider-command-session.ts +70 -2
- package/src/sandbox/providers/modal-command-control.ts +386 -242
- package/src/sandbox/providers/modal-command-router-wire.ts +248 -0
- package/src/sandbox/providers/modal-command-session.ts +177 -4
- package/src/sandbox/providers/modal-legacy-command-control.ts +351 -0
- package/src/sandbox/providers/modal-materialization-verification.ts +122 -0
- package/src/sandbox/providers/modal.ts +1 -9
- package/src/sandbox/routing/routing-session.ts +417 -79
- package/src/sandbox/turn-tool-cancellation.ts +38 -2
- package/src/skill-catalog.ts +1 -0
- package/src/skill-library.ts +0 -6
- package/src/workspace-governance.ts +0 -1
- package/src/workspace-skills.ts +114 -74
- package/dist/chunk-46BFUGFL.js.map +0 -1
- package/dist/chunk-FHGLYLBL.js.map +0 -1
- package/dist/chunk-GB763TSV.js.map +0 -1
- package/dist/chunk-RIJ6DCYS.js.map +0 -1
- package/dist/chunk-UU6EJR52.js.map +0 -1
- /package/dist/{localSources-PVFCAPN2.js.map → chunk-4A4XTKKX.js.map} +0 -0
|
@@ -1,136 +1,67 @@
|
|
|
1
|
+
import { randomUUID, randomBytes } from "node:crypto";
|
|
1
2
|
import { posix } from "node:path";
|
|
2
|
-
import { ModalClient } from "modal";
|
|
3
|
-
import { SandboxProviderCommand } from "@opengeni/contracts";
|
|
4
|
-
import type { ChannelAExecArgs } from "../channel-a";
|
|
5
3
|
import { shellQuote } from "@openai/agents-core/sandbox/internal";
|
|
4
|
+
import {
|
|
5
|
+
SandboxProviderCommand,
|
|
6
|
+
CommandSupervisionReceipt,
|
|
7
|
+
type ModalRouterProviderCommand,
|
|
8
|
+
} from "@opengeni/contracts";
|
|
9
|
+
import type { ChannelAExecArgs } from "../channel-a";
|
|
10
|
+
import type { ProviderCommandOutput } from "../provider-command-session";
|
|
11
|
+
import {
|
|
12
|
+
admittedCommandSupervisionReady,
|
|
13
|
+
markPendingCommandSupervised,
|
|
14
|
+
reserveSupervisedLaunch,
|
|
15
|
+
} from "../provider-command-session";
|
|
16
|
+
import {
|
|
17
|
+
ModalCommandControl as LegacyControl,
|
|
18
|
+
commandControlPlane,
|
|
19
|
+
decodePage,
|
|
20
|
+
} from "./modal-legacy-command-control";
|
|
21
|
+
import {
|
|
22
|
+
ModalCommandRouterWire,
|
|
23
|
+
ModalCommandStartRejectedError,
|
|
24
|
+
} from "./modal-command-router-wire";
|
|
6
25
|
|
|
7
|
-
|
|
8
|
-
ModalClient["cpClient"],
|
|
9
|
-
"sandboxGetTaskId" | "containerExec" | "containerExecGetOutput" | "containerExecPutInput"
|
|
10
|
-
>;
|
|
11
|
-
|
|
12
|
-
type CommandClient = Pick<ModalClient, "cpClient" | "version"> &
|
|
13
|
-
Partial<Pick<ModalClient, "profile" | "logger">>;
|
|
14
|
-
type GrpcMiddleware = NonNullable<
|
|
15
|
-
NonNullable<ConstructorParameters<typeof ModalClient>[0]>["grpcMiddleware"]
|
|
16
|
-
>[number];
|
|
17
|
-
/** This dedicated command client never retries a request behind the caller's
|
|
18
|
-
* back and preserves the exact cancellation signal, including streaming RPCs. */
|
|
19
|
-
export const modalCommandAbortMiddleware: GrpcMiddleware = async function* (call, options) {
|
|
20
|
-
options.signal?.throwIfAborted();
|
|
21
|
-
return yield* call.next(call.request, options);
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
// Version-pinned compatibility boundary: 0.9.0's retryMiddleware drops signal
|
|
25
|
-
// for streaming and retries=0. Its public custom middleware runs OUTSIDE that
|
|
26
|
-
// branch, so cannot repair it. Override only this dedicated instance's factory
|
|
27
|
-
// hook before construction; never mutate the SDK's global prototype or use
|
|
28
|
-
// negative/fractional retry counts to manipulate its branch conditions.
|
|
29
|
-
type PublicModalClient = Pick<ModalClient, keyof ModalClient>;
|
|
30
|
-
const ModalClientBase: new (
|
|
31
|
-
options?: ConstructorParameters<typeof ModalClient>[0],
|
|
32
|
-
) => PublicModalClient = ModalClient;
|
|
33
|
-
class ModalCommandClient extends ModalClientBase {
|
|
34
|
-
retryMiddleware(): GrpcMiddleware {
|
|
35
|
-
return modalCommandAbortMiddleware;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
const controlClients = new WeakMap<object, PublicModalClient>();
|
|
39
|
-
|
|
40
|
-
function commandControlPlane(client: CommandClient): ControlPlane {
|
|
41
|
-
// Minimal injected control planes are used by deterministic unit tests.
|
|
42
|
-
// Production installation requires the authenticated SDK profile below.
|
|
43
|
-
if (!client.profile) return client.cpClient;
|
|
44
|
-
if (typeof Reflect.get(ModalClient.prototype, "retryMiddleware") !== "function")
|
|
45
|
-
throw new Error("Verified Modal command cancellation contract is unavailable");
|
|
46
|
-
let controlled = controlClients.get(client);
|
|
47
|
-
if (!controlled) {
|
|
48
|
-
const profile = client.profile;
|
|
49
|
-
if (!profile.tokenId || !profile.tokenSecret)
|
|
50
|
-
throw new Error("Modal command control requires its original authenticated profile");
|
|
51
|
-
controlled = new ModalCommandClient({
|
|
52
|
-
tokenId: profile.tokenId,
|
|
53
|
-
tokenSecret: profile.tokenSecret,
|
|
54
|
-
endpoint: profile.serverUrl,
|
|
55
|
-
environment: profile.environment ?? "",
|
|
56
|
-
...(profile.maxThrottleWaitSecs !== undefined
|
|
57
|
-
? { maxThrottleWaitSecs: profile.maxThrottleWaitSecs }
|
|
58
|
-
: {}),
|
|
59
|
-
...(client.logger ? { logger: client.logger } : {}),
|
|
60
|
-
});
|
|
61
|
-
controlClients.set(client, controlled);
|
|
62
|
-
}
|
|
63
|
-
return controlled.cpClient;
|
|
64
|
-
}
|
|
65
|
-
export type ModalCommandStream = "stdout" | "stderr";
|
|
26
|
+
export { modalCommandAbortMiddleware } from "./modal-legacy-command-control";
|
|
66
27
|
export type ModalProviderCommand = SandboxProviderCommand;
|
|
67
|
-
export type ModalProviderOutputPage =
|
|
68
|
-
command: ModalProviderCommand;
|
|
69
|
-
chunks: Array<{ stream: ModalCommandStream; chunkId: string; text: string }>;
|
|
70
|
-
exitCode: number | null;
|
|
71
|
-
streamFidelity: "separate" | "merged";
|
|
72
|
-
};
|
|
28
|
+
export type ModalProviderOutputPage = ProviderCommandOutput;
|
|
73
29
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
for (let start = Math.max(0, bytes.length - 3); start < bytes.length; start++) {
|
|
82
|
-
const first = bytes[start]!;
|
|
83
|
-
const width =
|
|
84
|
-
first >= 0xc2 && first <= 0xdf
|
|
85
|
-
? 2
|
|
86
|
-
: first >= 0xe0 && first <= 0xef
|
|
87
|
-
? 3
|
|
88
|
-
: first >= 0xf0 && first <= 0xf4
|
|
89
|
-
? 4
|
|
90
|
-
: 0;
|
|
91
|
-
if (!width || bytes.length - start >= width) continue;
|
|
92
|
-
const suffix = bytes.subarray(start + 1);
|
|
93
|
-
if (!suffix.every((byte) => byte >= 0x80 && byte <= 0xbf)) continue;
|
|
94
|
-
const second = suffix[0];
|
|
95
|
-
if (
|
|
96
|
-
second !== undefined &&
|
|
97
|
-
((first === 0xe0 && second < 0xa0) ||
|
|
98
|
-
(first === 0xed && second > 0x9f) ||
|
|
99
|
-
(first === 0xf0 && second < 0x90) ||
|
|
100
|
-
(first === 0xf4 && second > 0x8f))
|
|
101
|
-
)
|
|
102
|
-
continue;
|
|
103
|
-
boundary = start;
|
|
104
|
-
break;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return {
|
|
108
|
-
text: bytes.subarray(0, boundary).toString("utf8"),
|
|
109
|
-
remainder: bytes.subarray(boundary).toString("base64"),
|
|
110
|
-
};
|
|
111
|
-
}
|
|
30
|
+
type Client = Parameters<typeof commandControlPlane>[0];
|
|
31
|
+
type RouterEntry = {
|
|
32
|
+
router: ModalCommandRouterWire;
|
|
33
|
+
users: number;
|
|
34
|
+
refreshAt: number;
|
|
35
|
+
idle?: ReturnType<typeof setTimeout>;
|
|
36
|
+
};
|
|
112
37
|
|
|
113
|
-
/**
|
|
114
|
-
*
|
|
115
|
-
* files or an SDK object's in-memory process map. Callers must store the
|
|
116
|
-
* returned locator/cursors outside the command's write authority. */
|
|
38
|
+
/** New commands use replayable task-router byte offsets. Legacy identifiers
|
|
39
|
+
* never cross that protocol boundary and are never used for new starts. */
|
|
117
40
|
export class ModalCommandControl {
|
|
41
|
+
private readonly routers = new Map<string, Promise<RouterEntry>>();
|
|
42
|
+
private readonly client: ReturnType<typeof commandControlPlane>;
|
|
43
|
+
private readonly legacy: LegacyControl;
|
|
44
|
+
private closed = false;
|
|
45
|
+
|
|
118
46
|
private constructor(
|
|
119
|
-
|
|
47
|
+
client: Client,
|
|
120
48
|
private readonly sandboxIdentity: string | (() => string),
|
|
121
49
|
private readonly root: string,
|
|
122
|
-
private readonly environment: Record<string, string> | (() => Record<string, string>)
|
|
123
|
-
) {
|
|
50
|
+
private readonly environment: Record<string, string> | (() => Record<string, string>),
|
|
51
|
+
) {
|
|
52
|
+
this.client = commandControlPlane(client);
|
|
53
|
+
this.legacy = LegacyControl.forSandbox(client, sandboxIdentity, root, environment);
|
|
54
|
+
}
|
|
124
55
|
|
|
125
56
|
static forSandbox(
|
|
126
|
-
client:
|
|
57
|
+
client: Client,
|
|
127
58
|
sandboxId: string | (() => string),
|
|
128
59
|
root: string,
|
|
129
60
|
environment: Record<string, string> | (() => Record<string, string>) = {},
|
|
130
61
|
): ModalCommandControl {
|
|
131
62
|
if (client.version() !== "0.9.0")
|
|
132
63
|
throw new Error("Modal command control requires the verified 0.9.0 SDK contract");
|
|
133
|
-
return new ModalCommandControl(
|
|
64
|
+
return new ModalCommandControl(client, sandboxId, root, environment);
|
|
134
65
|
}
|
|
135
66
|
|
|
136
67
|
private get sandboxId(): string {
|
|
@@ -139,164 +70,377 @@ export class ModalCommandControl {
|
|
|
139
70
|
: this.sandboxIdentity;
|
|
140
71
|
}
|
|
141
72
|
|
|
142
|
-
async
|
|
73
|
+
private async withRouter<T>(
|
|
74
|
+
taskId: string,
|
|
75
|
+
signal: AbortSignal | undefined,
|
|
76
|
+
run: (router: ModalCommandRouterWire) => Promise<T>,
|
|
77
|
+
): Promise<T> {
|
|
78
|
+
signal?.throwIfAborted();
|
|
79
|
+
if (this.closed) throw new Error("Modal command control is closed");
|
|
80
|
+
let pending = this.routers.get(taskId);
|
|
81
|
+
const reused = Boolean(pending);
|
|
82
|
+
if (!pending) {
|
|
83
|
+
pending = (async () => {
|
|
84
|
+
const access = await this.client.taskGetCommandRouterAccess(
|
|
85
|
+
{ taskId },
|
|
86
|
+
signal ? { signal } : undefined,
|
|
87
|
+
);
|
|
88
|
+
let refreshAt = Date.now() + 60_000;
|
|
89
|
+
try {
|
|
90
|
+
// Expiry only shortens cache lifetime; it never authenticates access.
|
|
91
|
+
const payload = JSON.parse(
|
|
92
|
+
Buffer.from(access.jwt.split(".")[1] ?? "", "base64url").toString(),
|
|
93
|
+
);
|
|
94
|
+
if (typeof payload.exp === "number" && Number.isFinite(payload.exp))
|
|
95
|
+
refreshAt = Math.min(refreshAt, payload.exp * 1000 - 15_000);
|
|
96
|
+
} catch {
|
|
97
|
+
/* Authentication remains the provider's responsibility. */
|
|
98
|
+
}
|
|
99
|
+
return { router: new ModalCommandRouterWire(access), users: 0, refreshAt };
|
|
100
|
+
})();
|
|
101
|
+
this.routers.set(taskId, pending);
|
|
102
|
+
void pending.catch(() => {
|
|
103
|
+
if (this.routers.get(taskId) === pending) this.routers.delete(taskId);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const entry = await pending;
|
|
107
|
+
// Refresh credentials between operations, never close another active read.
|
|
108
|
+
if (reused && !entry.users && Date.now() > entry.refreshAt) {
|
|
109
|
+
clearTimeout(entry.idle);
|
|
110
|
+
entry.router.close();
|
|
111
|
+
this.routers.delete(taskId);
|
|
112
|
+
return await this.withRouter(taskId, signal, run);
|
|
113
|
+
}
|
|
114
|
+
clearTimeout(entry.idle);
|
|
115
|
+
entry.users++;
|
|
116
|
+
try {
|
|
117
|
+
signal?.throwIfAborted();
|
|
118
|
+
return await run(entry.router);
|
|
119
|
+
} finally {
|
|
120
|
+
entry.users--;
|
|
121
|
+
if (!entry.users) {
|
|
122
|
+
entry.idle = setTimeout(() => {
|
|
123
|
+
if (this.routers.get(taskId) === pending && !entry.users) {
|
|
124
|
+
this.routers.delete(taskId);
|
|
125
|
+
entry.router.close();
|
|
126
|
+
}
|
|
127
|
+
}, 30_000);
|
|
128
|
+
entry.idle.unref?.();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async close(): Promise<void> {
|
|
134
|
+
this.closed = true;
|
|
135
|
+
const entries = await Promise.allSettled(this.routers.values());
|
|
136
|
+
this.routers.clear();
|
|
137
|
+
for (const entry of entries)
|
|
138
|
+
if (entry.status === "fulfilled") {
|
|
139
|
+
clearTimeout(entry.value.idle);
|
|
140
|
+
entry.value.router.close();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async start(args: ChannelAExecArgs, signal?: AbortSignal): Promise<ModalRouterProviderCommand> {
|
|
143
145
|
signal?.throwIfAborted();
|
|
146
|
+
const supervised = admittedCommandSupervisionReady() && !args.tty && !args.runAs;
|
|
147
|
+
if (supervised) markPendingCommandSupervised();
|
|
148
|
+
const sandboxId = this.sandboxId;
|
|
144
149
|
const workdir = posix.resolve(this.root, args.workdir ?? this.root);
|
|
145
150
|
if (workdir !== this.root && !workdir.startsWith(`${this.root.replace(/\/$/u, "")}/`))
|
|
146
151
|
throw new Error("Command workdir is outside the sandbox workspace");
|
|
147
|
-
// Hydration replaces the SDK sandbox. Freeze its current identity for this
|
|
148
|
-
// start so an asynchronous replacement cannot relabel the returned locator.
|
|
149
|
-
const sandboxId = this.sandboxId;
|
|
150
152
|
const task = await this.client.sandboxGetTaskId({ sandboxId }, signal ? { signal } : undefined);
|
|
151
153
|
if (!task.taskId || task.taskResult) throw new Error("Modal command task is unavailable");
|
|
152
|
-
|
|
154
|
+
const taskId = task.taskId;
|
|
155
|
+
if (this.sandboxId !== sandboxId)
|
|
153
156
|
throw new Error("Modal sandbox changed during command preparation");
|
|
154
|
-
const
|
|
155
|
-
|
|
157
|
+
const execId = randomUUID();
|
|
158
|
+
// PTY and runAs retain their existing explicit unsupported supervision
|
|
159
|
+
// semantics. Never attach a fabricated descriptor to either path.
|
|
160
|
+
const invocationId = randomUUID();
|
|
161
|
+
const supervision = supervised
|
|
162
|
+
? {
|
|
163
|
+
protocol: "native-subreaper-v1" as const,
|
|
164
|
+
invocationId,
|
|
165
|
+
nonce: randomBytes(32).toString("hex"),
|
|
166
|
+
controlPath: `/tmp/opengeni-supervision/${invocationId}.sock`,
|
|
167
|
+
}
|
|
168
|
+
: undefined;
|
|
169
|
+
let commandArgs = [
|
|
170
|
+
args.shell ?? "/bin/sh",
|
|
171
|
+
args.shell && (args.login ?? true) ? "-lc" : "-c",
|
|
172
|
+
args.cmd,
|
|
173
|
+
];
|
|
156
174
|
if (args.runAs) {
|
|
157
|
-
const user = shellQuote(args.runAs)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
175
|
+
const user = shellQuote(args.runAs),
|
|
176
|
+
invocation = commandArgs.map(shellQuote).join(" ");
|
|
177
|
+
commandArgs = [
|
|
178
|
+
"/bin/sh",
|
|
179
|
+
"-c",
|
|
180
|
+
`if [ "$(id -u)" = ${user} ] || [ "$(id -un 2>/dev/null)" = ${user} ]; then exec ${invocation}; elif [ "$(id -u)" = 0 ]; then exec su -s /bin/sh ${user} -c ${shellQuote(`exec ${invocation}`)}; else exec sudo -n -u ${user} -- ${invocation}; fi`,
|
|
181
|
+
];
|
|
161
182
|
}
|
|
162
|
-
const environment
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
"
|
|
183
|
+
const env = typeof this.environment === "function" ? this.environment() : this.environment;
|
|
184
|
+
if (supervision)
|
|
185
|
+
commandArgs = [
|
|
186
|
+
"/usr/local/bin/opengeni-command-supervisor",
|
|
187
|
+
"launch",
|
|
188
|
+
"--invocation",
|
|
189
|
+
supervision.invocationId,
|
|
190
|
+
"--nonce",
|
|
191
|
+
supervision.nonce,
|
|
192
|
+
"--socket",
|
|
193
|
+
supervision.controlPath,
|
|
167
194
|
"--",
|
|
168
|
-
...
|
|
169
|
-
...command,
|
|
195
|
+
...commandArgs,
|
|
170
196
|
];
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// provider API assigns the execution id in its response, not in our request.
|
|
174
|
-
const result = await this.client.containerExec(
|
|
175
|
-
{
|
|
176
|
-
taskId: task.taskId,
|
|
177
|
-
command,
|
|
178
|
-
terminateContainerOnExit: false,
|
|
179
|
-
runtimeDebug: false,
|
|
180
|
-
stdoutOutput: 2,
|
|
181
|
-
stderrOutput: 2,
|
|
182
|
-
timeoutSecs: 0,
|
|
183
|
-
workdir,
|
|
184
|
-
secretIds: [],
|
|
185
|
-
...(args.tty
|
|
186
|
-
? {
|
|
187
|
-
ptyInfo: {
|
|
188
|
-
enabled: true,
|
|
189
|
-
winszRows: 24,
|
|
190
|
-
winszCols: 80,
|
|
191
|
-
envTerm: "xterm",
|
|
192
|
-
envColorterm: "",
|
|
193
|
-
envTermProgram: "",
|
|
194
|
-
ptyType: 1,
|
|
195
|
-
noTerminateOnIdleStdin: true,
|
|
196
|
-
},
|
|
197
|
-
}
|
|
198
|
-
: {}),
|
|
199
|
-
},
|
|
200
|
-
{ retries: 0, ...(signal ? { signal } : {}) },
|
|
201
|
-
);
|
|
202
|
-
if (!result.execId) throw new Error("Modal command start returned no execution identity");
|
|
203
|
-
return {
|
|
204
|
-
kind: "modal-control-v1",
|
|
197
|
+
const command: ModalRouterProviderCommand = {
|
|
198
|
+
kind: "modal-router-v1",
|
|
205
199
|
sandboxId,
|
|
206
|
-
taskId
|
|
207
|
-
execId
|
|
200
|
+
taskId,
|
|
201
|
+
execId,
|
|
208
202
|
...(args.tty ? { pty: true } : {}),
|
|
203
|
+
...(supervision ? { supervision } : {}),
|
|
209
204
|
streams: {
|
|
210
|
-
stdout: {
|
|
211
|
-
stderr: {
|
|
205
|
+
stdout: { byteOffset: 0, utf8Remainder: "", eof: false, exitCode: null },
|
|
206
|
+
stderr: { byteOffset: 0, utf8Remainder: "", eof: false, exitCode: null },
|
|
212
207
|
},
|
|
213
208
|
};
|
|
209
|
+
if (supervision) await reserveSupervisedLaunch(command);
|
|
210
|
+
try {
|
|
211
|
+
await this.withRouter(taskId, signal, (router) =>
|
|
212
|
+
router.start(
|
|
213
|
+
{
|
|
214
|
+
taskId,
|
|
215
|
+
execId,
|
|
216
|
+
commandArgs,
|
|
217
|
+
workdir,
|
|
218
|
+
env,
|
|
219
|
+
...(args.tty
|
|
220
|
+
? {
|
|
221
|
+
ptyInfo: {
|
|
222
|
+
enabled: true,
|
|
223
|
+
winszRows: 24,
|
|
224
|
+
winszCols: 80,
|
|
225
|
+
envTerm: "xterm",
|
|
226
|
+
ptyType: 1,
|
|
227
|
+
noTerminateOnIdleStdin: true,
|
|
228
|
+
},
|
|
229
|
+
}
|
|
230
|
+
: {}),
|
|
231
|
+
},
|
|
232
|
+
signal,
|
|
233
|
+
),
|
|
234
|
+
);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
// A client-chosen router id remains the only possible invocation. The
|
|
237
|
+
// supervisor is idle, so an ambiguous launch never ran user code. Retain
|
|
238
|
+
// the descriptor and reconcile that id; do not replay the start.
|
|
239
|
+
if (!supervision || error instanceof ModalCommandStartRejectedError) throw error;
|
|
240
|
+
}
|
|
241
|
+
return command;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Read-only, authenticated control execution on this exact instance. Never
|
|
245
|
+
* inferred from a fleet image selector or user command stdout. No cache: warm
|
|
246
|
+
* instances and route/task replacement must each pass before admission. */
|
|
247
|
+
async verifySupervisionCapability(): Promise<{ sandboxId: string; taskId: string }> {
|
|
248
|
+
const sandboxId = this.sandboxId;
|
|
249
|
+
const signal = AbortSignal.timeout(5_000);
|
|
250
|
+
const task = await this.client.sandboxGetTaskId({ sandboxId }, { signal });
|
|
251
|
+
if (!task.taskId || task.taskResult) throw new Error("Modal command task is unavailable");
|
|
252
|
+
await this.withRouter(task.taskId, signal, async (router) => {
|
|
253
|
+
const identity = { taskId: task.taskId!, execId: randomUUID() };
|
|
254
|
+
await router.start(
|
|
255
|
+
{
|
|
256
|
+
...identity,
|
|
257
|
+
commandArgs: ["/usr/local/bin/opengeni-command-supervisor", "capabilities"],
|
|
258
|
+
workdir: "/tmp",
|
|
259
|
+
env: {},
|
|
260
|
+
},
|
|
261
|
+
signal,
|
|
262
|
+
);
|
|
263
|
+
let offset = 0;
|
|
264
|
+
let output = "";
|
|
265
|
+
let eof = false;
|
|
266
|
+
let exit: number | null = null;
|
|
267
|
+
while (!eof || exit === null) {
|
|
268
|
+
signal.throwIfAborted();
|
|
269
|
+
const page = await router.read(identity, "stdout", offset, 250, signal);
|
|
270
|
+
offset += page.bytes.length;
|
|
271
|
+
if (offset > 128) throw new Error("Modal supervision capability response exceeds bound");
|
|
272
|
+
output += page.bytes.toString("utf8");
|
|
273
|
+
eof = page.eof;
|
|
274
|
+
exit = await router.poll(identity, signal);
|
|
275
|
+
}
|
|
276
|
+
if (exit !== 0 || output !== "native-subreaper-v1")
|
|
277
|
+
throw new Error(
|
|
278
|
+
"Exact Modal instance lacks compatible native supervision; command not admitted",
|
|
279
|
+
);
|
|
280
|
+
});
|
|
281
|
+
if (sandboxId !== this.sandboxId)
|
|
282
|
+
throw new Error("Modal instance changed during supervision capability verification");
|
|
283
|
+
return { sandboxId, taskId: task.taskId };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Separate authenticated provider execution of the installed control helper.
|
|
287
|
+
* User command streams are never inspected for control evidence. */
|
|
288
|
+
async supervisionControl(
|
|
289
|
+
command: ModalRouterProviderCommand,
|
|
290
|
+
action: "release" | "cancel" | "status" | "ack",
|
|
291
|
+
receiptId?: string,
|
|
292
|
+
): Promise<{ state: "idle" | "running" | "quiescent"; receipt?: CommandSupervisionReceipt }> {
|
|
293
|
+
SandboxProviderCommand.parse(command);
|
|
294
|
+
const descriptor = command.supervision;
|
|
295
|
+
if (!descriptor || command.sandboxId !== this.sandboxId)
|
|
296
|
+
throw new Error("Supervised command identity is unavailable");
|
|
297
|
+
const identity = { taskId: command.taskId, execId: randomUUID() };
|
|
298
|
+
const signal = AbortSignal.timeout(5_000);
|
|
299
|
+
return await this.withRouter(command.taskId, signal, async (router) => {
|
|
300
|
+
await router.start(
|
|
301
|
+
{
|
|
302
|
+
...identity,
|
|
303
|
+
commandArgs: [
|
|
304
|
+
"/usr/local/bin/opengeni-command-supervisor",
|
|
305
|
+
"control",
|
|
306
|
+
"--invocation",
|
|
307
|
+
descriptor.invocationId,
|
|
308
|
+
"--nonce",
|
|
309
|
+
descriptor.nonce,
|
|
310
|
+
"--socket",
|
|
311
|
+
descriptor.controlPath,
|
|
312
|
+
"--action",
|
|
313
|
+
action,
|
|
314
|
+
...(receiptId ? ["--receipt", receiptId] : []),
|
|
315
|
+
],
|
|
316
|
+
workdir: "/tmp",
|
|
317
|
+
env: {},
|
|
318
|
+
},
|
|
319
|
+
signal,
|
|
320
|
+
);
|
|
321
|
+
const buffers: Buffer[] = [];
|
|
322
|
+
let offset = 0;
|
|
323
|
+
let eof = false;
|
|
324
|
+
let exit: number | null = null;
|
|
325
|
+
while (!eof || exit === null) {
|
|
326
|
+
signal.throwIfAborted();
|
|
327
|
+
const page = await router.read(identity, "stdout", offset, 250, signal);
|
|
328
|
+
offset += page.bytes.length;
|
|
329
|
+
if (offset > 4096) throw new Error("Supervisor control response exceeds its bound");
|
|
330
|
+
buffers.push(page.bytes);
|
|
331
|
+
eof = page.eof;
|
|
332
|
+
exit = await router.poll(identity, signal);
|
|
333
|
+
}
|
|
334
|
+
if (exit !== 0) throw new Error("Supervisor control is unavailable");
|
|
335
|
+
const result = JSON.parse(Buffer.concat(buffers).toString("utf8"));
|
|
336
|
+
if (!result || !["idle", "running", "quiescent"].includes(result.state))
|
|
337
|
+
throw new Error("Invalid supervisor control response");
|
|
338
|
+
if (result.receipt !== undefined) {
|
|
339
|
+
result.receipt = CommandSupervisionReceipt.parse(result.receipt);
|
|
340
|
+
if (result.receipt.invocationId !== descriptor.invocationId)
|
|
341
|
+
throw new Error("Supervisor receipt invocation mismatch");
|
|
342
|
+
}
|
|
343
|
+
if ((result.state === "quiescent") !== Boolean(result.receipt))
|
|
344
|
+
throw new Error("Supervisor quiescence response lacks its receipt");
|
|
345
|
+
return result;
|
|
346
|
+
});
|
|
214
347
|
}
|
|
215
348
|
|
|
216
349
|
async read(
|
|
217
350
|
command: ModalProviderCommand,
|
|
218
|
-
|
|
351
|
+
waitMs: number,
|
|
219
352
|
signal?: AbortSignal,
|
|
220
353
|
): Promise<ModalProviderOutputPage> {
|
|
221
|
-
|
|
222
|
-
this.
|
|
354
|
+
SandboxProviderCommand.parse(command);
|
|
355
|
+
if (command.sandboxId !== this.sandboxId)
|
|
356
|
+
throw new Error("Modal command does not belong to this sandbox");
|
|
357
|
+
if (command.kind === "modal-control-v1") return await this.legacy.read(command, waitMs, signal);
|
|
223
358
|
const next = structuredClone(command);
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
359
|
+
const cancellation = new AbortController();
|
|
360
|
+
const abort = () => cancellation.abort(signal?.reason);
|
|
361
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
362
|
+
if (signal?.aborted) abort();
|
|
363
|
+
try {
|
|
364
|
+
return await this.withRouter(command.taskId, cancellation.signal, async (router) => {
|
|
365
|
+
const operations = [
|
|
366
|
+
...(["stdout", "stderr"] as const).map(async (stream) =>
|
|
367
|
+
command.streams[stream].eof
|
|
368
|
+
? { bytes: Buffer.alloc(0), eof: true }
|
|
369
|
+
: await router.read(
|
|
370
|
+
command,
|
|
371
|
+
stream,
|
|
372
|
+
command.streams[stream].byteOffset,
|
|
373
|
+
waitMs,
|
|
374
|
+
cancellation.signal,
|
|
375
|
+
),
|
|
376
|
+
),
|
|
377
|
+
router.poll(command, cancellation.signal),
|
|
378
|
+
] as const;
|
|
379
|
+
const results = await Promise.allSettled(
|
|
380
|
+
operations.map((operation) =>
|
|
381
|
+
operation.catch((error) => {
|
|
382
|
+
cancellation.abort(error);
|
|
383
|
+
throw error;
|
|
384
|
+
}),
|
|
385
|
+
),
|
|
386
|
+
);
|
|
387
|
+
const failed = results.find((result) => result.status === "rejected");
|
|
388
|
+
if (failed?.status === "rejected") throw failed.reason;
|
|
389
|
+
const stdout = (results[0] as PromiseFulfilledResult<{ bytes: Buffer; eof: boolean }>)
|
|
390
|
+
.value;
|
|
391
|
+
const stderr = (results[1] as PromiseFulfilledResult<{ bytes: Buffer; eof: boolean }>)
|
|
392
|
+
.value;
|
|
393
|
+
const exit = (results[2] as PromiseFulfilledResult<number | null>).value;
|
|
394
|
+
const chunks: ModalProviderOutputPage["chunks"] = [];
|
|
395
|
+
for (const [stream, page] of [
|
|
396
|
+
["stdout", stdout],
|
|
397
|
+
["stderr", stderr],
|
|
398
|
+
] as const) {
|
|
399
|
+
const old = command.streams[stream];
|
|
400
|
+
const decoded = decodePage(old.utf8Remainder, [page.bytes], page.eof);
|
|
401
|
+
const byteOffset = old.byteOffset + page.bytes.length;
|
|
402
|
+
if (!Number.isSafeInteger(byteOffset)) throw new Error("Modal output offset exhausted");
|
|
252
403
|
next.streams[stream] = {
|
|
253
|
-
|
|
404
|
+
byteOffset,
|
|
254
405
|
utf8Remainder: decoded.remainder,
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
return {
|
|
258
|
-
stream,
|
|
259
|
-
chunkId: `modal:${command.execId}:${stream}:${cursor.batchIndex}:${batch.batchIndex}`,
|
|
260
|
-
text: decoded.text,
|
|
406
|
+
eof: page.eof,
|
|
407
|
+
exitCode: page.eof ? exit : null,
|
|
261
408
|
};
|
|
409
|
+
if (decoded.text)
|
|
410
|
+
chunks.push({
|
|
411
|
+
stream,
|
|
412
|
+
chunkId: `modal-router:${command.execId}:${stream}:${old.byteOffset}:${byteOffset}:${page.eof ? 1 : 0}`,
|
|
413
|
+
text: decoded.text,
|
|
414
|
+
});
|
|
262
415
|
}
|
|
263
|
-
return
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
exitCode: stdoutExit !== null && stderrExit !== null ? stdoutExit : null,
|
|
275
|
-
};
|
|
416
|
+
return {
|
|
417
|
+
command: next,
|
|
418
|
+
expected: structuredClone(command),
|
|
419
|
+
chunks,
|
|
420
|
+
exitCode: next.streams.stdout.eof && next.streams.stderr.eof ? exit : null,
|
|
421
|
+
streamFidelity: command.pty ? "merged" : "separate",
|
|
422
|
+
};
|
|
423
|
+
});
|
|
424
|
+
} finally {
|
|
425
|
+
signal?.removeEventListener("abort", abort);
|
|
426
|
+
}
|
|
276
427
|
}
|
|
277
428
|
|
|
278
|
-
async
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
await this.
|
|
284
|
-
{
|
|
285
|
-
execId: command.execId,
|
|
286
|
-
input: { message: Buffer.from(chars), messageIndex, eof: false },
|
|
287
|
-
},
|
|
288
|
-
{ retries: 0 },
|
|
289
|
-
);
|
|
429
|
+
async readProbe(
|
|
430
|
+
command: ModalProviderCommand,
|
|
431
|
+
waitMs: number,
|
|
432
|
+
cancellation: AbortController,
|
|
433
|
+
): Promise<ModalProviderOutputPage> {
|
|
434
|
+
return await this.read(command, waitMs, cancellation.signal);
|
|
290
435
|
}
|
|
291
436
|
|
|
292
|
-
|
|
437
|
+
async write(command: ModalProviderCommand, chars: string, index: number): Promise<void> {
|
|
293
438
|
SandboxProviderCommand.parse(command);
|
|
294
|
-
if (
|
|
295
|
-
command
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
)
|
|
300
|
-
throw new Error("Modal command locator does not match the original sandbox");
|
|
439
|
+
if (command.sandboxId !== this.sandboxId)
|
|
440
|
+
throw new Error("Modal command does not belong to this sandbox");
|
|
441
|
+
if (command.kind === "modal-control-v1") return await this.legacy.write(command, chars, index);
|
|
442
|
+
await this.withRouter(command.taskId, undefined, (router) =>
|
|
443
|
+
router.write(command, index, Buffer.from(chars)),
|
|
444
|
+
);
|
|
301
445
|
}
|
|
302
446
|
}
|