@astrosheep/keiyaku 2.9.2 → 2.9.4
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/build/.tsbuildinfo +1 -1
- package/build/agents/call-terms.js +13 -94
- package/build/agents/harness/activity-values.js +16 -0
- package/build/agents/harness/event-persistence.js +218 -0
- package/build/agents/harness/execution-handle.js +25 -222
- package/build/agents/harness/index.js +3 -1
- package/build/agents/harness/outcome.js +47 -4
- package/build/agents/harness/projection.js +42 -59
- package/build/agents/harness/provider-adapter.js +58 -0
- package/build/agents/harness/router.js +11 -187
- package/build/agents/harness/runtime.js +17 -4
- package/build/agents/harness/session.js +15 -0
- package/build/agents/launch-snapshot/model.js +15 -236
- package/build/agents/launch-snapshot/parse-helpers.js +114 -0
- package/build/agents/launch-snapshot/resolve.js +17 -43
- package/build/agents/opencode-sdk.js +45 -12
- package/build/agents/providers/claude-agent-sdk/adapter.js +6 -17
- package/build/agents/providers/claude-agent-sdk/registration.js +214 -0
- package/build/agents/providers/claude-agent-sdk/session-schema.js +2 -0
- package/build/agents/providers/codex-app-server/adapter.js +50 -53
- package/build/agents/providers/codex-app-server/events.js +67 -11
- package/build/agents/providers/codex-app-server/identity.js +1 -0
- package/build/agents/providers/codex-app-server/policy.js +29 -0
- package/build/agents/providers/codex-app-server/registration.js +248 -0
- package/build/agents/providers/codex-app-server/session-schema.js +6 -0
- package/build/agents/providers/codex-app-server/session.js +23 -17
- package/build/agents/providers/codex-app-server/terms.js +1 -0
- package/build/agents/providers/opencode-sdk/adapter.js +94 -50
- package/build/agents/providers/opencode-sdk/events.js +8 -4
- package/build/agents/providers/opencode-sdk/registration.js +135 -0
- package/build/agents/providers/opencode-sdk/session-schema.js +2 -0
- package/build/agents/providers/opencode-sdk/session.js +21 -14
- package/build/agents/providers/opencode-sdk/terms.js +1 -0
- package/build/agents/providers/pi/adapter.js +36 -30
- package/build/agents/providers/pi/registration.js +145 -0
- package/build/agents/providers/pi/session-schema.js +6 -0
- package/build/agents/providers/pi/terms.js +1 -0
- package/build/agents/providers/registration.js +7 -0
- package/build/agents/providers/registry.js +76 -0
- package/build/agents/selector.js +2 -29
- package/build/agents/types.js +8 -0
- package/build/artifact-directories.js +22 -0
- package/build/cli/commands/akuma/akuma/handler.js +1 -1
- package/build/cli/commands/akuma/view/handler.js +5 -4
- package/build/cli/commands/akuma.js +6 -0
- package/build/cli/commands/contract/amend/handler.js +8 -2
- package/build/cli/commands/contract/arc/handler.js +7 -1
- package/build/cli/commands/contract/bind/handler.js +4 -3
- package/build/cli/commands/contract/bind/meta.js +34 -7
- package/build/cli/commands/contract/forfeit/handler.js +4 -4
- package/build/cli/commands/contract/log/handler.js +3 -3
- package/build/cli/commands/contract/petition/handler.js +4 -6
- package/build/cli/commands/contract/renew/handler.js +20 -6
- package/build/cli/commands/contract/renew/meta.js +5 -4
- package/build/cli/commands/metadata.js +58 -0
- package/build/cli/commands/projection/call/handler.js +6 -5
- package/build/cli/commands/projection/kill/handler.js +16 -32
- package/build/cli/commands/projection/shared.js +28 -0
- package/build/cli/commands/projection/status/handler.js +1 -1
- package/build/cli/commands/projection/tell/handler.js +14 -30
- package/build/cli/commands/projection/wait/handler.js +45 -22
- package/build/cli/commands/projection/wait/meta.js +7 -2
- package/build/cli/commands/registry.js +2 -54
- package/build/cli/commands/shared.js +10 -7
- package/build/cli/commands/skills/install/handler.js +2 -5
- package/build/cli/commands/skills/skills/handler.js +1 -1
- package/build/cli/commands/task/add/handler.js +5 -13
- package/build/cli/commands/task/catalog.js +2 -2
- package/build/cli/commands/task/done/handler.js +4 -8
- package/build/cli/commands/task/drop/handler.js +3 -6
- package/build/cli/commands/task/ls/handler.js +3 -5
- package/build/cli/commands/task/shared.js +14 -4
- package/build/cli/commands/task/{view → show}/handler.js +7 -8
- package/build/cli/commands/task/show/meta.js +8 -0
- package/build/cli/commands/task/start/handler.js +3 -6
- package/build/cli/commands/task/stop/handler.js +4 -8
- package/build/cli/commands/task/task/handler.js +1 -1
- package/build/cli/commands/task/update/handler.js +4 -8
- package/build/cli/completion.js +1 -1
- package/build/cli/flags.js +19 -3
- package/build/cli/help.js +4 -11
- package/build/cli/index.js +57 -83
- package/build/cli/missing-subcommand.js +7 -0
- package/build/cli/parse-flags.js +14 -5
- package/build/cli/parse-metadata.js +3 -3
- package/build/cli/parse-selectors.js +1 -1
- package/build/cli/parse.js +21 -0
- package/build/cli/projection-address.js +7 -12
- package/build/cli/render/address.js +41 -37
- package/build/cli/render/arc.js +24 -12
- package/build/cli/render/call.js +11 -5
- package/build/cli/render/errors.js +36 -1
- package/build/cli/render/format.js +4 -0
- package/build/cli/render/kanshi.js +6 -12
- package/build/cli/render/kill.js +9 -0
- package/build/cli/render/misc.js +55 -12
- package/build/cli/render/petition.js +20 -15
- package/build/cli/render/projection-activity.js +3 -0
- package/build/cli/render/shared.js +60 -21
- package/build/cli/render/skills.js +10 -0
- package/build/cli/render/status.js +37 -14
- package/build/cli/render/success-response.js +77 -0
- package/build/cli/render/task.js +54 -14
- package/build/cli/render/tell.js +5 -0
- package/build/cli/render/terminal-failure.js +58 -0
- package/build/cli/render/types.js +1 -0
- package/build/cli/render/wait.js +38 -36
- package/build/cli/types.js +1 -1
- package/build/config/akuma-loader.js +0 -5
- package/build/config/env-keys.js +1 -1
- package/build/config/provider-policy-ownership.js +3 -32
- package/build/config/settings/disease.js +8 -1
- package/build/config/settings/loader.js +11 -3
- package/build/config/settings/provider-instance-fields.js +51 -0
- package/build/config/settings/schema.js +6 -176
- package/build/core/addressing.js +66 -16
- package/build/core/amend.js +135 -18
- package/build/core/arc.js +19 -67
- package/build/core/bind.js +82 -35
- package/build/core/call/call.js +15 -9
- package/build/core/call/context.js +6 -51
- package/build/core/call/execution.js +21 -22
- package/build/core/call/prompt.js +14 -1
- package/build/core/command-markdown.js +45 -0
- package/build/core/context.js +13 -7
- package/build/core/contract-view.js +20 -3
- package/build/core/draft.js +123 -16
- package/build/core/forfeit.js +5 -2
- package/build/core/hints.js +1 -75
- package/build/core/identifier-slug.js +66 -0
- package/build/core/ids.js +25 -11
- package/build/core/ledger.js +20 -68
- package/build/core/markdown/parser.js +0 -68
- package/build/core/markdown/render.js +68 -0
- package/build/core/markdown/sections.js +2 -1
- package/build/core/outcome-base.js +4 -0
- package/build/core/projection/generation/database.js +2 -38
- package/build/core/projection/generation/model.js +42 -1
- package/build/core/{projection-generation-continuation.js → projection/generation/projection-generation-continuation.js} +3 -3
- package/build/core/{projection-generation-execution.js → projection/generation/projection-generation-execution.js} +19 -49
- package/build/core/{projection-generation-identity.js → projection/generation/projection-generation-identity.js} +2 -2
- package/build/core/{projection-generation-launcher.js → projection/generation/projection-generation-launcher.js} +5 -3
- package/build/core/{projection-generation-process.js → projection/generation/projection-generation-process.js} +2 -1
- package/build/core/{projection-generation-runner.js → projection/generation/projection-generation-runner.js} +18 -3
- package/build/core/projection/generation/store.js +2 -2
- package/build/core/projection/generation/transitions.js +1 -1
- package/build/core/projection/index.js +27 -0
- package/build/core/{projection-activity.js → projection/projection-activity.js} +12 -0
- package/build/core/{projection-alias.js → projection/projection-alias.js} +14 -19
- package/build/core/projection/projection-coordinate.js +61 -0
- package/build/core/{projection-core.js → projection/projection-core.js} +9 -9
- package/build/core/{projection-execution-observer.js → projection/projection-execution-observer.js} +2 -57
- package/build/core/{projection-identity.js → projection/projection-identity.js} +1 -1
- package/build/core/{projection-kill.js → projection/projection-kill.js} +1 -1
- package/build/core/{projection-life-observer.js → projection/projection-life-observer.js} +1 -1
- package/build/core/{projection-mint.js → projection/projection-mint.js} +10 -10
- package/build/core/{projection-status.js → projection/projection-status.js} +98 -25
- package/build/core/projection/projection-terminal-failure.js +104 -0
- package/build/core/projection/projection-wait.js +278 -0
- package/build/core/projection/projection-wake.js +203 -0
- package/build/core/provider-loader-ports.js +4 -0
- package/build/core/ref-log.js +65 -40
- package/build/core/registry.js +63 -10
- package/build/core/renew-session.js +128 -0
- package/build/core/renew.js +426 -99
- package/build/core/scope.js +143 -66
- package/build/core/seal.js +139 -35
- package/build/core/{claim-delivery.js → settlement/claim-delivery.js} +9 -14
- package/build/core/{claim.js → settlement/claim.js} +32 -8
- package/build/core/settlement/index.js +19 -0
- package/build/core/{petition-claim-gates.js → settlement/petition-claim-gates.js} +22 -18
- package/build/core/{petition-forfeit.js → settlement/petition-forfeit.js} +2 -2
- package/build/core/{petition-head-guard.js → settlement/petition-head-guard.js} +2 -2
- package/build/core/settlement/petition-io.js +1 -0
- package/build/core/{petition-preview.js → settlement/petition-preview.js} +1 -1
- package/build/core/{petition.js → settlement/petition.js} +12 -10
- package/build/core/settlement/queue-read-model.js +143 -0
- package/build/core/settlement/queue-seat-allocation.js +156 -0
- package/build/core/settlement/queue.js +6 -0
- package/build/core/settlement/settlement.js +295 -0
- package/build/core/{verdict.js → settlement/verdict.js} +9 -37
- package/build/core/status/board.js +24 -23
- package/build/core/status/ledger-batch.js +158 -0
- package/build/core/status/reconciliation.js +10 -4
- package/build/core/stored-agent-event.js +3 -0
- package/build/core/task/board.js +40 -4
- package/build/core/task/commands.js +30 -21
- package/build/core/task/index.js +12 -0
- package/build/core/task/settlement-git.js +157 -65
- package/build/core/task/settlement-policy.js +54 -24
- package/build/core/task/task-contract.js +370 -0
- package/build/core/{task-doctor.js → task/task-doctor.js} +1 -1
- package/build/core/{task-git-actor.js → task/task-git-actor.js} +5 -10
- package/build/core/{task-git-runtime.js → task/task-git-runtime.js} +3 -3
- package/build/core/{task-git-store.js → task/task-git-store.js} +92 -29
- package/build/core/task/task-repository.js +1 -0
- package/build/core/{task-store-repository.js → task/task-store-repository.js} +5 -4
- package/build/core/task/task-store.js +1 -0
- package/build/core/{task-worktree.js → task/task-worktree.js} +2 -1
- package/build/core/task/task.js +5 -0
- package/build/core/task/tree.js +37 -0
- package/build/core/task/validation.js +5 -2
- package/build/core/transcripts.js +15 -3
- package/build/core/worktree-path.js +43 -28
- package/build/flow-error.js +85 -39
- package/build/fs/durability.js +27 -0
- package/build/fs/path-coordinate.js +16 -0
- package/build/generated/version.js +3 -2
- package/build/git/branches.js +2 -1
- package/build/git/core.js +106 -2
- package/build/git/path-coordinate.js +17 -0
- package/build/git/refs.js +57 -0
- package/build/git/streaming-batch.js +301 -0
- package/build/git/worktree.js +65 -0
- package/build/index.js +3 -3
- package/build/keiyaku.js +2 -36
- package/package.json +4 -10
- package/skills/keiyaku/SKILL.md +3 -2
- package/skills/keiyaku-akuma/SKILL.md +10 -6
- package/skills/keiyaku-task/SKILL.md +1 -1
- package/skills/keiyaku-workflow/SKILL.md +25 -4
- package/build/agents/providers/codex-sdk.js +0 -329
- package/build/cli/commands/task/view/meta.js +0 -8
- package/build/core/command-io.js +0 -174
- package/build/core/path-coordinate.js +0 -27
- package/build/core/petition-claim.js +0 -117
- package/build/core/petition-run.js +0 -83
- package/build/core/projection-coordinate.js +0 -63
- package/build/core/projection-wait.js +0 -165
- package/build/core/projection-wake.js +0 -136
- package/build/core/queue.js +0 -343
- package/build/core/task-contract.js +0 -237
- package/build/core/task.js +0 -4
- /package/build/{core/task-repository.js → agents/launch-snapshot/types.js} +0 -0
- /package/build/{core/task-store.js → agents/providers/claude-agent-sdk/terms.js} +0 -0
- /package/build/core/{projection-generation-runtime.js → projection/generation/projection-generation-runtime.js} +0 -0
- /package/build/core/{projection-life-protocol.js → projection/projection-life-protocol.js} +0 -0
- /package/build/core/{projection-runner-lock.js → projection/projection-runner-lock.js} +0 -0
package/build/core/registry.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { FlowError } from "../flow-error.js";
|
|
2
|
+
import { createGit, wrapGitError } from "../git/core.js";
|
|
2
3
|
import { readLedger } from "./ledger.js";
|
|
3
4
|
import { appendRefLogWithRetry } from "./ref-log.js";
|
|
4
|
-
import {
|
|
5
|
+
import { patternsOverlap, scopePathEvidence } from "./scope.js";
|
|
5
6
|
import { deriveContractState, isTerminalState } from "./status/lifecycle.js";
|
|
6
7
|
export const REGISTRY_REF = "refs/keiyaku/registry";
|
|
8
|
+
export const SCOPE_OVERLAP_PATH_PREVIEW_LIMIT = 10;
|
|
7
9
|
function parseRegistryEntry(message) {
|
|
8
10
|
const body = message.slice(message.indexOf("\n\n") + 2).trim();
|
|
9
11
|
const parsed = JSON.parse(body);
|
|
@@ -38,9 +40,19 @@ async function activeRegistrations(cwd, entries) {
|
|
|
38
40
|
return active;
|
|
39
41
|
}
|
|
40
42
|
export async function registerScope(cwd, input) {
|
|
41
|
-
let
|
|
43
|
+
let overlaps = [];
|
|
44
|
+
let trackedPaths;
|
|
45
|
+
try {
|
|
46
|
+
trackedPaths = (await createGit(cwd).raw(["ls-files", "-z"]))
|
|
47
|
+
.split("\0")
|
|
48
|
+
.filter(Boolean)
|
|
49
|
+
.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
throw wrapGitError("ls-files -z", error, cwd);
|
|
53
|
+
}
|
|
42
54
|
const result = await appendRefLogWithRetry(cwd, REGISTRY_REF, registrySubject, parseRegistryEntry, async (entries) => {
|
|
43
|
-
const
|
|
55
|
+
const nextOverlaps = [];
|
|
44
56
|
const active = await activeRegistrations(cwd, entries);
|
|
45
57
|
if (input.exclusive && active.length > 0) {
|
|
46
58
|
const blocking = active.find((existing) => existing.contract !== input.contractId) ?? active[0];
|
|
@@ -55,19 +67,60 @@ export async function registerScope(cwd, input) {
|
|
|
55
67
|
`Run \`keiyaku forfeit @${existing.contract}\` to free this tree.`,
|
|
56
68
|
].join(" "));
|
|
57
69
|
}
|
|
58
|
-
|
|
59
|
-
|
|
70
|
+
const [nextEvidence, existingEvidence] = await Promise.all([
|
|
71
|
+
scopePathEvidence(cwd, input.scope, trackedPaths),
|
|
72
|
+
scopePathEvidence(cwd, existing.scope, trackedPaths),
|
|
73
|
+
]);
|
|
74
|
+
const existingByPath = new Map(existingEvidence.map((evidence) => [evidence.path, evidence]));
|
|
75
|
+
const concreteByPatterns = new Map();
|
|
76
|
+
for (const next of nextEvidence) {
|
|
77
|
+
const prior = existingByPath.get(next.path);
|
|
78
|
+
if (!next.matched || !prior?.matched || !next.decisivePattern || !prior.decisivePattern)
|
|
60
79
|
continue;
|
|
80
|
+
const key = `${next.decisivePattern.index}:${prior.decisivePattern.index}`;
|
|
81
|
+
const group = concreteByPatterns.get(key) ?? {
|
|
82
|
+
next: next.decisivePattern.pattern,
|
|
83
|
+
existing: prior.decisivePattern.pattern,
|
|
84
|
+
paths: [],
|
|
85
|
+
};
|
|
86
|
+
group.paths.push(next.path);
|
|
87
|
+
concreteByPatterns.set(key, group);
|
|
88
|
+
}
|
|
89
|
+
for (const concrete of concreteByPatterns.values()) {
|
|
90
|
+
const preview = concrete.paths.slice(0, SCOPE_OVERLAP_PATH_PREVIEW_LIMIT);
|
|
91
|
+
nextOverlaps.push({
|
|
92
|
+
contracts: [input.contractId, existing.contract],
|
|
93
|
+
patterns: [concrete.next, concrete.existing],
|
|
94
|
+
kind: "concrete",
|
|
95
|
+
paths: {
|
|
96
|
+
preview,
|
|
97
|
+
count: concrete.paths.length,
|
|
98
|
+
truncated: preview.length < concrete.paths.length,
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
for (const nextPattern of input.scope) {
|
|
61
103
|
for (const existingPattern of existing.scope) {
|
|
62
|
-
if (isNegativeScopePattern(existingPattern))
|
|
63
|
-
continue;
|
|
64
104
|
if (patternsOverlap(nextPattern, existingPattern)) {
|
|
65
|
-
|
|
105
|
+
const [nextPatternEvidence, existingPatternEvidence] = await Promise.all([
|
|
106
|
+
scopePathEvidence(cwd, [nextPattern], trackedPaths),
|
|
107
|
+
scopePathEvidence(cwd, [existingPattern], trackedPaths),
|
|
108
|
+
]);
|
|
109
|
+
const existingPatternByPath = new Map(existingPatternEvidence.map((evidence) => [evidence.path, evidence]));
|
|
110
|
+
const hasCurrentWitness = nextPatternEvidence.some((evidence) => (evidence.matched && existingPatternByPath.get(evidence.path)?.matched));
|
|
111
|
+
if (!hasCurrentWitness) {
|
|
112
|
+
nextOverlaps.push({
|
|
113
|
+
contracts: [input.contractId, existing.contract],
|
|
114
|
+
patterns: [nextPattern, existingPattern],
|
|
115
|
+
kind: "potential",
|
|
116
|
+
paths: { preview: [], count: 0, truncated: false },
|
|
117
|
+
});
|
|
118
|
+
}
|
|
66
119
|
}
|
|
67
120
|
}
|
|
68
121
|
}
|
|
69
122
|
}
|
|
70
|
-
|
|
123
|
+
overlaps = nextOverlaps;
|
|
71
124
|
return {
|
|
72
125
|
v: 1,
|
|
73
126
|
kind: "register",
|
|
@@ -80,7 +133,7 @@ export async function registerScope(cwd, input) {
|
|
|
80
133
|
});
|
|
81
134
|
if (!result.ok)
|
|
82
135
|
throw new FlowError("INTERNAL_STATE", `failed to register scope for ${input.contractId}: ${result.reason}`);
|
|
83
|
-
return { head: result.head,
|
|
136
|
+
return { head: result.head, overlaps };
|
|
84
137
|
}
|
|
85
138
|
export async function releaseScope(cwd, input) {
|
|
86
139
|
const result = await appendRefLogWithRetry(cwd, REGISTRY_REF, registrySubject, parseRegistryEntry, (entries) => {
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { FlowError } from "../flow-error.js";
|
|
5
|
+
import { syncDirectory } from "../fs/durability.js";
|
|
6
|
+
import { deleteRecoveryRef } from "../git/refs.js";
|
|
7
|
+
import { KEIYAKU_FILE } from "../keiyaku.js";
|
|
8
|
+
import { renderContractView } from "./contract-view.js";
|
|
9
|
+
import { readLedger } from "./ledger.js";
|
|
10
|
+
import { latestBase } from "./status/drift.js";
|
|
11
|
+
import { findRegisteredContractWorktreePath } from "./worktree-path.js";
|
|
12
|
+
const RENEW_SESSION_DIRECTORY = path.join(".keiyaku", "recovery");
|
|
13
|
+
const RENEW_SESSION_FILE = "renew-session.json";
|
|
14
|
+
function isSha(value) {
|
|
15
|
+
return typeof value === "string" && /^[0-9a-f]{40}$/.test(value);
|
|
16
|
+
}
|
|
17
|
+
function parseSession(raw) {
|
|
18
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
19
|
+
throw new FlowError("INTERNAL_STATE", "renew recovery session is malformed");
|
|
20
|
+
}
|
|
21
|
+
const value = raw;
|
|
22
|
+
const keys = Object.keys(value).sort();
|
|
23
|
+
const expected = ["commission", "newBase", "oldBase", "oldHead", "recoveryRef"];
|
|
24
|
+
if (keys.length !== expected.length || keys.some((key, index) => key !== expected[index])) {
|
|
25
|
+
throw new FlowError("INTERNAL_STATE", "renew recovery session has unknown or missing fields");
|
|
26
|
+
}
|
|
27
|
+
if (typeof value.commission !== "string" || !value.commission ||
|
|
28
|
+
!isSha(value.oldBase) || !isSha(value.oldHead) || !isSha(value.newBase) ||
|
|
29
|
+
typeof value.recoveryRef !== "string" || !value.recoveryRef) {
|
|
30
|
+
throw new FlowError("INTERNAL_STATE", "renew recovery session has invalid coordinates");
|
|
31
|
+
}
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
async function sessionPath(cwd, commission) {
|
|
35
|
+
const worktree = await findRegisteredContractWorktreePath(cwd, commission);
|
|
36
|
+
return worktree ? path.join(worktree, RENEW_SESSION_DIRECTORY, RENEW_SESSION_FILE) : undefined;
|
|
37
|
+
}
|
|
38
|
+
export async function readRenewRecoverySession(cwd, commission) {
|
|
39
|
+
const filePath = await sessionPath(cwd, commission);
|
|
40
|
+
if (!filePath)
|
|
41
|
+
return null;
|
|
42
|
+
try {
|
|
43
|
+
return parseSession(JSON.parse(await fs.readFile(filePath, "utf8")));
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (error.code === "ENOENT")
|
|
47
|
+
return null;
|
|
48
|
+
if (error instanceof FlowError)
|
|
49
|
+
throw error;
|
|
50
|
+
throw new FlowError("INTERNAL_STATE", `cannot read renew recovery session for ${commission}`, { cause: error });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export async function writeRenewRecoverySession(cwd, session) {
|
|
54
|
+
const filePath = await sessionPath(cwd, session.commission);
|
|
55
|
+
if (!filePath) {
|
|
56
|
+
throw new FlowError("INTERNAL_STATE", `renew --resolve requires ${session.commission}'s registered contract worktree`);
|
|
57
|
+
}
|
|
58
|
+
const existing = await readRenewRecoverySession(cwd, session.commission);
|
|
59
|
+
if (existing) {
|
|
60
|
+
throw new FlowError("RENEW_SESSION_OPEN", `renew recovery session is already open for ${session.commission}`, {
|
|
61
|
+
facts: { kind: "renew_session", commission: session.commission, nextAction: "continue-or-abort" },
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
const directory = path.dirname(filePath);
|
|
65
|
+
await fs.mkdir(directory, { recursive: true });
|
|
66
|
+
const temporary = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
67
|
+
await fs.writeFile(temporary, `${JSON.stringify(session)}\n`, { encoding: "utf8", flag: "wx" });
|
|
68
|
+
await fs.rename(temporary, filePath);
|
|
69
|
+
await syncDirectory(directory);
|
|
70
|
+
return filePath;
|
|
71
|
+
}
|
|
72
|
+
export async function removeRenewRecoverySession(cwd, commission) {
|
|
73
|
+
const filePath = await sessionPath(cwd, commission);
|
|
74
|
+
if (!filePath)
|
|
75
|
+
return;
|
|
76
|
+
try {
|
|
77
|
+
await fs.unlink(filePath);
|
|
78
|
+
await syncDirectory(path.dirname(filePath));
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (error.code !== "ENOENT")
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function publishedRenewForSession(ledger, session) {
|
|
86
|
+
const tail = ledger.entries.at(-1);
|
|
87
|
+
if (session.commission !== ledger.contractId ||
|
|
88
|
+
tail?.kind !== "renew" ||
|
|
89
|
+
tail.data.oldBase !== session.oldBase ||
|
|
90
|
+
tail.data.oldHead !== session.oldHead ||
|
|
91
|
+
tail.data.newBase !== session.newBase ||
|
|
92
|
+
latestBase(ledger.entries) !== session.newBase)
|
|
93
|
+
return null;
|
|
94
|
+
const previous = ledger.entries.at(-2);
|
|
95
|
+
return {
|
|
96
|
+
renew: tail,
|
|
97
|
+
seal: previous?.kind === "seal" && previous.data.sealedBy === "renew" ? previous : null,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export async function convergeRenewRecoverySession(cwd, commission) {
|
|
101
|
+
const session = await readRenewRecoverySession(cwd, commission);
|
|
102
|
+
if (!session)
|
|
103
|
+
return { state: "none" };
|
|
104
|
+
const ledger = await readLedger(cwd, commission);
|
|
105
|
+
if (!ledger)
|
|
106
|
+
return { state: "live", session };
|
|
107
|
+
const published = publishedRenewForSession(ledger, session);
|
|
108
|
+
if (!published)
|
|
109
|
+
return { state: "live", session };
|
|
110
|
+
const worktree = await findRegisteredContractWorktreePath(cwd, commission);
|
|
111
|
+
if (!worktree) {
|
|
112
|
+
throw new FlowError("INTERNAL_STATE", `registered contract worktree disappeared for spent renew session ${commission}`);
|
|
113
|
+
}
|
|
114
|
+
await fs.writeFile(path.join(worktree, KEIYAKU_FILE), renderContractView({ id: commission, entries: ledger.entries }), "utf8");
|
|
115
|
+
const recoveryCleanup = await deleteRecoveryRef(cwd, session.recoveryRef, session.oldHead);
|
|
116
|
+
await removeRenewRecoverySession(cwd, commission);
|
|
117
|
+
return { state: "spent", session, ledger, ...published, recoveryCleanup };
|
|
118
|
+
}
|
|
119
|
+
export async function assertNoRenewRecoverySession(cwd, commission) {
|
|
120
|
+
const state = await convergeRenewRecoverySession(cwd, commission);
|
|
121
|
+
if (state.state === "none")
|
|
122
|
+
return null;
|
|
123
|
+
if (state.state === "spent")
|
|
124
|
+
return state;
|
|
125
|
+
throw new FlowError("RENEW_SESSION_OPEN", `renew recovery session is open for ${commission}`, {
|
|
126
|
+
facts: { kind: "renew_session", commission, nextAction: "continue-or-abort" },
|
|
127
|
+
});
|
|
128
|
+
}
|