@botlearn-course/daemon 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -7
- package/dist/agent-service-client.js +8 -8
- package/dist/agent-service-sandbox.d.ts +108 -0
- package/dist/agent-service-sandbox.js +1400 -0
- package/dist/agent-service-ws-protocol.d.ts +15 -7
- package/dist/agent-service-ws-protocol.js +86 -24
- package/dist/cli.js +26 -53
- package/dist/course-client.js +9 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/run-dispatcher.d.ts +3 -0
- package/dist/run-dispatcher.js +108 -38
- package/dist/runtime-env.d.ts +14 -6
- package/dist/runtime-env.js +69 -8
- package/dist/runtime-profile.js +12 -1
- package/dist/runtimes/deepseek-tui.d.ts +3 -2
- package/dist/runtimes/deepseek-tui.js +43 -12
- package/dist/runtimes/engine.js +21 -14
- package/dist/runtimes/fake.js +102 -6
- package/dist/runtimes/progress.js +2 -1
- package/dist/sandbox-supervisor.d.ts +10 -1
- package/dist/sandbox-supervisor.js +59 -28
- package/dist/transcript.d.ts +2 -1
- package/dist/transcript.js +6 -0
- package/dist/types.d.ts +5 -2
- package/dist/types.js +1 -1
- package/dist/workspace.d.ts +27 -4
- package/dist/workspace.js +96 -23
- package/package.json +1 -1
- package/dist/agent-service-session.d.ts +0 -67
- package/dist/agent-service-session.js +0 -799
|
@@ -1,28 +1,36 @@
|
|
|
1
|
-
export declare const AGENT_SERVICE_WS_SCHEMA: "botlearn-agent-sandbox-ws/0.
|
|
2
|
-
export declare const AGENT_SERVICE_WS_SUBPROTOCOL: "botlearn-agent-sandbox.
|
|
3
|
-
export type SandboxFrameType = "
|
|
1
|
+
export declare const AGENT_SERVICE_WS_SCHEMA: "botlearn-agent-sandbox-ws/0.2";
|
|
2
|
+
export declare const AGENT_SERVICE_WS_SUBPROTOCOL: "botlearn-agent-sandbox.v2";
|
|
3
|
+
export type SandboxFrameType = "sandbox.hello" | "sandbox.sync" | "session.open" | "session.activate" | "turn.start" | "turn.cancel" | "session.close" | "sandbox.drain" | "sandbox.shutdown" | "event.ack" | "turn.file.upload_grant" | "auth.rotate" | "ping" | "sandbox.ready" | "sandbox.heartbeat" | "command.ack" | "session.opened" | "session.closed" | "turn.event" | "turn.file.prepare" | "turn.file.committed" | "sandbox.drained" | "pong" | "protocol.error";
|
|
4
|
+
export declare class UnsupportedSandboxProtocolError extends Error {
|
|
5
|
+
readonly schemaVersion: unknown;
|
|
6
|
+
constructor(schemaVersion: unknown);
|
|
7
|
+
}
|
|
4
8
|
export interface SandboxFrame {
|
|
5
9
|
schema_version: typeof AGENT_SERVICE_WS_SCHEMA;
|
|
6
10
|
type: SandboxFrameType;
|
|
7
11
|
frame_id: string;
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
sandbox_id: string;
|
|
13
|
+
sandbox_generation: number;
|
|
10
14
|
connection_epoch: number;
|
|
11
15
|
seq: number;
|
|
12
16
|
sent_at: string;
|
|
17
|
+
runtime_session_id: string | null;
|
|
13
18
|
agent_run_id: string | null;
|
|
14
19
|
worker_attempt: number | null;
|
|
20
|
+
activation_id: string | null;
|
|
15
21
|
payload: Record<string, unknown>;
|
|
16
22
|
}
|
|
17
23
|
export declare function parseSandboxFrame(raw: string, maxBytes?: number): SandboxFrame;
|
|
18
24
|
export declare function createSandboxFrame(input: {
|
|
19
25
|
type: SandboxFrameType;
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
sandboxId: string;
|
|
27
|
+
sandboxGeneration: number;
|
|
22
28
|
connectionEpoch: number;
|
|
23
29
|
seq: number;
|
|
24
30
|
payload?: Record<string, unknown>;
|
|
31
|
+
runtimeSessionId?: string;
|
|
25
32
|
agentRunId?: string;
|
|
26
33
|
workerAttempt?: number;
|
|
34
|
+
activationId?: string;
|
|
27
35
|
frameId?: string;
|
|
28
36
|
}): SandboxFrame;
|
|
@@ -1,27 +1,41 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
export const AGENT_SERVICE_WS_SCHEMA = "botlearn-agent-sandbox-ws/0.
|
|
3
|
-
export const AGENT_SERVICE_WS_SUBPROTOCOL = "botlearn-agent-sandbox.
|
|
2
|
+
export const AGENT_SERVICE_WS_SCHEMA = "botlearn-agent-sandbox-ws/0.2";
|
|
3
|
+
export const AGENT_SERVICE_WS_SUBPROTOCOL = "botlearn-agent-sandbox.v2";
|
|
4
4
|
const FRAME_TYPES = new Set([
|
|
5
|
-
"
|
|
6
|
-
"
|
|
5
|
+
"sandbox.hello",
|
|
6
|
+
"sandbox.sync",
|
|
7
|
+
"session.open",
|
|
8
|
+
"session.activate",
|
|
7
9
|
"turn.start",
|
|
8
10
|
"turn.cancel",
|
|
9
|
-
"session.
|
|
10
|
-
"
|
|
11
|
+
"session.close",
|
|
12
|
+
"sandbox.drain",
|
|
13
|
+
"sandbox.shutdown",
|
|
11
14
|
"event.ack",
|
|
12
15
|
"turn.file.upload_grant",
|
|
13
16
|
"auth.rotate",
|
|
14
17
|
"ping",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
18
|
+
"sandbox.ready",
|
|
19
|
+
"sandbox.heartbeat",
|
|
17
20
|
"command.ack",
|
|
21
|
+
"session.opened",
|
|
22
|
+
"session.closed",
|
|
18
23
|
"turn.event",
|
|
19
24
|
"turn.file.prepare",
|
|
20
25
|
"turn.file.committed",
|
|
21
|
-
"
|
|
26
|
+
"sandbox.drained",
|
|
22
27
|
"pong",
|
|
23
28
|
"protocol.error",
|
|
24
29
|
]);
|
|
30
|
+
/** SESSION 帧:必须带 runtime_session_id;禁止 run/attempt。session.activate 额外必须带 activation_id。 */
|
|
31
|
+
const SESSION_TYPES = new Set([
|
|
32
|
+
"session.open",
|
|
33
|
+
"session.activate",
|
|
34
|
+
"session.close",
|
|
35
|
+
"session.opened",
|
|
36
|
+
"session.closed",
|
|
37
|
+
]);
|
|
38
|
+
/** TURN 帧:必须带 runtime_session_id + agent_run_id + worker_attempt + activation_id 四元组。 */
|
|
25
39
|
const TURN_TYPES = new Set([
|
|
26
40
|
"turn.start",
|
|
27
41
|
"turn.cancel",
|
|
@@ -35,22 +49,39 @@ const FRAME_KEYS = new Set([
|
|
|
35
49
|
"schema_version",
|
|
36
50
|
"type",
|
|
37
51
|
"frame_id",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
52
|
+
"sandbox_id",
|
|
53
|
+
"sandbox_generation",
|
|
40
54
|
"connection_epoch",
|
|
41
55
|
"seq",
|
|
42
56
|
"sent_at",
|
|
57
|
+
"runtime_session_id",
|
|
43
58
|
"agent_run_id",
|
|
44
59
|
"worker_attempt",
|
|
60
|
+
"activation_id",
|
|
45
61
|
"payload",
|
|
46
62
|
]);
|
|
47
63
|
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
64
|
+
export class UnsupportedSandboxProtocolError extends Error {
|
|
65
|
+
schemaVersion;
|
|
66
|
+
constructor(schemaVersion) {
|
|
67
|
+
super("unsupported sandbox WebSocket schema");
|
|
68
|
+
this.schemaVersion = schemaVersion;
|
|
69
|
+
this.name = "UnsupportedSandboxProtocolError";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
48
72
|
function positiveInteger(value, label) {
|
|
49
73
|
if (!Number.isInteger(value) || value < 1) {
|
|
50
74
|
throw new Error(`${label} must be a positive integer`);
|
|
51
75
|
}
|
|
52
76
|
return value;
|
|
53
77
|
}
|
|
78
|
+
function nullableString(value, label) {
|
|
79
|
+
if (value === null || value === undefined)
|
|
80
|
+
return null;
|
|
81
|
+
if (typeof value !== "string")
|
|
82
|
+
throw new Error(`${label} must be a string or null`);
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
54
85
|
export function parseSandboxFrame(raw, maxBytes = 262_144) {
|
|
55
86
|
if (Buffer.byteLength(raw, "utf8") > maxBytes)
|
|
56
87
|
throw new Error("frame exceeds size limit");
|
|
@@ -64,33 +95,60 @@ export function parseSandboxFrame(raw, maxBytes = 262_144) {
|
|
|
64
95
|
throw new Error(`unknown sandbox WebSocket frame field: ${key}`);
|
|
65
96
|
}
|
|
66
97
|
if (value.schema_version !== AGENT_SERVICE_WS_SCHEMA) {
|
|
67
|
-
throw new
|
|
98
|
+
throw new UnsupportedSandboxProtocolError(value.schema_version);
|
|
68
99
|
}
|
|
69
100
|
if (typeof value.type !== "string" || !FRAME_TYPES.has(value.type)) {
|
|
70
101
|
throw new Error("unknown sandbox WebSocket frame type");
|
|
71
102
|
}
|
|
72
103
|
const type = value.type;
|
|
73
|
-
const
|
|
104
|
+
const runtimeSessionId = nullableString(value.runtime_session_id, "runtime_session_id");
|
|
105
|
+
const agentRunId = nullableString(value.agent_run_id, "agent_run_id");
|
|
74
106
|
const workerAttempt = value.worker_attempt == null
|
|
75
107
|
? null
|
|
76
108
|
: positiveInteger(value.worker_attempt, "worker_attempt");
|
|
77
|
-
|
|
78
|
-
|
|
109
|
+
const activationId = nullableString(value.activation_id, "activation_id");
|
|
110
|
+
if (activationId !== null && (activationId.length < 1 || activationId.length > 120)) {
|
|
111
|
+
throw new Error("activation_id length is invalid");
|
|
112
|
+
}
|
|
113
|
+
if (TURN_TYPES.has(type)) {
|
|
114
|
+
if (!runtimeSessionId || !agentRunId || workerAttempt === null || !activationId) {
|
|
115
|
+
throw new Error(`${type} requires the full turn scope`);
|
|
116
|
+
}
|
|
79
117
|
}
|
|
80
|
-
if (
|
|
81
|
-
|
|
118
|
+
else if (SESSION_TYPES.has(type)) {
|
|
119
|
+
if (!runtimeSessionId)
|
|
120
|
+
throw new Error(`${type} requires runtime_session_id`);
|
|
121
|
+
if (agentRunId !== null || workerAttempt !== null) {
|
|
122
|
+
throw new Error(`${type} cannot carry turn fencing fields`);
|
|
123
|
+
}
|
|
124
|
+
if (type === "session.activate") {
|
|
125
|
+
if (!activationId)
|
|
126
|
+
throw new Error("session.activate requires activation_id");
|
|
127
|
+
}
|
|
128
|
+
else if (activationId !== null) {
|
|
129
|
+
throw new Error(`${type} cannot carry activation_id`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else if (runtimeSessionId !== null ||
|
|
133
|
+
agentRunId !== null ||
|
|
134
|
+
workerAttempt !== null ||
|
|
135
|
+
activationId !== null) {
|
|
136
|
+
throw new Error(`${type} cannot carry session or turn scope fields`);
|
|
82
137
|
}
|
|
83
138
|
if (!value.payload || typeof value.payload !== "object" || Array.isArray(value.payload)) {
|
|
84
139
|
throw new Error("frame payload must be an object");
|
|
85
140
|
}
|
|
86
|
-
for (const key of ["frame_id", "
|
|
141
|
+
for (const key of ["frame_id", "sandbox_id", "sent_at"]) {
|
|
87
142
|
if (typeof value[key] !== "string" || !value[key])
|
|
88
143
|
throw new Error(`${key} is required`);
|
|
89
144
|
}
|
|
90
145
|
if (value.frame_id.length > 120)
|
|
91
146
|
throw new Error("frame_id is too long");
|
|
92
|
-
if (!UUID_PATTERN.test(value.
|
|
93
|
-
throw new Error("
|
|
147
|
+
if (!UUID_PATTERN.test(value.sandbox_id))
|
|
148
|
+
throw new Error("sandbox_id must be a UUID");
|
|
149
|
+
if (runtimeSessionId !== null && !UUID_PATTERN.test(runtimeSessionId)) {
|
|
150
|
+
throw new Error("runtime_session_id must be a UUID");
|
|
151
|
+
}
|
|
94
152
|
if (agentRunId !== null && !UUID_PATTERN.test(agentRunId)) {
|
|
95
153
|
throw new Error("agent_run_id must be a UUID");
|
|
96
154
|
}
|
|
@@ -100,13 +158,15 @@ export function parseSandboxFrame(raw, maxBytes = 262_144) {
|
|
|
100
158
|
schema_version: AGENT_SERVICE_WS_SCHEMA,
|
|
101
159
|
type,
|
|
102
160
|
frame_id: value.frame_id,
|
|
103
|
-
|
|
104
|
-
|
|
161
|
+
sandbox_id: value.sandbox_id,
|
|
162
|
+
sandbox_generation: positiveInteger(value.sandbox_generation, "sandbox_generation"),
|
|
105
163
|
connection_epoch: positiveInteger(value.connection_epoch, "connection_epoch"),
|
|
106
164
|
seq: positiveInteger(value.seq, "seq"),
|
|
107
165
|
sent_at: value.sent_at,
|
|
166
|
+
runtime_session_id: runtimeSessionId,
|
|
108
167
|
agent_run_id: agentRunId,
|
|
109
168
|
worker_attempt: workerAttempt,
|
|
169
|
+
activation_id: activationId,
|
|
110
170
|
payload: value.payload,
|
|
111
171
|
};
|
|
112
172
|
}
|
|
@@ -115,13 +175,15 @@ export function createSandboxFrame(input) {
|
|
|
115
175
|
schema_version: AGENT_SERVICE_WS_SCHEMA,
|
|
116
176
|
type: input.type,
|
|
117
177
|
frame_id: input.frameId ?? `frm_${randomUUID().replaceAll("-", "")}`,
|
|
118
|
-
|
|
119
|
-
|
|
178
|
+
sandbox_id: input.sandboxId,
|
|
179
|
+
sandbox_generation: input.sandboxGeneration,
|
|
120
180
|
connection_epoch: input.connectionEpoch,
|
|
121
181
|
seq: input.seq,
|
|
122
182
|
sent_at: new Date().toISOString(),
|
|
183
|
+
runtime_session_id: input.runtimeSessionId ?? null,
|
|
123
184
|
agent_run_id: input.agentRunId ?? null,
|
|
124
185
|
worker_attempt: input.workerAttempt ?? null,
|
|
186
|
+
activation_id: input.activationId ?? null,
|
|
125
187
|
payload: input.payload ?? {},
|
|
126
188
|
};
|
|
127
189
|
return parseSandboxFrame(JSON.stringify(frame));
|
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ import { hostname } from "node:os";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { authFilePath, clearAuth, machineFingerprint, readAuth, writeAuth } from "./auth-store.js";
|
|
7
7
|
import { AgentServiceRunClient } from "./agent-service-client.js";
|
|
8
|
-
import {
|
|
8
|
+
import { AgentServiceSandboxClient } from "./agent-service-sandbox.js";
|
|
9
9
|
import { CourseClient, isAuthFailure, loginCourseDaemon } from "./course-client.js";
|
|
10
10
|
import { runCourseDoctor } from "./doctor.js";
|
|
11
11
|
import { log } from "./log.js";
|
|
@@ -16,16 +16,6 @@ import { clearAgentServiceControlEnv } from "./runtime-env.js";
|
|
|
16
16
|
import { RUNTIME_MODULES, detectAvailableRuntimeIds, fakeRuntimeEnabled, } from "./runtimes/index.js";
|
|
17
17
|
const pkg = createRequire(import.meta.url)("../package.json");
|
|
18
18
|
const SESSION_BOOTSTRAP_MAX_BYTES = 64 * 1024;
|
|
19
|
-
const SESSION_RUNTIME_ENV_KEYS = new Set([
|
|
20
|
-
"OPENAI_API_KEY",
|
|
21
|
-
"OPENAI_BASE_URL",
|
|
22
|
-
"ANTHROPIC_API_KEY",
|
|
23
|
-
"ANTHROPIC_BASE_URL",
|
|
24
|
-
"DEEPSEEK_API_KEY",
|
|
25
|
-
"DEEPSEEK_BASE_URL",
|
|
26
|
-
"GEMINI_API_KEY",
|
|
27
|
-
"GEMINI_BASE_URL",
|
|
28
|
-
]);
|
|
29
19
|
const HELP = `botlearn-course-daemon ${pkg.version} — run BotLearn Course tasks on your own machine (BYOA)
|
|
30
20
|
|
|
31
21
|
Usage:
|
|
@@ -57,8 +47,8 @@ Agent Service sandbox environment:
|
|
|
57
47
|
BOTLEARN_AGENT_SERVICE_RUN_TOKEN
|
|
58
48
|
BOTLEARN_AGENT_SERVICE_WORKER_ID
|
|
59
49
|
BOTLEARN_AGENT_SERVICE_WS_URL
|
|
60
|
-
|
|
61
|
-
|
|
50
|
+
BOTLEARN_AGENT_SERVICE_SANDBOX_ID
|
|
51
|
+
BOTLEARN_AGENT_SERVICE_SANDBOX_TOKEN`;
|
|
62
52
|
// ---------------------------------------------------------------
|
|
63
53
|
// flag parser(极简:--k v / --k=v / 布尔开关)
|
|
64
54
|
// ---------------------------------------------------------------
|
|
@@ -304,56 +294,41 @@ async function cmdAgentServiceRun() {
|
|
|
304
294
|
}
|
|
305
295
|
return 0;
|
|
306
296
|
}
|
|
307
|
-
async function
|
|
297
|
+
async function readSandboxBootstrapFromStdin() {
|
|
308
298
|
const chunks = [];
|
|
309
299
|
let size = 0;
|
|
310
300
|
for await (const chunk of process.stdin) {
|
|
311
301
|
const bytes = Buffer.from(chunk);
|
|
312
302
|
size += bytes.length;
|
|
313
303
|
if (size > SESSION_BOOTSTRAP_MAX_BYTES) {
|
|
314
|
-
throw new Error("Agent Service
|
|
304
|
+
throw new Error("Agent Service sandbox bootstrap exceeds size limit");
|
|
315
305
|
}
|
|
316
306
|
chunks.push(bytes);
|
|
317
307
|
}
|
|
318
308
|
const raw = Buffer.concat(chunks);
|
|
319
309
|
try {
|
|
320
310
|
const parsed = JSON.parse(raw.toString("utf8"));
|
|
321
|
-
if (parsed.
|
|
322
|
-
throw new Error("Agent Service
|
|
311
|
+
if (parsed.schema !== "agent-service-sandbox-bootstrap/0.2") {
|
|
312
|
+
throw new Error("Agent Service sandbox bootstrap schema is unsupported");
|
|
323
313
|
}
|
|
324
|
-
const runtimeEnv = {};
|
|
325
314
|
if (parsed.runtimeEnv !== undefined) {
|
|
326
|
-
|
|
327
|
-
typeof parsed.runtimeEnv !== "object" ||
|
|
328
|
-
Array.isArray(parsed.runtimeEnv)) {
|
|
329
|
-
throw new Error("Agent Service session bootstrap runtimeEnv is invalid");
|
|
330
|
-
}
|
|
331
|
-
for (const [key, value] of Object.entries(parsed.runtimeEnv)) {
|
|
332
|
-
if (!SESSION_RUNTIME_ENV_KEYS.has(key) ||
|
|
333
|
-
typeof value !== "string" ||
|
|
334
|
-
value.length < 1 ||
|
|
335
|
-
value.length > 8192) {
|
|
336
|
-
throw new Error(`Agent Service session bootstrap runtime env is forbidden: ${key}`);
|
|
337
|
-
}
|
|
338
|
-
runtimeEnv[key] = value;
|
|
339
|
-
}
|
|
315
|
+
throw new Error("Agent Service sandbox bootstrap runtimeEnv is unsupported; use session.activate");
|
|
340
316
|
}
|
|
341
317
|
const wsUrl = parsed.wsUrl;
|
|
342
|
-
const
|
|
343
|
-
const
|
|
318
|
+
const sandboxId = parsed.sandboxId;
|
|
319
|
+
const sandboxToken = parsed.sandboxToken;
|
|
344
320
|
if (typeof wsUrl !== "string" ||
|
|
345
321
|
!/^wss?:\/\//.test(wsUrl) ||
|
|
346
|
-
typeof
|
|
347
|
-
!
|
|
348
|
-
typeof
|
|
349
|
-
!
|
|
350
|
-
throw new Error("Agent Service
|
|
322
|
+
typeof sandboxId !== "string" ||
|
|
323
|
+
!sandboxId ||
|
|
324
|
+
typeof sandboxToken !== "string" ||
|
|
325
|
+
!sandboxToken) {
|
|
326
|
+
throw new Error("Agent Service sandbox bootstrap is incomplete");
|
|
351
327
|
}
|
|
352
328
|
return {
|
|
353
329
|
wsUrl,
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
...(Object.keys(runtimeEnv).length > 0 ? { runtimeEnv } : {}),
|
|
330
|
+
sandboxId,
|
|
331
|
+
sandboxToken,
|
|
357
332
|
};
|
|
358
333
|
}
|
|
359
334
|
finally {
|
|
@@ -364,16 +339,15 @@ async function readSessionBootstrapFromStdin() {
|
|
|
364
339
|
}
|
|
365
340
|
async function cmdAgentServiceSession(args) {
|
|
366
341
|
const bootstrap = args.flags["bootstrap-stdin"] === true
|
|
367
|
-
? await
|
|
342
|
+
? await readSandboxBootstrapFromStdin()
|
|
368
343
|
: {
|
|
369
344
|
wsUrl: process.env.BOTLEARN_AGENT_SERVICE_WS_URL,
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
runtimeEnv: undefined,
|
|
345
|
+
sandboxId: process.env.BOTLEARN_AGENT_SERVICE_SANDBOX_ID,
|
|
346
|
+
sandboxToken: process.env.BOTLEARN_AGENT_SERVICE_SANDBOX_TOKEN,
|
|
373
347
|
};
|
|
374
|
-
const { wsUrl,
|
|
375
|
-
if (!wsUrl || !
|
|
376
|
-
console.error("Agent Service sandbox
|
|
348
|
+
const { wsUrl, sandboxId, sandboxToken } = bootstrap;
|
|
349
|
+
if (!wsUrl || !sandboxId || !sandboxToken) {
|
|
350
|
+
console.error("Agent Service sandbox environment is incomplete");
|
|
377
351
|
return 1;
|
|
378
352
|
}
|
|
379
353
|
augmentProcessPath();
|
|
@@ -383,11 +357,10 @@ async function cmdAgentServiceSession(args) {
|
|
|
383
357
|
continue;
|
|
384
358
|
runtimes.set(mod.id, mod.create());
|
|
385
359
|
}
|
|
386
|
-
const client = new
|
|
360
|
+
const client = new AgentServiceSandboxClient({
|
|
387
361
|
wsUrl,
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
...(bootstrap.runtimeEnv ? { runtimeEnv: bootstrap.runtimeEnv } : {}),
|
|
362
|
+
sandboxId,
|
|
363
|
+
sandboxToken,
|
|
391
364
|
runtimes,
|
|
392
365
|
daemonVersion: pkg.version,
|
|
393
366
|
});
|
package/dist/course-client.js
CHANGED
|
@@ -43,11 +43,11 @@ export class CourseClient {
|
|
|
43
43
|
setAccessToken(token) {
|
|
44
44
|
this.accessToken = token;
|
|
45
45
|
}
|
|
46
|
-
// courseApiUrl 可能是 host 根,也可能已带 /
|
|
46
|
+
// courseApiUrl 可能是 host 根,也可能已带 /course/v1 —— 去重避免拼出 /course/v1/course/v1。
|
|
47
47
|
url(p) {
|
|
48
48
|
const base = this.baseUrl.replace(/\/+$/, "");
|
|
49
|
-
if (base.endsWith("/
|
|
50
|
-
return `${base}${p.slice("/
|
|
49
|
+
if (base.endsWith("/course/v1") && p.startsWith("/course/v1/")) {
|
|
50
|
+
return `${base}${p.slice("/course/v1".length)}`;
|
|
51
51
|
}
|
|
52
52
|
return `${base}${p}`;
|
|
53
53
|
}
|
|
@@ -98,18 +98,18 @@ export class CourseClient {
|
|
|
98
98
|
}
|
|
99
99
|
/** 领取下一个分配给本 daemon 的 queued run(无则返回 null)。 */
|
|
100
100
|
async claimNextRun() {
|
|
101
|
-
return this.request("GET", "/
|
|
101
|
+
return this.request("GET", "/course/v1/daemon/runs/next");
|
|
102
102
|
}
|
|
103
103
|
async postEvent(agentRunId, event) {
|
|
104
104
|
const credentials = [this.accessToken, this.refreshToken].filter((value) => typeof value === "string");
|
|
105
105
|
const sanitized = redactSecretsDeep(event, 8, credentials);
|
|
106
|
-
await this.request("POST", `/
|
|
106
|
+
await this.request("POST", `/course/v1/daemon/runs/${agentRunId}/events`, sanitized, event.trace_id);
|
|
107
107
|
}
|
|
108
108
|
async postFile(agentRunId, file) {
|
|
109
|
-
return this.request("POST", `/
|
|
109
|
+
return this.request("POST", `/course/v1/daemon/runs/${agentRunId}/files`, file);
|
|
110
110
|
}
|
|
111
111
|
async getRunRuntimeProfile(agentRunId) {
|
|
112
|
-
return this.request("GET", `/
|
|
112
|
+
return this.request("GET", `/course/v1/daemon/runs/${agentRunId}/runtime-profile`);
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
function authFromResponse(baseUrl, raw) {
|
|
@@ -131,7 +131,7 @@ function authFromResponse(baseUrl, raw) {
|
|
|
131
131
|
}
|
|
132
132
|
export async function loginCourseDaemon(opts) {
|
|
133
133
|
const client = new CourseClient(opts.courseApiUrl, "");
|
|
134
|
-
const raw = await client.fetchJson("POST", "/
|
|
134
|
+
const raw = await client.fetchJson("POST", "/course/v1/daemon/login", {
|
|
135
135
|
code: opts.code,
|
|
136
136
|
label: opts.label ?? "",
|
|
137
137
|
machine_fingerprint: opts.machineFingerprint,
|
|
@@ -141,6 +141,6 @@ export async function loginCourseDaemon(opts) {
|
|
|
141
141
|
}
|
|
142
142
|
export async function refreshCourseDaemonSession(courseApiUrl, refreshToken) {
|
|
143
143
|
const client = new CourseClient(courseApiUrl, "");
|
|
144
|
-
const raw = await client.fetchJson("POST", "/
|
|
144
|
+
const raw = await client.fetchJson("POST", "/course/v1/daemon/session", { refresh_token: refreshToken }, null);
|
|
145
145
|
return authFromResponse(courseApiUrl, raw);
|
|
146
146
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export * from "./types.js";
|
|
|
6
6
|
export * from "./auth-store.js";
|
|
7
7
|
export * from "./course-client.js";
|
|
8
8
|
export * from "./agent-service-client.js";
|
|
9
|
-
export * from "./agent-service-
|
|
9
|
+
export * from "./agent-service-sandbox.js";
|
|
10
10
|
export * from "./agent-service-ws-protocol.js";
|
|
11
11
|
export * from "./run-dispatcher.js";
|
|
12
12
|
export * from "./run-queue.js";
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ export * from "./types.js";
|
|
|
6
6
|
export * from "./auth-store.js";
|
|
7
7
|
export * from "./course-client.js";
|
|
8
8
|
export * from "./agent-service-client.js";
|
|
9
|
-
export * from "./agent-service-
|
|
9
|
+
export * from "./agent-service-sandbox.js";
|
|
10
10
|
export * from "./agent-service-ws-protocol.js";
|
|
11
11
|
export * from "./run-dispatcher.js";
|
|
12
12
|
export * from "./run-queue.js";
|
package/dist/run-dispatcher.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export interface PreparedPersistentTurn {
|
|
|
18
18
|
export interface PersistentSessionExecution {
|
|
19
19
|
prepareTurn(payload: RunStartPayload): PreparedPersistentTurn;
|
|
20
20
|
persistNativeSession(sessionId: string): void;
|
|
21
|
+
finishTurn(payload: RunStartPayload): void;
|
|
21
22
|
}
|
|
22
23
|
export interface RunReportingClient {
|
|
23
24
|
postEvent(agentRunId: string, event: RunEvent): Promise<void>;
|
|
@@ -52,6 +53,8 @@ export declare class RunDispatcher {
|
|
|
52
53
|
cancel(agentRunId: string): boolean;
|
|
53
54
|
cancelAll(): void;
|
|
54
55
|
get activeCount(): number;
|
|
56
|
+
/** Wait until the selected runs have left both the active and queued sets. */
|
|
57
|
+
waitForRuns(agentRunIds: Iterable<string>, timeoutMs: number): Promise<boolean>;
|
|
55
58
|
/** 等待所有 run(含排队中的)结束;超时返回 false。 */
|
|
56
59
|
drain(timeoutMs: number): Promise<boolean>;
|
|
57
60
|
private execute;
|