@botlearn-course/daemon 0.0.20-beta.13 → 0.0.20-beta.14
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
CHANGED
|
@@ -48,10 +48,10 @@ daemon,由 root-owned 固定 launcher 清空附加组后直接降权为 `user`
|
|
|
48
48
|
课程任务或 workspace shell。
|
|
49
49
|
|
|
50
50
|
veFaaS Origin 的托管 workspace 使用用户级 NAS backing image。root bootstrap 完成 NFS、marker、
|
|
51
|
-
ext4
|
|
51
|
+
ext4、`fuse2fs` mount 和固定 3 GiB 校验后,daemon 才发送 `sandbox.ready`。RuntimeSession 使用稳定路径
|
|
52
52
|
`/workspace/sessions/{runtime_session_id}/workspace`,Sandbox generation 只参与写入隔离。磁盘写满
|
|
53
53
|
统一上报 `workspace_user_quota_exceeded`。pause 和 cleanup 进入 drain 后,daemon 先停止 runtime
|
|
54
|
-
writer,再通过 root launcher 执行 ext4 sync
|
|
54
|
+
writer,再通过 root launcher 执行 ext4 sync、`fuse2fs` unmount、backing image 和 NFS parent fsync;完成后
|
|
55
55
|
才发送 `sandbox.drained` 并退出。
|
|
56
56
|
|
|
57
57
|
## 支持的 runtime
|
|
@@ -3,6 +3,8 @@ import type { WorkspaceScanMeasurement } from "./file-candidates.js";
|
|
|
3
3
|
import { type RuntimeSkillProviderFactory } from "./runtime-skills.js";
|
|
4
4
|
import { type PersistentSessionExecution, type PreparedPersistentTurn, type RunReportingClient } from "./run-dispatcher.js";
|
|
5
5
|
import type { CourseRuntime, CourseRuntimeProfile, RunEvent, RunEventReceipt, RunFileCandidate, RunFileRecord, RunStartPayload } from "./types.js";
|
|
6
|
+
export declare function workspaceDurabilityCapabilities(usesNasWorkspace?: boolean): string[];
|
|
7
|
+
export declare function assertWorkspaceRestoreAllowed(requestedRestore: unknown, usesNasWorkspace?: boolean): void;
|
|
6
8
|
export interface AgentServiceSandboxOptions {
|
|
7
9
|
wsUrl: string;
|
|
8
10
|
sandboxId: string;
|
|
@@ -4,7 +4,7 @@ import { Readable } from "node:stream";
|
|
|
4
4
|
import { pipeline } from "node:stream/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { ensureDaemonHome } from "./auth-store.js";
|
|
7
|
-
import { AGENT_SERVICE_WS_SCHEMA, AGENT_SERVICE_WS_SUBPROTOCOL, createSandboxFrame, createWorkspaceFileChunk, parseSandboxFrame, UnsupportedSandboxProtocolError, WORKSPACE_FILE_READ_BINARY_CAPABILITY,
|
|
7
|
+
import { AGENT_SERVICE_WS_SCHEMA, AGENT_SERVICE_WS_SUBPROTOCOL, createSandboxFrame, createWorkspaceFileChunk, parseSandboxFrame, UnsupportedSandboxProtocolError, WORKSPACE_FILE_READ_BINARY_CAPABILITY, } from "./agent-service-ws-protocol.js";
|
|
8
8
|
import { log as defaultLog, setOperationalLogSink, } from "./log.js";
|
|
9
9
|
import { availableRunCapabilities, runtimeSupportsCourseSkills, } from "./runtime-capabilities.js";
|
|
10
10
|
import { activationRuntimeEnv, runtimeChildEnv } from "./runtime-env.js";
|
|
@@ -41,6 +41,18 @@ function agentServiceHttpBase(wsUrl) {
|
|
|
41
41
|
}
|
|
42
42
|
const RUNTIME_LOG_WINDOW_MS = 60_000;
|
|
43
43
|
const WORKSPACE_FILE_TRANSFER_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
44
|
+
export function workspaceDurabilityCapabilities(usesNasWorkspace = nasWorkspaceEnabled()) {
|
|
45
|
+
return [
|
|
46
|
+
"workspace_writer_freeze_v1",
|
|
47
|
+
"workspace_snapshot_v2",
|
|
48
|
+
...(usesNasWorkspace ? ["workspace_nas_v1"] : ["workspace_restore_v1"]),
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
export function assertWorkspaceRestoreAllowed(requestedRestore, usesNasWorkspace = nasWorkspaceEnabled()) {
|
|
52
|
+
if (usesNasWorkspace && requestedRestore !== null && requestedRestore !== undefined) {
|
|
53
|
+
throw new Error("workspace_restore_forbidden_for_nas");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
44
56
|
const DISABLED_RUNTIME_LOG_POLICY = {
|
|
45
57
|
enabled: false,
|
|
46
58
|
maxEventBytes: 4096,
|
|
@@ -872,11 +884,7 @@ export class AgentServiceSandboxClient {
|
|
|
872
884
|
protocol_versions: [AGENT_SERVICE_WS_SCHEMA],
|
|
873
885
|
capabilities: [
|
|
874
886
|
WORKSPACE_FILE_READ_BINARY_CAPABILITY,
|
|
875
|
-
|
|
876
|
-
"workspace_snapshot_v2",
|
|
877
|
-
"workspace_restore_v1",
|
|
878
|
-
WORKSPACE_SNAPSHOT_ATTESTATION_CAPABILITY,
|
|
879
|
-
...(nasWorkspaceEnabled() ? ["workspace_nas_v1"] : []),
|
|
887
|
+
...workspaceDurabilityCapabilities(),
|
|
880
888
|
...(workspaceCopyV1Supported() ? ["workspace_copy_v1"] : []),
|
|
881
889
|
],
|
|
882
890
|
workspace_snapshot_policy: workspaceSnapshotPolicyCapability(),
|
|
@@ -1060,6 +1068,7 @@ export class AgentServiceSandboxClient {
|
|
|
1060
1068
|
const requestedWorkspaceRef = frame.payload.workspace_ref;
|
|
1061
1069
|
const requestedCopy = frame.payload.workspace_copy;
|
|
1062
1070
|
const requestedRestore = frame.payload.workspace_restore;
|
|
1071
|
+
assertWorkspaceRestoreAllowed(requestedRestore);
|
|
1063
1072
|
const requestedCopyRecord = requestedCopy && typeof requestedCopy === "object"
|
|
1064
1073
|
? requestedCopy
|
|
1065
1074
|
: undefined;
|
|
@@ -1345,10 +1354,14 @@ export class AgentServiceSandboxClient {
|
|
|
1345
1354
|
}
|
|
1346
1355
|
this.state.sessions[sessionId] = session;
|
|
1347
1356
|
this.persist();
|
|
1348
|
-
const
|
|
1357
|
+
const usesNasWorkspace = nasWorkspaceEnabled();
|
|
1358
|
+
const materialization = usesNasWorkspace
|
|
1359
|
+
? null
|
|
1360
|
+
: readWorkspaceMaterializationMarker(this.options.sandboxId, sessionId);
|
|
1349
1361
|
await this.sendSessionFrame("session.opened", sessionId, {
|
|
1350
1362
|
workspace_ref: session.workspaceRef,
|
|
1351
1363
|
native_session_id: session.nativeSessionId,
|
|
1364
|
+
workspace_authority: usesNasWorkspace ? "nas" : "snapshot",
|
|
1352
1365
|
...(materialization
|
|
1353
1366
|
? {
|
|
1354
1367
|
workspace_marker_version: 1,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export declare const AGENT_SERVICE_WS_SCHEMA: "botlearn-agent-sandbox-ws/0.4";
|
|
2
2
|
export declare const AGENT_SERVICE_WS_SUBPROTOCOL: "botlearn-agent-sandbox.v4";
|
|
3
3
|
export declare const WORKSPACE_FILE_READ_BINARY_CAPABILITY: "workspace_file_read_binary_v1";
|
|
4
|
-
export declare const WORKSPACE_SNAPSHOT_ATTESTATION_CAPABILITY: "workspace_snapshot_attestation_v1";
|
|
5
4
|
export declare const WORKSPACE_FILE_CHUNK_BYTES: number;
|
|
6
5
|
export declare const WORKSPACE_FILE_CHUNK_HEADER_BYTES = 29;
|
|
7
6
|
export type SandboxFrameType = "sandbox.hello" | "sandbox.sync" | "session.open" | "session.activate" | "turn.start" | "turn.cancel" | "session.close" | "sandbox.drain" | "sandbox.shutdown" | "event.ack" | "auth.rotate" | "workspace.file.read" | "workspace.checkpoint" | "workspace.thaw" | "ping" | "sandbox.ready" | "sandbox.heartbeat" | "command.ack" | "session.opened" | "session.open_failed" | "session.closed" | "turn.event" | "turn.file.report" | "workspace.file.result" | "sandbox.drained" | "sandbox.log" | "pong" | "protocol.error";
|
|
@@ -2,7 +2,6 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
export const AGENT_SERVICE_WS_SCHEMA = "botlearn-agent-sandbox-ws/0.4";
|
|
3
3
|
export const AGENT_SERVICE_WS_SUBPROTOCOL = "botlearn-agent-sandbox.v4";
|
|
4
4
|
export const WORKSPACE_FILE_READ_BINARY_CAPABILITY = "workspace_file_read_binary_v1";
|
|
5
|
-
export const WORKSPACE_SNAPSHOT_ATTESTATION_CAPABILITY = "workspace_snapshot_attestation_v1";
|
|
6
5
|
export const WORKSPACE_FILE_CHUNK_BYTES = 64 * 1024;
|
|
7
6
|
export const WORKSPACE_FILE_CHUNK_HEADER_BYTES = 29;
|
|
8
7
|
const WORKSPACE_FILE_CHUNK_MAGIC = Buffer.from("BLWF", "ascii");
|
package/package.json
CHANGED