@deksden-com/dd-flow-cli 0.9.0-beta.104 → 0.9.0-beta.106
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 +12 -0
- package/dist/build-info.json +3 -3
- package/dist/cli/run-cli.js +23 -8
- package/dist/harness-runtime/bin/dd-agy.mjs +1 -1
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +2 -2
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +4 -3
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +3 -0
- package/dist/harness-runtime/lib/delegation-instructions.mjs +10 -1
- package/dist/protocol/local-files.js +18 -9
- package/dist/runtime/context.js +2 -2
- package/dist/services/cleanup.js +81 -32
- package/dist/services/code-checks.js +18 -19
- package/dist/services/continuation-outcome.js +1 -1
- package/dist/services/controller-fanout.js +12 -4
- package/dist/services/eval-snapshots.js +69 -41
- package/dist/services/execution-policy.js +12 -1
- package/dist/services/external-work-launch.js +2 -1
- package/dist/services/hooks.js +153 -43
- package/dist/services/lanes.js +23 -18
- package/dist/services/lifecycle-command.js +11 -2
- package/dist/services/lifecycle-invocations.js +291 -54
- package/dist/services/managed-processes.js +35 -23
- package/dist/services/merge-queue.js +71 -67
- package/dist/services/merge-server.js +5 -12
- package/dist/services/migrations.js +28 -12
- package/dist/services/plan-runtime.js +8 -4
- package/dist/services/projects.js +3 -11
- package/dist/services/protocols.js +98 -68
- package/dist/services/recovery-snapshot-database.js +43 -8
- package/dist/services/run-control-worker.js +15 -32
- package/dist/services/run-control.js +3 -17
- package/dist/services/run-controller-capture.js +58 -27
- package/dist/services/run-controller-process.js +21 -30
- package/dist/services/run-controller-recovery.js +36 -37
- package/dist/services/run-controller.js +83 -88
- package/dist/services/run-fork.js +10 -6
- package/dist/services/run-projection.js +22 -64
- package/dist/services/run-recovery-runtime.js +25 -14
- package/dist/services/run-recovery.js +56 -54
- package/dist/services/runs.js +218 -160
- package/dist/services/runtime-budget.js +19 -45
- package/dist/services/runtime-scope-capture.js +8 -6
- package/dist/services/runtime-scope-control.js +49 -39
- package/dist/services/runtime-scope-resume.js +134 -122
- package/dist/services/runtime-scope-worker.js +48 -49
- package/dist/services/scope-writer-cleanup.js +26 -0
- package/dist/services/stage-context.js +6 -1
- package/dist/services/stage-lifecycle.js +1 -1
- package/dist/services/stage-pause.js +13 -29
- package/dist/services/stage-report-renderer.js +116 -6
- package/dist/services/usage.js +2 -9
- package/dist/services/vnext-code-review.js +10 -7
- package/dist/services/vnext-code.js +9 -17
- package/dist/services/vnext-merge.js +64 -21
- package/dist/services/vnext-plan-review.js +43 -30
- package/dist/services/vnext-plan.js +28 -24
- package/dist/services/vnext-protocolize.js +183 -20
- package/dist/services/vnext-specify.js +27 -19
- package/dist/services/work-registry.js +229 -150
- package/dist/storage/database.js +286 -56
- package/dist/storage/foreign-key-transaction.js +39 -0
- package/dist/storage/immutable-input.js +45 -0
- package/dist/storage/import-guard.js +102 -0
- package/dist/storage/native-cleanup.js +52 -0
- package/dist/storage/projection-lock.js +73 -0
- package/dist/storage/writer-contract.js +3 -3
- package/dist/storage/writer-migration.js +24 -41
- package/package.json +1 -1
- package/tools/repair-paused-run-status.mjs +48 -23
|
@@ -14,9 +14,9 @@ const processOperation = (context, row) => `controller:${digest({ database: cont
|
|
|
14
14
|
const birth = () => ({ pid: process.pid, pid_started_at: processStartedAt(process.pid) });
|
|
15
15
|
const dead = (value) => Boolean(value && Number.isSafeInteger(value.pid) && value.pid > 0 && typeof value.pid_started_at === "string" && value.pid_started_at && !processIsAlive(value));
|
|
16
16
|
/** Called inside the RUN intent transaction, before the client can disappear. */
|
|
17
|
-
export function recordControllerLauncher(context, row, retired) {
|
|
17
|
+
export function recordControllerLauncher(context, row, retired, identity = birth()) {
|
|
18
18
|
const request = { action: "controller_owner_launch", owner_id: launchId(row) };
|
|
19
|
-
const receipt = { ...
|
|
19
|
+
const receipt = { ...identity, registration: "controller_self@1", ...(retired ? { retired_process: { id: retired.id, lease_token: retired.lease_token, pid: retired.pid, pid_started_at: retired.pid_started_at } } : {}) };
|
|
20
20
|
const now = context.now();
|
|
21
21
|
context.db.run("INSERT INTO run_controller_operations (operation_id, controller_id, generation, input_hash, input_json, status, receipt_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?, 'completed', ?, ?, ?)", [launchId(row), row.controller_id, row.generation, digest(request), JSON.stringify(request), JSON.stringify(receipt), now, now]);
|
|
22
22
|
}
|
|
@@ -92,48 +92,37 @@ function prepareSpawn(context, row) {
|
|
|
92
92
|
return null;
|
|
93
93
|
if (launch.value.retired_process)
|
|
94
94
|
retire(resourceContext, row, launch.value.retired_process);
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
const launcher = birth();
|
|
96
|
+
return context.db.writeTransaction(() => {
|
|
97
97
|
assertLaunchAllowed(context, row);
|
|
98
98
|
const changed = context.db.run(`UPDATE run_controllers SET owner_token = ?, owner_pid = NULL, process_id = NULL, process_lease_token = NULL, status = 'starting', last_error_json = NULL, updated_at = ?
|
|
99
99
|
WHERE controller_id = ? AND owner_token = ? AND owner_pid IS ? AND process_id IS ? AND status = ? AND state_json = ?
|
|
100
100
|
AND (SELECT receipt_json FROM run_controller_operations WHERE operation_id = ?) = ?`, [token, context.now(), row.controller_id, row.owner_token, row.owner_pid, row.process_id, row.status, row.state_json, launchId(row), launch.receipt_json]);
|
|
101
|
-
if (changed.changes !== 1)
|
|
102
|
-
context.db.exec("ROLLBACK");
|
|
101
|
+
if (changed.changes !== 1)
|
|
103
102
|
return null;
|
|
104
|
-
}
|
|
105
103
|
const current = requireController(context, row.controller_id);
|
|
106
104
|
if (!ours) {
|
|
107
|
-
recordControllerLauncher(context, current);
|
|
105
|
+
recordControllerLauncher(context, current, undefined, launcher);
|
|
108
106
|
appendEvent(context, row.controller_id, "launch_owner_reconciled", { prior_launch: launchId(row), generation: row.generation, prior_process_id: row.process_id });
|
|
109
107
|
}
|
|
110
108
|
context.db.run("UPDATE run_controller_operations SET receipt_json = json_set(receipt_json, '$.spawn_started', json('true')), updated_at = ? WHERE operation_id = ?", [context.now(), launchId(current)]);
|
|
111
|
-
context.db.exec("COMMIT");
|
|
112
109
|
return current;
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
context.db.exec("ROLLBACK");
|
|
116
|
-
throw error;
|
|
117
|
-
}
|
|
110
|
+
});
|
|
118
111
|
}
|
|
119
112
|
/** The physical child records its own birth before resource registration. */
|
|
120
113
|
export function claimControllerProcess(context, row) {
|
|
121
114
|
const launch = launchRecord(context, row);
|
|
122
115
|
if (!launch && (!row.process_id || !row.process_lease_token))
|
|
123
116
|
throw new AppError("controller_owner_mismatch", "Managed registration is not authorized", 1);
|
|
124
|
-
|
|
125
|
-
|
|
117
|
+
const owner = birth();
|
|
118
|
+
context.db.writeTransaction(() => {
|
|
126
119
|
const claimed = context.db.run("UPDATE run_controllers SET owner_pid = ?, status = 'running', updated_at = ? WHERE controller_id = ? AND owner_token = ? AND status = 'starting' AND owner_pid IS NULL", [process.pid, context.now(), row.controller_id, row.owner_token]);
|
|
127
120
|
if (claimed.changes !== 1)
|
|
128
121
|
throw new AppError("controller_already_owned", "Managed owner has already claimed this launch", 1);
|
|
129
122
|
if (launch)
|
|
130
|
-
context.db.run("UPDATE run_controller_operations SET receipt_json = json_set(receipt_json, '$.owner', json(?)), updated_at = ? WHERE operation_id = ?", [JSON.stringify(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
catch (error) {
|
|
134
|
-
context.db.exec("ROLLBACK");
|
|
135
|
-
throw error;
|
|
136
|
-
}
|
|
123
|
+
context.db.run("UPDATE run_controller_operations SET receipt_json = json_set(receipt_json, '$.owner', json(?)), updated_at = ? WHERE operation_id = ?", [JSON.stringify(owner), context.now(), launchId(row)]);
|
|
124
|
+
});
|
|
125
|
+
row.owner_pid = process.pid;
|
|
137
126
|
if (!row.process_id) {
|
|
138
127
|
const manifest = JSON.parse(row.manifest_json);
|
|
139
128
|
const resourceContext = { ...context, env: { ...context.env, DD_FLOW_RESOURCE_HOME: manifest.resource_home } };
|
|
@@ -173,16 +162,18 @@ export async function spawnController(context, row) {
|
|
|
173
162
|
return controllerReceipt(requireController(context, controllerId));
|
|
174
163
|
}
|
|
175
164
|
catch (error) {
|
|
176
|
-
context.db.exec("SAVEPOINT controller_spawn_status");
|
|
177
165
|
try {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
166
|
+
context.db.writeTransaction(() => {
|
|
167
|
+
const changed = context.db.run("UPDATE run_controllers SET status = 'recovery_required', last_error_json = ?, updated_at = ? WHERE controller_id = ? AND owner_token = ? AND status = 'starting' AND owner_pid IS NULL", [JSON.stringify(errorRecord(error)), context.now(), controllerId, token]);
|
|
168
|
+
if (changed.changes && (recoveryGuard(context, row.project_id, row.run_id)?.generation ?? 0) === row.generation)
|
|
169
|
+
setFlowRunRecoveryPaused(context, row.project_id, row.run_id, true);
|
|
170
|
+
});
|
|
182
171
|
}
|
|
183
172
|
catch (failure) {
|
|
184
|
-
|
|
185
|
-
|
|
173
|
+
const secondary = { status_persistence_error: errorRecord(failure) };
|
|
174
|
+
if (error instanceof AppError)
|
|
175
|
+
throw new AppError(error.code, error.message, error.exitCode, { ...error.details, ...secondary });
|
|
176
|
+
throw new AppError("controller_spawn_failed", error instanceof Error ? error.message : String(error), 1, { cause: errorRecord(error), ...secondary });
|
|
186
177
|
}
|
|
187
178
|
throw error;
|
|
188
179
|
}
|
|
@@ -10,7 +10,7 @@ import { verifyRecoverySnapshot } from "./eval-snapshots.js";
|
|
|
10
10
|
import { recoveryGuard, resumeRunRecovery } from "./run-recovery.js";
|
|
11
11
|
import { prepareRunRecoveryDispatch } from "./run-recovery-runtime.js";
|
|
12
12
|
import { runtimeHarness } from "./execution-policy.js";
|
|
13
|
-
import { managedProcessRuntimeHome, managedProcessStatus, processIsAlive, processTreeIsAlive } from "./managed-processes.js";
|
|
13
|
+
import { managedProcessBinding, managedProcessRuntimeHome, managedProcessStatus, processIsAlive, processTreeIsAlive, processStartedAt } from "./managed-processes.js";
|
|
14
14
|
import { requireRun, requireController, controllerReceipt, appendEvent, saveState, digest, errorRecord } from "./run-controller-state.js";
|
|
15
15
|
import { controllerAdapter, sessionArguments } from "./run-controller-adapter.js";
|
|
16
16
|
import { spawnController, recordControllerLauncher } from "./run-controller-process.js";
|
|
@@ -18,6 +18,7 @@ import { readNativeDaemonState, requireManagedDaemonBinding } from "./managed-da
|
|
|
18
18
|
import { managedInvocationContext } from "./lifecycle-invocations.js";
|
|
19
19
|
import { runControlStatus } from "./run-control.js";
|
|
20
20
|
import { runtimeBudgetStatus, waitForRuntimeScopeRecovery } from "./runtime-budget.js";
|
|
21
|
+
import { prepareControllerPublication } from "./run-controller-capture.js";
|
|
21
22
|
/** CLI observation is bounded; the physical controller owns the recovery work. */
|
|
22
23
|
export async function resumeRunController(context, input) {
|
|
23
24
|
if (input.waitMs !== undefined && (!Number.isSafeInteger(input.waitMs) || input.waitMs < 0 || input.waitMs > 60_000))
|
|
@@ -35,6 +36,11 @@ export async function resumeRunController(context, input) {
|
|
|
35
36
|
}
|
|
36
37
|
/** Retain the logical controller and its receipts; replace only its dead owner. */
|
|
37
38
|
export function recordControllerResume(context, input) {
|
|
39
|
+
return prepareControllerResume(context, input)();
|
|
40
|
+
}
|
|
41
|
+
/** Verify immutable recovery evidence before a caller reserves the registry
|
|
42
|
+
* writer. The returned operation rechecks SQL ownership under the RUN CAS. */
|
|
43
|
+
export function prepareControllerResume(context, input) {
|
|
38
44
|
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(input.requestId) || !input.controlId.trim())
|
|
39
45
|
throw new AppError("usage", "Resume requires request-id and the exact source control", 2);
|
|
40
46
|
const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
|
|
@@ -53,7 +59,7 @@ export function recordControllerResume(context, input) {
|
|
|
53
59
|
};
|
|
54
60
|
const prior = findPrior();
|
|
55
61
|
if (prior)
|
|
56
|
-
return prior;
|
|
62
|
+
return () => findPrior() ?? prior;
|
|
57
63
|
try {
|
|
58
64
|
const row = context.db.get("SELECT * FROM run_controllers WHERE project_id = ? AND run_id = ? AND status NOT IN ('completed','cancelled','superseded')", [project.id, run.id]);
|
|
59
65
|
if (!row)
|
|
@@ -78,18 +84,20 @@ export function recordControllerResume(context, input) {
|
|
|
78
84
|
if (!previousOwner || previousOwner.lease_token !== row.process_lease_token || previousOwner.kind !== "run-controller" || previousOwner.owner_id !== row.controller_id || previousOwner.project_id !== project.id || previousOwner.run_id !== run.id || managedProcessRuntimeHome(previousOwner, context) !== context.ddFlowHome || !previousOwner.pid || !previousOwner.pid_started_at || processTreeIsAlive(previousOwner))
|
|
79
85
|
throw new AppError("controller_owner_unsettled", "Resume requires proof that the previous physical controller has exited", 1);
|
|
80
86
|
const token = crypto.randomUUID(), now = context.now();
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
const launcher = { pid: process.pid, pid_started_at: processStartedAt(process.pid) };
|
|
88
|
+
return () => context.db.writeTransaction(() => {
|
|
83
89
|
const duplicate = context.db.get("SELECT input_hash, status FROM run_controller_operations WHERE operation_id = ?", [operationId]);
|
|
84
90
|
if (duplicate) {
|
|
85
91
|
if (duplicate.input_hash !== hash)
|
|
86
92
|
throw new AppError("runtime_request_conflict", "Resume request id has different inputs", 1);
|
|
87
|
-
context.db.exec("COMMIT");
|
|
88
93
|
return { controller: requireController(context, row.controller_id), operationId };
|
|
89
94
|
}
|
|
90
95
|
const current = recoveryGuard(context, project.id, run.id);
|
|
91
96
|
if (current?.recovery_id !== guard.recovery_id || current.generation !== guard.generation || current.status !== "sealed" || current.capture_path !== guard.capture_path)
|
|
92
97
|
throw new AppError("run_control_superseded", "Control changed before resume could be accepted", 1);
|
|
98
|
+
const currentOwner = managedProcessStatus(resourceContext).find(record => record.id === previousOwner.id);
|
|
99
|
+
if (!currentOwner || digest(managedProcessBinding(currentOwner, manifest.resource_home)) !== digest(managedProcessBinding(previousOwner, manifest.resource_home)))
|
|
100
|
+
throw new AppError("controller_owner_unsettled", "Retired controller binding changed while recovery was prepared", 1);
|
|
93
101
|
if (!["running", "paused", "waiting_for_user"].includes(requireRun(context, project.id, run.id).status))
|
|
94
102
|
throw new AppError("invalid_run_state", "RUN became terminal before resume could be accepted", 1);
|
|
95
103
|
const changed = context.db.run("UPDATE run_controls SET status = 'resuming', updated_at = ? WHERE control_id = ? AND recovery_id = ? AND status = 'settled'", [now, input.controlId, guard.recovery_id]);
|
|
@@ -100,22 +108,17 @@ export function recordControllerResume(context, input) {
|
|
|
100
108
|
if (claimed.changes !== 1)
|
|
101
109
|
throw new AppError("controller_owner_mismatch", "Controller changed before resume could claim its next physical owner", 1);
|
|
102
110
|
context.db.run("INSERT INTO run_controller_operations (operation_id, controller_id, generation, input_hash, input_json, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, 'accepted', ?, ?)", [operationId, row.controller_id, guard.generation, hash, JSON.stringify(request), now, now]);
|
|
103
|
-
recordControllerLauncher(context, requireController(context, row.controller_id), previousOwner);
|
|
111
|
+
recordControllerLauncher(context, requireController(context, row.controller_id), previousOwner, launcher);
|
|
104
112
|
appendEvent(context, row.controller_id, "resume_requested", { operation_id: operationId, control_id: input.controlId, recovery_id: guard.recovery_id, generation: guard.generation, retired_process_id: previousOwner.id, retained_session_id: state.sessions[state.current_session].id });
|
|
105
|
-
context.
|
|
106
|
-
}
|
|
107
|
-
catch (error) {
|
|
108
|
-
context.db.exec("ROLLBACK");
|
|
109
|
-
throw error;
|
|
110
|
-
}
|
|
111
|
-
return { controller: requireController(context, row.controller_id), operationId, retiredProcess: previousOwner };
|
|
113
|
+
return { controller: requireController(context, row.controller_id), operationId, retiredProcess: previousOwner };
|
|
114
|
+
});
|
|
112
115
|
}
|
|
113
116
|
catch (error) {
|
|
114
117
|
// A concurrent identical client may commit between our first lookup and
|
|
115
118
|
// validation. Its durable request wins even if our preflight saw the fence.
|
|
116
119
|
const concurrent = findPrior();
|
|
117
120
|
if (concurrent)
|
|
118
|
-
return concurrent;
|
|
121
|
+
return () => concurrent;
|
|
119
122
|
throw error;
|
|
120
123
|
}
|
|
121
124
|
}
|
|
@@ -126,8 +129,9 @@ export async function resumeControllerSession(context, row, manifest, state, ass
|
|
|
126
129
|
throw new AppError("recovery_owner_missing", "Recovery lost its retained coordinator Session", 1);
|
|
127
130
|
let executionContext = { ...context, env: { ...context.env, DD_FLOW_BIN: manifest.flow_executable, DD_FLOW_RESOURCE_HOME: manifest.resource_home } };
|
|
128
131
|
const scopeControl = manifest.budget ? runtimeBudgetStatus(executionContext, manifest.budget.scope_id).control : null;
|
|
129
|
-
const assertRecoveryOwner = () => {
|
|
130
|
-
|
|
132
|
+
const assertRecoveryOwner = (physical = true) => {
|
|
133
|
+
if (physical)
|
|
134
|
+
assertPhysicalOwner();
|
|
131
135
|
const guard = recoveryGuard(context, row.project_id, row.run_id);
|
|
132
136
|
if (guard?.generation !== row.generation || guard.recovery_id !== recovery.recovery_id)
|
|
133
137
|
throw new AppError("run_recovery_generation_stale", "A newer control superseded this recovery owner", 1);
|
|
@@ -168,22 +172,21 @@ export async function resumeControllerSession(context, row, manifest, state, ass
|
|
|
168
172
|
throw new AppError("recovery_session_unsettled", "Retained native Session is not ready for same-Session recovery", 1);
|
|
169
173
|
assertRecoveryOwner();
|
|
170
174
|
const prepared = { prepared: true, recovery_id: recovery.recovery_id, session_id: session.id, daemon_id: daemonId, process_id: dispatch.process_id, resume_operation_id: dispatch.resume_operation_id };
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
const binding = requireManagedDaemonBinding(executionContext, { stateDir: session.state_dir, projectId: row.project_id, runId: row.run_id, ownerId: session.owner.owner_id });
|
|
176
|
+
if (binding.process.id !== dispatch.process_id || binding.state.daemon_id !== daemonId || binding.process.state !== "running" || !processIsAlive(binding.process))
|
|
177
|
+
throw new AppError("recovery_runtime_mismatch", "Prepared recovery lost its physical Session owner", 1);
|
|
178
|
+
const assertPreparedOwner = prepareControllerPublication(executionContext, row, assertPhysicalOwner);
|
|
179
|
+
context.db.writeTransaction(() => {
|
|
180
|
+
assertPreparedOwner();
|
|
181
|
+
assertRecoveryOwner(false);
|
|
182
|
+
const currentNative = managedProcessStatus(executionContext).find(item => item.id === binding.process.id);
|
|
183
|
+
if (!currentNative || currentNative.state !== "running" || currentNative.lease_expires_at <= context.now() || digest(managedProcessBinding(currentNative, manifest.resource_home)) !== digest(managedProcessBinding(binding.process, manifest.resource_home)))
|
|
184
|
+
throw new AppError("recovery_runtime_mismatch", "Prepared recovery lost its current native lease", 1);
|
|
177
185
|
const recorded = context.db.run("UPDATE run_controller_operations SET receipt_json = ?, updated_at = ? WHERE operation_id = ? AND controller_id = ? AND generation = ? AND status = 'accepted'", [JSON.stringify(prepared), context.now(), recovery.operation_id, row.controller_id, row.generation]);
|
|
178
186
|
if (recorded.changes !== 1)
|
|
179
187
|
throw new AppError("run_recovery_generation_stale", "Recovery preparation no longer owns its operation", 1);
|
|
180
188
|
appendEvent(context, row.controller_id, "recovery_prepared", prepared);
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
catch (error) {
|
|
184
|
-
context.db.exec("ROLLBACK");
|
|
185
|
-
throw error;
|
|
186
|
-
}
|
|
189
|
+
});
|
|
187
190
|
if (scopeControl && manifest.budget) {
|
|
188
191
|
await waitForRuntimeScopeRecovery(executionContext, { scopeId: manifest.budget.scope_id, generation: scopeControl.generation, controllerId: row.controller_id, operationId: recovery.operation_id }, assertRecoveryOwner);
|
|
189
192
|
}
|
|
@@ -195,9 +198,10 @@ export async function resumeControllerSession(context, row, manifest, state, ass
|
|
|
195
198
|
assertRecoveryOwner();
|
|
196
199
|
if (recoveryGuard(context, row.project_id, row.run_id)?.status !== "recovered")
|
|
197
200
|
throw new AppError("trusted_recovery_acceptance_required", "Recovery Turn ended without its native acknowledgement", 1);
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
+
const assertAcceptedOwner = prepareControllerPublication(executionContext, row, assertPhysicalOwner);
|
|
202
|
+
context.db.writeTransaction(() => {
|
|
203
|
+
assertAcceptedOwner();
|
|
204
|
+
assertRecoveryOwner(false);
|
|
201
205
|
context.db.run("UPDATE run_controls SET status = 'resumed', updated_at = ? WHERE control_id = ? AND status = 'resuming'", [context.now(), recovery.control_id]);
|
|
202
206
|
context.db.run("UPDATE run_controller_operations SET status = 'completed', receipt_json = ?, updated_at = ? WHERE operation_id = ? AND status = 'accepted'", [JSON.stringify({ resumed: true, recovery_id: recovery.recovery_id, session_id: session.id, daemon_id: daemonId }), context.now(), recovery.operation_id]);
|
|
203
207
|
state.last_receipt = receipt;
|
|
@@ -205,12 +209,7 @@ export async function resumeControllerSession(context, row, manifest, state, ass
|
|
|
205
209
|
delete state.recovery;
|
|
206
210
|
saveState(context, row, state);
|
|
207
211
|
appendEvent(context, row.controller_id, "recovery_resumed", { control_id: recovery.control_id, recovery_id: recovery.recovery_id, session_id: session.id, daemon_id: daemonId, generation: row.generation });
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
catch (error) {
|
|
211
|
-
context.db.exec("ROLLBACK");
|
|
212
|
-
throw error;
|
|
213
|
-
}
|
|
212
|
+
});
|
|
214
213
|
}
|
|
215
214
|
export function failControllerRecovery(context, row, state, error) {
|
|
216
215
|
if (state.recovery) {
|