@deksden-com/dd-flow-cli 0.9.0-beta.8 → 0.9.0-beta.80
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 +396 -0
- package/README.md +65 -0
- package/dist/build-info.json +8 -8
- package/dist/cli/command-inputs.js +300 -0
- package/dist/cli/help.js +81 -9
- package/dist/cli/hook-ingress.js +82 -0
- package/dist/cli/input-preparation.js +115 -0
- package/dist/cli/run-cli.js +1342 -375
- package/dist/cli.js +6 -2
- package/dist/harness-runtime/bin/dd-agy.mjs +37 -0
- package/dist/harness-runtime/bin/dd-codex.mjs +25 -0
- package/dist/harness-runtime/bin/dd-droid.mjs +33 -0
- package/dist/harness-runtime/bin/dd-grok.mjs +30 -0
- package/dist/harness-runtime/bin/dd-opencode.mjs +21 -0
- package/dist/harness-runtime/bin/dd-zcode.mjs +83 -0
- package/dist/harness-runtime/lib/daemon-operations.mjs +215 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +423 -0
- package/dist/harness-runtime/lib/dd-agy.mjs +59 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +146 -0
- package/dist/harness-runtime/lib/dd-codex.mjs +562 -0
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +125 -0
- package/dist/harness-runtime/lib/dd-droid.mjs +471 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +274 -0
- package/dist/harness-runtime/lib/dd-grok.mjs +174 -0
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +198 -0
- package/dist/harness-runtime/lib/dd-opencode.mjs +98 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +644 -0
- package/dist/harness-runtime/lib/dd-zcode.mjs +910 -0
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +10 -0
- package/dist/harness-runtime/lib/delegation-instructions.mjs +130 -0
- package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
- package/dist/harness-runtime/lib/driver-recovery.mjs +129 -0
- package/dist/harness-runtime/lib/droid-observation.mjs +66 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +260 -0
- package/dist/harness-runtime/lib/model-observations.mjs +77 -0
- package/dist/harness-runtime/lib/native-hook-command.d.mts +1 -0
- package/dist/harness-runtime/lib/native-hook-command.mjs +34 -0
- package/dist/harness-runtime/lib/observation-clock.mjs +36 -0
- package/dist/harness-runtime/lib/operation-context.mjs +5 -0
- package/dist/harness-runtime/lib/operation-errors.mjs +19 -0
- package/dist/harness-runtime/lib/process-json.mjs +70 -0
- package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
- package/dist/harness-runtime/lib/runner-events.mjs +200 -0
- package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
- package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
- package/dist/harness-runtime/lib/tool-observations.d.mts +17 -0
- package/dist/harness-runtime/lib/tool-observations.mjs +153 -0
- package/dist/schemas/agent-profile.schema.json +1 -1
- package/dist/schemas/code-review-result.schema.json +2 -2
- package/dist/schemas/code-work-batch.schema.json +4 -3
- package/dist/schemas/code-work-result.schema.json +4 -4
- package/dist/schemas/plan-review-result.schema.json +2 -2
- package/dist/schemas/run-control-receipt.schema.json +99 -0
- package/dist/schemas/run-control-request.schema.json +36 -0
- package/dist/schemas/vnext-protocol-plan.schema.json +2 -2
- package/dist/services/canon.js +11 -8
- package/dist/services/cleanup.js +57 -13
- package/dist/services/cli-operation-classifier.js +22 -24
- package/dist/services/code-checks.js +543 -96
- package/dist/services/codex-hook-delivery.js +28 -0
- package/dist/services/config.js +11 -8
- package/dist/services/controller-fanout.js +116 -0
- package/dist/services/dashboard.js +103 -31
- package/dist/services/engines.js +121 -76
- package/dist/services/eval-snapshots.js +750 -73
- package/dist/services/execution-policy.js +170 -0
- package/dist/services/external-work-launch.js +110 -0
- package/dist/services/harness-adapter.js +147 -24
- package/dist/services/harness-config.js +2 -0
- package/dist/services/hooks.js +553 -285
- package/dist/services/lanes.js +61 -53
- package/dist/services/lifecycle-command.js +87 -7
- package/dist/services/lifecycle-invocations.js +828 -0
- package/dist/services/managed-daemon-binding.js +38 -0
- package/dist/services/managed-processes.js +283 -27
- package/dist/services/merge-queue.js +179 -104
- package/dist/services/merge-server.js +73 -28
- package/dist/services/migrations.js +14 -9
- package/dist/services/native-daemon-history.js +49 -0
- package/dist/services/native-session-control.js +39 -0
- package/dist/services/plan-runtime.js +19 -8
- package/dist/services/plans.js +28 -25
- package/dist/services/projects.js +18 -5
- package/dist/services/prompts.js +23 -16
- package/dist/services/protocols.js +64 -20
- package/dist/services/recovery-observation-budget.js +61 -0
- package/dist/services/recovery-snapshot-database.js +107 -0
- package/dist/services/run-control-receipt.js +56 -0
- package/dist/services/run-control-worker.js +363 -0
- package/dist/services/run-control.js +876 -0
- package/dist/services/run-controller-adapter.js +222 -0
- package/dist/services/run-controller-capture.js +134 -0
- package/dist/services/run-controller-process.js +189 -0
- package/dist/services/run-controller-recovery.js +222 -0
- package/dist/services/run-controller-state.js +36 -0
- package/dist/services/run-controller.js +779 -0
- package/dist/services/run-engine-bindings.js +1 -1
- package/dist/services/run-fork.js +138 -0
- package/dist/services/run-observations.js +112 -0
- package/dist/services/run-recovery-runtime.js +69 -0
- package/dist/services/run-recovery.js +342 -0
- package/dist/services/runs.js +368 -74
- package/dist/services/runtime-budget.js +434 -0
- package/dist/services/runtime-scope-capture.js +50 -0
- package/dist/services/runtime-scope-control.js +450 -0
- package/dist/services/runtime-scope-resume.js +565 -0
- package/dist/services/runtime-scope-stop.js +99 -0
- package/dist/services/runtime-scope-worker.js +243 -0
- package/dist/services/runtime-service.js +97 -0
- package/dist/services/schema-validation.js +10 -8
- package/dist/services/sessions.js +35 -69
- package/dist/services/stage-blocker.js +17 -7
- package/dist/services/stage-context.js +44 -18
- package/dist/services/stage-lifecycle.js +99 -102
- package/dist/services/stage-pause.js +65 -23
- package/dist/services/usage.js +37 -93
- package/dist/services/vnext-code-review.js +189 -85
- package/dist/services/vnext-code.js +257 -243
- package/dist/services/vnext-execution-profile.js +5 -3
- package/dist/services/vnext-fanout.js +134 -19
- package/dist/services/vnext-merge.js +285 -102
- package/dist/services/vnext-plan-review.js +148 -94
- package/dist/services/vnext-plan.js +141 -72
- package/dist/services/vnext-protocolize.js +129 -56
- package/dist/services/vnext-specify.js +87 -64
- package/dist/services/work-registry.js +707 -156
- package/dist/services/workspace-bootstrap.js +76 -0
- package/dist/services/worktrees.js +85 -35
- package/dist/shared/errors.js +12 -0
- package/dist/storage/database.js +444 -32
- package/dist/storage/paths.js +17 -4
- package/dist/storage/writer-contract.js +77 -0
- package/dist/storage/writer-migration.js +102 -0
- package/package.json +11 -4
- package/tools/repair-paused-run-status.mjs +59 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { chmod, lstat, mkdir, open, unlink, realpath } from "node:fs/promises";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import net from "node:net";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import { DroidError, DroidRuntime, absolute, droidPaths, droidVersion, prepareDroidHome, readJson, removeDroidAuth, processSnapshot } from "./dd-droid.mjs";
|
|
7
|
+
import { durableDaemonDispatch, inspectDaemonOperation } from "./daemon-operations.mjs";
|
|
8
|
+
import { observedTimeout } from "./observation-clock.mjs";
|
|
9
|
+
import { writeJsonAtomic } from "./runner-events.mjs";
|
|
10
|
+
import { errorRecord } from "./operation-errors.mjs";
|
|
11
|
+
import { prepareRuntimeOwner, cleanupFailedStart, confirmDaemonProcess, confirmDaemonStopped, finishDaemonProcess, heartbeatDaemonProcess, registerDaemonProcess } from "./managed-daemon.mjs";
|
|
12
|
+
|
|
13
|
+
const REQUEST = "dd-droid/daemon-request@1", RESPONSE = "dd-droid/daemon-response@1";
|
|
14
|
+
async function removeSocket(file) { try { if (!(await lstat(file)).isSocket()) throw new DroidError("unsafe_socket", "Refusing to replace non-socket daemon path"); await unlink(file); } catch (error) { if (error.code !== "ENOENT") throw error; } }
|
|
15
|
+
export async function callDaemon(stateDir, operation, params = {}, timeoutMs = 30_000, operationId = process.env.DD_EVAL_OPERATION_ID ?? randomUUID()) {
|
|
16
|
+
const paths = droidPaths(stateDir), request = { schema_id: REQUEST, id: operationId, operation, params };
|
|
17
|
+
return await new Promise((resolve, reject) => {
|
|
18
|
+
const client = net.createConnection(paths.socket); let buffer = "", settled = false;
|
|
19
|
+
const finish = (error, value) => { if (settled) return; settled = true; clearTimeout(timer); client.destroy(); error ? reject(error) : resolve(value); };
|
|
20
|
+
const timer = operation === "session.prompt" ? null : observedTimeout(() => finish(new DroidError("daemon_timeout", `${operation} response was not observed`, { operation_id: operationId })), timeoutMs);
|
|
21
|
+
client.setEncoding("utf8"); client.on("connect", () => client.write(`${JSON.stringify(request)}\n`));
|
|
22
|
+
client.on("error", error => finish(new DroidError("daemon_not_running", error.message)));
|
|
23
|
+
client.on("end", () => finish(new DroidError("daemon_connection_closed", "Droid daemon closed the connection before its reply")));
|
|
24
|
+
client.on("close", () => finish(new DroidError("daemon_connection_closed", "Droid daemon connection closed")));
|
|
25
|
+
client.on("data", chunk => { buffer += chunk; const index = buffer.indexOf("\n"); if (index < 0) return; try { const response = JSON.parse(buffer.slice(0, index)); if (response.schema_id !== RESPONSE || response.id !== operationId) throw new DroidError("daemon_protocol", "Droid daemon returned an unrelated response"); response.ok ? finish(null, response.result) : finish(Object.assign(new Error(response.error.message), response.error)); } catch (error) { finish(error); } });
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function sameConfig(a, b) { return ["cwd", "bin", "model", "reasoning", "mode", "provider", "ddFlowBin", "ddFlowHome", "resourceHome", "projectRoot", "noFlow"].every(key => a?.[key] === b[key]); }
|
|
29
|
+
export async function startDaemon(options) {
|
|
30
|
+
const paths = droidPaths(options.stateDir), noFlow = options.noFlow === true || (!options.ddFlowBin && !options.ddFlowHome);
|
|
31
|
+
const config = { stateDir: paths.dir, home: paths.home, bin: options.bin ?? process.env.DD_DROID_BIN ?? "droid", cwd: absolute(options.cwd, "--cwd"), projectRoot: absolute(options.projectRoot ?? options.cwd, "--project-root"), journal: absolute(options.journal ?? `${paths.dir}/events.jsonl`, "--journal"), entryPath: absolute(options.entryPath, "adapter entry"), authHome: absolute(options.authHome ?? os.homedir(), "auth home"), model: options.model ?? "gpt-5.6-sol", reasoning: options.reasoning ?? "high", mode: options.mode ?? "auto-high", provider: options.provider ?? "openai", noFlow, ddFlowBin: noFlow ? null : absolute(options.ddFlowBin, "--dd-flow-bin"), ddFlowHome: noFlow ? null : absolute(options.ddFlowHome, "--dd-flow-home"), resourceHome: noFlow ? null : process.env.DD_FLOW_RESOURCE_HOME ?? null, inactivityMs: options.timeoutMs ?? 600_000 };
|
|
32
|
+
await prepareRuntimeOwner(config, paths.dir);
|
|
33
|
+
config.cwd = await realpath(config.cwd); config.projectRoot = await realpath(config.projectRoot);
|
|
34
|
+
if (config.mode !== "auto-high") throw new DroidError("droid_mode_unsupported", "The qualified Droid profile requires auto-high");
|
|
35
|
+
await mkdir(paths.dir, { recursive: true, mode: 0o700 }); await chmod(paths.dir, 0o700);
|
|
36
|
+
try { const live = await callDaemon(paths.dir, "daemon.status", {}, 1_000); if (!sameConfig(live.config, config)) throw new DroidError("daemon_config_mismatch", "Droid daemon already uses another configuration"); return { ...live, already_running: true }; } catch (error) { if (!["daemon_not_running", "daemon_timeout"].includes(error.code)) throw error; }
|
|
37
|
+
const previous = await readJson(paths.state);
|
|
38
|
+
if (previous) {
|
|
39
|
+
const rows = await processSnapshot();
|
|
40
|
+
if (rows.some(row => !row.zombie && ([previous.pid, previous.provider_pid].includes(row.pid) || previous.owned_processes?.some(old => old.pid === row.pid && old.started === row.started)))) throw new DroidError("invalid_harness_crash", "Previous daemon or provider ownership remains; refusing another owner");
|
|
41
|
+
}
|
|
42
|
+
if (["clean", "unclean"].includes(previous?.shutdown_state)) await authorizeRetainedDaemonResume(paths.dir, previous, options.sessionId, config);
|
|
43
|
+
if (previous && !["failed", "starting", "clean"].includes(previous.shutdown_state)) throw new DroidError("invalid_harness_crash", "Previous Droid ownership must be reconciled before another daemon starts");
|
|
44
|
+
config.daemonId = randomUUID(); config.version = await droidVersion(config.bin);
|
|
45
|
+
await removeSocket(paths.socket); const home = await prepareDroidHome(config); Object.assign(config, home);
|
|
46
|
+
const resource = await registerDaemonProcess(config, { kind: "droid-daemon", owner: `droid:${config.daemonId}`, operation: `droid-daemon:${config.daemonId}`, stdout: paths.log, stderr: paths.log });
|
|
47
|
+
await writeJsonAtomic(paths.state, { schema_id: "dd-droid/daemon-state@1", config, daemon_id: config.daemonId, shutdown_state: "starting", sessions: previous?.sessions ?? [], root_session_id: previous?.root_session_id ?? null, descendants: previous?.descendants ?? [], last_result: previous?.last_result ?? null, resource_process: resource });
|
|
48
|
+
const log = await open(paths.log, "a", 0o600); const child = spawn(process.execPath, [config.entryPath, "daemon", "serve", "--state-dir", paths.dir], { cwd: config.cwd, detached: true, stdio: ["ignore", log.fd, log.fd] }); child.unref(); await log.close();
|
|
49
|
+
try {
|
|
50
|
+
await confirmDaemonProcess(config, resource, child);
|
|
51
|
+
const deadline = Date.now() + 30_000;
|
|
52
|
+
while (Date.now() < deadline) { await new Promise(resolve => setTimeout(resolve, 100)); const saved = await readJson(paths.state); if (saved?.startup_error) throw Object.assign(new Error(saved.startup_error.message), saved.startup_error); try { return await callDaemon(paths.dir, "daemon.status", {}, 1_000); } catch (error) { if (!["daemon_not_running", "daemon_timeout"].includes(error.code)) throw error; } }
|
|
53
|
+
throw new DroidError("daemon_start_failed", "Droid daemon did not become ready");
|
|
54
|
+
} catch (error) { await cleanupFailedStart(config, resource, child, error); throw error; }
|
|
55
|
+
}
|
|
56
|
+
export async function stopDaemon(options) { return await confirmDaemonStopped(() => callDaemon(options.stateDir, "daemon.stop", { cancelTree: options.cancelTree === true }, 30_000), droidPaths(options.stateDir).socket, 30_000); }
|
|
57
|
+
export async function serveDaemon(stateDir) {
|
|
58
|
+
const paths = droidPaths(stateDir), state = await readJson(paths.state); if (!state?.config) throw new DroidError("daemon_state_missing", "Missing Droid daemon state");
|
|
59
|
+
let saving = Promise.resolve(), busy = null, stopping = false;
|
|
60
|
+
const save = patch => { Object.assign(state, patch, { pid: process.pid, updated_at: new Date().toISOString() }); const snapshot = structuredClone(state); saving = saving.then(() => writeJsonAtomic(paths.state, snapshot)); return saving; };
|
|
61
|
+
const runtime = new DroidRuntime(state.config, state, save);
|
|
62
|
+
const dispatch = async request => {
|
|
63
|
+
const params = request.params ?? {}, operation = request.operation;
|
|
64
|
+
if (operation === "daemon.status") { let observationError; try { await runtime.refreshTopology(); } catch (error) { observationError = errorRecord(error); } return { observation_error: observationError ?? null, daemon_id: state.daemon_id, pid: process.pid, config: state.config, shutdown_state: state.shutdown_state, provider_ready: Boolean(runtime.child && !runtime.closed), active_operation: busy, active_tree: Boolean(runtime.active || runtime.workingState !== "idle" || runtime.topologyIncomplete || [...runtime.topology.values()].some(child => !["completed", "cancelled", "failed"].includes(child.status))), sessions: runtime.rootId ? [runtime.rootId] : [], provider_pid: runtime.closed ? null : runtime.child?.pid }; }
|
|
65
|
+
if (operation === "hook.observe") return await runtime.observeHook(params.payload, params.eventId);
|
|
66
|
+
if (operation === "session.inspect") return await runtime.inspect(params.sessionId);
|
|
67
|
+
if (operation === "session.cancel") {
|
|
68
|
+
runtime.requireIdentity(params.sessionId); const cleanup = await runtime.closeTree(true); let inspected;
|
|
69
|
+
try { inspected = await runtime.inspect(params.sessionId, { ignoreActive: true }); }
|
|
70
|
+
catch (error) { inspected = { harness: "droid-cli", provider_session_id: params.sessionId, descendants: [...runtime.topology.values()], inspection_error: errorRecord(error) }; }
|
|
71
|
+
return { ...inspected, cancelled: true, settled: cleanup.settled, cleanup };
|
|
72
|
+
}
|
|
73
|
+
if (operation === "daemon.stop") {
|
|
74
|
+
if (stopping) throw new DroidError("operation_busy", "Droid shutdown is already in progress");
|
|
75
|
+
if (!params.cancelTree && (busy || (runtime.rootId && !(await runtime.inspect(runtime.rootId)).settled))) throw new DroidError("tree_not_settled", "Droid still owns unfinished work");
|
|
76
|
+
stopping = true;
|
|
77
|
+
try {
|
|
78
|
+
const cleanup = await runtime.closeTree(params.cancelTree === true);
|
|
79
|
+
if (runtime.rootId) try { await runtime.inspect(runtime.rootId, { ignoreActive: true }); } catch (error) { cleanup.inspection_error = errorRecord(error); }
|
|
80
|
+
// Native evidence failures remain visible, but cannot strand copied
|
|
81
|
+
// credentials or the daemon after the owned process tree is gone.
|
|
82
|
+
await removeDroidAuth(state.config); const clean = !cleanup.forced && !cleanup.topology_error && !cleanup.inspection_error;
|
|
83
|
+
await save({ shutdown_state: clean ? "clean" : "unclean", active_tree: false, cleanup }); await finishDaemonProcess(state.config, state.resource_process);
|
|
84
|
+
return { stopped: true, clean, settled: true, cleanup, _shutdown: true };
|
|
85
|
+
}
|
|
86
|
+
catch (error) { stopping = false; await save({ shutdown_state: "cleanup_failed", cleanup_error: errorRecord(error) }); throw error; }
|
|
87
|
+
}
|
|
88
|
+
if (busy || stopping) throw new DroidError("operation_busy", "Another Droid operation is active");
|
|
89
|
+
busy = operation;
|
|
90
|
+
try {
|
|
91
|
+
await save({ active_operation: busy });
|
|
92
|
+
if (operation === "session.create") { if (params.prompt) throw new DroidError("invalid_create", "create does not execute a prompt"); return await runtime.create(request.id); }
|
|
93
|
+
if (operation === "session.resume") return await runtime.resume(params.sessionId);
|
|
94
|
+
if (["session.prompt", "session.start"].includes(operation)) return await runtime.prompt(params.sessionId, params.prompt, request.id);
|
|
95
|
+
throw new DroidError("unknown_operation", `Unknown Droid operation: ${operation}`);
|
|
96
|
+
} finally { busy = null; await save({ active_operation: null }); }
|
|
97
|
+
};
|
|
98
|
+
await removeSocket(paths.socket);
|
|
99
|
+
const server = net.createServer(socket => { socket.setEncoding("utf8"); let buffer = "", consumed = false; socket.on("error", () => {}); socket.on("data", chunk => { if (consumed) return; buffer += chunk; const at = buffer.indexOf("\n"); if (at < 0) return; consumed = true;
|
|
100
|
+
void (async () => { let request; try { request = JSON.parse(buffer.slice(0, at)); if (request.schema_id !== REQUEST || typeof request.id !== "string" || typeof request.operation !== "string") throw new DroidError("daemon_protocol", "Malformed Droid daemon request"); let result;
|
|
101
|
+
if (request.operation === "operation.inspect") {
|
|
102
|
+
result = await inspectDaemonOperation(paths.dir, request.params?.operationId);
|
|
103
|
+
if (!["completed", "failed"].includes(result.state)) {
|
|
104
|
+
const recovered = await runtime.recoverOperation(request.params.operationId);
|
|
105
|
+
if (recovered) {
|
|
106
|
+
const terminal = { state: "completed", finished_at: new Date().toISOString(), result: recovered };
|
|
107
|
+
await writeJsonAtomic(`${paths.dir}/operations/${createHash("sha256").update(request.params.operationId).digest("hex")}/result.json`, terminal);
|
|
108
|
+
result = { ...result, ...terminal };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} else result = await durableDaemonDispatch(paths.dir, request, () => dispatch(request), () => dispatch({ operation: "session.inspect", params: request.params ?? {} })); const { _shutdown, ...receipt } = result;
|
|
112
|
+
// Close the listening endpoint before replying. `server.close()` keeps
|
|
113
|
+
// this already accepted connection alive, so the caller receives its
|
|
114
|
+
// durable stop receipt while the Unix pathname is atomically retired.
|
|
115
|
+
// Unlinking it from the request handler races that final response and
|
|
116
|
+
// can leave a stop caller observing the socket for its whole timeout.
|
|
117
|
+
if (_shutdown) server.close();
|
|
118
|
+
socket.end(`${JSON.stringify({ schema_id: RESPONSE, id: request.id, ok: true, result: receipt })}\n`);
|
|
119
|
+
} catch (error) { socket.end(`${JSON.stringify({ schema_id: RESPONSE, id: request?.id, ok: false, error: errorRecord(error) })}\n`); } })();
|
|
120
|
+
}); });
|
|
121
|
+
try { await runtime.start(); await heartbeatDaemonProcess(state.config, state.resource_process); await save({ shutdown_state: "running" }); await new Promise((resolve, reject) => { server.once("error", reject); server.listen(paths.socket, resolve); }); await chmod(paths.socket, 0o600); }
|
|
122
|
+
catch (error) { let cleanup; try { cleanup = await runtime.closeTree(true); await removeDroidAuth(state.config); } catch (failure) { await save({ shutdown_state: "cleanup_failed", startup_error: errorRecord(error), cleanup_error: errorRecord(failure) }); throw error; } await save({ shutdown_state: "failed", startup_error: errorRecord(error), cleanup }); await finishDaemonProcess(state.config, state.resource_process, "failed", "startup_failed"); throw error; }
|
|
123
|
+
await new Promise(resolve => server.once("close", resolve));
|
|
124
|
+
}
|
|
125
|
+
import { authorizeRetainedDaemonResume } from "./driver-recovery.mjs";
|