@astrosheep/keiyaku 4.5.11 → 4.5.13
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/skills/keiyaku-workflow/SKILL.md +135 -377
- package/build/integrations/pi/keiyaku.ts +11 -116
- package/build/src/akuma/akuma-errors.d.ts +1 -0
- package/build/src/akuma/akuma-errors.js +1 -0
- package/build/src/akuma/akuma-handle.d.ts +1 -0
- package/build/src/akuma/akuma-handle.js +6 -7
- package/build/src/akuma/akuma-product.d.ts +6 -8
- package/build/src/akuma/akuma-product.js +141 -57
- package/build/src/akuma/akuma.d.ts +5 -7
- package/build/src/akuma/akuma.js +1 -22
- package/build/src/akuma/archetype.d.ts +6 -5
- package/build/src/akuma/archetype.js +137 -38
- package/build/src/akuma/body.d.ts +5 -5
- package/build/src/akuma/body.js +223 -63
- package/build/src/akuma/call-input.d.ts +4 -0
- package/build/src/akuma/call-input.js +21 -0
- package/build/src/akuma/call-request.d.ts +5 -8
- package/build/src/akuma/call-request.js +28 -73
- package/build/src/akuma/fleet-execution.d.ts +26 -0
- package/build/src/akuma/fleet-execution.js +109 -0
- package/build/src/{library/fleet-result.d.ts → akuma/fleet-observation.d.ts} +13 -57
- package/build/src/{library/fleet-result.js → akuma/fleet-observation.js} +4 -25
- package/build/src/akuma/fleet-request.d.ts +122 -0
- package/build/src/akuma/fleet-request.js +168 -0
- package/build/src/akuma/heart/facts.d.ts +23 -0
- package/build/src/akuma/heart/facts.js +7 -0
- package/build/src/akuma/heart/index.d.ts +43 -4
- package/build/src/akuma/heart/index.js +107 -7
- package/build/src/akuma/heart/request-authority.d.ts +2 -0
- package/build/src/akuma/heart/request-authority.js +28 -3
- package/build/src/akuma/heart/request-rows.d.ts +2 -0
- package/build/src/akuma/heart/request-rows.js +24 -5
- package/build/src/akuma/heart/rows.d.ts +4 -1
- package/build/src/akuma/heart/rows.js +46 -19
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +28 -6
- package/build/src/akuma/heart/storage.d.ts +1 -1
- package/build/src/akuma/heart/tells.d.ts +29 -0
- package/build/src/akuma/heart/tells.js +136 -5
- package/build/src/akuma/heart/timeline.js +9 -3
- package/build/src/akuma/projection-read.js +36 -3
- package/build/src/akuma/projection.js +3 -4
- package/build/src/akuma/provider.d.ts +4 -0
- package/build/src/akuma/provider.js +56 -3
- package/build/src/akuma/providers/claude/index.js +3 -0
- package/build/src/akuma/providers/codex-app-server/index.js +14 -12
- package/build/src/akuma/providers/pi/index.js +7 -1
- package/build/src/akuma/publication.js +65 -49
- package/build/src/akuma/request-lifecycle.d.ts +5 -4
- package/build/src/akuma/request-lifecycle.js +29 -16
- package/build/src/akuma/request-rendezvous.d.ts +15 -12
- package/build/src/akuma/request-rendezvous.js +80 -51
- package/build/src/akuma/request-serve.d.ts +1 -2
- package/build/src/akuma/request-serve.js +94 -39
- package/build/src/akuma/request-wire.d.ts +57 -26
- package/build/src/akuma/request-wire.js +64 -25
- package/build/src/akuma/turn-drive.d.ts +5 -4
- package/build/src/akuma/turn-drive.js +27 -11
- package/build/src/akuma-body.d.ts +4 -6
- package/build/src/akuma-body.js +19 -17
- package/build/src/bounded-list.d.ts +8 -0
- package/build/src/bounded-list.js +14 -0
- package/build/src/cli/accepted.d.ts +1 -1
- package/build/src/cli/accepted.js +1 -0
- package/build/src/cli/commands/akuma-invoke.js +1 -25
- package/build/src/cli/commands/contract-help.d.ts +5 -2
- package/build/src/cli/commands/contract-help.js +62 -12
- package/build/src/cli/commands/contract-invoke.d.ts +5 -0
- package/build/src/cli/commands/contract-invoke.js +26 -1
- package/build/src/cli/commands/contract.js +50 -18
- package/build/src/cli/commands/task-invoke.js +10 -2
- package/build/src/cli/commands/task-query.js +3 -4
- package/build/src/cli/coordinates.d.ts +13 -0
- package/build/src/cli/coordinates.js +38 -24
- package/build/src/cli/draft.d.ts +1 -1
- package/build/src/cli/invoke.js +14 -1
- package/build/src/cli/render/akuma-activity.d.ts +0 -1
- package/build/src/cli/render/akuma-activity.js +5 -38
- package/build/src/cli/render/akuma.js +4 -1
- package/build/src/cli/render/audit.js +4 -1
- package/build/src/cli/render/catalog.js +13 -39
- package/build/src/cli/render/contract.js +4 -14
- package/build/src/cli/render/kanshi-akuma.js +3 -8
- package/build/src/cli/render/kanshi.js +8 -34
- package/build/src/cli/render/nuke.js +7 -3
- package/build/src/cli/render/receipt.d.ts +1 -0
- package/build/src/cli/render/receipt.js +8 -0
- package/build/src/cli/render/refusal.js +4 -3
- package/build/src/cli/render/task.js +4 -37
- package/build/src/cli/render/terminal.d.ts +2 -6
- package/build/src/cli/render/terminal.js +4 -9
- package/build/src/cli/result.d.ts +2 -1
- package/build/src/contract-guidance.js +13 -3
- package/build/src/contract-worktree.d.ts +1 -0
- package/build/src/contract-worktree.js +26 -2
- package/build/src/core/facts/codec.d.ts +2 -1
- package/build/src/core/facts/codec.js +3 -0
- package/build/src/core/facts/gate.d.ts +2 -1
- package/build/src/core/facts/gate.js +63 -0
- package/build/src/core/verbs/abandon.d.ts +2 -1
- package/build/src/core/verbs/abandon.js +16 -0
- package/build/src/core/verbs/amend.d.ts +2 -1
- package/build/src/core/verbs/amend.js +20 -0
- package/build/src/core/verbs/arc.d.ts +2 -1
- package/build/src/core/verbs/arc.js +16 -0
- package/build/src/core/verbs/attestation.d.ts +2 -1
- package/build/src/core/verbs/attestation.js +16 -0
- package/build/src/core/verbs/bind.d.ts +2 -1
- package/build/src/core/verbs/bind.js +16 -0
- package/build/src/core/verbs/deliver.d.ts +2 -1
- package/build/src/core/verbs/deliver.js +16 -0
- package/build/src/core/verbs/placement.d.ts +2 -1
- package/build/src/core/verbs/placement.js +58 -1
- package/build/src/dispatch/association.d.ts +13 -0
- package/build/src/dispatch/association.js +20 -0
- package/build/src/dispatch/index.d.ts +6 -0
- package/build/src/dispatch/index.js +27 -3
- package/build/src/git/hooks.d.ts +9 -1
- package/build/src/git/hooks.js +79 -1
- package/build/src/git/nuke.d.ts +2 -1
- package/build/src/git/nuke.js +6 -11
- package/build/src/git/private-state-seat.d.ts +26 -1
- package/build/src/git/private-state-seat.js +78 -13
- package/build/src/git/process.d.ts +2 -0
- package/build/src/git/reconcile.d.ts +1 -2
- package/build/src/git/reconcile.js +0 -6
- package/build/src/git/repository.d.ts +0 -2
- package/build/src/git/repository.js +27 -25
- package/build/src/git/result-codec.d.ts +29 -0
- package/build/src/git/result-codec.js +306 -0
- package/build/src/git/target-placement.js +2 -8
- package/build/src/git/workspace.d.ts +2 -0
- package/build/src/git/workspace.js +4 -0
- package/build/src/index.d.ts +1 -0
- package/build/src/kanshi/fleet.js +1 -1
- package/build/src/kanshi/read.js +55 -28
- package/build/src/kanshi/report.d.ts +4 -3
- package/build/src/kanshi/select.js +14 -1
- package/build/src/library/address.d.ts +1 -0
- package/build/src/library/address.js +4 -7
- package/build/src/library/akuma-creation.d.ts +5 -3
- package/build/src/library/akuma-creation.js +90 -33
- package/build/src/library/audit.js +2 -1
- package/build/src/library/catalog.d.ts +10 -2
- package/build/src/library/catalog.js +65 -25
- package/build/src/library/composition.d.ts +1 -0
- package/build/src/library/composition.js +1 -0
- package/build/src/library/configuration.d.ts +4 -21
- package/build/src/library/configuration.js +2 -158
- package/build/src/library/continuation.d.ts +20 -1
- package/build/src/library/continuation.js +36 -0
- package/build/src/library/contract-bind.d.ts +1 -0
- package/build/src/library/contract-bind.js +16 -5
- package/build/src/library/contract-forwarding.d.ts +2 -2
- package/build/src/library/contract-forwarding.js +18 -4
- package/build/src/library/contract-handle.js +4 -3
- package/build/src/library/contract-operations.d.ts +5 -9
- package/build/src/library/contract-operations.js +34 -47
- package/build/src/library/contract.d.ts +11 -2
- package/build/src/library/contract.js +21 -1
- package/build/src/library/delivery.d.ts +4 -1
- package/build/src/library/delivery.js +47 -0
- package/build/src/library/fleet.d.ts +4 -136
- package/build/src/library/fleet.js +31 -315
- package/build/src/library/keiyaku.d.ts +7 -3
- package/build/src/library/keiyaku.js +1 -0
- package/build/src/library/mutation.d.ts +431 -3
- package/build/src/library/mutation.js +192 -19
- package/build/src/library/nuke.d.ts +7 -1
- package/build/src/library/nuke.js +18 -4
- package/build/src/library/reconcile.d.ts +14 -0
- package/build/src/library/reconcile.js +22 -8
- package/build/src/library/refusal.d.ts +9 -1
- package/build/src/library/refusal.js +76 -0
- package/build/src/library/region.d.ts +0 -8
- package/build/src/library/region.js +1 -29
- package/build/src/library/result-codec.d.ts +2 -0
- package/build/src/library/result-codec.js +12 -0
- package/build/src/markdown/lex.js +9 -2
- package/build/src/markdown/types.d.ts +2 -1
- package/build/src/markdown/types.js +3 -1
- package/build/src/plugin/akuma-signals.d.ts +12 -0
- package/build/src/plugin/akuma-signals.js +14 -0
- package/build/src/plugin/public.d.ts +51 -0
- package/build/src/plugin/public.js +1 -0
- package/build/src/plugin/runtime.d.ts +14 -0
- package/build/src/plugin/runtime.js +357 -0
- package/build/src/protocol/amend.js +3 -3
- package/build/src/protocol/attempt.d.ts +2 -1
- package/build/src/protocol/attempt.js +1 -1
- package/build/src/protocol/audit.d.ts +1 -1
- package/build/src/protocol/audit.js +1 -1
- package/build/src/protocol/bind.d.ts +3 -1
- package/build/src/protocol/bind.js +26 -0
- package/build/src/protocol/completion.js +0 -1
- package/build/src/protocol/deliver.d.ts +1 -1
- package/build/src/protocol/deliver.js +136 -57
- package/build/src/protocol/intent.d.ts +0 -1
- package/build/src/protocol/intent.js +0 -1
- package/build/src/protocol/operations.d.ts +9 -2
- package/build/src/protocol/operations.js +16 -1
- package/build/src/protocol/outcome.d.ts +3 -0
- package/build/src/protocol/outcome.js +17 -1
- package/build/src/protocol/placement.js +10 -3
- package/build/src/protocol/read/status.d.ts +8 -0
- package/build/src/protocol/read/status.js +49 -0
- package/build/src/protocol/reintegrate.js +10 -3
- package/build/src/protocol/result-codec.d.ts +22 -0
- package/build/src/protocol/result-codec.js +442 -0
- package/build/src/protocol/review.d.ts +1 -1
- package/build/src/protocol/review.js +81 -46
- package/build/src/protocol/run.d.ts +22 -2
- package/build/src/protocol/run.js +119 -29
- package/build/src/runtime/proc/lifecycle.js +1 -1
- package/build/src/runtime/proc/line-rpc.d.ts +7 -1
- package/build/src/runtime/proc/line-rpc.js +49 -9
- package/build/src/runtime/proc/run.d.ts +0 -1
- package/build/src/runtime/proc/run.js +1 -11
- package/build/src/runtime/proc/termination.d.ts +1 -0
- package/build/src/runtime/proc/termination.js +63 -18
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/build/src/settings.d.ts +10 -0
- package/build/src/settings.js +88 -2
- package/build/src/settlement/result-codec.d.ts +2 -0
- package/build/src/settlement/result-codec.js +56 -0
- package/build/src/settlement/settle.d.ts +5 -12
- package/build/src/settlement/settle.js +60 -55
- package/build/src/task/board.d.ts +3 -0
- package/build/src/task/board.js +22 -4
- package/build/src/task/catalog.d.ts +13 -2
- package/build/src/task/catalog.js +59 -5
- package/build/src/task/compose.d.ts +4 -2
- package/build/src/task/compose.js +56 -30
- package/build/src/task/created-observation.d.ts +39 -0
- package/build/src/task/created-observation.js +26 -0
- package/build/src/task/identity.d.ts +6 -0
- package/build/src/task/identity.js +48 -7
- package/build/src/task/index.d.ts +4 -3
- package/build/src/task/index.js +19 -16
- package/build/src/task/input.d.ts +3 -1
- package/build/src/task/input.js +7 -4
- package/build/src/task/lifecycle-operations.d.ts +6 -0
- package/build/src/task/lifecycle-operations.js +116 -0
- package/build/src/task/mutation-result.d.ts +762 -20
- package/build/src/task/mutation-result.js +34 -9
- package/build/src/task/mutation.d.ts +4 -9
- package/build/src/task/mutation.js +17 -52
- package/build/src/task/operation-types.d.ts +114 -0
- package/build/src/task/operation-types.js +1 -0
- package/build/src/task/operations.d.ts +6 -113
- package/build/src/task/operations.js +75 -117
- package/build/src/task/query.d.ts +8 -11
- package/build/src/task/query.js +7 -14
- package/build/src/task/store.d.ts +11 -1
- package/build/src/task/store.js +169 -55
- package/build/src/task/view.d.ts +4 -0
- package/build/src/task/view.js +7 -0
- package/build/src/verification/declaration.d.ts +1 -0
- package/build/src/verification/declaration.js +18 -1
- package/build/src/verification/execution.d.ts +0 -1
- package/build/src/verification/execution.js +5 -5
- package/build/src/world.d.ts +0 -2
- package/build/src/world.js +3 -7
- package/package.json +17 -5
- package/build/src/cli/square-edge.d.ts +0 -7
- package/build/src/cli/square-edge.js +0 -159
- package/build/src/git/ref-migration.d.ts +0 -22
- package/build/src/git/ref-migration.js +0 -55
- package/build/src/library/contract-forwarding-reconciliation-result.d.ts +0 -102
- package/build/src/library/contract-forwarding-reconciliation-result.js +0 -92
- package/build/src/library/contract-forwarding-result.d.ts +0 -4270
- package/build/src/library/contract-forwarding-result.js +0 -521
|
@@ -1,91 +1,14 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import type { ExtensionAPI,
|
|
5
|
-
|
|
6
|
-
type Section = Readonly<{ kind?: string; value?: Readonly<{ rows?: readonly Record<string, unknown>[] }> }>;
|
|
7
|
-
type Report = Readonly<{
|
|
8
|
-
contracts?: Section;
|
|
9
|
-
tasks?: Section;
|
|
10
|
-
akuma?: Section;
|
|
11
|
-
}>;
|
|
4
|
+
import type { ExtensionAPI, Theme } from "@earendil-works/pi-coding-agent";
|
|
12
5
|
|
|
13
6
|
type ExecResult = Readonly<{ stdout: string; stderr: string; code: number }>;
|
|
14
7
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
function rows(section: Section | undefined): readonly Record<string, unknown>[] {
|
|
18
|
-
return section?.kind === "present" && Array.isArray(section.value?.rows) ? section.value.rows : [];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function failed(section: Section | undefined): boolean {
|
|
22
|
-
return section?.kind === "failed";
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function contractNeedsAttention(row: Record<string, unknown>): boolean {
|
|
26
|
-
const gates = row.gates;
|
|
27
|
-
if (typeof gates === "object" && gates !== null && "reports" in gates && Array.isArray(gates.reports)) {
|
|
28
|
-
for (const report of gates.reports) {
|
|
29
|
-
if (
|
|
30
|
-
typeof report === "object" &&
|
|
31
|
-
report !== null &&
|
|
32
|
-
"current" in report &&
|
|
33
|
-
typeof report.current === "object" &&
|
|
34
|
-
report.current !== null &&
|
|
35
|
-
"kind" in report.current &&
|
|
36
|
-
report.current.kind === "attested" &&
|
|
37
|
-
"verdict" in report.current &&
|
|
38
|
-
report.current.verdict === "unsatisfied"
|
|
39
|
-
)
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return row.targetLag !== undefined &&
|
|
44
|
-
typeof row.targetLag === "object" &&
|
|
45
|
-
row.targetLag !== null &&
|
|
46
|
-
"kind" in row.targetLag
|
|
47
|
-
? row.targetLag.kind === "unknown"
|
|
48
|
-
: false;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function taskNeedsAttention(row: Record<string, unknown>): boolean {
|
|
52
|
-
return row.disposition === "blocked";
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function akumaNeedsAttention(row: Record<string, unknown>): boolean {
|
|
56
|
-
return row.life === "stillborn" || row.life === "hung" || row.life === "untidy";
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function summary(report: Report): string {
|
|
60
|
-
const contractRows = rows(report.contracts);
|
|
61
|
-
const taskRows = rows(report.tasks);
|
|
62
|
-
const akumaRows = rows(report.akuma);
|
|
63
|
-
const attention =
|
|
64
|
-
Number(failed(report.contracts)) +
|
|
65
|
-
Number(failed(report.tasks)) +
|
|
66
|
-
Number(failed(report.akuma)) +
|
|
67
|
-
contractRows.filter(contractNeedsAttention).length +
|
|
68
|
-
taskRows.filter(taskNeedsAttention).length +
|
|
69
|
-
akumaRows.filter(akumaNeedsAttention).length;
|
|
70
|
-
const parts = [`Keiyaku · ${contractRows.length} contracts`, `${akumaRows.length} fleet`, `${taskRows.length} tasks`];
|
|
71
|
-
if (attention > 0) parts.push(`! ${attention}`);
|
|
72
|
-
return parts.join(" · ");
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function parseReport(result: ExecResult): Report | undefined {
|
|
76
|
-
if (result.code !== 0 || result.stdout.trim().length === 0) return undefined;
|
|
77
|
-
try {
|
|
78
|
-
const value: unknown = JSON.parse(result.stdout);
|
|
79
|
-
return typeof value === "object" && value !== null ? (value as Report) : undefined;
|
|
80
|
-
} catch {
|
|
81
|
-
return undefined;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async function run(pi: ExtensionAPI, args: readonly string[], timeout = REFRESH_TIMEOUT_MS): Promise<ExecResult> {
|
|
8
|
+
async function runStatus(pi: ExtensionAPI): Promise<ExecResult> {
|
|
86
9
|
const bundledCli = resolve(dirname(fileURLToPath(import.meta.url)), "../../src/cli/index.js");
|
|
87
|
-
if (existsSync(bundledCli)) return await pi.exec(process.execPath, [bundledCli,
|
|
88
|
-
return await pi.exec("keiyaku", [
|
|
10
|
+
if (existsSync(bundledCli)) return await pi.exec(process.execPath, [bundledCli, "status"]);
|
|
11
|
+
return await pi.exec("keiyaku", ["status"]);
|
|
89
12
|
}
|
|
90
13
|
|
|
91
14
|
function lineForWidth(value: string, width: number): string {
|
|
@@ -119,49 +42,21 @@ function overlay(lines: readonly string[], theme: Theme, done: () => void) {
|
|
|
119
42
|
}
|
|
120
43
|
|
|
121
44
|
export default function keiyakuExtension(pi: ExtensionAPI): void {
|
|
122
|
-
let refreshInFlight: Promise<void> | undefined;
|
|
123
|
-
|
|
124
|
-
const refresh = async (ctx: Pick<ExtensionContext, "hasUI" | "ui">): Promise<void> => {
|
|
125
|
-
if (!ctx.hasUI) return;
|
|
126
|
-
refreshInFlight ??= (async () => {
|
|
127
|
-
try {
|
|
128
|
-
const result = await run(pi, ["status", "--json"]);
|
|
129
|
-
const report = parseReport(result);
|
|
130
|
-
if (report === undefined) {
|
|
131
|
-
const diagnostic = result.stderr.trim() || "status unavailable";
|
|
132
|
-
ctx.ui.setWidget("keiyaku", [`Keiyaku · ${lineForWidth(diagnostic, 100)}`]);
|
|
133
|
-
} else ctx.ui.setWidget("keiyaku", [summary(report)]);
|
|
134
|
-
} catch {
|
|
135
|
-
ctx.ui.setWidget("keiyaku", ["Keiyaku · status unavailable"]);
|
|
136
|
-
}
|
|
137
|
-
})().finally(() => {
|
|
138
|
-
refreshInFlight = undefined;
|
|
139
|
-
});
|
|
140
|
-
await refreshInFlight;
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const scheduleRefresh = (ctx: Pick<ExtensionContext, "hasUI" | "ui">): void => {
|
|
144
|
-
void refresh(ctx).catch(() => undefined);
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
pi.on("session_start", (_event, ctx) => {
|
|
148
|
-
scheduleRefresh(ctx);
|
|
149
|
-
});
|
|
150
|
-
pi.on("agent_end", (_event, ctx) => {
|
|
151
|
-
scheduleRefresh(ctx);
|
|
152
|
-
});
|
|
153
45
|
pi.registerCommand("keiyaku", {
|
|
154
46
|
description: "Open the Kanshi world status",
|
|
155
47
|
handler: async (_args, ctx) => {
|
|
156
48
|
if (!ctx.hasUI) return;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
result
|
|
49
|
+
let body: string[];
|
|
50
|
+
try {
|
|
51
|
+
const result = await runStatus(pi);
|
|
52
|
+
body = result.code === 0 ? result.stdout.trimEnd().split("\n") : [result.stderr.trim() || "status unavailable"];
|
|
53
|
+
} catch {
|
|
54
|
+
body = ["status unavailable"];
|
|
55
|
+
}
|
|
160
56
|
await ctx.ui.custom<void>((_tui, theme, _keybindings, done) => overlay(body, theme, done), {
|
|
161
57
|
overlay: true,
|
|
162
58
|
overlayOptions: { anchor: "center", width: 100, maxHeight: 40 },
|
|
163
59
|
});
|
|
164
|
-
await refresh(ctx);
|
|
165
60
|
},
|
|
166
61
|
});
|
|
167
62
|
}
|
|
@@ -11,6 +11,7 @@ export declare class AkumaHandle {
|
|
|
11
11
|
readonly [CALL_EXECUTION]?: AkumaCallExecution;
|
|
12
12
|
constructor(id: AkuId, worldPath: WorldRoot, execution?: AkumaCallExecution);
|
|
13
13
|
private get paths();
|
|
14
|
+
private projectHistoryTurns;
|
|
14
15
|
private exactHistory;
|
|
15
16
|
status(): Promise<AkumaStatus>;
|
|
16
17
|
history(input?: Readonly<{
|
|
@@ -83,6 +83,10 @@ export class AkumaHandle {
|
|
|
83
83
|
get paths() {
|
|
84
84
|
return pathsForAkuId(this.worldPath, this.id);
|
|
85
85
|
}
|
|
86
|
+
async projectHistoryTurns() {
|
|
87
|
+
const slice = await activitySlice(this.paths);
|
|
88
|
+
return projectTurns(slice.rows, { lowestRetained: slice.lowestRetained, highest: slice.highest });
|
|
89
|
+
}
|
|
86
90
|
async exactHistory(input) {
|
|
87
91
|
if (typeof input.id !== "string" || input.id.trim() === "")
|
|
88
92
|
throw new TypeError("Akuma history id must be a nonblank string");
|
|
@@ -90,8 +94,7 @@ export class AkumaHandle {
|
|
|
90
94
|
throw new TypeError("Akuma history id cannot be combined with before, since, or limit");
|
|
91
95
|
if (parsePublicHistoryId(input.id) === null)
|
|
92
96
|
throw new TypeError("Akuma history id must match turn/<positive safe integer>");
|
|
93
|
-
|
|
94
|
-
return selectExactHistory(projectTurns(slice.rows, { lowestRetained: slice.lowestRetained, highest: slice.highest }).rows, input.id);
|
|
97
|
+
return selectExactHistory((await this.projectHistoryTurns()).rows, input.id);
|
|
95
98
|
}
|
|
96
99
|
async status() {
|
|
97
100
|
return (await bornStatus(this.paths, this.id, { aperture: "monitoring" })).status;
|
|
@@ -114,11 +117,7 @@ export class AkumaHandle {
|
|
|
114
117
|
if (!Number.isSafeInteger(limit) || limit <= 0 || limit > 5_000) {
|
|
115
118
|
throw new TypeError("Akuma history limit must be a positive safe integer no greater than 5000");
|
|
116
119
|
}
|
|
117
|
-
|
|
118
|
-
return selectHistory(projectTurns(slice.rows, {
|
|
119
|
-
lowestRetained: slice.lowestRetained,
|
|
120
|
-
highest: slice.highest,
|
|
121
|
-
}), {
|
|
120
|
+
return selectHistory(await this.projectHistoryTurns(), {
|
|
122
121
|
...(input.before === undefined ? {} : { before: input.before }),
|
|
123
122
|
...(input.since === undefined ? {} : { since: input.since }),
|
|
124
123
|
limit,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AkumaHandle } from "./akuma-handle.js";
|
|
2
|
-
import type { AkumaCallContext, AkumaCallInput, AkumaConfiguration, AkumaList, AkumaListInput } from "./akuma.js";
|
|
2
|
+
import type { AkumaCallContext, AkumaCallInput, AkumaCompleteList, AkumaConfiguration, AkumaList, AkumaListInput } from "./akuma.js";
|
|
3
3
|
import { CALL_WITH_CONTEXT } from "./akuma-product-symbols.js";
|
|
4
4
|
import type { WorldRoot } from "../world.js";
|
|
5
5
|
import type { BodyLaunch } from "./body.js";
|
|
@@ -31,6 +31,8 @@ export type RequestedAkumaCall = Readonly<{
|
|
|
31
31
|
}>;
|
|
32
32
|
export type AkumaBornCall = BornAkumaCall | RequestedAkumaCall;
|
|
33
33
|
type AkumaCallLaunchInput = AkumaCallInput;
|
|
34
|
+
export declare const PAGE_POOL_SIZE = 16;
|
|
35
|
+
export declare function boundedMap<Value, Result>(values: readonly Value[], mapper: (value: Value) => Promise<Result>): Promise<readonly Result[]>;
|
|
34
36
|
export declare class Akuma {
|
|
35
37
|
private readonly path;
|
|
36
38
|
private readonly configuration;
|
|
@@ -43,16 +45,12 @@ export declare class Akuma {
|
|
|
43
45
|
call(input: AkumaCallLaunchInput): Promise<AkumaHandle>;
|
|
44
46
|
beginCall(input: AkumaCallLaunchInput, context: AkumaCallContext): Promise<AkumaBornCall>;
|
|
45
47
|
finishCall(born: AkumaBornCall, completion?: Readonly<{
|
|
46
|
-
participantName?: string;
|
|
47
48
|
contractId?: string;
|
|
48
49
|
}>): Promise<AkumaHandle>;
|
|
49
50
|
[CALL_WITH_CONTEXT](input: AkumaCallLaunchInput, context: AkumaCallContext): Promise<AkumaHandle>;
|
|
51
|
+
listComplete(input?: Readonly<{
|
|
52
|
+
archetype?: string;
|
|
53
|
+
}>): Promise<AkumaCompleteList>;
|
|
50
54
|
list(input?: AkumaListInput): Promise<AkumaList>;
|
|
51
55
|
}
|
|
52
|
-
export declare function callAkumaWithContext(akuma: Akuma, input: AkumaCallLaunchInput, context: AkumaCallContext): Promise<AkumaHandle>;
|
|
53
|
-
export declare function beginAkumaCall(akuma: Akuma, input: AkumaCallLaunchInput, context: AkumaCallContext): Promise<AkumaBornCall>;
|
|
54
|
-
export declare function finishAkumaCall(akuma: Akuma, born: AkumaBornCall, completion?: Readonly<{
|
|
55
|
-
participantName?: string;
|
|
56
|
-
contractId?: string;
|
|
57
|
-
}>): Promise<AkumaHandle>;
|
|
58
56
|
export {};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { readdir,
|
|
3
|
-
import {
|
|
2
|
+
import { readdir, stat } from "node:fs/promises";
|
|
3
|
+
import { boundedListLimit, projectBoundedList } from "../bounded-list.js";
|
|
4
4
|
import { AkumaHandle } from "./akuma-handle.js";
|
|
5
5
|
import { CALL_WITH_CONTEXT } from "./akuma-product-symbols.js";
|
|
6
|
+
import { callReadonly, canonicalBirthCwd } from "./call-input.js";
|
|
6
7
|
import { fleetListRow, readAkumaBirthCwd } from "./akuma-observe.js";
|
|
7
8
|
import { akuIdFromDirectoryName, akumaPaths, akumaRunRoot, archetypeName, parseAkuId } from "./identity.js";
|
|
8
9
|
import { loadArchetype, listArchetypes as readArchetypes } from "./archetype.js";
|
|
@@ -12,24 +13,98 @@ import { requestForwardedAkumaCall } from "./call-request.js";
|
|
|
12
13
|
import { executionChannel } from "./requests.js";
|
|
13
14
|
import { decodeAllowedActions, unionAllowedActions } from "./allowed.js";
|
|
14
15
|
import { settings as readSettings } from "../settings.js";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
export const PAGE_POOL_SIZE = 16;
|
|
17
|
+
function activityAt(row) {
|
|
18
|
+
if (!("lifeAt" in row))
|
|
19
|
+
return null;
|
|
20
|
+
if (row.lifeAt === null)
|
|
21
|
+
return row.lastActivityAt;
|
|
22
|
+
if (row.lastActivityAt === null)
|
|
23
|
+
return row.lifeAt;
|
|
24
|
+
return row.lifeAt > row.lastActivityAt ? row.lifeAt : row.lastActivityAt;
|
|
21
25
|
}
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
function compareActivity(left, right) {
|
|
27
|
+
if (left === right)
|
|
28
|
+
return 0;
|
|
29
|
+
if (left === null)
|
|
30
|
+
return 1;
|
|
31
|
+
if (right === null)
|
|
32
|
+
return -1;
|
|
33
|
+
return left > right ? -1 : 1;
|
|
34
|
+
}
|
|
35
|
+
function compareRows(left, right) {
|
|
36
|
+
const activity = compareActivity(activityAt(left), activityAt(right));
|
|
37
|
+
if (activity !== 0)
|
|
38
|
+
return activity;
|
|
39
|
+
return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
|
|
40
|
+
}
|
|
41
|
+
export async function boundedMap(values, mapper) {
|
|
42
|
+
const results = [];
|
|
43
|
+
let index = 0;
|
|
44
|
+
const worker = async () => {
|
|
45
|
+
for (;;) {
|
|
46
|
+
const selected = index++;
|
|
47
|
+
if (selected >= values.length)
|
|
48
|
+
return;
|
|
49
|
+
results[selected] = await mapper(values[selected]);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
await Promise.all(Array.from({ length: Math.min(PAGE_POOL_SIZE, values.length) }, worker));
|
|
53
|
+
return results;
|
|
54
|
+
}
|
|
55
|
+
async function mtimeBound(paths) {
|
|
56
|
+
const read = async (path) => {
|
|
57
|
+
try {
|
|
58
|
+
return (await stat(path)).mtimeMs;
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
return error.code === "ENOENT" ? 0 : Number.POSITIVE_INFINITY;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
return Math.max(await read(paths.heart), await read(`${paths.heart}-wal`));
|
|
65
|
+
}
|
|
66
|
+
async function knownAkuma(path, selected) {
|
|
67
|
+
const runRoot = akumaRunRoot(path);
|
|
68
|
+
let names;
|
|
24
69
|
try {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
70
|
+
names = (await readdir(runRoot, { withFileTypes: true }))
|
|
71
|
+
.filter((entry) => entry.isDirectory())
|
|
72
|
+
.map((entry) => entry.name)
|
|
73
|
+
.sort();
|
|
29
74
|
}
|
|
30
|
-
catch {
|
|
31
|
-
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (error.code === "ENOENT")
|
|
77
|
+
return { runRoot, rows: [] };
|
|
78
|
+
throw error;
|
|
32
79
|
}
|
|
80
|
+
const rows = [];
|
|
81
|
+
for (const name of names) {
|
|
82
|
+
let physical;
|
|
83
|
+
try {
|
|
84
|
+
physical = akuIdFromDirectoryName(name);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (selected !== undefined && physical.archetype !== selected)
|
|
90
|
+
continue;
|
|
91
|
+
rows.push({
|
|
92
|
+
id: physical.id,
|
|
93
|
+
paths: akumaPaths({ runRoot, archetype: physical.archetype, suffix: physical.suffix }),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return { runRoot, rows };
|
|
97
|
+
}
|
|
98
|
+
async function readableRows(rows) {
|
|
99
|
+
const loaded = await boundedMap(rows, async ({ id, paths }) => {
|
|
100
|
+
try {
|
|
101
|
+
return await fleetListRow(paths, id);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
return [...loaded].filter((row) => row !== null);
|
|
33
108
|
}
|
|
34
109
|
export class Akuma {
|
|
35
110
|
path;
|
|
@@ -47,7 +122,10 @@ export class Akuma {
|
|
|
47
122
|
return new AkumaHandle(parseAkuId(input.id).id, this.path);
|
|
48
123
|
}
|
|
49
124
|
async listArchetypes() {
|
|
50
|
-
return readArchetypes(
|
|
125
|
+
return readArchetypes({
|
|
126
|
+
project: this.path,
|
|
127
|
+
...(this.configuration.home === undefined ? {} : { home: this.configuration.home }),
|
|
128
|
+
});
|
|
51
129
|
}
|
|
52
130
|
async call(input) {
|
|
53
131
|
return await this[CALL_WITH_CONTEXT](input, { initiatorCwd: process.cwd() });
|
|
@@ -57,7 +135,7 @@ export class Akuma {
|
|
|
57
135
|
const name = archetypeName(input.archetype);
|
|
58
136
|
const home = this.configuration.home === undefined ? {} : { home: this.configuration.home };
|
|
59
137
|
const settings = this.configuration.settings ?? (await readSettings({ root: this.path, ...home }));
|
|
60
|
-
const archetype = await loadArchetype({ name, ...home, settings, ...readonly });
|
|
138
|
+
const archetype = await loadArchetype({ name, project: this.path, ...home, settings, ...readonly });
|
|
61
139
|
const allowed = input.allowed === undefined
|
|
62
140
|
? archetype.allowed
|
|
63
141
|
: unionAllowedActions(archetype.allowed, decodeAllowedActions(input.allowed, "Akuma call allowed"));
|
|
@@ -134,53 +212,59 @@ export class Akuma {
|
|
|
134
212
|
async [CALL_WITH_CONTEXT](input, context) {
|
|
135
213
|
return await this.finishCall(await this.beginCall(input, context));
|
|
136
214
|
}
|
|
215
|
+
async listComplete(input = {}) {
|
|
216
|
+
if (typeof input !== "object" || input === null || Array.isArray(input))
|
|
217
|
+
throw new TypeError("Akuma complete list input must be an object");
|
|
218
|
+
const unknown = Object.keys(input).find((key) => key !== "archetype");
|
|
219
|
+
if (unknown !== undefined)
|
|
220
|
+
throw new TypeError(`Akuma complete list input has unknown field: ${unknown}`);
|
|
221
|
+
const selected = input.archetype === undefined ? undefined : archetypeName(input.archetype);
|
|
222
|
+
const known = await knownAkuma(this.path, selected);
|
|
223
|
+
return {
|
|
224
|
+
observedAt: new Date().toISOString(),
|
|
225
|
+
rows: [...(await readableRows(known.rows))].sort(compareRows),
|
|
226
|
+
searched: [known.runRoot],
|
|
227
|
+
};
|
|
228
|
+
}
|
|
137
229
|
async list(input = {}) {
|
|
138
230
|
if (typeof input !== "object" || input === null || Array.isArray(input))
|
|
139
231
|
throw new TypeError("Akuma list input must be an object");
|
|
140
|
-
const unknown = Object.keys(input).find((key) => key !== "archetype");
|
|
232
|
+
const unknown = Object.keys(input).find((key) => key !== "archetype" && key !== "limit");
|
|
141
233
|
if (unknown !== undefined)
|
|
142
234
|
throw new TypeError(`Akuma list input has unknown field: ${unknown}`);
|
|
143
235
|
const selected = input.archetype === undefined ? undefined : archetypeName(input.archetype);
|
|
236
|
+
const limit = boundedListLimit(input.limit);
|
|
144
237
|
const observedAt = new Date().toISOString();
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
238
|
+
const known = await knownAkuma(this.path, selected);
|
|
239
|
+
const candidatesWithBounds = await boundedMap(known.rows, async (row) => ({
|
|
240
|
+
...row,
|
|
241
|
+
bound: await mtimeBound(row.paths),
|
|
242
|
+
}));
|
|
243
|
+
const candidates = [...candidatesWithBounds].sort((left, right) => right.bound - left.bound);
|
|
244
|
+
const readable = [];
|
|
245
|
+
let cursor = 0;
|
|
246
|
+
while (cursor < candidates.length) {
|
|
247
|
+
const batch = candidates.slice(cursor, cursor + PAGE_POOL_SIZE);
|
|
248
|
+
readable.push(...(await readableRows(batch)));
|
|
249
|
+
cursor += batch.length;
|
|
250
|
+
const ranked = readable.sort(compareRows);
|
|
251
|
+
const lookahead = ranked[limit];
|
|
252
|
+
const activity = lookahead === undefined ? null : activityAt(lookahead);
|
|
253
|
+
const unreadBound = candidates[cursor]?.bound;
|
|
254
|
+
if (lookahead !== undefined &&
|
|
255
|
+
activity !== null &&
|
|
256
|
+
unreadBound !== undefined &&
|
|
257
|
+
Number.isFinite(unreadBound) &&
|
|
258
|
+
Number.isFinite(Date.parse(activity)) &&
|
|
259
|
+
unreadBound < Date.parse(activity)) {
|
|
260
|
+
break;
|
|
166
261
|
}
|
|
167
|
-
if (selected !== undefined && physical.archetype !== selected)
|
|
168
|
-
continue;
|
|
169
|
-
const paths = akumaPaths({ runRoot, archetype: physical.archetype, suffix: physical.suffix });
|
|
170
|
-
try {
|
|
171
|
-
rows.push(await fleetListRow(paths, physical.id));
|
|
172
|
-
}
|
|
173
|
-
catch { }
|
|
174
262
|
}
|
|
175
|
-
|
|
263
|
+
const ranked = readable.sort(compareRows);
|
|
264
|
+
return {
|
|
265
|
+
observedAt,
|
|
266
|
+
searched: [known.runRoot],
|
|
267
|
+
...projectBoundedList(ranked, limit),
|
|
268
|
+
};
|
|
176
269
|
}
|
|
177
270
|
}
|
|
178
|
-
export async function callAkumaWithContext(akuma, input, context) {
|
|
179
|
-
return await akuma[CALL_WITH_CONTEXT](input, context);
|
|
180
|
-
}
|
|
181
|
-
export async function beginAkumaCall(akuma, input, context) {
|
|
182
|
-
return await akuma.beginCall(input, context);
|
|
183
|
-
}
|
|
184
|
-
export async function finishAkumaCall(akuma, born, completion = {}) {
|
|
185
|
-
return await akuma.finishCall(born, completion);
|
|
186
|
-
}
|
|
@@ -6,11 +6,8 @@ import type { WorldRoot } from "../world.js";
|
|
|
6
6
|
import type { AllowedAction } from "./allowed.js";
|
|
7
7
|
import type { ExecutionContext } from "./requests.js";
|
|
8
8
|
import { z } from "zod";
|
|
9
|
+
import type { BoundedList } from "../bounded-list.js";
|
|
9
10
|
export declare const POLL_MS = 100;
|
|
10
|
-
export declare function canonicalBirthCwd(input: string): Promise<string>;
|
|
11
|
-
export declare function callReadonly(value: unknown): Readonly<{
|
|
12
|
-
readonly?: true;
|
|
13
|
-
}>;
|
|
14
11
|
export type AkumaListRow = Readonly<{
|
|
15
12
|
id: AkuId;
|
|
16
13
|
archetype: string;
|
|
@@ -419,13 +416,14 @@ export type UnbornAkumaListRow = Readonly<{
|
|
|
419
416
|
at: string;
|
|
420
417
|
}>;
|
|
421
418
|
}>;
|
|
422
|
-
export type AkumaList = Readonly<{
|
|
419
|
+
export type AkumaList = BoundedList<AkumaListRow | UnbornAkumaListRow> & Readonly<{
|
|
423
420
|
observedAt: string;
|
|
424
|
-
rows: readonly (AkumaListRow | UnbornAkumaListRow)[];
|
|
425
421
|
searched: readonly string[];
|
|
426
422
|
}>;
|
|
423
|
+
export type AkumaCompleteList = Omit<AkumaList, "hasMore">;
|
|
427
424
|
export type AkumaListInput = Readonly<{
|
|
428
425
|
archetype?: string;
|
|
426
|
+
limit?: number;
|
|
429
427
|
}>;
|
|
430
428
|
export type AkumaCallExecution = Readonly<{
|
|
431
429
|
cwd: string;
|
|
@@ -493,4 +491,4 @@ export type AkumaConfiguration = Readonly<{
|
|
|
493
491
|
execution?: ExecutionContext;
|
|
494
492
|
}>;
|
|
495
493
|
export { AkumaHandle, akumaCallExecution, type LastAnswer } from "./akuma-handle.js";
|
|
496
|
-
export { Akuma
|
|
494
|
+
export { Akuma } from "./akuma-product.js";
|
package/build/src/akuma/akuma.js
CHANGED
|
@@ -1,31 +1,10 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { realpath, stat } from "node:fs/promises";
|
|
3
|
-
import { resolve } from "node:path";
|
|
4
2
|
import { CONTROL_RESPONSE_MS, handoffPendingTells, wakeRecordedTell, } from "./body.js";
|
|
5
3
|
import { HeldAkumaLeash, readHeart, readKill, recordTell, requestStop, } from "./heart/index.js";
|
|
6
4
|
import { parseAkuId, pathsForAkuId } from "./identity.js";
|
|
7
5
|
import { activitySnapshotSchema } from "./projection.js";
|
|
8
6
|
import { z } from "zod";
|
|
9
7
|
export const POLL_MS = 100;
|
|
10
|
-
export async function canonicalBirthCwd(input) {
|
|
11
|
-
const selected = resolve(input);
|
|
12
|
-
try {
|
|
13
|
-
const canonical = await realpath(selected);
|
|
14
|
-
if (!(await stat(canonical)).isDirectory())
|
|
15
|
-
throw new Error("not a directory");
|
|
16
|
-
return canonical;
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
throw new Error(`cwd is not an existing directory: ${input}`);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
export function callReadonly(value) {
|
|
23
|
-
if (value === undefined)
|
|
24
|
-
return {};
|
|
25
|
-
if (value !== true)
|
|
26
|
-
throw new TypeError("Akuma call readonly must be true");
|
|
27
|
-
return { readonly: true };
|
|
28
|
-
}
|
|
29
8
|
export const akumaIdSchema = z.string().transform((value, context) => {
|
|
30
9
|
try {
|
|
31
10
|
const id = parseAkuId(value).id;
|
|
@@ -127,4 +106,4 @@ export async function killAkumaWithRecovery(paths, recover = handoffPendingTells
|
|
|
127
106
|
}
|
|
128
107
|
export { bornStatus, fleetListRow, readAkumaBirthCwd, readBudgetedStatus, } from "./akuma-observe.js";
|
|
129
108
|
export { AkumaHandle, akumaCallExecution } from "./akuma-handle.js";
|
|
130
|
-
export { Akuma
|
|
109
|
+
export { Akuma } from "./akuma-product.js";
|
|
@@ -30,14 +30,15 @@ export declare class AkumaArchetypeError extends Error {
|
|
|
30
30
|
readonly kind = "akuma-archetype";
|
|
31
31
|
constructor(archetype: string, searched: readonly string[], reason: string, guidance?: string);
|
|
32
32
|
}
|
|
33
|
-
|
|
33
|
+
type ArchetypeCoordinates = Readonly<{
|
|
34
|
+
project?: string;
|
|
34
35
|
home?: string;
|
|
35
|
-
}
|
|
36
|
-
export declare function
|
|
37
|
-
|
|
38
|
-
}>): Promise<readonly ArchetypeCatalogRow[]>;
|
|
36
|
+
}>;
|
|
37
|
+
export declare function listArchetypes(input?: ArchetypeCoordinates): Promise<readonly string[]>;
|
|
38
|
+
export declare function listArchetypeDefinitions(input?: ArchetypeCoordinates): Promise<readonly ArchetypeCatalogRow[]>;
|
|
39
39
|
export declare function loadArchetype(input: Readonly<{
|
|
40
40
|
name: string;
|
|
41
|
+
project?: string;
|
|
41
42
|
home?: string;
|
|
42
43
|
settings: Settings;
|
|
43
44
|
readonly?: true;
|