toolroll 0.9.77 → 0.9.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +96 -0
- package/dist/agent-fence.js +18 -2
- package/dist/backup-ui.d.ts +9 -1
- package/dist/backup-ui.js +28 -3
- package/dist/backup.js +4 -3
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +18 -18
- package/dist/browser-workspace.d.ts +2 -0
- package/dist/builder.d.ts +4 -3
- package/dist/builder.js +14 -8
- package/dist/check-failure.js +31 -3
- package/dist/check-report.d.ts +18 -3
- package/dist/check-report.js +67 -7
- package/dist/claim.d.ts +9 -0
- package/dist/claim.js +9 -1
- package/dist/contracts/console-api.d.ts +1 -1
- package/dist/contracts/console-api.js +1 -1
- package/dist/contracts/release-terms.d.ts +41 -0
- package/dist/contracts/release-terms.js +53 -0
- package/dist/controller-service.js +4 -1
- package/dist/deploy-rescue.d.ts +41 -0
- package/dist/deploy-rescue.js +94 -0
- package/dist/dispose.js +4 -3
- package/dist/evidence.d.ts +9 -0
- package/dist/evidence.js +15 -3
- package/dist/exec.d.ts +2 -0
- package/dist/exec.js +15 -1
- package/dist/git-views.d.ts +1 -1
- package/dist/git-views.js +2 -2
- package/dist/guides.js +18 -8
- package/dist/latency-timeline.d.ts +46 -0
- package/dist/latency-timeline.js +120 -0
- package/dist/live-backup.d.ts +39 -0
- package/dist/live-backup.js +271 -0
- package/dist/operate.js +203 -16
- package/dist/planner.js +2 -2
- package/dist/project-knowledge.d.ts +7 -7
- package/dist/release-adapters.d.ts +41 -3
- package/dist/release-adapters.js +275 -73
- package/dist/release-carry.d.ts +21 -1
- package/dist/release-carry.js +22 -0
- package/dist/release-cli.d.ts +3 -0
- package/dist/release-cli.js +74 -5
- package/dist/release-gate.d.ts +5 -0
- package/dist/release-gate.js +1 -1
- package/dist/release-net.d.ts +90 -0
- package/dist/release-net.js +253 -0
- package/dist/release-recheck.js +3 -2
- package/dist/release.d.ts +40 -5
- package/dist/release.js +142 -52
- package/dist/runner-process.d.ts +67 -0
- package/dist/runner-process.js +219 -0
- package/dist/runner.d.ts +48 -6
- package/dist/runner.js +71 -8
- package/dist/scope.d.ts +19 -1
- package/dist/scope.js +19 -2
- package/dist/scout.js +2 -2
- package/dist/serve.js +11 -0
- package/dist/server/http.d.ts +2 -0
- package/dist/server/pages.js +7 -3
- package/dist/server/remote-hooks.d.ts +1 -1
- package/dist/server/remote-hooks.js +5 -3
- package/dist/server/render-results.d.ts +2 -0
- package/dist/server/render-results.js +1 -0
- package/dist/server/request-context.d.ts +2 -0
- package/dist/server/settings.js +22 -2
- package/dist/server/tasks.js +4 -1
- package/dist/storage-sweep.js +1 -1
- package/dist/store.d.ts +125 -2
- package/dist/store.js +250 -12
- package/dist/surface.js +3 -1
- package/dist/teardown-git.d.ts +1 -1
- package/dist/teardown-git.js +12 -12
- package/dist/toolroll-update.js +2 -2
- package/dist/up-role-child.js +6 -2
- package/dist/workspace-revision.js +3 -1
- package/package.json +1 -1
package/dist/teardown-git.js
CHANGED
|
@@ -190,7 +190,7 @@ function nestedRepository(path) {
|
|
|
190
190
|
* repository (boundDirs; `binding.repo` when the caller has the project's), is refused; so is a command whose
|
|
191
191
|
* repository changed under it while git ran.
|
|
192
192
|
*/
|
|
193
|
-
async function inView(cwd, binding, work, after = true) {
|
|
193
|
+
async function inView(cwd, binding, work, after = true, lockTimeoutMs, gitFile = "git") {
|
|
194
194
|
const root = checkoutRootOf(cwd);
|
|
195
195
|
if (root === null)
|
|
196
196
|
return NOT_A_REPOSITORY;
|
|
@@ -206,7 +206,7 @@ async function inView(cwd, binding, work, after = true) {
|
|
|
206
206
|
return await withCheckoutLock(realpathSync(homedir()), key, async () => {
|
|
207
207
|
let view;
|
|
208
208
|
try {
|
|
209
|
-
view = gitView(root, "write", binding);
|
|
209
|
+
view = gitView(root, "write", binding, gitFile);
|
|
210
210
|
}
|
|
211
211
|
catch (error) {
|
|
212
212
|
return refused(error instanceof Error ? error.message : "the checkout couldn't be read");
|
|
@@ -221,7 +221,7 @@ async function inView(cwd, binding, work, after = true) {
|
|
|
221
221
|
finally {
|
|
222
222
|
view.dispose();
|
|
223
223
|
}
|
|
224
|
-
});
|
|
224
|
+
}, lockTimeoutMs);
|
|
225
225
|
}
|
|
226
226
|
catch (error) {
|
|
227
227
|
return refused(error instanceof Error ? error.message : "the checkout is busy");
|
|
@@ -234,11 +234,11 @@ async function inView(cwd, binding, work, after = true) {
|
|
|
234
234
|
* target is bound to the same repository as the folder it is removed from. A removal without --force runs under the
|
|
235
235
|
* target's own view (its own config.worktree folded in), so git's last check of whether it is clean reads it as the
|
|
236
236
|
* target itself would. `binding.repo`: the project's main checkout, from the plane's records, when the caller has it. */
|
|
237
|
-
export function teardownGit(runner = run, binding = {}) {
|
|
237
|
+
export function teardownGit(runner = run, binding = {}, lockTimeoutMs, gitFile = "git") {
|
|
238
238
|
const options = (cwd, env) => ({ cwd, envAllowlist: ENV_ALLOWLIST, env: { ...ENV, ...env }, timeoutMs: WORKTREE_TIMEOUT_MS, maxBuffer: 512 * 1024 * 1024 });
|
|
239
239
|
const configProblem = async (cwd, env) => {
|
|
240
240
|
// git-lfs's own filters may stand only beside a .lfsconfig that names no extension (commandSetting's lfsChecked).
|
|
241
|
-
if (lfsExtensionIn(env["GIT_WORK_TREE"] ?? cwd))
|
|
241
|
+
if (lfsExtensionIn(env["GIT_WORK_TREE"] ?? cwd, gitFile))
|
|
242
242
|
return refused("the checkout's .lfsconfig sets a git-lfs key that can run a command");
|
|
243
243
|
const config = await runner("git", ["--no-optional-locks", "config", "--list", "--no-includes", "--show-scope", "-z"], options(cwd, env));
|
|
244
244
|
if (config.code !== 0)
|
|
@@ -287,8 +287,8 @@ export function teardownGit(runner = run, binding = {}) {
|
|
|
287
287
|
// the target's view, inspected above, so it judges the target by the target's own settings.
|
|
288
288
|
return ran(removal && !forced ? { ...view.env, GIT_COMMON_DIR: targetView.env["GIT_COMMON_DIR"] } : view.env);
|
|
289
289
|
// A removed target has no git directory left to prove: the folder it was removed from is proven instead.
|
|
290
|
-
}, !removal);
|
|
291
|
-
});
|
|
290
|
+
}, !removal, lockTimeoutMs, gitFile);
|
|
291
|
+
}, true, lockTimeoutMs, gitFile);
|
|
292
292
|
}
|
|
293
293
|
/**
|
|
294
294
|
* Git on a build's checkout, as the plane runs it outside every fence (staging, committing and inspecting the build's
|
|
@@ -364,7 +364,7 @@ function includeIn(path, gitFile = "git") {
|
|
|
364
364
|
const text = regularFile(path);
|
|
365
365
|
if (text === null)
|
|
366
366
|
return "an unreadable file";
|
|
367
|
-
const parsed = spawnSync(gitFile, ["config", "--file", path, "--no-includes", "--name-only", "--list", "-z"], { cwd: "/", encoding: "utf8", timeout: 5_000, env: { PATH: process.env["PATH"] ?? "", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", HOME: "/nonexistent" } });
|
|
367
|
+
const parsed = spawnSync(gitFile, ["config", "--file", path, "--no-includes", "--name-only", "--list", "-z"], { cwd: "/", encoding: "utf8", timeout: 5_000, killSignal: "SIGKILL", env: { PATH: process.env["PATH"] ?? "", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", HOME: "/nonexistent" } });
|
|
368
368
|
if (parsed.status !== 0)
|
|
369
369
|
return "an unreadable file";
|
|
370
370
|
const named = parsed.stdout.split("\0").find(name => /^(include\.path|includeif\..*\.path)$/i.test(name));
|
|
@@ -488,7 +488,7 @@ function gitView(checkout, kind, binding = {}, gitFile = "git") {
|
|
|
488
488
|
throw new Error("the checkout's git config couldn't be read as a plain file");
|
|
489
489
|
const configPath = join(view, "config");
|
|
490
490
|
writeFileSync(configPath, config, { mode: 0o600 });
|
|
491
|
-
const plain = { cwd: "/", encoding: "utf8", timeout: 5_000, env: { PATH: process.env["PATH"] ?? "", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", HOME: "/nonexistent" } };
|
|
491
|
+
const plain = { cwd: "/", encoding: "utf8", timeout: 5_000, killSignal: "SIGKILL", env: { PATH: process.env["PATH"] ?? "", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", HOME: "/nonexistent" } };
|
|
492
492
|
if (/worktreeconfig/i.test(config)) {
|
|
493
493
|
const flag = spawnSync(gitFile, ["config", "--file", configPath, "--no-includes", "--type=bool", "--get", "extensions.worktreeConfig"], plain);
|
|
494
494
|
if (flag.status !== 0 && flag.status !== 1)
|
|
@@ -1038,7 +1038,7 @@ export function lfsExtensionIn(checkout, gitFile = "git") {
|
|
|
1038
1038
|
catch {
|
|
1039
1039
|
return false;
|
|
1040
1040
|
}
|
|
1041
|
-
const parsed = spawnSync(gitFile, ["config", "--file", path, "--no-includes", "--name-only", "--list", "-z"], { cwd: "/", encoding: "utf8", timeout: 5_000, env: { PATH: process.env["PATH"] ?? "", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", HOME: "/nonexistent" } });
|
|
1041
|
+
const parsed = spawnSync(gitFile, ["config", "--file", path, "--no-includes", "--name-only", "--list", "-z"], { cwd: "/", encoding: "utf8", timeout: 5_000, killSignal: "SIGKILL", env: { PATH: process.env["PATH"] ?? "", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", HOME: "/nonexistent" } });
|
|
1042
1042
|
return parsed.status !== 0 || parsed.stdout.split("\0").filter(Boolean).some(name => lfsCommand(name) || ["include", "includeif"].includes(keyParts(name).section));
|
|
1043
1043
|
}
|
|
1044
1044
|
/** Every command runs against a view of the checkout's repository (gitView) whose config and info are copies the
|
|
@@ -1148,7 +1148,7 @@ export function viewedGitSync(cwd, args, options = {}) {
|
|
|
1148
1148
|
const view = gitView(cwd, "read", options.binding, gitFile);
|
|
1149
1149
|
try {
|
|
1150
1150
|
const env = { PATH: process.env["PATH"] ?? "", HOME: process.env["HOME"] ?? "", LANG: "C", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", GIT_TERMINAL_PROMPT: "0", ...view.env };
|
|
1151
|
-
const bounded = { cwd, env, encoding: "utf8", timeout: options.timeoutMs ?? 60_000, maxBuffer: options.maxBuffer ?? 1_048_576, stdio: ["ignore", "pipe", "pipe"] };
|
|
1151
|
+
const bounded = { cwd, env, encoding: "utf8", timeout: options.timeoutMs ?? 60_000, killSignal: "SIGKILL", maxBuffer: options.maxBuffer ?? 1_048_576, stdio: ["ignore", "pipe", "pipe"] };
|
|
1152
1152
|
const own = spawnSync(gitFile, ["--no-optional-locks", "config", "--list", "--show-scope", "-z"], bounded);
|
|
1153
1153
|
if (own.status !== 0)
|
|
1154
1154
|
throw new Error("its config couldn't be read");
|
|
@@ -1234,7 +1234,7 @@ export function lazyFetchSupported(gitFile = "git") {
|
|
|
1234
1234
|
const executable = gitExecutablePath(gitFile);
|
|
1235
1235
|
let known = lazyFetchByGit.get(executable);
|
|
1236
1236
|
if (known === undefined) {
|
|
1237
|
-
const probe = spawnSync(executable, ["--no-lazy-fetch", "version"], { cwd: "/", encoding: "utf8", timeout: 5_000, stdio: ["ignore", "pipe", "pipe"],
|
|
1237
|
+
const probe = spawnSync(executable, ["--no-lazy-fetch", "version"], { cwd: "/", encoding: "utf8", timeout: 5_000, killSignal: "SIGKILL", stdio: ["ignore", "pipe", "pipe"],
|
|
1238
1238
|
env: { PATH: process.env["PATH"] ?? "", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", HOME: "/nonexistent" } });
|
|
1239
1239
|
known = probe.status === 0;
|
|
1240
1240
|
lazyFetchByGit.set(executable, known);
|
package/dist/toolroll-update.js
CHANGED
|
@@ -37,7 +37,7 @@ import { removeTerminalUpdateGate } from "./terminal-catalog.js";
|
|
|
37
37
|
import { assertCodingUpdateStopped, backupCodingCatalog, codingCatalogExists, releaseStaleCodingOwner, removeCodingUpdateGate } from "./coding-update.js";
|
|
38
38
|
import { installLaunchdService, launchdPlist, stopLaunchdService, writeFileDurably } from "./daemon.js";
|
|
39
39
|
import { NAME } from "./names.js";
|
|
40
|
-
import { orphanKey, updateSafeSchema, V117_RENAMED_COLUMNS, V117_RENAMED_TABLES, V118_MERGED_TABLES, V119_ADDED_COLUMNS, V120_ADDED_COLUMNS, V121_ADDED_COLUMNS, V122_ADDED_COLUMNS, V123_ADDED_COLUMNS, V125_ADDED_COLUMNS, V126_ADDED_COLUMNS, V128_ADDED_COLUMNS, V129_ADDED_COLUMNS, V134_ADDED_COLUMNS, V135_ADDED_COLUMNS } from "./store.js";
|
|
40
|
+
import { orphanKey, updateSafeSchema, V117_RENAMED_COLUMNS, V117_RENAMED_TABLES, V118_MERGED_TABLES, V119_ADDED_COLUMNS, V120_ADDED_COLUMNS, V121_ADDED_COLUMNS, V122_ADDED_COLUMNS, V123_ADDED_COLUMNS, V125_ADDED_COLUMNS, V126_ADDED_COLUMNS, V128_ADDED_COLUMNS, V129_ADDED_COLUMNS, V134_ADDED_COLUMNS, V135_ADDED_COLUMNS, V138_ADDED_COLUMNS } from "./store.js";
|
|
41
41
|
import { isNewer, REGISTRY } from "./releases.js";
|
|
42
42
|
import { markNeverIndex } from "./never-index.js";
|
|
43
43
|
import { readRuntimeUpdateJournal, RUNTIME_PHASES, RUNTIME_UPDATE_STEPS, stagedStartedAt, updaterStartingOf } from "./contracts/update-journal.js";
|
|
@@ -409,7 +409,7 @@ const HISTORY_RULES = {
|
|
|
409
409
|
// v126: a receipt's state, acceptance time and held item, a trigger's credential generation and turned-away count,
|
|
410
410
|
// and who made a card read none, 0, or none. A table with an earlier rule (flow_trigger's v115 move) keeps it too.
|
|
411
411
|
// v128: a lead message's pending flag reads waiting exactly for a team queue's unadmitted operator message.
|
|
412
|
-
...Object.fromEntries(Object.entries(addedColumns(V119_ADDED_COLUMNS, V120_ADDED_COLUMNS, V121_ADDED_COLUMNS, V122_ADDED_COLUMNS, V123_ADDED_COLUMNS, V125_ADDED_COLUMNS, V126_ADDED_COLUMNS, V128_ADDED_COLUMNS, V129_ADDED_COLUMNS, V134_ADDED_COLUMNS, V135_ADDED_COLUMNS))
|
|
412
|
+
...Object.fromEntries(Object.entries(addedColumns(V119_ADDED_COLUMNS, V120_ADDED_COLUMNS, V121_ADDED_COLUMNS, V122_ADDED_COLUMNS, V123_ADDED_COLUMNS, V125_ADDED_COLUMNS, V126_ADDED_COLUMNS, V128_ADDED_COLUMNS, V129_ADDED_COLUMNS, V134_ADDED_COLUMNS, V135_ADDED_COLUMNS, V138_ADDED_COLUMNS))
|
|
413
413
|
.map(([table, rule]) => [table, { ...EARLIER_RULES[table], ...rule }])),
|
|
414
414
|
};
|
|
415
415
|
/** The tables a migration may remove whole (v115's removed features). */
|
package/dist/up-role-child.js
CHANGED
|
@@ -39,8 +39,12 @@ const port = {
|
|
|
39
39
|
// stop on the second), so a role never acts on SIGINT itself. SIGTERM (the supervisor's stop) drains, once.
|
|
40
40
|
process.on("SIGINT", () => { });
|
|
41
41
|
process.on("SIGTERM", () => stop(false));
|
|
42
|
-
// The parent gone (its channel closed): drain, as a stop would. The worker's own grace timer bounds it
|
|
43
|
-
|
|
42
|
+
// The parent gone (its channel closed): drain, as a stop would. The worker's own grace timer bounds it; past a minute
|
|
43
|
+
// with nobody left to stop it, the role stops its work outright and ends, never running on as an orphan.
|
|
44
|
+
process.once("disconnect", () => {
|
|
45
|
+
stop(false);
|
|
46
|
+
setTimeout(() => { stop(true); setTimeout(() => process.exit(1), 5_000).unref(); }, 60_000).unref();
|
|
47
|
+
});
|
|
44
48
|
const start = await new Promise(resolve => {
|
|
45
49
|
const timer = setTimeout(() => resolve(null), 60_000);
|
|
46
50
|
process.once("message", value => { clearTimeout(timer); resolve(value); });
|
|
@@ -8,7 +8,9 @@ const OMIT = new Set(['schema_version', 'service_cursor', 'wake', 'notification_
|
|
|
8
8
|
// Who is watching an agent right now: renewed by every open page's keep-alive, never news.
|
|
9
9
|
'agent_view',
|
|
10
10
|
// A run's How it ran, derived from its own records on first view (run-efficiency.ts): a cache, never news.
|
|
11
|
-
'run_efficiency'
|
|
11
|
+
'run_efficiency',
|
|
12
|
+
// v137: lifecycle heartbeats, latency timelines and the continuous copy's record are bookkeeping, never news.
|
|
13
|
+
'service_lifecycle', 'latency_timeline', 'live_backup']);
|
|
12
14
|
const QUIET = {
|
|
13
15
|
runner: ['heartbeat_at'], claim: ['heartbeat_at', 'expires_at'], watch_lease: ['heartbeat_at', 'expires_at'],
|
|
14
16
|
watch_episode: ['ticks'], provider_readiness: ['observed_at'],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolroll",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.79",
|
|
4
4
|
"description": "A control plane for unattended coding agents — queue tasks, walk away, come back to pull requests. Agents build in leased worktrees, never touch your default branch, and interrupt you only for decisions that need a human.",
|
|
5
5
|
"directories": {
|
|
6
6
|
"doc": "docs"
|