toolroll 0.9.89 → 0.9.90
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 +44 -0
- package/README.md +16 -10
- package/THIRD_PARTY_NOTICES.md +37 -0
- package/dist/agent-board.d.ts +9 -0
- package/dist/agent-board.js +20 -0
- package/dist/agent-fence.js +20 -2
- package/dist/approval-authority.d.ts +7 -0
- package/dist/approval-authority.js +13 -2
- package/dist/browser/THIRD_PARTY_NOTICES.txt +692 -28
- package/dist/browser/terminal.js +42 -42
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +32 -32
- package/dist/browser-workspace.d.ts +13 -0
- package/dist/builder.js +5 -2
- package/dist/chat-actions.d.ts +5 -0
- package/dist/chat-actions.js +7 -1
- package/dist/chat-delivery.js +2 -0
- package/dist/chat-task-actions.d.ts +1 -0
- package/dist/check-access.d.ts +3 -0
- package/dist/check-access.js +4 -1
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +6 -2
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +23 -0
- package/dist/coding-ui.js +4 -2
- package/dist/connect-cli.js +2 -1
- package/dist/console-instance.d.ts +34 -1
- package/dist/console-instance.js +124 -15
- package/dist/contracts/cli.d.ts +72 -0
- package/dist/contracts/cli.js +13 -0
- package/dist/contracts/console-api.d.ts +9 -2
- package/dist/contracts/console-api.js +9 -2
- package/dist/desktop-host.js +8 -3
- package/dist/desktop-update.js +2 -1
- package/dist/dispatch.d.ts +3 -0
- package/dist/dispatch.js +22 -2
- package/dist/easy-start-args.d.ts +13 -0
- package/dist/easy-start-args.js +24 -0
- package/dist/easy-start.d.ts +77 -0
- package/dist/easy-start.js +321 -0
- package/dist/first-run.d.ts +77 -0
- package/dist/first-run.js +45 -0
- package/dist/guides.js +1 -1
- package/dist/invoke.js +11 -6
- package/dist/laptop-background.d.ts +171 -0
- package/dist/laptop-background.js +550 -0
- package/dist/laptop-service.d.ts +111 -0
- package/dist/laptop-service.js +268 -0
- package/dist/lead-cli.js +2 -1
- package/dist/lead-doors.d.ts +6 -0
- package/dist/lead-doors.js +20 -2
- package/dist/live-records.d.ts +1 -1
- package/dist/live-records.js +3 -12
- package/dist/local-owner.d.ts +124 -0
- package/dist/local-owner.js +212 -0
- package/dist/login-secrets.d.ts +60 -0
- package/dist/login-secrets.js +195 -0
- package/dist/mcp-oauth.d.ts +4 -0
- package/dist/mcp-oauth.js +13 -7
- package/dist/model-choices.d.ts +43 -0
- package/dist/model-choices.js +80 -0
- package/dist/operate.d.ts +46 -1
- package/dist/operate.js +461 -40
- package/dist/person-proof.js +3 -0
- package/dist/project-knowledge.d.ts +7 -7
- package/dist/quick-start.d.ts +3 -1
- package/dist/quick-start.js +6 -1
- package/dist/release-cli.js +2 -1
- package/dist/runner-process.d.ts +9 -0
- package/dist/runner-process.js +18 -0
- package/dist/sample-project.d.ts +31 -0
- package/dist/sample-project.js +141 -0
- package/dist/scope.d.ts +24 -2
- package/dist/scope.js +74 -3
- package/dist/serve.js +129 -22
- package/dist/server/chat.js +10 -3
- package/dist/server/chrome.d.ts +10 -2
- package/dist/server/chrome.js +7 -5
- package/dist/server/console.css +10 -0
- package/dist/server/guards.d.ts +3 -0
- package/dist/server/guards.js +22 -2
- package/dist/server/http.d.ts +22 -0
- package/dist/server/http.js +58 -0
- package/dist/server/live.js +4 -14
- package/dist/server/mutation-key.d.ts +70 -0
- package/dist/server/mutation-key.js +142 -0
- package/dist/server/pages.js +10 -7
- package/dist/server/people-tokens.js +240 -20
- package/dist/server/remote.d.ts +2 -0
- package/dist/server/remote.js +19 -4
- package/dist/server/render-new-task.js +5 -1
- package/dist/server/render-pages.d.ts +2 -1
- package/dist/server/render-pages.js +1 -1
- package/dist/server/render-people.d.ts +36 -0
- package/dist/server/render-people.js +65 -1
- package/dist/server/render-projects.js +4 -2
- package/dist/server/render-tasks.d.ts +12 -0
- package/dist/server/render-tasks.js +12 -4
- package/dist/server/request-context.d.ts +6 -0
- package/dist/server/route-table.d.ts +16 -0
- package/dist/server/route-table.js +121 -0
- package/dist/server/runtime.d.ts +6 -4
- package/dist/server/session.d.ts +31 -1
- package/dist/server/session.js +12 -2
- package/dist/server/settings.js +63 -8
- package/dist/server/task-views.js +10 -1
- package/dist/server/tasks.d.ts +10 -0
- package/dist/server/tasks.js +59 -9
- package/dist/server/terminals.js +15 -2
- package/dist/server/welcome.d.ts +22 -16
- package/dist/server/welcome.js +150 -52
- package/dist/settings-groups-ui.d.ts +7 -1
- package/dist/settings-groups-ui.js +23 -2
- package/dist/settings-proofs.js +1 -0
- package/dist/setup-guide.d.ts +2 -2
- package/dist/store.d.ts +124 -1
- package/dist/store.js +245 -12
- package/dist/surface.d.ts +10 -0
- package/dist/surface.js +40 -1
- package/dist/task-text.d.ts +6 -0
- package/dist/task-text.js +17 -0
- package/dist/telegram-lead.js +2 -0
- package/dist/terminal-text.d.ts +11 -0
- package/dist/terminal-text.js +26 -0
- package/dist/toolroll-update.d.ts +8 -1
- package/dist/toolroll-update.js +151 -4
- package/dist/tui-contract.d.ts +118 -0
- package/dist/tui-contract.js +9 -0
- package/dist/tui-source.d.ts +14 -0
- package/dist/tui-source.js +182 -0
- package/dist/tui.js +129 -0
- package/dist/up-processes.d.ts +13 -1
- package/dist/up-processes.js +13 -3
- package/dist/welcome-facts.d.ts +18 -0
- package/dist/welcome-facts.js +79 -0
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.90 — 2026-11-04
|
|
4
|
+
|
|
5
|
+
- **Type `toolroll` and you're in.** At your own terminal, bare `toolroll`
|
|
6
|
+
starts Toolroll on this computer (or opens the one already running) and
|
|
7
|
+
shows your tasks, what each agent is doing and what needs you, right in the
|
|
8
|
+
terminal: file a task, read the full plan and approve it, stop, revise,
|
|
9
|
+
complete, answer a question, or press `w` for the browser. `toolroll --web`
|
|
10
|
+
opens the browser instead, `toolroll open` gets you back in signed in, and
|
|
11
|
+
`toolroll welcome` walks the three setup steps. Scripts, agents and
|
|
12
|
+
`--json` see exactly what they did before.
|
|
13
|
+
- **No password on your own computer.** A new install started with
|
|
14
|
+
`toolroll` has one owner and no password or login file. You can file
|
|
15
|
+
tasks, chat with the lead, approve plans for work you start, answer
|
|
16
|
+
questions, stop, revise and complete work, and add projects. Hands-off
|
|
17
|
+
modes, merging, releases, people and access, and most settings ask you to
|
|
18
|
+
set a password first: the page says **Set a password**, or run
|
|
19
|
+
`toolroll password set`. Agents always run inside the fence while this is
|
|
20
|
+
on. Existing installs keep their passwords and work as before.
|
|
21
|
+
- **A first result in three steps.** The welcome page (and
|
|
22
|
+
`toolroll welcome`) asks for your agent (Claude Code or Codex, with the
|
|
23
|
+
command that signs each in), a project (a folder, one of your GitHub
|
|
24
|
+
repositories, or a small sample project) and a first task from a few
|
|
25
|
+
starters.
|
|
26
|
+
- **One-line install opens Toolroll.** `curl … | sh` now opens Toolroll in
|
|
27
|
+
your terminal when it finishes. Without a terminal it prints the exact
|
|
28
|
+
command to start it.
|
|
29
|
+
- **Keep it running, or stop it.** Quitting asks once whether Toolroll
|
|
30
|
+
should keep running in the background. `toolroll background on|off|show`
|
|
31
|
+
changes that later (a login item on macOS, a user service on Linux), and
|
|
32
|
+
`toolroll stop` stops it with running work saved.
|
|
33
|
+
- **Browser changes need this browser's key.** On an install that only
|
|
34
|
+
listens on this computer, a signed-in cookie alone can no longer change
|
|
35
|
+
anything: a page on another port can read but not act. After Toolroll
|
|
36
|
+
restarts, an open tab says **Reconnect**: run `toolroll open`, or type
|
|
37
|
+
your password. The first start of this version asks open tabs to reconnect
|
|
38
|
+
once.
|
|
39
|
+
- **Port 4180 already taken?** A new install picks the next free port and
|
|
40
|
+
keeps it, instead of failing to start.
|
|
41
|
+
- **Your saved login stays out of every build.** Builds could never read your
|
|
42
|
+
saved login, but one saved after a build had already started was missed.
|
|
43
|
+
Saved logins now live in a private folder next to the database that every
|
|
44
|
+
build is denied as a whole, on macOS, Linux and with Codex. The next start
|
|
45
|
+
moves an existing saved login there for you.
|
|
46
|
+
|
|
3
47
|
## 0.9.89 — 2026-11-03
|
|
4
48
|
|
|
5
49
|
- **The app no longer freezes when a drive stops answering.** If an external
|
package/README.md
CHANGED
|
@@ -29,24 +29,30 @@ Approve the exact scope once. Get results back Ready with the diff, the passing
|
|
|
29
29
|
|
|
30
30
|
## Quick start
|
|
31
31
|
|
|
32
|
-
macOS or Linux, Node.js 22.13+, git 2.41+, and
|
|
33
|
-
`
|
|
32
|
+
macOS or Linux, Node.js 22.13+, git 2.41+, and Claude Code (`claude`) or
|
|
33
|
+
Codex (`codex`) signed in on this computer.
|
|
34
34
|
|
|
35
35
|
```sh
|
|
36
|
-
|
|
37
|
-
toolroll demo # optional: a seeded sandbox, no model calls, no spend
|
|
38
|
-
toolroll up # the app and a builder; opens http://127.0.0.1:4180
|
|
36
|
+
curl -fsSL https://raw.githubusercontent.com/ap9000/toolroll/main/install.sh | sh
|
|
39
37
|
```
|
|
40
38
|
|
|
41
|
-
|
|
42
|
-
it (never the password itself), then keeps running. Then, in a repository:
|
|
39
|
+
or
|
|
43
40
|
|
|
44
41
|
```sh
|
|
45
|
-
|
|
42
|
+
npm install -g toolroll # or: brew install ap9000/toolroll/toolroll
|
|
43
|
+
toolroll
|
|
46
44
|
```
|
|
47
45
|
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
Toolroll opens in your terminal and runs while that window is open. No
|
|
47
|
+
password and no login file: on your own computer you're already in. Pick an
|
|
48
|
+
agent, a project (or the sample one) and a first task; approve the plan, and
|
|
49
|
+
the result comes back **Ready**. Press `w` for the browser, or run
|
|
50
|
+
`toolroll --web`. To look around first, `toolroll demo` opens a seeded
|
|
51
|
+
sandbox with no model calls and no spend.
|
|
52
|
+
|
|
53
|
+
**Letting agents work on their own.** Approving work you start on this
|
|
54
|
+
computer needs no password. Hands-off modes, merging, releasing and most
|
|
55
|
+
settings ask you to set one first: `toolroll password set`. More: [Getting started](docs/guide/getting-started.md).
|
|
50
56
|
|
|
51
57
|
## How it works
|
|
52
58
|
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -431,6 +431,43 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
431
431
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
432
432
|
SOFTWARE.
|
|
433
433
|
|
|
434
|
+
## Terminal UI bundle (`dist/tui.js`)
|
|
435
|
+
|
|
436
|
+
The terminal UI is one esbuild bundle of Ink 6.8.0 (MIT, Vadim Demedes), React 19 and react-reconciler (MIT, Meta),
|
|
437
|
+
yoga-layout 3.2.1 (MIT, Meta; its WebAssembly ships inlined as base64 in the bundle) and their small MIT/ISC helper
|
|
438
|
+
packages (chalk, ansi-styles, wrap-ansi, string-width, emoji-regex, signal-exit and others). They are development
|
|
439
|
+
dependencies only: nothing extra is installed with Toolroll. The build appends each bundled package's own license file
|
|
440
|
+
to `dist/browser/THIRD_PARTY_NOTICES.txt`, next to the browser bundle's, and fails if one is missing.
|
|
441
|
+
|
|
442
|
+
## yoga-layout 3.2.1 — MIT
|
|
443
|
+
|
|
444
|
+
This Ink dependency declares MIT in its package metadata but omits its license file from the published npm package.
|
|
445
|
+
The upstream license was preserved from the repository at tag `v3.2.1`, revision
|
|
446
|
+
`042f5013152eb81c1552dec945b88f7b95ca350f`, inspected 3 November 2026:
|
|
447
|
+
https://raw.githubusercontent.com/facebook/yoga/042f5013152eb81c1552dec945b88f7b95ca350f/LICENSE
|
|
448
|
+
|
|
449
|
+
MIT License
|
|
450
|
+
|
|
451
|
+
Copyright (c) Facebook, Inc. and its affiliates.
|
|
452
|
+
|
|
453
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
454
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
455
|
+
in the Software without restriction, including without limitation the rights
|
|
456
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
457
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
458
|
+
furnished to do so, subject to the following conditions:
|
|
459
|
+
|
|
460
|
+
The above copyright notice and this permission notice shall be included in all
|
|
461
|
+
copies or substantial portions of the Software.
|
|
462
|
+
|
|
463
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
464
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
465
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
466
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
467
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
468
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
469
|
+
SOFTWARE.
|
|
470
|
+
|
|
434
471
|
## Geist Sans and Geist Mono (@fontsource/geist-sans, @fontsource/geist-mono 5.3.0) — SIL OFL 1.1
|
|
435
472
|
|
|
436
473
|
The console embeds the latin woff2 subsets (weights 400, 500 and 600) in
|
package/dist/agent-board.d.ts
CHANGED
|
@@ -218,3 +218,12 @@ export declare function watchedPing(store: Store, row: {
|
|
|
218
218
|
}, account: string, now: Date): {
|
|
219
219
|
hold: string;
|
|
220
220
|
} | "skip" | "send";
|
|
221
|
+
/** A page's own watch id: 24 hex characters the shell makes (never a database row id). */
|
|
222
|
+
export declare const AGENT_VIEW_ID: RegExp;
|
|
223
|
+
/**
|
|
224
|
+
* POST /agents/watch, hold: this person's page watches a run (renewed on each keep-alive). A view id another person
|
|
225
|
+
* already holds is never taken over or renewed: only the holder's own row moves. False when nothing was held.
|
|
226
|
+
*/
|
|
227
|
+
export declare function holdOwnAgentView(store: Store, view: string, account: string, run: number, at: Date): boolean;
|
|
228
|
+
/** POST /agents/watch, release: this person's page went away. Only their own view is released (and lapsed ones swept). */
|
|
229
|
+
export declare function releaseOwnAgentView(store: Store, view: string, account: string, at: Date): void;
|
package/dist/agent-board.js
CHANGED
|
@@ -472,3 +472,23 @@ export function watchedPing(store, row, account, now) {
|
|
|
472
472
|
const seen = store.handle.prepare("SELECT 1 AS hit FROM agent_seen WHERE account = ? AND run = ?").get(account, boardRunOf(store, row.run));
|
|
473
473
|
return seen === undefined ? "send" : "skip";
|
|
474
474
|
}
|
|
475
|
+
// ---- easy-start C1 (N1): the watch lease and the seen mark, from keyed POSTs only -------------------------------------
|
|
476
|
+
/** A page's own watch id: 24 hex characters the shell makes (never a database row id). */
|
|
477
|
+
export const AGENT_VIEW_ID = /^[0-9a-f]{24}$/;
|
|
478
|
+
/**
|
|
479
|
+
* POST /agents/watch, hold: this person's page watches a run (renewed on each keep-alive). A view id another person
|
|
480
|
+
* already holds is never taken over or renewed: only the holder's own row moves. False when nothing was held.
|
|
481
|
+
*/
|
|
482
|
+
export function holdOwnAgentView(store, view, account, run, at) {
|
|
483
|
+
if (!AGENT_VIEW_ID.test(view))
|
|
484
|
+
return false;
|
|
485
|
+
return Number(store.handle.prepare(`INSERT INTO agent_view (view, account, run, expires_at) VALUES (?, ?, ?, ?)
|
|
486
|
+
ON CONFLICT (view) DO UPDATE SET expires_at = excluded.expires_at WHERE agent_view.account = excluded.account AND agent_view.run = excluded.run`)
|
|
487
|
+
.run(view, account, boardRunOf(store, run), new Date(at.getTime() + VIEW_LEASE_MS).toISOString()).changes) > 0;
|
|
488
|
+
}
|
|
489
|
+
/** POST /agents/watch, release: this person's page went away. Only their own view is released (and lapsed ones swept). */
|
|
490
|
+
export function releaseOwnAgentView(store, view, account, at) {
|
|
491
|
+
if (!AGENT_VIEW_ID.test(view))
|
|
492
|
+
return;
|
|
493
|
+
store.handle.prepare("DELETE FROM agent_view WHERE (view = ? AND account = ?) OR expires_at < ?").run(view, account, at.toISOString());
|
|
494
|
+
}
|
package/dist/agent-fence.js
CHANGED
|
@@ -37,6 +37,7 @@ import { ensureRunnerProcessFolder, RUNNER_PROCESS_DIR } from "./runner-process.
|
|
|
37
37
|
import { DEPS_FOLDER, linkedKey, readyCopy, sharedCopies } from "./shared-deps.js";
|
|
38
38
|
import { SEATBELT_PROBE, sandboxRuntimeProblem, srtSpawn } from "./sandbox-runtime.js";
|
|
39
39
|
import { protectedStorageDenies, protectedStorageNames } from "./protected-storage.js";
|
|
40
|
+
import { ensureSecretsDir, legacyLoginFileOf, SECRETS_DIR, secretsDirOf } from "./login-secrets.js";
|
|
40
41
|
import { getDefaultWritePaths } from "@anthropic-ai/sandbox-runtime";
|
|
41
42
|
/** Names that are secrets wherever the database lives (used when its folder is shared with other things). */
|
|
42
43
|
const SENSITIVE_NAME = /(^up-login\.txt$|token|secret|login|password|credential|vapid|keys?\b|\.pem$|\.key$|^backups$|^evidence$|^remote$|^sign-in\.json$|^monitoring\.json$)/i;
|
|
@@ -183,6 +184,23 @@ export function agentFence(options) {
|
|
|
183
184
|
catch { /* name and target remain denied */ }
|
|
184
185
|
fence.add(backups);
|
|
185
186
|
fence.add(real(backups));
|
|
187
|
+
// Saved credentials (the remembered login, login-secrets.ts) live in a reserved owner-only folder, made here before
|
|
188
|
+
// any launch and denied whole, so one first written after this launch is behind this fence too: a listing can't
|
|
189
|
+
// name a file that isn't there yet, and bubblewrap can't deny one. If it can't be made or proven, its name is
|
|
190
|
+
// denied anyway (and nothing is saved there).
|
|
191
|
+
const secrets = secretsDirOf(state);
|
|
192
|
+
try {
|
|
193
|
+
ensureSecretsDir(state);
|
|
194
|
+
}
|
|
195
|
+
catch { /* name and target remain denied */ }
|
|
196
|
+
fence.add(secrets);
|
|
197
|
+
fence.add(real(secrets));
|
|
198
|
+
// An older version's login beside the database: listed below when it is there; by name, there or not, where the
|
|
199
|
+
// sandbox denies a missing path without making it (Seatbelt; Codex's profile gets the same list). On Linux a
|
|
200
|
+
// missing path would be stubbed into being for the launch, and nothing writes that name any more.
|
|
201
|
+
const legacyLogin = legacyLoginFileOf(state);
|
|
202
|
+
if (process.platform === "darwin" || existsSync(legacyLogin))
|
|
203
|
+
fence.add(legacyLogin);
|
|
186
204
|
// Only a folder that is Toolroll's own (~/.config/toolroll or an older name,
|
|
187
205
|
// the desktop app's "Toolroll") is fenced whole: a database kept
|
|
188
206
|
// in a shared folder (a projects folder, a test's temp folder) must not
|
|
@@ -230,7 +248,7 @@ export function agentFence(options) {
|
|
|
230
248
|
// Secret, state, database, explicit-extra and home-config denies are never exemptible.
|
|
231
249
|
for (const path of options.boundary?.protectedShell ?? [])
|
|
232
250
|
fence.add(real(path));
|
|
233
|
-
const protectedPaths = new Set([...fence].flatMap(path => path === runFolder || [RUNNER_PROCESS_DIR, "backups"].includes(basename(path)) ? [path, real(path)] : [real(path)]));
|
|
251
|
+
const protectedPaths = new Set([...fence].flatMap(path => path === runFolder || [RUNNER_PROCESS_DIR, "backups", SECRETS_DIR].includes(basename(path)) ? [path, real(path)] : [real(path)]));
|
|
234
252
|
// Protected storage (chat apps, browser profiles, password managers): every launch, every policy, never exemptible,
|
|
235
253
|
// kept by the name it was resolved under as well as where it leads.
|
|
236
254
|
const storage = options.protectedStorage ?? protectedStorageDenies(home);
|
|
@@ -242,7 +260,7 @@ export function agentFence(options) {
|
|
|
242
260
|
fence.add(path);
|
|
243
261
|
const binding = options.binding;
|
|
244
262
|
const needed = binding === undefined || binding === null ? [] : [binding.gitdir, real(join(binding.main, ".git"))];
|
|
245
|
-
return [...new Set([...[...fence].flatMap(path => path === runFolder || [RUNNER_PROCESS_DIR, "backups"].includes(basename(path)) ? [path, real(path)] : [real(path)]), ...storage.flatMap(path => [path, real(path)])])].filter(path => {
|
|
263
|
+
return [...new Set([...[...fence].flatMap(path => path === runFolder || [RUNNER_PROCESS_DIR, "backups", SECRETS_DIR].includes(basename(path)) ? [path, real(path)] : [real(path)]), ...storage.flatMap(path => [path, real(path)])])].filter(path => {
|
|
246
264
|
if (protectedPaths.has(path) || !needed.some(target => target === path || inside(target, path)))
|
|
247
265
|
return true;
|
|
248
266
|
console.warn(`Dropped build fence entry covering this checkout's own Git data: ${path}`);
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
* expression, used exactly three times: the mode and grant clauses, and the release's expiry). It never asks whether the row is approved.
|
|
3
3
|
* COALESCE: a NULL basis would make the OR chain NULL, and NOT NULL reads as "not dead"; NULL stands on nothing. */
|
|
4
4
|
export declare const authorityStandsSql: (scope: string, repo: string, now: string) => string;
|
|
5
|
+
/**
|
|
6
|
+
* SQL, conjunctive with every alternative above (easy-start plan C5): a seal a local owner gave (its proof row at the
|
|
7
|
+
* seal's instant says `local-owner`) stands only while local mode is on, for that owner, at the local generation its
|
|
8
|
+
* proof names (approvalProofDetail's " · local <generation> <transport> · "), and while its project's checks aren't
|
|
9
|
+
* stored as unsandboxed. Turning local mode off, or moving its generation, stops every such seal dispatching.
|
|
10
|
+
*/
|
|
11
|
+
export declare const localSealStandsSql: (scope: string, repo: string) => string;
|
|
5
12
|
/** SQL: a `carried` seal stands only on its own carry row — this task, this approved digest, this candidate, and the
|
|
6
13
|
* reference `<authority>:<carry>` the carry transaction wrote (release-once.md). */
|
|
7
14
|
export declare const carrySealSql: (scope: string) => string;
|
|
@@ -16,7 +16,7 @@ import { grantStandsSql } from "./flow-unattended.js";
|
|
|
16
16
|
import { accountReachesSql } from "./project-access.js";
|
|
17
17
|
import { autoSealSql } from "./release-authority.js";
|
|
18
18
|
/** The proofs a person gives (src/person-proof.ts), as an SQL list. */
|
|
19
|
-
const PERSON_PROOFS = "('password-now', 'sso-now', 'fresh-password', 'fresh-sso', 'session', 'cli-login', 'chat', 'passkey')";
|
|
19
|
+
const PERSON_PROOFS = "('password-now', 'sso-now', 'fresh-password', 'fresh-sso', 'session', 'cli-login', 'chat', 'passkey', 'local-owner')";
|
|
20
20
|
/** SQL over a `task_scope` alias and a repo expression: whether that row's approval authority stands at `now` (an SQL
|
|
21
21
|
* expression, used exactly three times: the mode and grant clauses, and the release's expiry). It never asks whether the row is approved.
|
|
22
22
|
* COALESCE: a NULL basis would make the OR chain NULL, and NOT NULL reads as "not dead"; NULL stands on nothing. */
|
|
@@ -30,7 +30,18 @@ export const authorityStandsSql = (scope, repo, now) => `COALESCE((((COALESCE(${
|
|
|
30
30
|
OR (${scope}.approval_basis = 'grant' AND ${grantStandsSql(`${scope}.mode_digest`, now, repo)})
|
|
31
31
|
OR (${scope}.approval_basis = 'schedule' AND ${approverStandsSql(scope, repo)})
|
|
32
32
|
OR (${scope}.approval_basis IN ('auto-check', 'auto-green') AND ${autoSealSql(scope, repo)} AND ${approverStandsSql(scope, repo)}))
|
|
33
|
-
AND ${releaseStandsSql(scope, now)}), 0)`;
|
|
33
|
+
AND ${releaseStandsSql(scope, now)} AND ${localSealStandsSql(scope, repo)}), 0)`;
|
|
34
|
+
/**
|
|
35
|
+
* SQL, conjunctive with every alternative above (easy-start plan C5): a seal a local owner gave (its proof row at the
|
|
36
|
+
* seal's instant says `local-owner`) stands only while local mode is on, for that owner, at the local generation its
|
|
37
|
+
* proof names (approvalProofDetail's " · local <generation> <transport> · "), and while its project's checks aren't
|
|
38
|
+
* stored as unsandboxed. Turning local mode off, or moving its generation, stops every such seal dispatching.
|
|
39
|
+
*/
|
|
40
|
+
export const localSealStandsSql = (scope, repo) => `NOT EXISTS (SELECT 1 FROM action_ledger lp WHERE lp.task_id = ${scope}.task_id AND lp.action = 'approval proved'
|
|
41
|
+
AND lp.actor = ${scope}.approved_by AND lp.at = ${scope}.approved_at AND lp.outcome = 'local-owner'
|
|
42
|
+
AND NOT (EXISTS (SELECT 1 FROM local_mode lm WHERE lm.id = 1 AND lm.enabled = 1 AND lm.owner = lp.actor
|
|
43
|
+
AND lp.detail LIKE '% · local ' || lm.generation || ' %')
|
|
44
|
+
AND NOT EXISTS (SELECT 1 FROM project_check_access lca WHERE lca.repo = ${repo} AND lca.level = 'unsandboxed')))`;
|
|
34
45
|
/** SQL: a `carried` seal stands only on its own carry row — this task, this approved digest, this candidate, and the
|
|
35
46
|
* reference `<authority>:<carry>` the carry transaction wrote (release-once.md). */
|
|
36
47
|
export const carrySealSql = (scope) => `EXISTS (SELECT 1 FROM release_carry rc2 JOIN release_authority ra2 ON ra2.id = rc2.authority
|