@nowcrew/daemon 0.6.102 → 0.6.103
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/codex-home-launch.js +7 -0
- package/dist/codex-home-mode-controller.js +68 -0
- package/dist/daemon-migration-controller.js +210 -0
- package/dist/daemon-migration-wiring.js +22 -0
- package/dist/execution-runner.js +5 -2
- package/dist/local-executor.js +8 -1
- package/dist/project-skills/capability.js +109 -0
- package/dist/project-skills/controller-convergence.js +57 -0
- package/dist/project-skills/projection-set-switch.js +419 -0
- package/dist/project-skills/projection-state-domain.js +170 -0
- package/dist/project-skills/projection-state-store.js +842 -0
- package/dist/project-skills/projection-state-transaction.js +322 -0
- package/dist/project-skills/projection-state.js +3 -0
- package/dist/project-skills/runtime-root-bootstrap.js +47 -0
- package/dist/project-skills/runtime-root-domain.js +268 -0
- package/dist/project-skills/runtime-root-gc.js +293 -0
- package/dist/project-skills/runtime-root-lease-artifact.js +46 -0
- package/dist/project-skills/runtime-root-leases.js +487 -0
- package/dist/project-skills/runtime-root-source-identity.js +60 -0
- package/dist/project-skills/runtime-root-startup.js +49 -0
- package/dist/project-skills/runtime-root-state-artifact-domain.js +143 -0
- package/dist/project-skills/runtime-root-state-index.js +356 -0
- package/dist/project-skills/runtime-root-store.js +722 -0
- package/dist/project-skills/serve-capability.js +28 -0
- package/dist/project-skills/serve-startup.js +37 -0
- package/dist/runtime-observation.js +219 -0
- package/dist/runtimes/claude-observation-runner.js +55 -0
- package/dist/runtimes/claude-thread-runner.js +17 -3
- package/dist/runtimes/claude.js +5 -3
- package/dist/runtimes/codex-app-server-runner.js +16 -2
- package/dist/runtimes/codex-home-migration-cli.js +26 -0
- package/dist/runtimes/codex-home-migration.js +112 -0
- package/dist/runtimes/codex-home.js +236 -0
- package/dist/runtimes/deepseek-harness-runner.js +16 -0
- package/dist/runtimes/kimi-acp-runner.js +19 -0
- package/dist/runtimes/observation-context.js +18 -0
- package/dist/runtimes/observation-mapper.js +314 -0
- package/dist/runtimes/opencode-runner.js +18 -4
- package/dist/runtimes/pi-runner.js +17 -1
- package/dist/storage-control.js +212 -0
- package/dist/storage-copy.js +628 -0
- package/dist/storage-credential-guard.js +21 -0
- package/dist/storage-doctor.js +228 -0
- package/dist/storage-manifest.js +1027 -0
- package/dist/storage-migration-controller.js +441 -0
- package/dist/storage-migration-manifest.js +105 -0
- package/dist/storage-migration-policy.js +100 -0
- package/dist/storage-runtime-controller.js +361 -0
- package/dist/storage-runtime-readiness.js +206 -0
- package/dist/storage-safe-fs.js +302 -0
- package/dist/storage-status-delivery.js +224 -0
- package/dist/storage-status-replay.js +140 -0
- package/dist/storage-target-confirmation.js +129 -0
- package/dist/storage-worker-command.js +37 -0
- package/dist/storage-worker-executor.js +431 -0
- package/dist/storage-worker-handoff.js +151 -0
- package/dist/storage-worker-operation.js +288 -0
- package/dist/storage-worker-status.js +259 -0
- package/dist/token.js +4 -1
- package/package.json +1 -1
|
@@ -6,6 +6,8 @@ import spawn from "cross-spawn";
|
|
|
6
6
|
import { augmentedPath } from "../runtime-path.js";
|
|
7
7
|
import { prepareThreadDelivery } from "../thread-input-delivery.js";
|
|
8
8
|
import { buildOpenCodeArgs, createOpenCodeEventDecoder } from "./opencode.js";
|
|
9
|
+
import { createRuntimeObservationCapture } from "../runtime-observation.js";
|
|
10
|
+
import { createObservationMapper } from "./observation-mapper.js";
|
|
9
11
|
const ERROR_CAP = 2_000;
|
|
10
12
|
async function readPrompt() {
|
|
11
13
|
process.stdin.setEncoding("utf8");
|
|
@@ -25,7 +27,8 @@ export function isOpenCodeSessionNotFound(message) {
|
|
|
25
27
|
return /(?:session|conversation).*(?:not found|does not exist|unknown|invalid)|(?:not found|unknown|invalid).*(?:session|conversation)/iu
|
|
26
28
|
.test(message);
|
|
27
29
|
}
|
|
28
|
-
async function runAttempt(options, prompt) {
|
|
30
|
+
async function runAttempt(options, prompt, capture, attempt) {
|
|
31
|
+
const observer = createObservationMapper("opencode", capture, `attempt:${attempt}`);
|
|
29
32
|
const cwd = process.cwd();
|
|
30
33
|
const child = spawn(options.bin, buildOpenCodeArgs({ cwd, ...options }), {
|
|
31
34
|
cwd,
|
|
@@ -37,6 +40,7 @@ async function runAttempt(options, prompt) {
|
|
|
37
40
|
}
|
|
38
41
|
child.stderr.pipe(process.stderr, { end: false });
|
|
39
42
|
const childExit = once(child, "close");
|
|
43
|
+
observer.context({ prompt });
|
|
40
44
|
child.stdin.end(prompt);
|
|
41
45
|
const decoder = createOpenCodeEventDecoder();
|
|
42
46
|
const lines = createInterface({ input: child.stdout });
|
|
@@ -50,6 +54,7 @@ async function runAttempt(options, prompt) {
|
|
|
50
54
|
catch {
|
|
51
55
|
continue;
|
|
52
56
|
}
|
|
57
|
+
observer.observe(event);
|
|
53
58
|
for (const normalized of decoder.push(event)) {
|
|
54
59
|
const type = normalized.type;
|
|
55
60
|
const semantic = type === "opencode.text_delta"
|
|
@@ -87,17 +92,26 @@ async function runAttempt(options, prompt) {
|
|
|
87
92
|
return { code: 0, staleSession: false };
|
|
88
93
|
}
|
|
89
94
|
export async function runOpenCode(options) {
|
|
95
|
+
const capture = createRuntimeObservationCapture("opencode");
|
|
96
|
+
try {
|
|
97
|
+
return await runOpenCodeObserved(options, capture);
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
capture.finish();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async function runOpenCodeObserved(options, capture) {
|
|
90
104
|
const prompt = (await prepareThreadDelivery({ env: process.env, resumed: false,
|
|
91
105
|
sessionId: "unverified-cold-opencode", wakePrompt: await readPrompt() })).prompt;
|
|
92
106
|
if (process.env.CREW_THREAD_ROOT) {
|
|
93
107
|
const { sessionId: _unverified, ...coldOptions } = options;
|
|
94
|
-
return (await runAttempt(coldOptions, prompt)).code;
|
|
108
|
+
return (await runAttempt(coldOptions, prompt, capture, 1)).code;
|
|
95
109
|
}
|
|
96
|
-
const first = await runAttempt(options, prompt);
|
|
110
|
+
const first = await runAttempt(options, prompt, capture, 1);
|
|
97
111
|
if (!first.staleSession)
|
|
98
112
|
return first.code;
|
|
99
113
|
const { sessionId: _staleSessionId, ...freshOptions } = options;
|
|
100
|
-
return (await runAttempt(freshOptions, prompt)).code;
|
|
114
|
+
return (await runAttempt(freshOptions, prompt, capture, 2)).code;
|
|
101
115
|
}
|
|
102
116
|
function optionsFromArgv(argv) {
|
|
103
117
|
const { values } = parseArgs({
|
|
@@ -6,11 +6,24 @@ import spawn from "cross-spawn";
|
|
|
6
6
|
import { augmentedPath } from "../runtime-path.js";
|
|
7
7
|
import { prepareThreadDelivery } from "../thread-input-delivery.js";
|
|
8
8
|
import { buildPiArgs, createPiEventDecoder } from "./pi.js";
|
|
9
|
+
import { createRuntimeObservationCapture } from "../runtime-observation.js";
|
|
10
|
+
import { createObservationMapper } from "./observation-mapper.js";
|
|
9
11
|
async function readInput() { process.stdin.setEncoding("utf8"); let text = ""; for await (const chunk of process.stdin)
|
|
10
12
|
text += String(chunk); return JSON.parse(text); }
|
|
11
13
|
async function emit(event) { if (process.stdout.write(`${JSON.stringify(event)}\n`))
|
|
12
14
|
return; await once(process.stdout, "drain"); }
|
|
13
15
|
export async function runPi(bin) {
|
|
16
|
+
const capture = createRuntimeObservationCapture("pi");
|
|
17
|
+
const observer = createObservationMapper("pi", capture);
|
|
18
|
+
try {
|
|
19
|
+
return await runPiObserved(bin, observer);
|
|
20
|
+
}
|
|
21
|
+
finally {
|
|
22
|
+
observer.finish();
|
|
23
|
+
capture.finish();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function runPiObserved(bin, observer) {
|
|
14
27
|
const input = await readInput();
|
|
15
28
|
// Pi 不持久化原生会话,每轮都须在启动模型前补齐当前线程快照。
|
|
16
29
|
const delivery = await prepareThreadDelivery({
|
|
@@ -27,11 +40,14 @@ export async function runPi(bin) {
|
|
|
27
40
|
let inputError;
|
|
28
41
|
child.stdin.on("error", error => { inputError = error; });
|
|
29
42
|
// Pi print 模式读取 stdin;完整线程历史不能塞进受长度限制的 argv。
|
|
43
|
+
observer.context({ systemPrompt: input.systemPrompt, prompt: delivery.prompt });
|
|
30
44
|
child.stdin.end(delivery.prompt);
|
|
31
45
|
const decoder = createPiEventDecoder();
|
|
32
46
|
for await (const line of createInterface({ input: child.stdout })) {
|
|
33
47
|
try {
|
|
34
|
-
|
|
48
|
+
const raw = JSON.parse(line);
|
|
49
|
+
observer.observe(raw);
|
|
50
|
+
for (const event of decoder.push(raw))
|
|
35
51
|
await emit(event);
|
|
36
52
|
}
|
|
37
53
|
catch { }
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const STORAGE_MIGRATION_CAPABILITY = "storage_migration_v1";
|
|
3
|
+
export const STORAGE_MIGRATION_STATUS_CAPABILITY = "storage_migration_status_v1";
|
|
4
|
+
const IdentitySchema = z.object({
|
|
5
|
+
migrationId: z.string().uuid(),
|
|
6
|
+
environment: z.string().regex(/^[a-z0-9][a-z0-9_-]{0,31}$/u),
|
|
7
|
+
workspaceId: z.string().uuid(),
|
|
8
|
+
ownerEpoch: z.number().int().positive().safe().optional(),
|
|
9
|
+
ownershipClaimFingerprint: z.string().regex(/^sha256:[0-9a-f]{64}$/u).optional(),
|
|
10
|
+
});
|
|
11
|
+
const DoctorRequestSchema = IdentitySchema.extend({ type: z.literal("daemon:storage-doctor") }).strict();
|
|
12
|
+
const MigrateRequestSchema = IdentitySchema.extend({
|
|
13
|
+
type: z.literal("daemon:storage-migrate"),
|
|
14
|
+
manifestDigest: z.string().regex(/^[0-9a-f]{64}$/u),
|
|
15
|
+
}).strict();
|
|
16
|
+
const StatusSchema = z.enum([
|
|
17
|
+
"preflight_running", "preflight_passed", "backing_up", "initial_sync", "quiescing", "final_sync", "verified",
|
|
18
|
+
"service_switched", "hello_confirmed", "completed", "failed", "rollback_pending", "rolled_back",
|
|
19
|
+
"rollback_failed",
|
|
20
|
+
]);
|
|
21
|
+
const TOKEN = /(?:\bsk_(?:machine|user|agent)_[A-Za-z0-9_-]{4,}\b|\bbearer\s+[^\s]+|(?:password|secret|token)\s*[:=]\s*[^\s]+)/giu;
|
|
22
|
+
const safeError = (error) => (error instanceof Error ? error.message : String(error))
|
|
23
|
+
.replaceAll(TOKEN, "<redacted>").slice(0, 500) || "storage migration failed";
|
|
24
|
+
const stableErrorCode = (candidate, fallback) => typeof candidate === "string" && /^[a-z0-9._-]{1,128}$/u.test(candidate) ? candidate : fallback;
|
|
25
|
+
export function createStorageControlController(input) {
|
|
26
|
+
const environment = z.string().regex(/^[a-z0-9][a-z0-9_-]{0,31}$/u).parse(input.environment);
|
|
27
|
+
const workspaceId = z.string().uuid().parse(input.workspaceId);
|
|
28
|
+
const profile = z.string().regex(/^[a-z0-9][a-z0-9_-]{0,47}$/u).parse(input.profile);
|
|
29
|
+
const sourceStorageFingerprint = z.string().regex(/^sha256:[0-9a-f]{64}$/u).parse(input.sourceStorageFingerprint);
|
|
30
|
+
const targetStorageFingerprint = z.string().regex(/^sha256:[0-9a-f]{64}$/u).parse(input.targetStorageFingerprint);
|
|
31
|
+
const ownership = input.ownership === undefined ? undefined : Object.freeze({
|
|
32
|
+
ownerEpoch: z.number().int().positive().safe().parse(input.ownership.ownerEpoch),
|
|
33
|
+
ownershipClaimFingerprint: z.string().regex(/^sha256:[0-9a-f]{64}$/u)
|
|
34
|
+
.parse(input.ownership.ownershipClaimFingerprint),
|
|
35
|
+
});
|
|
36
|
+
const preflights = new Map();
|
|
37
|
+
const preflightRuns = new Map();
|
|
38
|
+
const appliedMigrationIds = new Set();
|
|
39
|
+
let runningMigrationId = null;
|
|
40
|
+
const preflightTimeoutMs = z.number().int().positive().max(24 * 60 * 60 * 1000)
|
|
41
|
+
.parse(input.preflightTimeoutMs ?? 30 * 60 * 1000);
|
|
42
|
+
const frame = (identity, status, extra = {}) => ({
|
|
43
|
+
type: "daemon:storage-migration-status",
|
|
44
|
+
migrationId: identity.migrationId,
|
|
45
|
+
status,
|
|
46
|
+
environment,
|
|
47
|
+
workspaceId,
|
|
48
|
+
profile,
|
|
49
|
+
...(ownership === undefined ? {} : ownership),
|
|
50
|
+
...extra,
|
|
51
|
+
});
|
|
52
|
+
const failure = (identity, errorCode, error, reopenAdmission = false, extra = {}) => {
|
|
53
|
+
reportStatus(frame(identity, "failed", {
|
|
54
|
+
errorCode,
|
|
55
|
+
...extra,
|
|
56
|
+
...(error === undefined ? {} : { errorMessage: safeError(error) }),
|
|
57
|
+
...(reopenAdmission || error?.reopenAdmission === true
|
|
58
|
+
? { reopenAdmission: true }
|
|
59
|
+
: {}),
|
|
60
|
+
}));
|
|
61
|
+
};
|
|
62
|
+
const reportStatus = (statusFrame) => {
|
|
63
|
+
try {
|
|
64
|
+
input.sendStatus(statusFrame);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
input.onStatusError?.(error, statusFrame);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const matchesIdentity = (identity) => identity.environment === environment && identity.workspaceId === workspaceId
|
|
71
|
+
&& (ownership === undefined
|
|
72
|
+
|| (identity.ownerEpoch === ownership.ownerEpoch
|
|
73
|
+
&& identity.ownershipClaimFingerprint === ownership.ownershipClaimFingerprint));
|
|
74
|
+
const preflightKey = (identity) => `${identity.migrationId}\0${identity.environment}\0${identity.workspaceId}`;
|
|
75
|
+
const runPreflight = (identity, failureFallback = "doctor_failed") => {
|
|
76
|
+
const key = preflightKey(identity);
|
|
77
|
+
const existing = preflightRuns.get(key);
|
|
78
|
+
if (existing !== undefined)
|
|
79
|
+
return existing;
|
|
80
|
+
const controller = new AbortController();
|
|
81
|
+
let scannedEntries = 0;
|
|
82
|
+
let timedOut = false;
|
|
83
|
+
reportStatus(frame(identity, "preflight_running", {
|
|
84
|
+
progress: { state: "running", scannedEntries: 0 },
|
|
85
|
+
}));
|
|
86
|
+
const doctorPromise = Promise.resolve().then(() => input.runDoctor(identity.migrationId, {
|
|
87
|
+
signal: controller.signal,
|
|
88
|
+
onProgress: (progress) => {
|
|
89
|
+
if (timedOut)
|
|
90
|
+
return;
|
|
91
|
+
scannedEntries = progress.scannedEntries;
|
|
92
|
+
reportStatus(frame(identity, "preflight_running", {
|
|
93
|
+
progress: { state: "running", scannedEntries },
|
|
94
|
+
}));
|
|
95
|
+
},
|
|
96
|
+
}));
|
|
97
|
+
let timeoutHandle;
|
|
98
|
+
const timeout = new Promise((_, reject) => {
|
|
99
|
+
timeoutHandle = setTimeout(() => {
|
|
100
|
+
timedOut = true;
|
|
101
|
+
controller.abort();
|
|
102
|
+
reject(Object.assign(new Error("storage doctor preflight timed out"), {
|
|
103
|
+
code: "preflight_stale",
|
|
104
|
+
scannedEntries,
|
|
105
|
+
}));
|
|
106
|
+
}, preflightTimeoutMs);
|
|
107
|
+
timeoutHandle.unref?.();
|
|
108
|
+
});
|
|
109
|
+
void doctorPromise.catch(() => undefined);
|
|
110
|
+
let outcome;
|
|
111
|
+
outcome = Promise.race([doctorPromise, timeout]).then((doctor) => {
|
|
112
|
+
if (doctor.manifest.migrationId !== identity.migrationId
|
|
113
|
+
|| doctor.environment !== environment || doctor.workspaceId !== workspaceId
|
|
114
|
+
|| doctor.profile !== profile) {
|
|
115
|
+
failure(identity, "storage_identity_mismatch", undefined, true);
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
if (!doctor.applyAllowed) {
|
|
119
|
+
failure(identity, doctor.blockers[0]?.code ?? "doctor_blocked", undefined, true);
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
preflights.set(identity.migrationId, doctor);
|
|
123
|
+
reportStatus(frame(identity, "preflight_passed", {
|
|
124
|
+
sourceStorageFingerprint,
|
|
125
|
+
targetStorageFingerprint,
|
|
126
|
+
manifestDigest: doctor.manifestDigest,
|
|
127
|
+
}));
|
|
128
|
+
return doctor;
|
|
129
|
+
}).catch((error) => {
|
|
130
|
+
const code = error.code;
|
|
131
|
+
if (code === "preflight_stale") {
|
|
132
|
+
failure(identity, "preflight_stale", undefined, true, {
|
|
133
|
+
retryable: true,
|
|
134
|
+
progress: { state: "stale", scannedEntries: Number(error.scannedEntries) || scannedEntries },
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
else if (code === "preflight_aborted") {
|
|
138
|
+
failure(identity, "preflight_aborted", error, true, {
|
|
139
|
+
retryable: true,
|
|
140
|
+
progress: { state: "stale", scannedEntries },
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
failure(identity, stableErrorCode(code, failureFallback), error, true);
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}).finally(() => {
|
|
148
|
+
if (timeoutHandle !== undefined)
|
|
149
|
+
clearTimeout(timeoutHandle);
|
|
150
|
+
if (preflightRuns.get(key) === outcome)
|
|
151
|
+
preflightRuns.delete(key);
|
|
152
|
+
});
|
|
153
|
+
preflightRuns.set(key, outcome);
|
|
154
|
+
return outcome;
|
|
155
|
+
};
|
|
156
|
+
const handle = async (raw) => {
|
|
157
|
+
const type = typeof raw === "object" && raw !== null && "type" in raw ? raw.type : null;
|
|
158
|
+
if (type !== "daemon:storage-doctor" && type !== "daemon:storage-migrate")
|
|
159
|
+
return false;
|
|
160
|
+
const schema = type === "daemon:storage-doctor" ? DoctorRequestSchema : MigrateRequestSchema;
|
|
161
|
+
const parsed = schema.safeParse(raw);
|
|
162
|
+
if (!parsed.success)
|
|
163
|
+
return true;
|
|
164
|
+
const identity = parsed.data;
|
|
165
|
+
if (!matchesIdentity(identity)) {
|
|
166
|
+
failure(identity, "storage_identity_mismatch", undefined, true);
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
if (type === "daemon:storage-doctor") {
|
|
170
|
+
await runPreflight(identity);
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
if (runningMigrationId !== null) {
|
|
174
|
+
if (runningMigrationId !== identity.migrationId)
|
|
175
|
+
failure(identity, "migration_active");
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
const migrate = MigrateRequestSchema.parse(identity);
|
|
179
|
+
if (appliedMigrationIds.has(identity.migrationId))
|
|
180
|
+
return true;
|
|
181
|
+
let doctor = preflights.get(identity.migrationId);
|
|
182
|
+
if (doctor === undefined) {
|
|
183
|
+
const rebuilt = await runPreflight(identity, "preflight_rebuild_failed");
|
|
184
|
+
if (rebuilt === null)
|
|
185
|
+
return true;
|
|
186
|
+
doctor = rebuilt;
|
|
187
|
+
}
|
|
188
|
+
if (!doctor.applyAllowed || doctor.manifestDigest !== migrate.manifestDigest
|
|
189
|
+
|| doctor.manifest.migrationId !== identity.migrationId
|
|
190
|
+
|| doctor.environment !== environment || doctor.workspaceId !== workspaceId || doctor.profile !== profile) {
|
|
191
|
+
failure(identity, "preflight_digest_mismatch", undefined, true);
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
preflights.delete(identity.migrationId);
|
|
195
|
+
runningMigrationId = identity.migrationId;
|
|
196
|
+
try {
|
|
197
|
+
await input.runMigration(doctor, (status) => {
|
|
198
|
+
if (StatusSchema.safeParse(status).success)
|
|
199
|
+
reportStatus(frame(identity, status));
|
|
200
|
+
});
|
|
201
|
+
appliedMigrationIds.add(identity.migrationId);
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
failure(identity, stableErrorCode(error.code, "storage_migration_failed"), error);
|
|
205
|
+
}
|
|
206
|
+
finally {
|
|
207
|
+
runningMigrationId = null;
|
|
208
|
+
}
|
|
209
|
+
return true;
|
|
210
|
+
};
|
|
211
|
+
return Object.freeze({ handle });
|
|
212
|
+
}
|