@astrosheep/keiyaku 4.0.2 → 4.0.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/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +5 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +3 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +9 -8
- package/build/src/akuma/akuma.d.ts +34 -11
- package/build/src/akuma/akuma.js +121 -61
- package/build/src/akuma/allowed.d.ts +7 -0
- package/build/src/akuma/allowed.js +40 -0
- package/build/src/akuma/archetype.d.ts +7 -4
- package/build/src/akuma/archetype.js +15 -16
- package/build/src/akuma/body.d.ts +3 -1
- package/build/src/akuma/body.js +19 -8
- package/build/src/akuma/heart/facts.d.ts +49 -7
- package/build/src/akuma/heart/index.d.ts +4 -8
- package/build/src/akuma/heart/index.js +76 -15
- package/build/src/akuma/heart/rows.d.ts +7 -5
- package/build/src/akuma/heart/rows.js +98 -11
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +15 -10
- package/build/src/akuma/heart/soul.js +3 -1
- package/build/src/akuma/heart/storage.d.ts +2 -0
- package/build/src/akuma/heart/storage.js +20 -0
- package/build/src/akuma/heart/timeline.d.ts +1 -5
- package/build/src/akuma/heart/timeline.js +8 -17
- package/build/src/akuma/index.d.ts +4 -1
- package/build/src/akuma/index.js +1 -0
- package/build/src/akuma/projection.d.ts +17 -3
- package/build/src/akuma/projection.js +40 -3
- package/build/src/akuma/provider.d.ts +1 -1
- package/build/src/akuma/provider.js +2 -1
- package/build/src/akuma/providers/acp/core.d.ts +3 -1
- package/build/src/akuma/providers/acp/core.js +4 -4
- package/build/src/akuma/providers/acp/events.d.ts +11 -3
- package/build/src/akuma/providers/acp/events.js +78 -4
- package/build/src/akuma/providers/claude/events.js +62 -3
- package/build/src/akuma/providers/codex-app-server/events.js +1 -13
- package/build/src/akuma/providers/grok-build/index.d.ts +2 -1
- package/build/src/akuma/providers/grok-build/index.js +72 -2
- package/build/src/akuma/providers/opencode-sdk/events.js +71 -6
- package/build/src/akuma/providers/pi/events.js +17 -3
- package/build/src/akuma/providers/unified-patch.d.ts +4 -0
- package/build/src/akuma/providers/unified-patch.js +14 -0
- package/build/src/akuma/publication.d.ts +1 -1
- package/build/src/akuma/publication.js +36 -7
- package/build/src/akuma/requests.d.ts +70 -4
- package/build/src/akuma/requests.js +288 -77
- package/build/src/akuma-body.d.ts +1 -0
- package/build/src/akuma-body.js +31 -0
- package/build/src/cli/accepted.d.ts +4 -1
- package/build/src/cli/accepted.js +26 -4
- package/build/src/cli/commands/akuma-invoke.d.ts +8 -7
- package/build/src/cli/commands/akuma-invoke.js +18 -10
- package/build/src/cli/commands/akuma.d.ts +11 -2
- package/build/src/cli/commands/akuma.js +67 -28
- package/build/src/cli/commands/amend.d.ts +1 -1
- package/build/src/cli/commands/amend.js +1 -1
- package/build/src/cli/commands/contract.d.ts +3 -3
- package/build/src/cli/commands/contract.js +1 -1
- package/build/src/cli/commands/install.js +4 -4
- package/build/src/cli/commands/task-query.d.ts +1 -1
- package/build/src/cli/commands/task-query.js +11 -5
- package/build/src/cli/commands/task.js +4 -4
- package/build/src/cli/coordinates.d.ts +1 -0
- package/build/src/cli/coordinates.js +5 -1
- package/build/src/cli/invoke.d.ts +1 -0
- package/build/src/cli/invoke.js +90 -73
- package/build/src/cli/main.d.ts +2 -0
- package/build/src/cli/main.js +29 -4
- package/build/src/cli/parse.d.ts +1 -0
- package/build/src/cli/parse.js +8 -4
- package/build/src/cli/render/akuma.d.ts +2 -2
- package/build/src/cli/render/akuma.js +84 -13
- package/build/src/cli/render/audit.js +16 -2
- package/build/src/cli/render/contract.d.ts +2 -0
- package/build/src/cli/render/contract.js +203 -36
- package/build/src/cli/render/kanshi.js +64 -29
- package/build/src/cli/render/receipt.d.ts +2 -1
- package/build/src/cli/render/receipt.js +9 -1
- package/build/src/cli/render/text.js +3 -1
- package/build/src/cli/result.d.ts +38 -3
- package/build/src/cli/selectors.d.ts +1 -0
- package/build/src/cli/selectors.js +9 -6
- package/build/src/cli/usage.d.ts +2 -0
- package/build/src/cli/usage.js +4 -0
- package/build/src/contract-worktree.js +3 -0
- package/build/src/dispatch/index.js +50 -39
- package/build/src/git/hooks.d.ts +0 -4
- package/build/src/git/hooks.js +0 -36
- package/build/src/git/integration.d.ts +2 -6
- package/build/src/git/integration.js +8 -8
- package/build/src/git/read-observation.js +1 -0
- package/build/src/git/reconcile.d.ts +6 -1
- package/build/src/git/reconcile.js +55 -9
- package/build/src/git/tender.d.ts +19 -4
- package/build/src/git/tender.js +44 -13
- package/build/src/git/terminal-seal.d.ts +14 -0
- package/build/src/git/terminal-seal.js +15 -2
- package/build/src/git/workspace.d.ts +1 -0
- package/build/src/git/workspace.js +12 -4
- package/build/src/index.d.ts +1 -1
- package/build/src/kanshi/index.d.ts +1 -1
- package/build/src/kanshi/index.js +1 -1
- package/build/src/kanshi/read.d.ts +9 -1
- package/build/src/kanshi/read.js +91 -37
- package/build/src/kanshi/report.d.ts +1 -0
- package/build/src/library/address.d.ts +5 -14
- package/build/src/library/address.js +20 -22
- package/build/src/library/akuma-creation.d.ts +4 -2
- package/build/src/library/akuma-creation.js +43 -26
- package/build/src/library/bind.js +3 -3
- package/build/src/library/catalog.d.ts +1 -2
- package/build/src/library/catalog.js +11 -6
- package/build/src/library/configuration.d.ts +4 -0
- package/build/src/library/configuration.js +19 -0
- package/build/src/library/contract.d.ts +15 -1
- package/build/src/library/contract.js +51 -4
- package/build/src/library/fleet.d.ts +47 -9
- package/build/src/library/fleet.js +184 -44
- package/build/src/library/input.js +1 -0
- package/build/src/library/keiyaku.d.ts +4 -4
- package/build/src/library/keiyaku.js +1 -2
- package/build/src/protocol/bind.d.ts +1 -2
- package/build/src/protocol/bind.js +2 -16
- package/build/src/protocol/operations.d.ts +4 -1
- package/build/src/protocol/operations.js +33 -27
- package/build/src/protocol/read/status.d.ts +3 -1
- package/build/src/protocol/read/status.js +24 -14
- package/build/src/runtime/proc/run.d.ts +1 -0
- package/build/src/runtime/proc/run.js +9 -2
- package/build/src/settlement/settle.d.ts +1 -0
- package/build/src/settlement/settle.js +4 -1
- package/build/src/task/board.d.ts +18 -3
- package/build/src/task/board.js +73 -26
- package/build/src/task/index.d.ts +3 -3
- package/build/src/task/index.js +7 -5
- package/build/src/task/operations.d.ts +2 -19
- package/build/src/task/operations.js +29 -7
- package/build/src/task/query.d.ts +9 -2
- package/build/src/task/query.js +32 -64
- package/build/src/verification/execution.js +2 -1
- package/package.json +3 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { join } from "node:path";
|
|
2
3
|
import { AuthorityCorruptionError } from "../core/facts/errors.js";
|
|
3
4
|
import { contractId } from "../core/facts/types.js";
|
|
4
5
|
import { parseAkuId } from "../akuma/identity.js";
|
|
6
|
+
import { acquireSqliteTransactionLock } from "../coordination/sqlite-transaction-lock.js";
|
|
5
7
|
import { GIT_FORMAT_BYTES, GIT_FORMAT_PATH, GIT_REF, readBlob, readGitPaths, updateGitTree, updateRefsAtomically, writeBlob, writeCommit, } from "../git/repository.js";
|
|
6
8
|
import { withGitDecodeChannel, withGitReadObservation } from "../git/read-observation.js";
|
|
7
9
|
const DISPATCH_ROOT = "dispatch";
|
|
@@ -123,46 +125,55 @@ export async function publishDispatch(input) {
|
|
|
123
125
|
const owner = contractId(input.contractId);
|
|
124
126
|
const intended = { akuId, contractId: owner, dispatchedAt: new Date().toISOString() };
|
|
125
127
|
const path = pathFor(akuId);
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
128
|
+
const held = await acquireSqliteTransactionLock({
|
|
129
|
+
path: join(input.repository.commonDirectory, "keiyaku", "locks", "dispatch.sqlite"),
|
|
130
|
+
mode: "immediate",
|
|
131
|
+
});
|
|
132
|
+
try {
|
|
133
|
+
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt += 1) {
|
|
134
|
+
const snapshot = await readGitPaths(input.repository, [path]);
|
|
135
|
+
const current = await dispatchFromSnapshot(input.repository, snapshot, akuId);
|
|
136
|
+
if (current !== null) {
|
|
137
|
+
return current.contractId === owner
|
|
138
|
+
? { kind: "dispatched", dispatch: current }
|
|
139
|
+
: { kind: "failed", failure: { kind: "conflict", current } };
|
|
140
|
+
}
|
|
141
|
+
const changes = new Map();
|
|
142
|
+
if (snapshot.commit === null) {
|
|
143
|
+
changes.set(GIT_FORMAT_PATH, { oid: await writeBlob(input.repository, GIT_FORMAT_BYTES) });
|
|
144
|
+
}
|
|
145
|
+
changes.set(path, { oid: await writeBlob(input.repository, bytesFor(intended)) });
|
|
146
|
+
const tree = await updateGitTree(input.repository, snapshot.tree, changes);
|
|
147
|
+
const commit = await writeCommit({
|
|
148
|
+
repository: input.repository,
|
|
149
|
+
tree,
|
|
150
|
+
parent: snapshot.commit,
|
|
151
|
+
message: `dispatch ${akuId}`,
|
|
152
|
+
at: intended.dispatchedAt,
|
|
153
|
+
});
|
|
154
|
+
const publication = await updateRefsAtomically(input.repository, [{
|
|
155
|
+
ref: GIT_REF,
|
|
156
|
+
newOid: commit,
|
|
157
|
+
expectedOid: snapshot.commit,
|
|
158
|
+
}]);
|
|
159
|
+
if (publication.kind === "published")
|
|
160
|
+
return { kind: "dispatched", dispatch: intended };
|
|
161
|
+
const observed = await observedPublication(input.repository, akuId, owner);
|
|
162
|
+
if (observed !== null)
|
|
163
|
+
return observed;
|
|
164
|
+
if (publication.kind === "non-published") {
|
|
165
|
+
const fresh = await readGitPaths(input.repository, [path]);
|
|
166
|
+
if (fresh.commit === snapshot.commit) {
|
|
167
|
+
return {
|
|
168
|
+
kind: "failed",
|
|
169
|
+
failure: { kind: "publication-failed", diagnostic: diagnostic(publication.error) },
|
|
170
|
+
};
|
|
171
|
+
}
|
|
164
172
|
}
|
|
165
173
|
}
|
|
174
|
+
return { kind: "failed", failure: { kind: "contention" } };
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
held.close();
|
|
166
178
|
}
|
|
167
|
-
return { kind: "failed", failure: { kind: "contention" } };
|
|
168
179
|
}
|
package/build/src/git/hooks.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { type Settings } from "../settings.js";
|
|
2
1
|
export type HookCommand = Readonly<{
|
|
3
2
|
argv: readonly string[];
|
|
4
3
|
timeoutMs: number;
|
|
@@ -29,9 +28,6 @@ export type WorktreeHookLag = Readonly<{
|
|
|
29
28
|
failure: HookFailure;
|
|
30
29
|
}>;
|
|
31
30
|
type HookPhase = "create" | "destroy";
|
|
32
|
-
export declare function worktreeHooksFrom(input: Readonly<{
|
|
33
|
-
settings: Settings;
|
|
34
|
-
}>): WorktreeHooks;
|
|
35
31
|
export declare function hookMarkerPath(administrationDirectory: string): string;
|
|
36
32
|
export type HookCommandRun = Readonly<{
|
|
37
33
|
kind: "ok";
|
package/build/src/git/hooks.js
CHANGED
|
@@ -4,46 +4,10 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import { replaceFileDurably } from "../coordination/durable-file.js";
|
|
5
5
|
import { acquireSqliteTransactionLock } from "../coordination/sqlite-transaction-lock.js";
|
|
6
6
|
import { runProcess, runProcessToExit } from "../runtime/proc/run.js";
|
|
7
|
-
import { SettingsError } from "../settings.js";
|
|
8
7
|
const MARKER_VERSION = 1;
|
|
9
8
|
function object(value) {
|
|
10
9
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
11
10
|
}
|
|
12
|
-
export function worktreeHooksFrom(input) {
|
|
13
|
-
if (!object(input))
|
|
14
|
-
throw new TypeError("worktreeHooksFrom input must be an object");
|
|
15
|
-
const view = input.settings.namespace("worktree");
|
|
16
|
-
if (view.kind === "failed") {
|
|
17
|
-
throw new SettingsError(view.failures.map((failure) => `${failure.scope}: ${failure.diagnostic}`).join("; "));
|
|
18
|
-
}
|
|
19
|
-
for (const entry of view.entries) {
|
|
20
|
-
if (entry.name !== "create" && entry.name !== "destroy")
|
|
21
|
-
throw new SettingsError(`worktree has unknown entry: ${entry.name}`);
|
|
22
|
-
}
|
|
23
|
-
const commands = (phase) => {
|
|
24
|
-
const selected = view.entries.find((entry) => entry.name === phase);
|
|
25
|
-
if (selected === undefined)
|
|
26
|
-
return Object.freeze([]);
|
|
27
|
-
if (!Array.isArray(selected.value))
|
|
28
|
-
throw new SettingsError(`worktree.${phase} must be an array`);
|
|
29
|
-
return Object.freeze(selected.value.map((item, index) => {
|
|
30
|
-
if (!object(item))
|
|
31
|
-
throw new SettingsError(`worktree.${phase}[${index}] must be an object`);
|
|
32
|
-
const coordinate = `worktree.${phase}[${index}]`;
|
|
33
|
-
exactKeys(item, ["argv", "timeoutMs"], coordinate);
|
|
34
|
-
if (!Array.isArray(item.argv) || item.argv.length === 0 || !item.argv.every((value) => typeof value === "string")) {
|
|
35
|
-
throw new SettingsError(`${coordinate}.argv must be a nonempty string array`);
|
|
36
|
-
}
|
|
37
|
-
if (item.argv[0].trim().length === 0)
|
|
38
|
-
throw new SettingsError(`${coordinate}.argv[0] must be nonblank`);
|
|
39
|
-
if (!Number.isSafeInteger(item.timeoutMs) || item.timeoutMs < 1 || item.timeoutMs > 2_147_483_647) {
|
|
40
|
-
throw new SettingsError(`${coordinate}.timeoutMs must be an integer from 1 through 2147483647`);
|
|
41
|
-
}
|
|
42
|
-
return Object.freeze({ argv: Object.freeze([...item.argv]), timeoutMs: item.timeoutMs });
|
|
43
|
-
}));
|
|
44
|
-
};
|
|
45
|
-
return Object.freeze({ create: commands("create"), destroy: commands("destroy") });
|
|
46
|
-
}
|
|
47
11
|
function exactKeys(value, keys, coordinate) {
|
|
48
12
|
const actual = Object.keys(value).sort();
|
|
49
13
|
const expected = [...keys].sort();
|
|
@@ -2,7 +2,7 @@ import type { Preparation } from "../core/decide.js";
|
|
|
2
2
|
import type { ChangeId, ContractCoordinates, ContractId, SnapshotId } from "../core/facts/types.js";
|
|
3
3
|
import { type GitObjectId } from "./identity.js";
|
|
4
4
|
import { type GitRepository } from "./repository.js";
|
|
5
|
-
import type { TenderCapture } from "./tender.js";
|
|
5
|
+
import type { DeliveryCommitMetadata, TenderCapture } from "./tender.js";
|
|
6
6
|
declare const REQUIRED_GIT: "2.38";
|
|
7
7
|
export type IntegrationPreparationRefusal = Readonly<{
|
|
8
8
|
kind: "integration-failed";
|
|
@@ -26,11 +26,7 @@ export type IntegrationPlan = Readonly<{
|
|
|
26
26
|
/** Mint the one worktree-content identity shared by review and delivery. */
|
|
27
27
|
export declare function worktreeChangeId(repository: GitRepository, input: IntegrationCoordinates, tender: TenderCapture): Promise<ChangeId>;
|
|
28
28
|
/** Materialize the exact integration tree against its observed predecessor. */
|
|
29
|
-
export declare function materializeIntegrationSnapshot(repository: GitRepository, tree: GitObjectId, parent: SnapshotId,
|
|
30
|
-
contractId: ContractId;
|
|
31
|
-
title: string;
|
|
32
|
-
message?: string;
|
|
33
|
-
}>): Promise<SnapshotId>;
|
|
29
|
+
export declare function materializeIntegrationSnapshot(repository: GitRepository, tree: GitObjectId, parent: SnapshotId, metadata: DeliveryCommitMetadata): Promise<SnapshotId>;
|
|
34
30
|
export declare function planIntegration(repository: GitRepository, input: IntegrationCoordinates, tender: TenderCapture, requireBranchesToBeUpToDate: boolean): Promise<Preparation<IntegrationPlan, Readonly<{
|
|
35
31
|
kind: "target-missing";
|
|
36
32
|
contractId: ContractId;
|
|
@@ -44,14 +44,14 @@ export async function worktreeChangeId(repository, input, tender) {
|
|
|
44
44
|
return mintChangeId(id === "" ? "0000000000000000000000000000000000000000" : id);
|
|
45
45
|
}
|
|
46
46
|
/** Materialize the exact integration tree against its observed predecessor. */
|
|
47
|
-
export async function materializeIntegrationSnapshot(repository, tree, parent,
|
|
48
|
-
const commit = (await runGitWithEnvironment(repository, ["commit-tree", tree, "-p", gitObjectIdForSnapshot(parent)],
|
|
49
|
-
GIT_AUTHOR_NAME:
|
|
50
|
-
GIT_AUTHOR_EMAIL:
|
|
51
|
-
GIT_COMMITTER_NAME:
|
|
52
|
-
GIT_COMMITTER_EMAIL:
|
|
53
|
-
GIT_AUTHOR_DATE:
|
|
54
|
-
GIT_COMMITTER_DATE:
|
|
47
|
+
export async function materializeIntegrationSnapshot(repository, tree, parent, metadata) {
|
|
48
|
+
const commit = (await runGitWithEnvironment(repository, ["commit-tree", tree, "-p", gitObjectIdForSnapshot(parent)], metadata.message, {
|
|
49
|
+
GIT_AUTHOR_NAME: metadata.identity.name,
|
|
50
|
+
GIT_AUTHOR_EMAIL: metadata.identity.email,
|
|
51
|
+
GIT_COMMITTER_NAME: metadata.identity.name,
|
|
52
|
+
GIT_COMMITTER_EMAIL: metadata.identity.email,
|
|
53
|
+
GIT_AUTHOR_DATE: metadata.at,
|
|
54
|
+
GIT_COMMITTER_DATE: metadata.at,
|
|
55
55
|
})).toString("utf8").trim();
|
|
56
56
|
return mintSnapshotId(commit);
|
|
57
57
|
}
|
|
@@ -39,6 +39,7 @@ function batchObjectReader(repository) {
|
|
|
39
39
|
const child = spawn("git", ["cat-file", "--batch"], {
|
|
40
40
|
cwd: repository.effectiveCwd,
|
|
41
41
|
stdio: ["pipe", "pipe", "pipe"],
|
|
42
|
+
windowsHide: true,
|
|
42
43
|
});
|
|
43
44
|
const cursor = streamCursor(child.stdout);
|
|
44
45
|
const cache = new Map();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type GitOid, type GitRepository } from "./repository.js";
|
|
2
|
-
import type { ContractId, ContractState } from "../core/facts/types.js";
|
|
2
|
+
import type { ContractId, ContractState, SnapshotId } from "../core/facts/types.js";
|
|
3
3
|
import type { GitDecodeChannel } from "./read-observation.js";
|
|
4
4
|
import { type TargetCheckoutEffect, type TargetCheckoutLag } from "./target-placement.js";
|
|
5
5
|
import { type WorktreeHookLag, type WorktreeHooks } from "./hooks.js";
|
|
@@ -8,6 +8,11 @@ export type Effect = Readonly<{
|
|
|
8
8
|
kind: "worktree";
|
|
9
9
|
path: string;
|
|
10
10
|
action: "created" | "removed" | "unchanged";
|
|
11
|
+
}> | Readonly<{
|
|
12
|
+
kind: "recovery-snapshot";
|
|
13
|
+
action: "created";
|
|
14
|
+
snapshot: SnapshotId;
|
|
15
|
+
retention: "ephemeral";
|
|
11
16
|
}> | TargetCheckoutEffect | Readonly<{
|
|
12
17
|
kind: "ref";
|
|
13
18
|
name: string;
|
|
@@ -2,14 +2,14 @@ import { access, mkdir } from "node:fs/promises";
|
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { acquireSqliteTransactionLock } from "../coordination/sqlite-transaction-lock.js";
|
|
4
4
|
import { AuthorityCorruptionError } from "../core/facts/errors.js";
|
|
5
|
-
import { CANDIDATE_PIN_REF_NAMESPACE, commonGitDirectory, DELIVERY_REF_NAMESPACE, GitPlumbingError, isKeiyakuOwnedRef, readRef, registeredWorktreePaths, runGit, worktreeGitDirectory, } from "./repository.js";
|
|
5
|
+
import { CANDIDATE_PIN_REF_NAMESPACE, commonGitDirectory, DELIVERY_REF_NAMESPACE, GitPlumbingError, isKeiyakuOwnedRef, readRef, registeredWorktreePaths, runGit, writeCommit, worktreeGitDirectory, } from "./repository.js";
|
|
6
6
|
import { contractLocator, contractPhysicalName, gitObjectIdForSnapshot, mintSnapshotId, } from "./identity.js";
|
|
7
7
|
import { observeContractAt } from "./observe.js";
|
|
8
8
|
import { acquireTargetPlacementFence, recoverTargetPlacement, } from "./target-placement.js";
|
|
9
9
|
import { runCreateHooks, runDestroyHooks, } from "./hooks.js";
|
|
10
10
|
import { worktreePath } from "./workspace.js";
|
|
11
11
|
import { collectableScratchWorktrees } from "./scratch.js";
|
|
12
|
-
import { terminalSealExpectations as decodeTerminalSealExpectations, terminalSealSnapshots,
|
|
12
|
+
import { terminalSealExpectations as decodeTerminalSealExpectations, terminalSealSnapshots, observeTerminalWorkspace, } from "./terminal-seal.js";
|
|
13
13
|
const pathExists = (path) => access(path).then(() => true, () => false);
|
|
14
14
|
function deliveryRefFor(contract) {
|
|
15
15
|
return `${DELIVERY_REF_NAMESPACE}/${contractPhysicalName(contract)}`;
|
|
@@ -230,17 +230,62 @@ async function terminalSealExpectations(channel, state) {
|
|
|
230
230
|
const objects = await channel.readObjects(snapshots.map(gitObjectIdForSnapshot));
|
|
231
231
|
return decodeTerminalSealExpectations(state, objects);
|
|
232
232
|
}
|
|
233
|
-
async function removeSealedTerminalWorktree({ repository, topology, path, expected, hooks, retryHooks, acc }) {
|
|
233
|
+
async function removeSealedTerminalWorktree({ repository, topology, path, state, expected, hooks, retryHooks, acc }) {
|
|
234
234
|
if (topology.paths.has(path) && await pathExists(path)) {
|
|
235
|
-
const
|
|
236
|
-
if (
|
|
237
|
-
|
|
235
|
+
const terminal = state.terminal;
|
|
236
|
+
if (terminal === null)
|
|
237
|
+
throw new Error(`terminal worktree cleanup received active Contract ${state.id}`);
|
|
238
|
+
const canRecover = terminal.kind === "abandoned";
|
|
239
|
+
let recovery = null;
|
|
240
|
+
const recordRecovery = async (workspace) => {
|
|
241
|
+
const snapshot = mintSnapshotId(await writeCommit({
|
|
242
|
+
repository,
|
|
243
|
+
tree: workspace.tree,
|
|
244
|
+
parent: gitObjectIdForSnapshot(recovery?.snapshot ?? workspace.head),
|
|
245
|
+
message: `${state.id}: ephemeral abandoned-workspace recovery\n\nKeiyaku-Contract: ${state.id}`,
|
|
246
|
+
actor: "Keiyaku Recovery",
|
|
247
|
+
at: terminal.at,
|
|
248
|
+
}));
|
|
249
|
+
const effect = {
|
|
250
|
+
kind: "recovery-snapshot",
|
|
251
|
+
action: "created",
|
|
252
|
+
snapshot,
|
|
253
|
+
retention: "ephemeral",
|
|
254
|
+
};
|
|
255
|
+
const existing = acc.effects.findIndex((item) => item.kind === "recovery-snapshot");
|
|
256
|
+
if (existing < 0)
|
|
257
|
+
acc.effects.push(effect);
|
|
258
|
+
else
|
|
259
|
+
acc.effects[existing] = effect;
|
|
260
|
+
return { snapshot, workspace };
|
|
261
|
+
};
|
|
262
|
+
const before = await observeTerminalWorkspace(repository, path, expected);
|
|
263
|
+
const beforeWorkspace = before.workspace;
|
|
264
|
+
const beforeHooks = before.unsealed;
|
|
265
|
+
if (beforeHooks !== null) {
|
|
266
|
+
if (!canRecover || beforeWorkspace.submodules.length > 0) {
|
|
267
|
+
return retainTerminalWorktree(path, beforeHooks, acc);
|
|
268
|
+
}
|
|
269
|
+
recovery = await recordRecovery(beforeWorkspace);
|
|
270
|
+
}
|
|
238
271
|
const hookLag = await runDestroyHooks(path, await worktreeGitDirectory(repository, path), hooks, retryHooks);
|
|
239
272
|
if (hookLag !== null)
|
|
240
273
|
return retainTerminalWorktree(path, hookLag, acc);
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
274
|
+
const after = await observeTerminalWorkspace(repository, path, expected);
|
|
275
|
+
const afterWorkspace = after.workspace;
|
|
276
|
+
const afterHooks = after.unsealed;
|
|
277
|
+
if (afterHooks !== null) {
|
|
278
|
+
if (!canRecover || afterWorkspace.submodules.length > 0) {
|
|
279
|
+
return retainTerminalWorktree(path, afterHooks, acc);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (canRecover
|
|
283
|
+
&& (recovery === null
|
|
284
|
+
|| recovery.workspace.head !== afterWorkspace.head
|
|
285
|
+
|| recovery.workspace.tree !== afterWorkspace.tree)
|
|
286
|
+
&& (recovery !== null || beforeWorkspace.head !== afterWorkspace.head || beforeWorkspace.tree !== afterWorkspace.tree)) {
|
|
287
|
+
recovery = await recordRecovery(afterWorkspace);
|
|
288
|
+
}
|
|
244
289
|
}
|
|
245
290
|
const removal = await removeWorktree(repository, topology, path, true);
|
|
246
291
|
if (removal.retained)
|
|
@@ -300,6 +345,7 @@ async function reconcileTerminalManagedWorktree({ repository, channel, hooks, re
|
|
|
300
345
|
repository: primary,
|
|
301
346
|
topology,
|
|
302
347
|
path,
|
|
348
|
+
state,
|
|
303
349
|
expected,
|
|
304
350
|
hooks,
|
|
305
351
|
retryHooks,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Preparation } from "../core/decide.js";
|
|
2
|
-
import type { ContractCoordinates, ContractId, SnapshotId } from "../core/facts/types.js";
|
|
2
|
+
import type { ActorId, ContractCoordinates, ContractId, SnapshotId } from "../core/facts/types.js";
|
|
3
3
|
import { type GitObjectId } from "./identity.js";
|
|
4
4
|
import { type GitRepository } from "./repository.js";
|
|
5
5
|
import { captureWorkspaceTree } from "./workspace.js";
|
|
@@ -34,6 +34,7 @@ export type DirtyWorkspaceRefusal = Readonly<{
|
|
|
34
34
|
export type TenderCapture = Readonly<{
|
|
35
35
|
tree: GitObjectId;
|
|
36
36
|
head: SnapshotId;
|
|
37
|
+
at: string;
|
|
37
38
|
dirty: boolean;
|
|
38
39
|
changes: Awaited<ReturnType<typeof captureWorkspaceTree>>["changes"];
|
|
39
40
|
}>;
|
|
@@ -41,9 +42,23 @@ export type TenderCapture = Readonly<{
|
|
|
41
42
|
export declare function captureTender(repository: GitRepository, input: TenderCaptureCoordinates): Promise<Preparation<TenderCapture, TenderCaptureRefusal>>;
|
|
42
43
|
export declare function dirtyTenderRefusal(repository: GitRepository, contractId: ContractId, tender: TenderCapture): Promise<DirtyWorkspaceRefusal>;
|
|
43
44
|
export declare function dirtyTenderDelta(repository: GitRepository, tender: TenderCapture): Promise<WorkspaceDirtyDelta | undefined>;
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
type GitCommitIdentity = Readonly<{
|
|
46
|
+
name: string;
|
|
47
|
+
email: string;
|
|
48
|
+
}>;
|
|
49
|
+
export type DeliveryCommitMetadata = Readonly<{
|
|
50
|
+
message: string;
|
|
51
|
+
at: string;
|
|
52
|
+
identity: GitCommitIdentity;
|
|
53
|
+
}>;
|
|
54
|
+
export declare function prepareDeliveryCommitMetadata(repository: GitRepository, input: Readonly<{
|
|
46
55
|
contractId: ContractId;
|
|
47
56
|
title: string;
|
|
57
|
+
document: string;
|
|
58
|
+
at: string;
|
|
59
|
+
actor?: ActorId;
|
|
48
60
|
message?: string;
|
|
49
|
-
}>): Promise<
|
|
61
|
+
}>): Promise<DeliveryCommitMetadata>;
|
|
62
|
+
/** Materialize the tender tree when it contains admitted dirty workspace bytes. */
|
|
63
|
+
export declare function materializeTenderSnapshot(repository: GitRepository, tender: TenderCapture, commit: DeliveryCommitMetadata): Promise<SnapshotId>;
|
|
64
|
+
export {};
|
package/build/src/git/tender.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { access } from "node:fs/promises";
|
|
2
2
|
import { gitObjectIdForSnapshot, mintSnapshotId } from "./identity.js";
|
|
3
|
-
import { decodeGitNumstat, registeredWorktreePaths, runGit, runGitWithEnvironment } from "./repository.js";
|
|
3
|
+
import { decodeGitNumstat, GitPlumbingError, registeredWorktreePaths, runGit, runGitWithEnvironment, } from "./repository.js";
|
|
4
4
|
import { captureWorkspaceTree, worktreePath } from "./workspace.js";
|
|
5
5
|
async function workspaceExists(repository, workspace, path) {
|
|
6
6
|
const exists = await access(path).then(() => true, () => false);
|
|
@@ -43,21 +43,52 @@ export async function dirtyTenderDelta(repository, tender) {
|
|
|
43
43
|
const { staged, unstaged, untracked } = tender.changes;
|
|
44
44
|
return { staged, unstaged, untracked, shortStat: await dirtyShortStat(repository, tender) };
|
|
45
45
|
}
|
|
46
|
-
function
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
async function effectiveConfig(repository, key) {
|
|
47
|
+
try {
|
|
48
|
+
const value = (await runGit(repository, ["config", "--get", key])).toString("utf8").trim();
|
|
49
|
+
return value.length === 0 ? undefined : value;
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
if (error instanceof GitPlumbingError && error.status === 1)
|
|
53
|
+
return undefined;
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function contractBody(bytes) {
|
|
58
|
+
return `${bytes.replace(/(?:\r\n|\r|\n)+$/u, "")}\n`;
|
|
59
|
+
}
|
|
60
|
+
export async function prepareDeliveryCommitMetadata(repository, input) {
|
|
61
|
+
let identity;
|
|
62
|
+
if (input.actor !== undefined) {
|
|
63
|
+
identity = { name: input.actor, email: "keiyaku@localhost" };
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
const [name, email] = await Promise.all([
|
|
67
|
+
effectiveConfig(repository, "user.name"),
|
|
68
|
+
effectiveConfig(repository, "user.email"),
|
|
69
|
+
]);
|
|
70
|
+
identity = name !== undefined && email !== undefined
|
|
71
|
+
? { name, email }
|
|
72
|
+
: { name: "Keiyaku", email: "keiyaku@localhost" };
|
|
73
|
+
}
|
|
74
|
+
const subject = input.message ?? `${input.contractId}: ${input.title}`;
|
|
75
|
+
return {
|
|
76
|
+
message: `${subject}\n\n${contractBody(input.document)}\nKeiyaku-Contract: ${input.contractId}\n`,
|
|
77
|
+
at: input.at,
|
|
78
|
+
identity,
|
|
79
|
+
};
|
|
49
80
|
}
|
|
50
81
|
/** Materialize the tender tree when it contains admitted dirty workspace bytes. */
|
|
51
|
-
export async function materializeTenderSnapshot(repository, tender,
|
|
82
|
+
export async function materializeTenderSnapshot(repository, tender, commit) {
|
|
52
83
|
if (!tender.dirty)
|
|
53
84
|
return tender.head;
|
|
54
|
-
const
|
|
55
|
-
GIT_AUTHOR_NAME:
|
|
56
|
-
GIT_AUTHOR_EMAIL:
|
|
57
|
-
GIT_COMMITTER_NAME:
|
|
58
|
-
GIT_COMMITTER_EMAIL:
|
|
59
|
-
GIT_AUTHOR_DATE:
|
|
60
|
-
GIT_COMMITTER_DATE:
|
|
85
|
+
const oid = (await runGitWithEnvironment(repository, ["commit-tree", tender.tree, "-p", gitObjectIdForSnapshot(tender.head)], commit.message, {
|
|
86
|
+
GIT_AUTHOR_NAME: commit.identity.name,
|
|
87
|
+
GIT_AUTHOR_EMAIL: commit.identity.email,
|
|
88
|
+
GIT_COMMITTER_NAME: commit.identity.name,
|
|
89
|
+
GIT_COMMITTER_EMAIL: commit.identity.email,
|
|
90
|
+
GIT_AUTHOR_DATE: commit.at,
|
|
91
|
+
GIT_COMMITTER_DATE: commit.at,
|
|
61
92
|
})).toString("utf8").trim();
|
|
62
|
-
return mintSnapshotId(
|
|
93
|
+
return mintSnapshotId(oid);
|
|
63
94
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ContractState, SnapshotId } from "../core/facts/types.js";
|
|
2
2
|
import { type GitObjectId } from "./identity.js";
|
|
3
3
|
import { type GitOid, type GitRepository } from "./repository.js";
|
|
4
|
+
import { captureWorkspaceTree } from "./workspace.js";
|
|
4
5
|
export type UnsealedBytes = Readonly<{
|
|
5
6
|
kind: "unsealed-bytes";
|
|
6
7
|
path: string;
|
|
@@ -19,6 +20,19 @@ export type TerminalSealObject = Readonly<{
|
|
|
19
20
|
}> | Readonly<{
|
|
20
21
|
kind: "missing";
|
|
21
22
|
}>;
|
|
23
|
+
type WorkspaceTree = Awaited<ReturnType<typeof captureWorkspaceTree>>;
|
|
24
|
+
export type TerminalWorkspace = Readonly<{
|
|
25
|
+
tree: GitObjectId;
|
|
26
|
+
head: SnapshotId;
|
|
27
|
+
submodules: readonly string[];
|
|
28
|
+
}>;
|
|
29
|
+
export type TerminalWorkspaceObservation = Readonly<{
|
|
30
|
+
workspace: TerminalWorkspace;
|
|
31
|
+
unsealed: UnsealedBytes | null;
|
|
32
|
+
}>;
|
|
22
33
|
export declare function terminalSealSnapshots(state: ContractState): readonly SnapshotId[];
|
|
23
34
|
export declare function terminalSealExpectations(state: ContractState, objects: ReadonlyMap<GitOid, TerminalSealObject>): TerminalSealExpectations;
|
|
35
|
+
export declare function unsealedWorkspaceBytes(repository: GitRepository, path: string, expected: TerminalSealExpectations, workspace: WorkspaceTree): Promise<UnsealedBytes | null>;
|
|
24
36
|
export declare function unsealedBytes(repository: GitRepository, path: string, expected: TerminalSealExpectations): Promise<UnsealedBytes | null>;
|
|
37
|
+
export declare function observeTerminalWorkspace(repository: GitRepository, path: string, expected: TerminalSealExpectations): Promise<TerminalWorkspaceObservation>;
|
|
38
|
+
export {};
|
|
@@ -63,8 +63,7 @@ async function changedPaths(repository, left, right) {
|
|
|
63
63
|
throw new Error("Git tree diff paths are not NUL terminated");
|
|
64
64
|
return fields.slice(0, -1).sort();
|
|
65
65
|
}
|
|
66
|
-
export async function
|
|
67
|
-
const workspace = await captureWorkspaceTree(repository, path);
|
|
66
|
+
export async function unsealedWorkspaceBytes(repository, path, expected, workspace) {
|
|
68
67
|
const headIsSealed = expected.heads.includes(workspace.head);
|
|
69
68
|
if (workspace.changes.submodules.length > 0) {
|
|
70
69
|
return {
|
|
@@ -87,3 +86,17 @@ export async function unsealedBytes(repository, path, expected) {
|
|
|
87
86
|
...(headIsSealed ? {} : { head: workspace.head }),
|
|
88
87
|
};
|
|
89
88
|
}
|
|
89
|
+
export async function unsealedBytes(repository, path, expected) {
|
|
90
|
+
return (await observeTerminalWorkspace(repository, path, expected)).unsealed;
|
|
91
|
+
}
|
|
92
|
+
export async function observeTerminalWorkspace(repository, path, expected) {
|
|
93
|
+
const captured = await captureWorkspaceTree(repository, path);
|
|
94
|
+
return {
|
|
95
|
+
workspace: {
|
|
96
|
+
tree: captured.tree,
|
|
97
|
+
head: captured.head,
|
|
98
|
+
submodules: captured.changes.submodules,
|
|
99
|
+
},
|
|
100
|
+
unsealed: await unsealedWorkspaceBytes(repository, path, expected, captured),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -114,24 +114,32 @@ export async function observeTargetLag(repository, workspace, head) {
|
|
|
114
114
|
}
|
|
115
115
|
/** Capture tracked and untracked workspace bytes without changing its real index. */
|
|
116
116
|
export async function captureWorkspaceTree(repository, workspace) {
|
|
117
|
-
const identities = (await runGit(repository, ["-C", workspace, "show", "-s", "--format=%H%n%T", "HEAD"]))
|
|
117
|
+
const identities = (await runGit(repository, ["-C", workspace, "show", "-s", "--format=%H%n%T%n%cI", "HEAD"]))
|
|
118
118
|
.toString("utf8")
|
|
119
119
|
.split("\n");
|
|
120
|
-
if (identities.length !==
|
|
121
|
-
|
|
120
|
+
if (identities.length !== 4
|
|
121
|
+
|| identities[0] === undefined
|
|
122
|
+
|| identities[1] === undefined
|
|
123
|
+
|| identities[2] === undefined
|
|
124
|
+
|| identities[3] !== "") {
|
|
125
|
+
throw new Error("workspace HEAD/tree/time resolution returned an unexpected shape");
|
|
122
126
|
}
|
|
123
127
|
const head = mintSnapshotId(identities[0]);
|
|
124
128
|
const tree = gitObjectId(identities[1], "workspace tree");
|
|
129
|
+
const at = identities[2];
|
|
130
|
+
if (Number.isNaN(Date.parse(at)))
|
|
131
|
+
throw new Error("workspace HEAD has an invalid committer timestamp");
|
|
125
132
|
const changes = await workspaceChanges(repository, workspace);
|
|
126
133
|
const dirty = changes.staged.length > 0 || changes.unstaged.length > 0 || changes.untracked.length > 0;
|
|
127
134
|
if (!dirty)
|
|
128
|
-
return { tree, head, dirty, changes };
|
|
135
|
+
return { tree, head, at, dirty, changes };
|
|
129
136
|
return await withPrivateGitIndex(async (environment) => {
|
|
130
137
|
await runGitWithEnvironment(repository, ["-C", workspace, "read-tree", "HEAD"], undefined, environment);
|
|
131
138
|
await runGitWithEnvironment(repository, ["-C", workspace, "add", "--all"], undefined, environment);
|
|
132
139
|
return {
|
|
133
140
|
tree: gitObjectId((await runGitWithEnvironment(repository, ["-C", workspace, "write-tree"], undefined, environment)).toString("utf8").trim(), "workspace tree"),
|
|
134
141
|
head,
|
|
142
|
+
at,
|
|
135
143
|
dirty,
|
|
136
144
|
changes,
|
|
137
145
|
};
|
package/build/src/index.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export { settings } from "./settings.js";
|
|
|
3
3
|
export { World, WorldError } from "./world.js";
|
|
4
4
|
export type { WorldResolution, WorldResolutionInput, WorldRoot } from "./world.js";
|
|
5
5
|
export type { Settings, SettingsEntry, SettingsInput, SettingsNamespaceFailure, SettingsNamespaceView, SettingsScope, SettingsScopeState, } from "./settings.js";
|
|
6
|
-
export type { AttestationVerdict, AbandonInput, ActorId, AkuId, AkumaAddressInput, AkumaAlias, AkumaGlob, AkumaHistoryInput, AkumaHistoryResult, AkumaInterruptInput, AkumaInterruptResult, AkumaKillResult, AkumaSetAddressInput, AkumaStatus,
|
|
6
|
+
export type { AttestationVerdict, AbandonInput, ActorId, AkuId, AllowedAction, AllowedActions, AkumaAddressInput, AkumaAlias, AkumaGlob, AkumaHistoryInput, AkumaHistoryResult, AkumaInterruptInput, AkumaInterruptResult, AkumaKillResult, AkumaObservation, AkumaSetAddressInput, AkumaStatus, AkumaTellInput, CreatedTaskObservation, AkumaTellResult, AkumaWaitInput, AkumaWaitResult, AkumaWorldScopeRefusal, AliasBinding, AliasStage, AmendResult, AmendInput, ArcInput, AuditReport, DeliveryPreparationRefusal, BindInput, BindResult, CallInput, CallObservation, CallResult, Catalog, CatalogInput, CatalogQuery, ChangeId, ContractId, ContractState, ContractBoard, ContractDisposition, ContractGateCurrent, ContractGateReport, ContractHistory, ContractHistoryEvent, ContractListInput, ContractObservation, ContractObservationInput, ContractPhase, ContractRow, DeliverInput, Fact, FactKind, Dispatch, DispatchFailure, DispatchStage, DirectAkumaSelector, Gate, GatesFromInput, HookCommand, RequireBranchesToBeUpToDateFromInput, KeiyakuOfInput, KeiyakuRefusal, KeiyakuRetryReason, ForkInput, ForkResult, IntegrationFailure, Lag, MutationResult, PlacementStop, RegionOverlap, ReconcileInput, RepoReconcileReport, Review, ReconcileReport, ReviewInput, RepoAtInput, AuditInput, SnapshotId, SettlementAction, SettlementLag, SettlementReport, SetAkumaSelector, TaskId, TopologyEffect, VerificationReuse, VerificationStop, WorktreeHooks, } from "./library/keiyaku.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { kanshi } from "./read.js";
|
|
1
|
+
export { kanshi, observeKanshi } from "./read.js";
|
|
2
2
|
export { selectKanshi, selectRegion } from "./select.js";
|
|
3
3
|
export type { KanshiInput } from "./read.js";
|
|
4
4
|
export type { AkumaKanshiRow, AkumaKanshiWorld, ContractEndpointObservation, ContractHolderObservation, ContractKanshiBoard, ContractKanshiRow, KanshiReport, Section, TaskKanshiRow, TaskKanshiWorld, KanshiRegionSelection, RegionDeclaration, RegionIntersection, RegionPathMatch, RegionRead, } from "./report.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { kanshi } from "./read.js";
|
|
1
|
+
export { kanshi, observeKanshi } from "./read.js";
|
|
2
2
|
export { selectKanshi, selectRegion } from "./select.js";
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { Repo } from "../library/repo.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ContractBoard } from "../library/contract.js";
|
|
3
|
+
import { type AliasBinding } from "../alias/index.js";
|
|
4
|
+
import type { KanshiReport, Section, KanshiRegionSelection } from "./report.js";
|
|
3
5
|
import type { WorldRoot } from "../world.js";
|
|
4
6
|
export type KanshiInput = Readonly<{
|
|
5
7
|
world: WorldRoot | null;
|
|
6
8
|
repo?: Repo;
|
|
7
9
|
region?: KanshiRegionSelection;
|
|
10
|
+
contract?: ContractBoard["rows"][number]["id"];
|
|
8
11
|
}>;
|
|
12
|
+
export type KanshiObservation = Readonly<{
|
|
13
|
+
report: KanshiReport;
|
|
14
|
+
aliases: Section<readonly AliasBinding[]>;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function observeKanshi(input: KanshiInput): Promise<KanshiObservation>;
|
|
9
17
|
export declare function kanshi(input: KanshiInput): Promise<KanshiReport>;
|