@astrosheep/keiyaku 2.9.11 → 2.9.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/README.md +79 -85
- package/build/.tsbuildinfo +1 -1
- package/build/agents/call-terms.js +1 -0
- package/build/agents/providers/claude-agent-sdk/adapter.js +17 -4
- package/build/cli/commands/akuma/akuma/handler.js +2 -1
- package/build/cli/commands/akuma/akuma/meta.js +4 -3
- package/build/cli/commands/akuma/catalog.js +2 -0
- package/build/cli/commands/akuma/list/handler.js +1 -1
- package/build/cli/commands/akuma/list/meta-list.js +12 -0
- package/build/cli/commands/akuma/list/meta-ls.js +2 -2
- package/build/cli/commands/akuma.js +5 -0
- package/build/cli/commands/contract/amend/meta.js +4 -3
- package/build/cli/commands/contract/arc/meta.js +2 -0
- package/build/cli/commands/contract/audit/handler.js +28 -2
- package/build/cli/commands/contract/audit/meta.js +4 -3
- package/build/cli/commands/contract/bind/meta.js +4 -4
- package/build/cli/commands/contract/petition/handler.js +2 -1
- package/build/cli/commands/contract/petition/meta.js +2 -2
- package/build/cli/commands/contract/renew/handler.js +44 -3
- package/build/cli/commands/contract/renew/meta.js +5 -4
- package/build/cli/commands/metadata.js +14 -10
- package/build/cli/commands/projection/call/handler.js +1 -1
- package/build/cli/commands/projection/call/meta.js +1 -1
- package/build/cli/commands/projection/catalog.js +2 -2
- package/build/cli/commands/projection/{revive → fork}/handler.js +10 -5
- package/build/cli/commands/projection/fork/meta.js +12 -0
- package/build/cli/commands/projection/history/handler.js +46 -2
- package/build/cli/commands/projection/history/meta.js +3 -3
- package/build/cli/commands/projection/kill/handler.js +4 -5
- package/build/cli/commands/projection/kill/meta.js +2 -2
- package/build/cli/commands/projection/status/handler.js +29 -13
- package/build/cli/commands/projection/status/meta.js +14 -3
- package/build/cli/commands/projection/tell/handler.js +8 -4
- package/build/cli/commands/projection/tell/meta.js +1 -1
- package/build/cli/commands/projection/wait/handler.js +45 -20
- package/build/cli/commands/projection/wait/meta.js +1 -1
- package/build/cli/commands/shared.js +2 -2
- package/build/cli/commands/system/catalog.js +2 -0
- package/build/cli/commands/task/add/handler.js +28 -12
- package/build/cli/commands/task/add/meta.js +18 -6
- package/build/cli/commands/task/catalog.js +4 -0
- package/build/cli/commands/task/compose/handler.js +18 -0
- package/build/cli/commands/task/compose/meta.js +10 -0
- package/build/cli/commands/task/doctor/handler.js +2 -3
- package/build/cli/commands/task/doctor/meta.js +2 -2
- package/build/cli/commands/task/done/meta.js +2 -2
- package/build/cli/commands/task/drop/handler.js +7 -2
- package/build/cli/commands/task/drop/meta.js +6 -3
- package/build/cli/commands/task/hold/meta.js +2 -2
- package/build/cli/commands/task/log/meta.js +2 -2
- package/build/cli/commands/task/ls/meta.js +2 -2
- package/build/cli/commands/task/note/handler.js +6 -0
- package/build/cli/commands/task/note/meta.js +11 -0
- package/build/cli/commands/task/resume/meta.js +2 -2
- package/build/cli/commands/task/shared.js +29 -15
- package/build/cli/commands/task/show/meta.js +2 -2
- package/build/cli/commands/task/start/meta.js +2 -2
- package/build/cli/commands/task/stop/meta.js +2 -2
- package/build/cli/commands/task/update/meta.js +3 -2
- package/build/cli/commands/verification/handler.js +43 -0
- package/build/cli/commands/verification/meta.js +13 -0
- package/build/cli/completion.js +16 -13
- package/build/cli/flags.js +48 -12
- package/build/cli/help.js +29 -30
- package/build/cli/index.js +14 -8
- package/build/cli/parse-flags.js +60 -21
- package/build/cli/parse-metadata.js +10 -4
- package/build/cli/parse-selectors.js +22 -4
- package/build/cli/parse.js +43 -30
- package/build/cli/projection-address.js +7 -7
- package/build/cli/render/arc.js +12 -3
- package/build/cli/render/audit.js +2 -2
- package/build/cli/render/call.js +4 -4
- package/build/cli/render/kanshi.js +122 -85
- package/build/cli/render/misc.js +2 -2
- package/build/cli/render/petition.js +16 -1
- package/build/cli/render/projection-history.js +17 -4
- package/build/cli/render/shared.js +5 -5
- package/build/cli/render/status-indicator.js +47 -0
- package/build/cli/render/status.js +106 -76
- package/build/cli/render/success-response.js +20 -5
- package/build/cli/render/task.js +33 -5
- package/build/cli/render/tell.js +10 -4
- package/build/cli/render/tool-command-normalization.js +137 -0
- package/build/cli/render/tool-presentation.js +2 -1
- package/build/cli/render/verification.js +23 -0
- package/build/cli/render/wait.js +101 -33
- package/build/cli/types.js +2 -1
- package/build/config/akuma-loader.js +21 -1
- package/build/config/settings/knobs.js +7 -1
- package/build/config/settings/schema.js +10 -5
- package/build/core/addressing.js +3 -3
- package/build/core/amend.js +102 -17
- package/build/core/arc.js +4 -4
- package/build/core/audit/candidate.js +10 -18
- package/build/core/audit/coordinates.js +12 -17
- package/build/core/audit/evidence.js +6 -4
- package/build/core/audit/facade.js +3 -3
- package/build/core/audit/report.js +1 -1
- package/build/core/bind-reconciliation.js +42 -23
- package/build/core/bind.js +182 -81
- package/build/core/call/call.js +6 -7
- package/build/core/call/context.js +46 -33
- package/build/core/call/execution.js +21 -6
- package/build/core/call/prompt.js +11 -42
- package/build/core/contract-carrier-runtime.js +209 -105
- package/build/core/contract-carrier.js +156 -73
- package/build/core/contract-view.js +43 -6
- package/build/core/contract.js +2 -1
- package/build/core/derived-replay.js +168 -35
- package/build/core/draft.js +60 -27
- package/build/core/forfeit.js +1 -1
- package/build/core/lifecycle-history.js +13 -0
- package/build/core/lifecycle-recovery.js +32 -27
- package/build/core/lifecycle-runner.js +14 -14
- package/build/core/log.js +1 -1
- package/build/core/outcome-base.js +2 -2
- package/build/core/projection/akuma-name.js +43 -0
- package/build/core/projection/generation/database.js +141 -79
- package/build/core/projection/generation/ledger.js +91 -0
- package/build/core/projection/generation/model.js +19 -14
- package/build/core/projection/generation/projection-generation-continuation.js +1 -1
- package/build/core/projection/generation/projection-generation-doorbell.js +88 -0
- package/build/core/projection/generation/projection-generation-execution.js +24 -0
- package/build/core/projection/generation/projection-generation-identity.js +4 -0
- package/build/core/projection/generation/projection-generation-launcher.js +72 -138
- package/build/core/projection/generation/projection-generation-process.js +16 -15
- package/build/core/projection/generation/projection-generation-runner.js +132 -76
- package/build/core/projection/generation/projection-generation-runtime.js +10 -61
- package/build/core/projection/generation/protocol.js +68 -0
- package/build/core/projection/generation/store.js +17 -7
- package/build/core/projection/generation/transitions.js +24 -14
- package/build/core/projection/index.js +5 -5
- package/build/core/projection/projection-core.js +6 -2
- package/build/core/projection/projection-execution-observer.js +1 -1
- package/build/core/projection/projection-history.js +8 -8
- package/build/core/projection/projection-kill.js +20 -10
- package/build/core/projection/projection-life-observer.js +8 -8
- package/build/core/projection/projection-mint.js +6 -8
- package/build/core/projection/projection-status-observation.js +77 -8
- package/build/core/projection/projection-status.js +151 -4
- package/build/core/projection/projection-wait.js +48 -29
- package/build/core/projection/projection-wake.js +28 -88
- package/build/core/projection/tell/launch-store.js +1 -1
- package/build/core/projection/tell/store.js +1 -1
- package/build/core/renew-build.js +204 -74
- package/build/core/renew-plan.js +34 -9
- package/build/core/renew-rewrite.js +56 -15
- package/build/core/renew.js +39 -35
- package/build/core/repository-ledger/accepted-fold-read.js +6 -3
- package/build/core/repository-ledger/claim-evidence.js +1 -1
- package/build/core/repository-ledger/codec.js +11 -5
- package/build/core/repository-ledger/current-state-store.js +464 -349
- package/build/core/repository-ledger/fold-repository.js +5 -6
- package/build/core/repository-ledger/identity.js +60 -0
- package/build/core/repository-ledger/inventory.js +3 -3
- package/build/core/repository-ledger/read-model.js +57 -9
- package/build/core/repository-ledger/write-transaction.js +24 -17
- package/build/core/run-control/connection-bound-close.js +41 -0
- package/build/core/run-control/detached-lease-bootstrap.js +208 -0
- package/build/core/run-control/detached-process.js +14 -0
- package/build/core/run-control/index.js +6 -0
- package/build/core/{process-group.js → run-control/process-tree.js} +1 -1
- package/build/core/run-control/runner-lease.js +172 -0
- package/build/core/run-control/sqlite-process-lifetime-lock.js +174 -0
- package/build/core/seal.js +44 -32
- package/build/core/settlement/claim-delivery.js +22 -9
- package/build/core/settlement/claim.js +8 -6
- package/build/core/settlement/petition-claim-gates.js +9 -8
- package/build/core/settlement/petition-head-guard.js +5 -6
- package/build/core/settlement/petition-preview.js +21 -5
- package/build/core/settlement/petition.js +51 -17
- package/build/core/settlement/settlement.js +20 -15
- package/build/core/settlement/verdict.js +2 -2
- package/build/core/settlement/verification.js +492 -364
- package/build/core/status/board.js +184 -75
- package/build/core/status/drift.js +8 -10
- package/build/core/task/board.js +1 -0
- package/build/core/task/commands.js +25 -0
- package/build/core/task/compose.js +328 -0
- package/build/core/task/document.js +102 -39
- package/build/core/task/index.js +4 -3
- package/build/core/task/settlement-git.js +70 -30
- package/build/core/task/settlement-policy.js +27 -14
- package/build/core/task/source-board.js +1 -0
- package/build/core/task/task-bind-preparation.js +59 -9
- package/build/core/task/task-contract.js +2 -2
- package/build/core/task/task-git-store.js +1 -0
- package/build/core/task/task-store-repository.js +2 -0
- package/build/core/task/task.js +2 -2
- package/build/core/transcripts.js +4 -4
- package/build/core/verification-declaration.js +95 -0
- package/build/core/worktree-bootstrap.js +1 -1
- package/build/core/worktree-path.js +7 -11
- package/build/flow-error.js +15 -2
- package/build/generated/version.js +2 -2
- package/build/git/branches.js +41 -70
- package/build/git/commits.js +92 -21
- package/build/git/core.js +9 -161
- package/build/git/diff/preview.js +4 -4
- package/build/git/diff/read.js +4 -4
- package/build/git/diff/structured.js +5 -5
- package/build/git/process.js +229 -0
- package/build/git/refs.js +30 -5
- package/build/git/staging.js +39 -115
- package/build/git/streaming-batch.js +9 -16
- package/build/git/worktree.js +104 -91
- package/build/index.js +7 -0
- package/package.json +2 -2
- package/skills/keiyaku-akuma/SKILL.md +27 -21
- package/skills/keiyaku-task/SKILL.md +61 -5
- package/skills/keiyaku-workflow/SKILL.md +81 -26
- package/build/cli/commands/projection/revive/meta.js +0 -12
- package/build/core/projection/projection-life-protocol.js +0 -115
- package/build/core/projection/projection-runner-lock.js +0 -382
- package/build/core/projection/tell/database.js +0 -127
- package/build/core/settlement/verification-coordination.js +0 -305
- package/build/core/settlement/verification-supervisor.js +0 -275
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { getConfig } from "../config/env.js";
|
|
3
|
+
import { normalizeFilesystemCoordinate } from "../fs/path-coordinate.js";
|
|
4
|
+
export const GIT_ABORT_FORCE_KILL_MS = 250;
|
|
5
|
+
const ADMITTED_GIT_ENV_KEYS = new Set([
|
|
6
|
+
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
|
|
7
|
+
"GIT_AUTHOR_DATE",
|
|
8
|
+
"GIT_AUTHOR_EMAIL",
|
|
9
|
+
"GIT_AUTHOR_NAME",
|
|
10
|
+
"GIT_COMMITTER_DATE",
|
|
11
|
+
"GIT_COMMITTER_EMAIL",
|
|
12
|
+
"GIT_COMMITTER_NAME",
|
|
13
|
+
"GIT_INDEX_FILE",
|
|
14
|
+
"GIT_NO_LAZY_FETCH",
|
|
15
|
+
"GIT_OBJECT_DIRECTORY",
|
|
16
|
+
"GIT_SHALLOW_FILE",
|
|
17
|
+
]);
|
|
18
|
+
const GIT_HOST_ENV_KEYS = [
|
|
19
|
+
"APPDATA",
|
|
20
|
+
"COMSPEC",
|
|
21
|
+
"ComSpec",
|
|
22
|
+
"HOME",
|
|
23
|
+
"HOMEDRIVE",
|
|
24
|
+
"HOMEPATH",
|
|
25
|
+
"LOCALAPPDATA",
|
|
26
|
+
"PATH",
|
|
27
|
+
"PATHEXT",
|
|
28
|
+
"SYSTEMDRIVE",
|
|
29
|
+
"SYSTEMROOT",
|
|
30
|
+
"SystemRoot",
|
|
31
|
+
"TEMP",
|
|
32
|
+
"TMP",
|
|
33
|
+
"TMPDIR",
|
|
34
|
+
"USER",
|
|
35
|
+
"USERNAME",
|
|
36
|
+
"WINDIR",
|
|
37
|
+
];
|
|
38
|
+
function initializedRuntimeEnv() {
|
|
39
|
+
try {
|
|
40
|
+
return getConfig().runtimeEnv;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Resolve one initialized executable coordinate. Values are never split into shell words. */
|
|
47
|
+
export function resolveGitExecutable() {
|
|
48
|
+
const configured = initializedRuntimeEnv()?.KEIYAKU_GIT;
|
|
49
|
+
return configured !== undefined && configured.trim().length > 0 ? configured : "git";
|
|
50
|
+
}
|
|
51
|
+
/** Configured Git timeout, with a bootstrap-safe default before env loads. */
|
|
52
|
+
export function resolveGitTimeoutMs() {
|
|
53
|
+
try {
|
|
54
|
+
return getConfig().git.timeoutMs;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return 60_000;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function isAdmittedGitEnvironmentKey(key) {
|
|
61
|
+
return ADMITTED_GIT_ENV_KEYS.has(key) || key.startsWith("GIT_TRACE2");
|
|
62
|
+
}
|
|
63
|
+
/** Non-interactive controlled Git environment shared by all owned Git processes. */
|
|
64
|
+
export function gitProcessEnv(extra) {
|
|
65
|
+
const runtimeEnv = initializedRuntimeEnv() ?? process.env;
|
|
66
|
+
const environment = {};
|
|
67
|
+
for (const key of GIT_HOST_ENV_KEYS) {
|
|
68
|
+
const value = runtimeEnv[key];
|
|
69
|
+
if (typeof value === "string")
|
|
70
|
+
environment[key] = value;
|
|
71
|
+
}
|
|
72
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
73
|
+
if (typeof value === "string" && key.startsWith("GIT_TRACE2"))
|
|
74
|
+
environment[key] = value;
|
|
75
|
+
}
|
|
76
|
+
for (const [key, value] of Object.entries(runtimeEnv)) {
|
|
77
|
+
if (typeof value === "string" && key.startsWith("GIT_TRACE2"))
|
|
78
|
+
environment[key] = value;
|
|
79
|
+
}
|
|
80
|
+
for (const [key, value] of Object.entries(extra ?? {})) {
|
|
81
|
+
if (typeof value === "string" && isAdmittedGitEnvironmentKey(key))
|
|
82
|
+
environment[key] = value;
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
...environment,
|
|
86
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
87
|
+
GCM_INTERACTIVE: "Never",
|
|
88
|
+
GIT_MERGE_AUTOEDIT: "no",
|
|
89
|
+
LC_ALL: "C",
|
|
90
|
+
PATH: runtimeEnv.PATH ?? "",
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The only physical Git process factory. It resolves the executable once and
|
|
95
|
+
* preserves normalized cwd, inherited environment, argv, and timeout facts.
|
|
96
|
+
*/
|
|
97
|
+
export function openGitProcess(cwd, args, options = {}) {
|
|
98
|
+
const normalizedCwd = normalizeFilesystemCoordinate(cwd);
|
|
99
|
+
const env = gitProcessEnv(options.env);
|
|
100
|
+
const executable = resolveGitExecutable();
|
|
101
|
+
return {
|
|
102
|
+
child: spawn(executable, [...args], {
|
|
103
|
+
cwd: normalizedCwd,
|
|
104
|
+
env,
|
|
105
|
+
shell: false,
|
|
106
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
107
|
+
}),
|
|
108
|
+
cwd: normalizedCwd,
|
|
109
|
+
executable,
|
|
110
|
+
env,
|
|
111
|
+
timeoutMs: options.timeoutMs ?? resolveGitTimeoutMs(),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/** Terminate one non-detached Git child and wait until Node drains its stdio close. */
|
|
115
|
+
export async function terminateGitChild(child, closed, graceMs = GIT_ABORT_FORCE_KILL_MS) {
|
|
116
|
+
if (child.exitCode === null && child.signalCode === null)
|
|
117
|
+
child.kill("SIGTERM");
|
|
118
|
+
let grace;
|
|
119
|
+
const exited = await Promise.race([
|
|
120
|
+
closed.then(() => true),
|
|
121
|
+
new Promise((resolve) => {
|
|
122
|
+
grace = setTimeout(() => resolve(false), graceMs);
|
|
123
|
+
grace.unref();
|
|
124
|
+
}),
|
|
125
|
+
]);
|
|
126
|
+
if (grace)
|
|
127
|
+
clearTimeout(grace);
|
|
128
|
+
if (!exited)
|
|
129
|
+
child.kill("SIGKILL");
|
|
130
|
+
await closed;
|
|
131
|
+
}
|
|
132
|
+
function processError(result) {
|
|
133
|
+
if (result.error)
|
|
134
|
+
return result.error;
|
|
135
|
+
if (result.status === 0)
|
|
136
|
+
return undefined;
|
|
137
|
+
const error = new Error(`git exited with status ${result.status ?? "unknown"}`);
|
|
138
|
+
Object.assign(error, {
|
|
139
|
+
stdout: result.stdout.toString("utf8"),
|
|
140
|
+
stderr: result.stderr.toString("utf8"),
|
|
141
|
+
status: result.status,
|
|
142
|
+
});
|
|
143
|
+
return error;
|
|
144
|
+
}
|
|
145
|
+
/** Soft-result Git process mouth: preserves output, exit, spawn, timeout, and abort facts. */
|
|
146
|
+
export async function runGitProcessBytes(cwd, args, options = {}) {
|
|
147
|
+
options.signal?.throwIfAborted();
|
|
148
|
+
const process = openGitProcess(cwd, args, options);
|
|
149
|
+
const { child } = process;
|
|
150
|
+
const stdoutChunks = [];
|
|
151
|
+
const stderrChunks = [];
|
|
152
|
+
return await new Promise((resolve) => {
|
|
153
|
+
let settled = false;
|
|
154
|
+
let timedOut = false;
|
|
155
|
+
let aborting = false;
|
|
156
|
+
let termination;
|
|
157
|
+
const closed = new Promise((resolveClosed) => child.once("close", () => resolveClosed()));
|
|
158
|
+
const finish = (result) => {
|
|
159
|
+
if (settled)
|
|
160
|
+
return;
|
|
161
|
+
settled = true;
|
|
162
|
+
clearTimeout(timeout);
|
|
163
|
+
options.signal?.removeEventListener("abort", abort);
|
|
164
|
+
resolve(result);
|
|
165
|
+
};
|
|
166
|
+
const abort = () => {
|
|
167
|
+
if (aborting)
|
|
168
|
+
return;
|
|
169
|
+
aborting = true;
|
|
170
|
+
termination = terminateGitChild(child, closed);
|
|
171
|
+
};
|
|
172
|
+
const timeout = setTimeout(() => {
|
|
173
|
+
timedOut = true;
|
|
174
|
+
child.kill("SIGKILL");
|
|
175
|
+
}, process.timeoutMs);
|
|
176
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
177
|
+
if (options.signal?.aborted)
|
|
178
|
+
abort();
|
|
179
|
+
child.stdout?.on("data", (chunk) => stdoutChunks.push(chunk));
|
|
180
|
+
child.stderr?.on("data", (chunk) => stderrChunks.push(chunk));
|
|
181
|
+
child.on("error", (error) => {
|
|
182
|
+
Object.assign(error, { gitCwd: process.cwd });
|
|
183
|
+
finish({ stdout: Buffer.concat(stdoutChunks), stderr: Buffer.concat(stderrChunks), status: null, error });
|
|
184
|
+
});
|
|
185
|
+
child.on("close", async (code, signal) => {
|
|
186
|
+
if (termination)
|
|
187
|
+
await termination;
|
|
188
|
+
const stdout = Buffer.concat(stdoutChunks);
|
|
189
|
+
const stderr = Buffer.concat(stderrChunks);
|
|
190
|
+
if (timedOut)
|
|
191
|
+
return finish({ stdout, stderr, status: null, error: new Error("git timed out") });
|
|
192
|
+
if (signal) {
|
|
193
|
+
const error = options.signal?.aborted
|
|
194
|
+
? options.signal.reason instanceof Error ? options.signal.reason : new DOMException("The operation was aborted", "AbortError")
|
|
195
|
+
: new Error(`git exited from signal ${signal}`);
|
|
196
|
+
return finish({ stdout, stderr, status: null, error });
|
|
197
|
+
}
|
|
198
|
+
finish({ stdout, stderr, status: code });
|
|
199
|
+
});
|
|
200
|
+
if (options.input === undefined)
|
|
201
|
+
child.stdin?.end();
|
|
202
|
+
else if (typeof options.input === "string")
|
|
203
|
+
child.stdin?.end(options.input, "utf8");
|
|
204
|
+
else
|
|
205
|
+
child.stdin?.end(options.input);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
export async function runGitProcess(cwd, args, options = {}) {
|
|
209
|
+
const result = await runGitProcessBytes(cwd, args, options);
|
|
210
|
+
return {
|
|
211
|
+
stdout: result.stdout.toString("utf8"),
|
|
212
|
+
stderr: result.stderr.toString("utf8"),
|
|
213
|
+
status: result.status,
|
|
214
|
+
...(result.error ? { error: result.error } : {}),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/** Throwing Git process mouth for commands whose non-zero exit is exceptional. */
|
|
218
|
+
export async function runGitOrThrow(cwd, args, options = {}) {
|
|
219
|
+
const result = await runGitProcess(cwd, args, options);
|
|
220
|
+
const error = processError({
|
|
221
|
+
stdout: Buffer.from(result.stdout),
|
|
222
|
+
stderr: Buffer.from(result.stderr),
|
|
223
|
+
status: result.status,
|
|
224
|
+
...(result.error ? { error: result.error } : {}),
|
|
225
|
+
});
|
|
226
|
+
if (error)
|
|
227
|
+
throw error;
|
|
228
|
+
return result;
|
|
229
|
+
}
|
package/build/git/refs.js
CHANGED
|
@@ -61,10 +61,35 @@ export async function resolveLocalBranchHead(cwd, branch, run = runGitProcess) {
|
|
|
61
61
|
}
|
|
62
62
|
return throwGitProcessFailure(`show-ref ${ref}`, result, cwd);
|
|
63
63
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
/** Resolve an exact set of direct refs with one Git process. */
|
|
65
|
+
export async function resolveExactRefHeads(cwd, refs, run = runGitProcess) {
|
|
66
|
+
const unique = [...new Set(refs.map((ref) => normalizeLocalBranchRef(ref)))];
|
|
67
|
+
if (unique.length === 0)
|
|
68
|
+
return new Map();
|
|
69
|
+
const result = await run(cwd, ["show-ref", ...unique]);
|
|
70
|
+
if (result.error || (result.status !== 0 && result.status !== 1)) {
|
|
71
|
+
return throwGitProcessFailure(`show-ref ${unique.join(" ")}`, result, cwd);
|
|
72
|
+
}
|
|
73
|
+
const rows = result.stdout === "" ? [] : parseShowRefRows(result.stdout);
|
|
74
|
+
if (rows === null)
|
|
75
|
+
return throwGitProcessFailure(`show-ref ${unique.join(" ")}`, result, cwd);
|
|
76
|
+
const requested = new Set(unique);
|
|
77
|
+
const found = new Map();
|
|
78
|
+
for (const row of rows) {
|
|
79
|
+
if (!requested.has(row.ref) || found.has(row.ref)) {
|
|
80
|
+
return throwGitProcessFailure(`show-ref ${unique.join(" ")}`, {
|
|
81
|
+
...result,
|
|
82
|
+
status: 2,
|
|
83
|
+
stderr: `show-ref returned an invalid exact value for ${row.ref}`,
|
|
84
|
+
}, cwd);
|
|
85
|
+
}
|
|
86
|
+
found.set(row.ref, row.objectId);
|
|
87
|
+
}
|
|
88
|
+
return new Map(unique.map((ref) => [ref, found.get(ref) ?? null]));
|
|
89
|
+
}
|
|
90
|
+
async function validateTransactionOperation(operation, objectIdLength) {
|
|
91
|
+
if (!/^refs\/(?![./])(?!.*(?:\.\.|@\{|[~^:?*\\\[\x00-\x20\x7f]))(?!.*\/\.)(?!.*\.lock(?:\/|$))(?!.*\/$).+$/.test(operation.ref)) {
|
|
92
|
+
throw new RefTransactionError(`invalid transaction ref ${JSON.stringify(operation.ref)}`);
|
|
68
93
|
}
|
|
69
94
|
const objectIds = operation.kind === "update"
|
|
70
95
|
? [["new", operation.newOid], ["old", operation.expectedOldOid]]
|
|
@@ -160,7 +185,7 @@ export async function commitRefTransaction(cwd, operations, objectIdLength, opti
|
|
|
160
185
|
throw new RefTransactionError("a ref transaction requires at least one operation");
|
|
161
186
|
const seen = new Set();
|
|
162
187
|
for (const operation of operations) {
|
|
163
|
-
await validateTransactionOperation(
|
|
188
|
+
await validateTransactionOperation(operation, objectIdLength);
|
|
164
189
|
if (seen.has(operation.ref))
|
|
165
190
|
throw new RefTransactionError(`duplicate transaction ref ${operation.ref}`);
|
|
166
191
|
seen.add(operation.ref);
|
package/build/git/staging.js
CHANGED
|
@@ -1,155 +1,79 @@
|
|
|
1
1
|
import { getConfig } from "../config/env.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { wrapGitError } from "./core.js";
|
|
3
|
+
import { runGitOrThrow } from "./process.js";
|
|
4
|
+
async function run(cwd, args, label) {
|
|
5
5
|
try {
|
|
6
|
-
await
|
|
6
|
+
return (await runGitOrThrow(cwd, args)).stdout;
|
|
7
7
|
}
|
|
8
|
-
catch (
|
|
9
|
-
throw wrapGitError(
|
|
8
|
+
catch (error) {
|
|
9
|
+
throw wrapGitError(label, error, cwd);
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
+
export async function addFiles(cwd, files) {
|
|
13
|
+
await run(cwd, ["add", "--", ...(typeof files === "string" ? [files] : files)], "add");
|
|
14
|
+
}
|
|
12
15
|
export async function addFilesForce(cwd, files) {
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
await git.raw(["add", "--force", "--", ...files]);
|
|
16
|
-
}
|
|
17
|
-
catch (err) {
|
|
18
|
-
throw wrapGitError("add --force", err, cwd);
|
|
19
|
-
}
|
|
16
|
+
await run(cwd, ["add", "--force", "--", ...files], "add --force");
|
|
20
17
|
}
|
|
21
18
|
export async function addUpdatedFiles(cwd) {
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
await git.raw(["add", "-u"]);
|
|
25
|
-
}
|
|
26
|
-
catch (err) {
|
|
27
|
-
throw wrapGitError("add -u", err, cwd);
|
|
28
|
-
}
|
|
19
|
+
await run(cwd, ["add", "-u"], "add -u");
|
|
29
20
|
}
|
|
30
21
|
export async function resetStaging(cwd) {
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
await git.raw(["reset"]);
|
|
34
|
-
}
|
|
35
|
-
catch (err) {
|
|
36
|
-
throw wrapGitError("reset", err, cwd);
|
|
37
|
-
}
|
|
22
|
+
await run(cwd, ["reset"], "reset");
|
|
38
23
|
}
|
|
39
|
-
|
|
40
|
-
const git = createGit(cwd);
|
|
24
|
+
function commitArgs(message, allowEmpty = false) {
|
|
41
25
|
const { noVerify: skipVerify, noGpgSign } = getConfig().git;
|
|
42
|
-
const args = ["commit", "-m", message];
|
|
26
|
+
const args = ["commit", ...(allowEmpty ? ["--allow-empty"] : []), "-m", message];
|
|
43
27
|
if (skipVerify)
|
|
44
28
|
args.push("--no-verify");
|
|
45
29
|
if (noGpgSign)
|
|
46
30
|
args.push("--no-gpg-sign");
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
31
|
+
return args;
|
|
32
|
+
}
|
|
33
|
+
export async function commit(cwd, message) {
|
|
34
|
+
const args = commitArgs(message);
|
|
35
|
+
await run(cwd, args, args.join(" "));
|
|
53
36
|
}
|
|
54
37
|
export async function commitAllowEmpty(cwd, message) {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
const args = ["commit", "--allow-empty", "-m", message];
|
|
58
|
-
if (skipVerify)
|
|
59
|
-
args.push("--no-verify");
|
|
60
|
-
if (noGpgSign)
|
|
61
|
-
args.push("--no-gpg-sign");
|
|
62
|
-
try {
|
|
63
|
-
await git.raw(args);
|
|
64
|
-
}
|
|
65
|
-
catch (err) {
|
|
66
|
-
throw wrapGitError(args.join(" "), err, cwd);
|
|
67
|
-
}
|
|
38
|
+
const args = commitArgs(message, true);
|
|
39
|
+
await run(cwd, args, args.join(" "));
|
|
68
40
|
}
|
|
69
41
|
export async function revertCommit(cwd, commitHash, message) {
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
await git.raw(["revert", "--no-commit", commitHash]);
|
|
73
|
-
}
|
|
74
|
-
catch (err) {
|
|
75
|
-
throw wrapGitError(`revert --no-commit ${commitHash}`, err, cwd);
|
|
76
|
-
}
|
|
42
|
+
await run(cwd, ["revert", "--no-commit", commitHash], `revert --no-commit ${commitHash}`);
|
|
77
43
|
await commit(cwd, message);
|
|
78
44
|
}
|
|
79
45
|
export async function merge(cwd, branchName, message, options = {}) {
|
|
80
|
-
const git = createGit(cwd);
|
|
81
46
|
const { squash = true } = options;
|
|
82
47
|
const { noVerify: skipVerify, noGpgSign } = getConfig().git;
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (noGpgSign)
|
|
88
|
-
mergeOptions.push("--no-gpg-sign");
|
|
89
|
-
try {
|
|
90
|
-
await git.merge(mergeOptions);
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
catch (err) {
|
|
94
|
-
throw wrapGitError(`merge ${mergeOptions.join(" ")}`, err, cwd);
|
|
95
|
-
}
|
|
48
|
+
if (squash) {
|
|
49
|
+
await mergeSquashStaged(cwd, branchName);
|
|
50
|
+
await commitAllowEmpty(cwd, message);
|
|
51
|
+
return;
|
|
96
52
|
}
|
|
97
|
-
|
|
98
|
-
|
|
53
|
+
const args = ["merge", branchName, "--no-ff", "--no-edit", "-m", message];
|
|
54
|
+
if (skipVerify)
|
|
55
|
+
args.push("--no-verify");
|
|
56
|
+
if (noGpgSign)
|
|
57
|
+
args.push("--no-gpg-sign");
|
|
58
|
+
await run(cwd, args, `merge ${args.slice(1).join(" ")}`);
|
|
99
59
|
}
|
|
100
60
|
export async function mergeSquashStaged(cwd, branchName) {
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
try {
|
|
104
|
-
await git.raw(squashArgs);
|
|
105
|
-
}
|
|
106
|
-
catch (err) {
|
|
107
|
-
throw wrapGitError(squashArgs.join(" "), err, cwd);
|
|
108
|
-
}
|
|
61
|
+
const args = ["merge", "--squash", branchName];
|
|
62
|
+
await run(cwd, args, args.join(" "));
|
|
109
63
|
}
|
|
110
64
|
export async function restoreFilesFromHead(cwd, files) {
|
|
111
65
|
if (files.length === 0)
|
|
112
66
|
return;
|
|
113
|
-
const git = createGit(cwd);
|
|
114
67
|
const args = ["restore", "--source=HEAD", "--staged", "--worktree", "--", ...files];
|
|
115
|
-
|
|
116
|
-
await git.raw(args);
|
|
117
|
-
}
|
|
118
|
-
catch (err) {
|
|
119
|
-
throw wrapGitError(args.join(" "), err, cwd);
|
|
120
|
-
}
|
|
68
|
+
await run(cwd, args, args.join(" "));
|
|
121
69
|
}
|
|
122
70
|
export async function getUnmergedFiles(cwd) {
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
output = await git.raw(["diff", "--name-only", "--diff-filter=U"]);
|
|
127
|
-
}
|
|
128
|
-
catch (err) {
|
|
129
|
-
throw wrapGitError("diff --name-only --diff-filter=U", err, cwd);
|
|
130
|
-
}
|
|
131
|
-
const files = output
|
|
132
|
-
.split(/\r?\n/)
|
|
133
|
-
.map((line) => line.trim())
|
|
134
|
-
.filter((line) => line.length > 0);
|
|
135
|
-
return Array.from(new Set(files));
|
|
71
|
+
const output = await run(cwd, ["diff", "--name-only", "--diff-filter=U", "-z"], "diff --name-only --diff-filter=U");
|
|
72
|
+
return Array.from(new Set(output.split("\0").filter((file) => file.length > 0)));
|
|
136
73
|
}
|
|
137
74
|
export async function getLatestCommitHash(cwd) {
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
const hash = await git.revparse(["--short", "HEAD"]);
|
|
141
|
-
return hash.trim();
|
|
142
|
-
}
|
|
143
|
-
catch (err) {
|
|
144
|
-
throw wrapGitError("rev-parse --short HEAD", err, cwd);
|
|
145
|
-
}
|
|
75
|
+
return (await run(cwd, ["rev-parse", "--short", "HEAD"], "rev-parse --short HEAD")).trim();
|
|
146
76
|
}
|
|
147
77
|
export async function createTagAtHead(cwd, tagName) {
|
|
148
|
-
|
|
149
|
-
try {
|
|
150
|
-
await git.raw(["tag", tagName, "HEAD"]);
|
|
151
|
-
}
|
|
152
|
-
catch (err) {
|
|
153
|
-
throw wrapGitError(`tag ${tagName} HEAD`, err, cwd);
|
|
154
|
-
}
|
|
78
|
+
await run(cwd, ["tag", tagName, "HEAD"], `tag ${tagName} HEAD`);
|
|
155
79
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
1
|
import { isFullGitObjectId } from "./core.js";
|
|
3
|
-
import {
|
|
4
|
-
import { gitProcessEnv, resolveGitTimeoutMs, terminateGitChild, } from "./core.js";
|
|
2
|
+
import { openGitProcess, terminateGitChild, } from "./process.js";
|
|
5
3
|
/** Default long-lived batch argv. One-shot calls must not use this gate. */
|
|
6
4
|
export const GIT_STREAMING_BATCH_DEFAULT_ARGS = ["cat-file", "--batch"];
|
|
7
5
|
/** Safety cap for a single response header line before the body frame. */
|
|
@@ -43,7 +41,8 @@ class BufferedReader {
|
|
|
43
41
|
if (newline > maxBytes) {
|
|
44
42
|
throw new GitStreamingBatchFramingError(`git streaming batch header exceeds ${maxBytes} bytes`);
|
|
45
43
|
}
|
|
46
|
-
const
|
|
44
|
+
const rawLine = this.#buffer.subarray(0, newline);
|
|
45
|
+
const line = rawLine.subarray(0, rawLine.length > 0 && rawLine[rawLine.length - 1] === 0x0d ? -1 : rawLine.length).toString("ascii");
|
|
47
46
|
this.#buffer = this.#buffer.subarray(newline + 1);
|
|
48
47
|
return line;
|
|
49
48
|
}
|
|
@@ -114,10 +113,6 @@ export class GitStreamingBatchSession {
|
|
|
114
113
|
});
|
|
115
114
|
this.#maxHeaderBytes = maxHeaderBytes;
|
|
116
115
|
this.#signal = signal;
|
|
117
|
-
this.#abortListener = () => this.abort();
|
|
118
|
-
signal?.addEventListener("abort", this.#abortListener, { once: true });
|
|
119
|
-
if (signal?.aborted)
|
|
120
|
-
this.abort();
|
|
121
116
|
this.#child.stderr.on("data", (chunk) => this.#stderr.push(chunk));
|
|
122
117
|
this.#child.stdin.on("error", (error) => {
|
|
123
118
|
this.#inputError = error;
|
|
@@ -130,6 +125,10 @@ export class GitStreamingBatchSession {
|
|
|
130
125
|
resolve({ code, signal });
|
|
131
126
|
});
|
|
132
127
|
});
|
|
128
|
+
this.#abortListener = () => this.abort();
|
|
129
|
+
signal?.addEventListener("abort", this.#abortListener, { once: true });
|
|
130
|
+
if (signal?.aborted)
|
|
131
|
+
this.abort();
|
|
133
132
|
}
|
|
134
133
|
#armTimeout(owner) {
|
|
135
134
|
this.#clearTimeout();
|
|
@@ -158,15 +157,9 @@ export class GitStreamingBatchSession {
|
|
|
158
157
|
*/
|
|
159
158
|
static open(cwd, options) {
|
|
160
159
|
const args = options?.args ?? GIT_STREAMING_BATCH_DEFAULT_ARGS;
|
|
161
|
-
const
|
|
160
|
+
const process = openGitProcess(cwd, args, options);
|
|
162
161
|
const maxHeaderBytes = options?.maxHeaderBytes ?? GIT_STREAMING_BATCH_MAX_HEADER_BYTES;
|
|
163
|
-
|
|
164
|
-
cwd: normalizeFilesystemCoordinate(cwd),
|
|
165
|
-
env: gitProcessEnv(options?.env),
|
|
166
|
-
shell: false,
|
|
167
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
168
|
-
});
|
|
169
|
-
return new GitStreamingBatchSession(cwd, child, timeoutMs, maxHeaderBytes, options?.signal);
|
|
162
|
+
return new GitStreamingBatchSession(process.cwd, process.child, process.timeoutMs, maxHeaderBytes, options?.signal);
|
|
170
163
|
}
|
|
171
164
|
/**
|
|
172
165
|
* Write one request line and read one framed response record.
|