toolroll 0.9.53 → 0.9.55
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 +17 -0
- package/README.md +5 -4
- package/dist/agent-onboard.js +4 -1
- package/dist/approval-rules-ui.d.ts +3 -2
- package/dist/approval-rules-ui.js +15 -20
- package/dist/assignment-ui.d.ts +3 -2
- package/dist/assignment-ui.js +4 -11
- package/dist/backup-ui.d.ts +2 -1
- package/dist/backup-ui.js +16 -27
- package/dist/brand-mark.d.ts +3 -2
- package/dist/brand-mark.js +4 -4
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +25 -25
- package/dist/browser-shell.d.ts +2 -1
- package/dist/browser-shell.js +9 -9
- package/dist/browser-workspace.d.ts +2 -3
- package/dist/browser-workspace.js +0 -3
- package/dist/chat-ask.js +8 -8
- package/dist/chat-core.d.ts +171 -0
- package/dist/chat-core.js +288 -0
- package/dist/chat-decide.d.ts +10 -8
- package/dist/chat-decide.js +26 -21
- package/dist/chat-delivery-state.d.ts +51 -75
- package/dist/chat-delivery-state.js +140 -170
- package/dist/chat-delivery.d.ts +21 -2
- package/dist/chat-delivery.js +313 -194
- package/dist/chat-flow.d.ts +29 -1
- package/dist/chat-flow.js +24 -50
- package/dist/chat-messages.d.ts +319 -0
- package/dist/chat-messages.js +841 -0
- package/dist/chat-migration.d.ts +30 -0
- package/dist/chat-migration.js +802 -0
- package/dist/chat-polish.d.ts +5 -4
- package/dist/chat-polish.js +9 -10
- package/dist/chat-quiet.d.ts +3 -0
- package/dist/chat-quiet.js +5 -0
- package/dist/check-levels-ui.d.ts +2 -1
- package/dist/check-levels-ui.js +26 -53
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +9 -3
- package/dist/coding-shipping-ui.d.ts +2 -1
- package/dist/coding-shipping-ui.js +6 -6
- package/dist/coding-ui.d.ts +3 -1
- package/dist/coding-ui.js +30 -29
- package/dist/contracts/chat-content.d.ts +71 -2
- package/dist/contracts/chat-content.js +22 -3
- package/dist/contracts/chat-tables.d.ts +11 -4
- package/dist/contracts/chat-tables.js +165 -87
- package/dist/contracts/cli.d.ts +1 -3
- package/dist/contracts/console-api.d.ts +1 -1
- package/dist/contracts/console-api.js +6 -5
- package/dist/contracts/discord-callback.js +2 -1
- package/dist/contracts/health.d.ts +1 -3
- package/dist/contracts/slack-callback.js +3 -2
- package/dist/contracts/store-json.d.ts +1 -5
- package/dist/contracts/store-json.js +1 -4
- package/dist/contracts/teams-callback.js +2 -1
- package/dist/control-ui.d.ts +5 -4
- package/dist/control-ui.js +9 -34
- package/dist/credentials-ui.d.ts +5 -4
- package/dist/credentials-ui.js +23 -24
- package/dist/demo-chat.d.ts +3 -3
- package/dist/demo-chat.js +29 -76
- package/dist/discord-chat.js +19 -38
- package/dist/discord-settings.d.ts +3 -2
- package/dist/discord-settings.js +21 -55
- package/dist/discord.js +4 -14
- package/dist/envelope.js +10 -0
- package/dist/evidence-pack.d.ts +2 -1
- package/dist/evidence-pack.js +45 -49
- package/dist/export-ui.d.ts +7 -2
- package/dist/export-ui.js +4 -10
- package/dist/export.js +4 -4
- package/dist/flow-gallery-ui.d.ts +4 -3
- package/dist/flow-gallery-ui.js +28 -43
- package/dist/flow-starters-ui.d.ts +2 -1
- package/dist/flow-starters-ui.js +12 -15
- package/dist/flow-triggers.d.ts +1 -0
- package/dist/flow-triggers.js +16 -14
- package/dist/flows-ui.d.ts +4 -3
- package/dist/flows-ui.js +19 -30
- package/dist/flows.js +2 -1
- package/dist/guides.js +108 -3
- package/dist/html.d.ts +76 -0
- package/dist/html.js +173 -0
- package/dist/integrations-ui.d.ts +2 -1
- package/dist/integrations-ui.js +19 -23
- package/dist/kits-ui.d.ts +5 -4
- package/dist/kits-ui.js +18 -20
- package/dist/knowledge-ui.d.ts +6 -5
- package/dist/knowledge-ui.js +26 -25
- package/dist/lead-context.d.ts +11 -1
- package/dist/lead-context.js +6 -8
- package/dist/ledger-view.d.ts +2 -1
- package/dist/ledger-view.js +27 -29
- package/dist/limits-ui.d.ts +2 -1
- package/dist/limits-ui.js +7 -7
- package/dist/live-bus.d.ts +50 -8
- package/dist/live-bus.js +133 -50
- package/dist/mate-tools.js +3 -2
- package/dist/mcp-oauth.js +35 -36
- package/dist/models-ui.d.ts +2 -1
- package/dist/models-ui.js +25 -35
- package/dist/modes.d.ts +13 -1
- package/dist/modes.js +17 -1
- package/dist/monitoring-ui.d.ts +4 -3
- package/dist/monitoring-ui.js +13 -26
- package/dist/notification-rows.d.ts +30 -0
- package/dist/notification-rows.js +102 -0
- package/dist/openrouter-models.d.ts +2 -1
- package/dist/openrouter-models.js +5 -9
- package/dist/operate.js +16 -9
- package/dist/people-audit-ui.d.ts +2 -1
- package/dist/people-audit-ui.js +13 -13
- package/dist/plane-review.js +5 -11
- package/dist/policy-ui.d.ts +3 -2
- package/dist/policy-ui.js +17 -23
- package/dist/project-delete-ui.d.ts +4 -3
- package/dist/project-delete-ui.js +15 -27
- package/dist/project-delete.js +21 -26
- package/dist/pull-request-ui.d.ts +2 -1
- package/dist/pull-request-ui.js +17 -32
- package/dist/recipe-ui.d.ts +8 -7
- package/dist/recipe-ui.js +39 -32
- package/dist/release-adapters.d.ts +60 -0
- package/dist/release-adapters.js +435 -0
- package/dist/release-cli.d.ts +24 -0
- package/dist/release-cli.js +181 -0
- package/dist/release-gate.d.ts +80 -0
- package/dist/release-gate.js +150 -0
- package/dist/release-links.d.ts +29 -0
- package/dist/release-links.js +102 -0
- package/dist/release.d.ts +331 -0
- package/dist/release.js +550 -0
- package/dist/reply-shape.d.ts +2 -1
- package/dist/reply-shape.js +5 -5
- package/dist/repository-context-ui.d.ts +2 -1
- package/dist/repository-context-ui.js +2 -7
- package/dist/request-budget-ui.d.ts +2 -1
- package/dist/request-budget-ui.js +16 -16
- package/dist/result-review.d.ts +2 -3
- package/dist/result-review.js +3 -9
- package/dist/result-shots.js +2 -2
- package/dist/retention-ui.d.ts +7 -1
- package/dist/retention-ui.js +7 -11
- package/dist/retention.js +1 -1
- package/dist/serve.d.ts +11 -2
- package/dist/serve.js +69 -58
- package/dist/server/chat.d.ts +3 -6
- package/dist/server/chat.js +969 -1006
- package/dist/server/chrome.d.ts +291 -0
- package/dist/server/chrome.js +747 -0
- package/dist/server/console.css +2819 -0
- package/dist/server/flows.d.ts +2 -6
- package/dist/server/flows.js +840 -766
- package/dist/server/guards.d.ts +1 -1
- package/dist/server/guards.js +5 -2
- package/dist/server/handler-context.d.ts +1 -1
- package/dist/server/handler-context.js +1 -1
- package/dist/server/handler-registry.d.ts +8 -6
- package/dist/server/handler-registry.js +14 -2
- package/dist/server/http.d.ts +274 -0
- package/dist/server/http.js +239 -0
- package/dist/server/live.d.ts +34 -0
- package/dist/server/live.js +309 -0
- package/dist/server/pages.d.ts +2 -6
- package/dist/server/pages.js +808 -789
- package/dist/server/people-tokens.d.ts +1 -6
- package/dist/server/people-tokens.js +580 -572
- package/dist/server/remote.d.ts +4 -7
- package/dist/server/remote.js +37 -70
- package/dist/server/render-chat.d.ts +402 -0
- package/dist/server/render-chat.js +927 -0
- package/dist/server/render-pages.d.ts +535 -0
- package/dist/server/render-pages.js +1277 -0
- package/dist/server/render-people.d.ts +32 -0
- package/dist/server/render-people.js +121 -0
- package/dist/server/render-results.d.ts +744 -0
- package/dist/server/render-results.js +2237 -0
- package/dist/server/render-settings.d.ts +147 -0
- package/dist/server/render-settings.js +577 -0
- package/dist/server/render-tasks.d.ts +632 -0
- package/dist/server/render-tasks.js +2345 -0
- package/dist/server/request-context.d.ts +46 -0
- package/dist/server/request-context.js +17 -0
- package/dist/server/route-table.d.ts +25 -3
- package/dist/server/route-table.js +163 -75
- package/dist/server/runtime.d.ts +15 -7
- package/dist/server/runtime.js +7 -1
- package/dist/server/session.d.ts +138 -0
- package/dist/server/session.js +94 -0
- package/dist/server/settings.d.ts +1 -5
- package/dist/server/settings.js +2182 -2117
- package/dist/server/task-views.d.ts +11 -4
- package/dist/server/task-views.js +26 -22
- package/dist/server/tasks.d.ts +9 -9
- package/dist/server/tasks.js +1486 -1418
- package/dist/server-telemetry.d.ts +4 -3
- package/dist/server-telemetry.js +4 -8
- package/dist/skills-ui.d.ts +4 -3
- package/dist/skills-ui.js +14 -17
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +2 -1
- package/dist/slack-api.d.ts +5 -1
- package/dist/slack-api.js +2 -2
- package/dist/slack-chat.d.ts +9 -4
- package/dist/slack-chat.js +29 -52
- package/dist/slack-settings.d.ts +3 -2
- package/dist/slack-settings.js +23 -57
- package/dist/slack.js +6 -16
- package/dist/spend-ui.d.ts +2 -1
- package/dist/spend-ui.js +9 -32
- package/dist/sso-ui.d.ts +3 -2
- package/dist/sso-ui.js +26 -27
- package/dist/storage-ui.d.ts +2 -1
- package/dist/storage-ui.js +14 -33
- package/dist/store.d.ts +97 -364
- package/dist/store.js +207 -1531
- package/dist/surface.js +14 -3
- package/dist/task-composer.d.ts +2 -1
- package/dist/task-composer.js +7 -7
- package/dist/task-status.d.ts +4 -3
- package/dist/task-status.js +15 -16
- package/dist/team-http.d.ts +5 -9
- package/dist/team-http.js +5 -63
- package/dist/team-ui.d.ts +2 -1
- package/dist/team-ui.js +2 -5
- package/dist/teammate-question.js +12 -12
- package/dist/teammates-ui.d.ts +5 -4
- package/dist/teammates-ui.js +61 -90
- package/dist/teams-chat.js +12 -9
- package/dist/teams-settings.d.ts +3 -2
- package/dist/teams-settings.js +22 -33
- package/dist/teams.js +4 -5
- package/dist/telegram-flow.js +5 -5
- package/dist/telegram-settings.d.ts +3 -2
- package/dist/telegram-settings.js +14 -25
- package/dist/telegram.js +4 -4
- package/dist/toolroll-update-ui.d.ts +3 -2
- package/dist/toolroll-update-ui.js +35 -39
- package/dist/toolroll-update.d.ts +10 -0
- package/dist/toolroll-update.js +50 -3
- package/dist/tools-ui.d.ts +5 -2
- package/dist/tools-ui.js +31 -31
- package/dist/when-html.d.ts +11 -3
- package/dist/when-html.js +5 -6
- package/dist/workspace-revision.js +3 -1
- package/dist/workspace-ui.d.ts +4 -2
- package/dist/workspace-ui.js +10 -10
- package/package.json +1 -1
- package/dist/server/shared.d.ts +0 -3151
- package/dist/server/shared.js +0 -12001
- package/dist/slack-state.d.ts +0 -13
- package/dist/slack-state.js +0 -10
package/dist/discord-settings.js
CHANGED
|
@@ -1,75 +1,41 @@
|
|
|
1
1
|
import { loadPrimary } from "./webhooks.js";
|
|
2
|
+
import { html, postForm } from "./html.js";
|
|
2
3
|
import { loadDiscordCredentials } from "./discord-api.js";
|
|
3
4
|
import { ChatState } from "./chat-delivery-state.js";
|
|
4
|
-
|
|
5
|
-
.replaceAll("&", "&")
|
|
6
|
-
.replaceAll("<", "<")
|
|
7
|
-
.replaceAll(">", ">")
|
|
8
|
-
.replaceAll('"', """)
|
|
9
|
-
.replaceAll("'", "'");
|
|
10
|
-
export function discordSettingsHtml(store, dir, csrf, options = {}) {
|
|
5
|
+
export function discordSettingsHtml(store, dir, options = {}) {
|
|
11
6
|
const credentials = loadDiscordCredentials(dir), state = new ChatState(store, "discord"), bindings = credentials ? state.bindings(credentials.installation).filter(one => state.live(one)) : [], binding = options.who === undefined ? null : bindings.find(one => one.approver === options.who) ?? null, others = bindings.length - (binding === null ? 0 : 1);
|
|
12
|
-
const runtime = credentials
|
|
13
|
-
? state.db
|
|
14
|
-
.prepare("SELECT connected,problem,lease_until FROM discord_runtime WHERE installation=?")
|
|
15
|
-
.get(credentials.installation)
|
|
16
|
-
: null;
|
|
7
|
+
const runtime = credentials ? state.runtime(credentials.installation) : null;
|
|
17
8
|
const now = options.now ?? new Date(), live = runtime &&
|
|
18
|
-
|
|
19
|
-
runtime.
|
|
9
|
+
runtime.leaseUntil !== null &&
|
|
10
|
+
runtime.leaseUntil > now.toISOString() &&
|
|
20
11
|
runtime.connected;
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
let content = "";
|
|
12
|
+
const password = html `<label>Your Toolroll password<input style="min-height:44px" type="password" name="password" autocomplete="current-password" required></label>`;
|
|
13
|
+
const post = (action, content) => postForm(`/settings/discord/${action}`, content, { attrs: { class: "card" } });
|
|
14
|
+
const parts = [];
|
|
25
15
|
if (!credentials) {
|
|
26
|
-
|
|
27
|
-
"<p>Manage projects and review results in a private Discord conversation.</p>" +
|
|
28
|
-
'<details open><summary>Create your Discord app</summary><ol><li><a href="https://discord.com/developers/applications" target="_blank" rel="noopener noreferrer">Create an application</a>, then copy its token from the Bot page.</li><li>Keep the Interactions Endpoint URL empty and privileged intents off.</li><li>Under Installation, enable Guild Install with the bot scope and no server permissions. Use its install link to add it to your server, then open a direct message to the bot.</li></ol></details>' +
|
|
29
|
-
post("connect", '<label>Bot token<input style="min-height:44px" type="password" name="bot-token" autocomplete="off" required></label>' +
|
|
30
|
-
password +
|
|
31
|
-
'<p class="meta">The token stays on this installation. Next, pair your own Discord account.</p><button type="submit">Connect Discord</button>');
|
|
16
|
+
parts.push(html `<p>Manage projects and review results in a private Discord conversation.</p>`, html `<details open><summary>Create your Discord app</summary><ol><li><a href="https://discord.com/developers/applications" target="_blank" rel="noopener noreferrer">Create an application</a>, then copy its token from the Bot page.</li><li>Keep the Interactions Endpoint URL empty and privileged intents off.</li><li>Under Installation, enable Guild Install with the bot scope and no server permissions. Use its install link to add it to your server, then open a direct message to the bot.</li></ol></details>`, post("connect", html `<label>Bot token<input style="min-height:44px" type="password" name="bot-token" autocomplete="off" required></label>${password}<p class="meta">The token stays on this installation. Next, pair your own Discord account.</p><button type="submit">Connect Discord</button>`));
|
|
32
17
|
}
|
|
33
18
|
else {
|
|
34
|
-
|
|
19
|
+
parts.push(html `<p><strong>${credentials.workspace}</strong> · ${live ? "Connected" : "Waiting for connection"}</p>`);
|
|
35
20
|
if (runtime?.problem)
|
|
36
|
-
|
|
21
|
+
parts.push(html `<p role="status">${String(runtime.problem)}</p>`);
|
|
37
22
|
if (!live && !runtime?.problem)
|
|
38
|
-
|
|
39
|
-
"<p>Keep the Toolroll worker running to receive Discord messages.</p>";
|
|
23
|
+
parts.push(html `<p>Keep the Toolroll worker running to receive Discord messages.</p>`);
|
|
40
24
|
if (options.code) {
|
|
41
|
-
|
|
42
|
-
"<h2>Pair your account</h2><p>Send this in a direct message to your Toolroll Discord app. It expires in 10 minutes.</p>" +
|
|
43
|
-
`<label>Pairing message<input type="text" readonly value="pair ${escape(options.code)}" autocomplete="off" style="width:100%;max-width:100%;font-size:14px;min-height:44px"></label>` +
|
|
44
|
-
'<a class="button-link" style="min-height:44px;white-space:nowrap" href="/settings/discord">Check connection</a>';
|
|
25
|
+
parts.push(html `<h2>Pair your account</h2><p>Send this in a direct message to your Toolroll Discord app. It expires in 10 minutes.</p>`, html `<label>Pairing message<input type="text" readonly value="pair ${options.code}" autocomplete="off" style="width:100%;max-width:100%;font-size:14px;min-height:44px"></label>`, html `<a class="button-link" style="min-height:44px;white-space:nowrap" href="/settings/discord">Check connection</a>`);
|
|
45
26
|
}
|
|
46
27
|
else if (binding && state.live(binding)) {
|
|
47
|
-
const pending =
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
.prepare("SELECT count(*) n FROM discord_part p JOIN discord_event e ON e.id=p.event WHERE e.binding=? AND p.state='dropped'")
|
|
52
|
-
.get(binding.id)?.n ?? 0);
|
|
53
|
-
content +=
|
|
54
|
-
`<p>Your Discord account is paired.${others ? ` ${others} teammate${others === 1 ? " is" : "s are"} paired too.` : ""}${pending ? ` ${pending} replies waiting to send.` : ""}${failed ? ` ${failed} replies could not be delivered. Open the saved chat to recover them.` : ""}</p>` +
|
|
55
|
-
"<p>Try “What needs my attention?” or “Show the evidence for the latest result.”</p>" +
|
|
56
|
-
'<p><a href="/chat">Open saved chat</a></p>' +
|
|
57
|
-
(loadPrimary(process.env, dir) === "discord"
|
|
58
|
-
? "<p>Task updates are sent here.</p>"
|
|
59
|
-
: post("alerts", '<button type="submit">Send task updates here</button>'));
|
|
28
|
+
const { pending, dropped: failed } = state.partCounts(binding.id);
|
|
29
|
+
parts.push(html `<p>Your Discord account is paired.${others ? ` ${others} teammate${others === 1 ? " is" : "s are"} paired too.` : ""}${pending ? ` ${pending} replies waiting to send.` : ""}${failed ? ` ${failed} replies could not be delivered. Open the saved chat to recover them.` : ""}</p>`, html `<p>Try “What needs my attention?” or “Show the evidence for the latest result.”</p>`, html `<p><a href="/chat">Open saved chat</a></p>`, loadPrimary(process.env, dir) === "discord"
|
|
30
|
+
? html `<p>Task updates are sent here.</p>`
|
|
31
|
+
: post("alerts", html `<button type="submit">Send task updates here</button>`));
|
|
60
32
|
}
|
|
61
33
|
else {
|
|
62
|
-
|
|
63
|
-
(others ? ` ${others} teammate${others === 1 ? " is" : "s are"} already paired.` : "") + "</p>" +
|
|
64
|
-
password +
|
|
65
|
-
'<button type="submit">Create pairing code</button>');
|
|
34
|
+
parts.push(post("pair", html `<h2>Pair your account</h2><p>Your paired Discord account can read your connected projects and confirm proposed changes. Password approvals still open in Toolroll.${others ? ` ${others} teammate${others === 1 ? " is" : "s are"} already paired.` : ""}</p>${password}<button type="submit">Create pairing code</button>`));
|
|
66
35
|
}
|
|
67
36
|
if (binding && state.live(binding) && !options.code)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
"<details><summary>Connection settings</summary>" +
|
|
71
|
-
post("disconnect", password + '<button type="submit">Disconnect Discord</button>') +
|
|
72
|
-
"</details>";
|
|
37
|
+
parts.push(post("unpair", html `${password}<p class="meta">Unpairing ends every open button in your chat. Teammates are unaffected.</p><button type="submit">Unpair my account</button>`));
|
|
38
|
+
parts.push(html `<details><summary>Connection settings</summary>${post("disconnect", html `${password}<button type="submit">Disconnect Discord</button>`)}</details>`);
|
|
73
39
|
}
|
|
74
|
-
return `<section style="max-width:42rem;overflow-wrap:anywhere"><p><a href="/settings">Settings</a></p><h1>Discord</h1>${options.problem ? `<p class="problem" role="alert">${
|
|
40
|
+
return html `<section style="max-width:42rem;overflow-wrap:anywhere"><p><a href="/settings">Settings</a></p><h1>Discord</h1>${options.problem ? html `<p class="problem" role="alert">${options.problem}</p>` : ""}${parts}<details><summary>Access and setup</summary><p>Private messages and replies use your Toolroll permissions. Shared channels are not supported. Secure review links use this installation’s configured HTTPS console address.</p><p>The worker connects out to Discord. It does not need a public webhook or a Tailscale account.</p></details></section>`;
|
|
75
41
|
}
|
package/dist/discord.js
CHANGED
|
@@ -37,9 +37,7 @@ export async function followDiscord(options) {
|
|
|
37
37
|
current.botToken === credentials.botToken &&
|
|
38
38
|
state.owns(credentials.installation, owner));
|
|
39
39
|
};
|
|
40
|
-
const problem = (message) => state
|
|
41
|
-
.prepare("UPDATE chat_runtime SET problem=? WHERE installation=? AND owner=?")
|
|
42
|
-
.run(message, credentials.installation, owner);
|
|
40
|
+
const problem = (message) => state.setProblem(credentials.installation, owner, message);
|
|
43
41
|
const api = discordApi(credentials.botToken);
|
|
44
42
|
const client = new Client({
|
|
45
43
|
// Guild messages and their content are needed for rooms; the Message
|
|
@@ -63,9 +61,7 @@ export async function followDiscord(options) {
|
|
|
63
61
|
if (!same() || !validated)
|
|
64
62
|
return;
|
|
65
63
|
connected = true;
|
|
66
|
-
state
|
|
67
|
-
.prepare("UPDATE chat_runtime SET connected=?,problem=NULL WHERE installation=? AND owner=?")
|
|
68
|
-
.run(new Date().toISOString(), credentials.installation, owner);
|
|
64
|
+
state.setConnected(credentials.installation, owner, new Date());
|
|
69
65
|
};
|
|
70
66
|
client.on("shardReady", online);
|
|
71
67
|
client.on("shardResume", online);
|
|
@@ -133,11 +129,7 @@ export async function followDiscord(options) {
|
|
|
133
129
|
try {
|
|
134
130
|
await client.login(credentials.botToken);
|
|
135
131
|
while (same()) {
|
|
136
|
-
|
|
137
|
-
.prepare("SELECT retry_at FROM chat_runtime WHERE installation=?")
|
|
138
|
-
.get(credentials.installation)?.retry_at;
|
|
139
|
-
if (!connected ||
|
|
140
|
-
(typeof retry === "string" && retry > new Date().toISOString())) {
|
|
132
|
+
if (!connected || state.retryAt(credentials.installation) > new Date().toISOString()) {
|
|
141
133
|
await pause(1000);
|
|
142
134
|
continue;
|
|
143
135
|
}
|
|
@@ -160,9 +152,7 @@ export async function followDiscord(options) {
|
|
|
160
152
|
alive = false;
|
|
161
153
|
options.signal.removeEventListener("abort", stop);
|
|
162
154
|
await client.destroy().catch(() => { });
|
|
163
|
-
state
|
|
164
|
-
.prepare("UPDATE chat_runtime SET owner=NULL,lease_until=NULL,connected=NULL WHERE installation=? AND owner=?")
|
|
165
|
-
.run(credentials.installation, owner);
|
|
155
|
+
state.stop(credentials.installation, owner);
|
|
166
156
|
}
|
|
167
157
|
await pause(5000);
|
|
168
158
|
}
|
package/dist/envelope.js
CHANGED
|
@@ -71,6 +71,16 @@ export const DOCUMENTED_REASONS = [
|
|
|
71
71
|
"repair-attempts-spent", // the chain's signed attempt cap is spent
|
|
72
72
|
"repair-no-progress", // two consecutive repair attempts failed to shrink the unresolved set
|
|
73
73
|
"repair-refused-integrity", // a refutation named an altered term, not a gap — no automatic repair
|
|
74
|
+
"ambiguous", // a value could mean two things (a lease id or a branch) — refused, never guessed
|
|
75
|
+
"drift", // the branch, pull request or result moved from the gated commit
|
|
76
|
+
"check-failed", // the release check failed; it stays visible and nothing reruns it
|
|
77
|
+
"ci-failed", // a required pull-request check failed or is missing — nothing merged
|
|
78
|
+
"base-moved", // main gained a commit the release branch lacks — nothing merged
|
|
79
|
+
"tree-mismatch", // main's merge commit has a different tree than the gated commit — nothing tagged
|
|
80
|
+
"conflict", // a task or tag exists for a different commit
|
|
81
|
+
"mixed-runtime", // the console, worker and CLI names don't run one build
|
|
82
|
+
"gate-held", // a deployment stopped before its swap and left new work paused — `release --release-gate <id>`
|
|
83
|
+
"after-swap", // the deployment got past stopping the service, so its pause is its own recovery's to lift
|
|
74
84
|
];
|
|
75
85
|
let captured = null;
|
|
76
86
|
let captureHook = null;
|
package/dist/evidence-pack.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type Filer } from "./approval-policy.js";
|
|
|
2
2
|
import { type AssignmentAccess, type AssignmentSnapshot } from "./assignment.js";
|
|
3
3
|
import type { SealedLedgerEntry, Store } from "./store.js";
|
|
4
4
|
import type { LedgerChainReport } from "./ledger-chain.js";
|
|
5
|
+
import { type Html } from "./html.js";
|
|
5
6
|
export declare const EVIDENCE_PACK_FORMAT = "standing-orders/evidence-pack/v1";
|
|
6
7
|
/** How to recheck one entry's seal, for whoever reads the JSON. */
|
|
7
8
|
export declare const SEAL_RECIPE = "hash = sha256(prev + \"\\n\" + JSON.stringify([id, at, actor, repo, taskId, runId, action, outcome, source, detail]))";
|
|
@@ -179,7 +180,7 @@ export declare function ledgerExportChunks(store: Store, range: {
|
|
|
179
180
|
}, access: AssignmentAccess, who: string, now: Date, evidenceRoot: string): Generator<string, void, undefined>;
|
|
180
181
|
export declare const EVIDENCE_PACK_CSS: string;
|
|
181
182
|
/** The pack as a page: the same facts, in the order an auditor reads them. No script. */
|
|
182
|
-
export declare function evidencePackHtml(pack: EvidencePack):
|
|
183
|
+
export declare function evidencePackHtml(pack: EvidencePack): Html;
|
|
183
184
|
/** The page as a file on its own (the CLI's --html): the same facts with just enough style to print. */
|
|
184
185
|
export declare function standaloneEvidenceHtml(pack: EvidencePack): string;
|
|
185
186
|
export {};
|
package/dist/evidence-pack.js
CHANGED
|
@@ -15,6 +15,7 @@ import { rulesWords } from "./approval-policy.js";
|
|
|
15
15
|
import { assignmentOf } from "./assignment.js";
|
|
16
16
|
import { projectName } from "./project.js";
|
|
17
17
|
import { changedFilesOf } from "./result-completion.js";
|
|
18
|
+
import { html, htmlString, joinHtml, styleElement } from "./html.js";
|
|
18
19
|
export const EVIDENCE_PACK_FORMAT = "standing-orders/evidence-pack/v1";
|
|
19
20
|
/** How to recheck one entry's seal, for whoever reads the JSON. */
|
|
20
21
|
export const SEAL_RECIPE = 'hash = sha256(prev + "\\n" + JSON.stringify([id, at, actor, repo, taskId, runId, action, outcome, source, detail]))';
|
|
@@ -173,13 +174,12 @@ export function* ledgerExportChunks(store, range, scope, access, who, now, evide
|
|
|
173
174
|
yield `],"truncated":${JSON.stringify({ entries: moreEntries, packs: morePacks })}}`;
|
|
174
175
|
}
|
|
175
176
|
// ---- The printable page -----------------------------------------------------
|
|
176
|
-
const
|
|
177
|
-
const
|
|
178
|
-
const short = (hash) => hash === null ? "—" : `<code title="${e(hash)}">${e(hash.slice(0, 12))}</code>`;
|
|
177
|
+
const when = (at) => at === null ? html `—` : html `${at.slice(0, 16).replace("T", " ")} UTC`;
|
|
178
|
+
const short = (hash) => hash === null ? html `—` : html `<code title="${hash}">${hash.slice(0, 12)}</code>`;
|
|
179
179
|
const usd = (value) => value === null ? "—" : `$${value.toFixed(value < 1 ? 4 : 2)}`;
|
|
180
180
|
/** Only a web address links; anything else (a stored value that isn't one) is shown as text. */
|
|
181
|
-
const link = (url) => /^https:\/\/[^\s"'<>]+$/.test(url) ? `<a href="${
|
|
182
|
-
const filer = (who) => who === null || who.name === null ?
|
|
181
|
+
const link = (url) => /^https:\/\/[^\s"'<>]+$/.test(url) ? html `<a href="${url}">${url}</a>` : html `${url}`;
|
|
182
|
+
const filer = (who) => who === null || who.name === null ? html `automation` : who.kind === "person" ? html `${who.name}` : html `${who.name} (${who.kind})`;
|
|
183
183
|
export const EVIDENCE_PACK_CSS = `.evidence-pack{max-width:960px;min-width:0}.evidence-pack h1{margin:0}.evidence-pack h2{font-size:.9375rem;margin:24px 0 8px}.evidence-pack h3{font-size:.875rem;margin:16px 0 6px}` +
|
|
184
184
|
`.evidence-head{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;align-items:baseline}.evidence-pack dl{display:grid;grid-template-columns:minmax(120px,180px) minmax(0,1fr);gap:4px 16px;margin:0}` +
|
|
185
185
|
`.evidence-pack dt{color:var(--muted-foreground);font-size:.8125rem}.evidence-pack dd{margin:0;min-width:0;overflow-wrap:anywhere}.evidence-pack table{width:100%;border-collapse:collapse;font-size:.8125rem}` +
|
|
@@ -193,66 +193,62 @@ export const EVIDENCE_PACK_CSS = `.evidence-pack{max-width:960px;min-width:0}.ev
|
|
|
193
193
|
export function evidencePackHtml(pack) {
|
|
194
194
|
const task = pack.task;
|
|
195
195
|
const chain = pack.ledger.chain.ok
|
|
196
|
-
? `<p class="evidence-chain ok" data-evidence-chain="ok"><strong>Ledger chain verified</strong> · ${pack.ledger.chain.entries} entries · head ${short(pack.ledger.chain.head)}${pack.ledger.chain.checkedAt === null ? "" : ` · checked in full ${when(pack.ledger.chain.checkedAt)}`}${pack.ledger.checkpoint === null ? "" : ` · last checkpoint at entry #${pack.ledger.checkpoint.through}`}</p>`
|
|
197
|
-
: `<p class="evidence-chain problem" role="alert" data-evidence-chain="broken"><strong>Ledger chain broken</strong> · ${
|
|
196
|
+
? html `<p class="evidence-chain ok" data-evidence-chain="ok"><strong>Ledger chain verified</strong> · ${pack.ledger.chain.entries} entries · head ${short(pack.ledger.chain.head)}${pack.ledger.chain.checkedAt === null ? "" : html ` · checked in full ${when(pack.ledger.chain.checkedAt)}`}${pack.ledger.checkpoint === null ? "" : html ` · last checkpoint at entry #${pack.ledger.checkpoint.through}`}</p>`
|
|
197
|
+
: html `<p class="evidence-chain problem" role="alert" data-evidence-chain="broken"><strong>Ledger chain broken</strong> · ${pack.ledger.chain.problem}</p>`;
|
|
198
198
|
const versions = pack.versions.map((version, index) => {
|
|
199
199
|
const scope = version.scope;
|
|
200
200
|
const approved = scope?.approved ?? null;
|
|
201
201
|
const approvals = scope === null ? [] : scope.approvals.filter(one => one.digest === (approved?.digest ?? scope.digest));
|
|
202
202
|
const rows = [
|
|
203
|
-
["Filed", `${when(version.filedAt)} by ${filer(version.filedBy)}`],
|
|
204
|
-
...(scope === null ? [["Scope",
|
|
205
|
-
["Goal",
|
|
206
|
-
...(scope.touches.length ? [["Touches", scope.touches
|
|
207
|
-
...(scope.acceptance.length ? [["Acceptance", `<ol class="criteria">${scope.acceptance.map(one => `<li>${
|
|
208
|
-
...(scope.writtenBy.length ? [["Scope written by", [...new Set(scope.writtenBy.map(one => one.author))]
|
|
209
|
-
["Approved", approved === null ?
|
|
210
|
-
...(approvals.length > 1 ? [["Approvals", approvals.map(one => `${
|
|
203
|
+
["Filed", html `${when(version.filedAt)} by ${filer(version.filedBy)}`],
|
|
204
|
+
...(scope === null ? [["Scope", html `None written`]] : [
|
|
205
|
+
["Goal", html `${scope.goal}`],
|
|
206
|
+
...(scope.touches.length ? [["Touches", joinHtml(scope.touches, ", ")]] : []),
|
|
207
|
+
...(scope.acceptance.length ? [["Acceptance", html `<ol class="criteria">${scope.acceptance.map(one => html `<li>${one.statement}</li>`)}</ol>`]] : []),
|
|
208
|
+
...(scope.writtenBy.length ? [["Scope written by", joinHtml([...new Set(scope.writtenBy.map(one => one.author))], ", ")]] : []),
|
|
209
|
+
["Approved", approved === null ? html `Not approved` : html `${approved.by}${approved.basis === "mode" ? " (operating mode)" : ""} · ${when(approved.at)}${approved.current ? "" : " · the scope changed after this"}`],
|
|
210
|
+
...(approvals.length > 1 ? [["Approvals", joinHtml(approvals.map(one => html `${one.approver} · ${when(one.at)}`), html `<br>`)]] : []),
|
|
211
211
|
["Scope digest", short(scope.digest)],
|
|
212
212
|
]),
|
|
213
213
|
];
|
|
214
|
-
const runs = version.runs.length === 0 ? `<p class="meta">No agent ran.</p>` :
|
|
215
|
-
`<div class="evidence-table"><table><thead><tr><th>Run</th><th>Role</th><th>Agent</th><th>Outcome</th><th>Cost</th><th>Finished</th><th>Commit</th></tr></thead><tbody>`
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
(run.changedFiles === null ? `<p class="meta">Its changed files couldn't be read.</p>` : run.changedFiles.length === 0 ? `<p class="meta">No files changed.</p>` : `<ul class="files">${run.changedFiles.map(file => `<li>${e(file)}</li>`).join("")}</ul>`)).join("");
|
|
220
|
-
return `<section data-evidence-version="${e(version.id)}"><h2>${pack.versions.length > 1 ? `${index === 0 ? "Request" : `Revision ${index}`} · ` : "Request and approval · "}${e(version.id)}</h2><dl>${rows.map(([label, value]) => `<dt>${label}</dt><dd>${value}</dd>`).join("")}</dl>` +
|
|
221
|
-
`<h3>Agents</h3>${runs}${changes === "" ? "" : `<h3>Changes</h3>${changes}`}</section>`;
|
|
222
|
-
}).join("");
|
|
214
|
+
const runs = version.runs.length === 0 ? html `<p class="meta">No agent ran.</p>` :
|
|
215
|
+
html `<div class="evidence-table"><table><thead><tr><th>Run</th><th>Role</th><th>Agent</th><th>Outcome</th><th>Cost</th><th>Finished</th><th>Commit</th></tr></thead><tbody>${version.runs.map(run => html `<tr><td><a href="/r/${run.id}">#${run.id}</a></td><td>${run.role}</td><td>${run.agent}${run.model ? html ` · ${run.model}` : ""}</td><td>${run.outcome ?? "running"}</td><td>${usd(run.costUsd)}</td><td>${when(run.finishedAt)}</td><td>${short(run.head)}</td></tr>`)}</tbody></table></div>`;
|
|
216
|
+
const changes = version.runs.filter(run => run.role === "builder" || run.role === "repair").map(run => html `<p class="meta">Run #${run.id}${run.diff === null ? "" : html ` · diff ${short(run.diff.sha256)}${run.diff.complete ? "" : " (partial)"}`}</p>${run.changedFiles === null ? html `<p class="meta">Its changed files couldn't be read.</p>` : run.changedFiles.length === 0 ? html `<p class="meta">No files changed.</p>` : html `<ul class="files">${run.changedFiles.map(file => html `<li>${file}</li>`)}</ul>`}`);
|
|
217
|
+
return html `<section data-evidence-version="${version.id}"><h2>${pack.versions.length > 1 ? `${index === 0 ? "Request" : `Revision ${index}`} · ` : "Request and approval · "}${version.id}</h2><dl>${rows.map(([label, value]) => html `<dt>${label}</dt><dd>${value}</dd>`)}</dl><h3>Agents</h3>${runs}${changes.length === 0 ? "" : html `<h3>Changes</h3>${changes}`}</section>`;
|
|
218
|
+
});
|
|
223
219
|
const result = pack.result;
|
|
224
|
-
const outcome = result === null ? `<section><h2>Result</h2><p class="meta">No finished result yet.</p></section>` :
|
|
225
|
-
`<section data-evidence-result><h2>Result</h2><dl
|
|
226
|
-
`<dt>Checks</dt><dd>${
|
|
227
|
-
`<dt>Marked complete</dt><dd>${result.completedBy === null ? "Not yet" : `${
|
|
228
|
-
`<dt>Published</dt><dd>${result.publication === null ? "Not published" : `${
|
|
229
|
-
`<dt>Commit</dt><dd>${short(result.base)} → ${short(result.head)}</dd
|
|
230
|
-
|
|
231
|
-
`</dl></section
|
|
220
|
+
const outcome = result === null ? html `<section><h2>Result</h2><p class="meta">No finished result yet.</p></section>` :
|
|
221
|
+
joinHtml([html `<section data-evidence-result><h2>Result</h2><dl>`,
|
|
222
|
+
html `<dt>Checks</dt><dd>${result.checks.detail}${result.checks.command ? html ` <code>${result.checks.command}</code>` : ""}</dd>`,
|
|
223
|
+
html `<dt>Marked complete</dt><dd>${result.completedBy === null ? "Not yet" : html `${result.completedBy} · ${when(result.completedAt)}`}</dd>`,
|
|
224
|
+
html `<dt>Published</dt><dd>${result.publication === null ? "Not published" : html `${result.publication.state}${result.publication.prUrl ? html ` · ${link(result.publication.prUrl)}` : ""}${result.publication.remoteState ? html ` · ${result.publication.remoteState.toLowerCase()}` : ""}`}</dd>`,
|
|
225
|
+
html `<dt>Commit</dt><dd>${short(result.base)} → ${short(result.head)}</dd>`,
|
|
226
|
+
result.caveats.length ? html `<dt>Caveats</dt><dd>${joinHtml(result.caveats, html `<br>`)}</dd>` : "",
|
|
227
|
+
html `</dl></section>`]);
|
|
232
228
|
const decisions = pack.ledger.entries.filter(one => one.source !== "request");
|
|
233
229
|
const requests = pack.ledger.entries.length - decisions.length;
|
|
234
230
|
const askedWords = `${requests} ${requests === 1 ? "request" : "requests"} made through the console (accepted or refused)`;
|
|
235
|
-
const ledger = `<section><h2>Ledger</h2>${decisions.length === 0 ? `<p class="meta">No entries.</p>` :
|
|
236
|
-
`<div class="evidence-table"><table><thead><tr><th>#</th><th>Time</th><th>Who</th><th>What</th><th>Outcome</th><th>Seal</th></tr></thead><tbody>`
|
|
237
|
-
decisions.map(one => `<tr data-ledger-id="${one.id}"><td>${one.id}</td><td>${when(one.at)}</td><td>${e(one.actor)}</td><td>${e(one.action)}${one.detail ? `<br><span class="meta">${e(one.detail)}</span>` : ""}</td><td>${e(one.outcome)}</td><td>${short(one.seal?.hash ?? null)}</td></tr>`).join("") +
|
|
238
|
-
`</tbody></table></div>`}${requests > 0 ? `<p class="meta">Plus ${askedWords}, listed in the JSON.</p>` : ""}${pack.ledger.truncated ? `<p class="meta">This pack holds ${decisions.length} of the task's entries and the newest ${requests} requests; the ledger export has the rest.</p>` : ""}</section>`;
|
|
231
|
+
const ledger = html `<section><h2>Ledger</h2>${decisions.length === 0 ? html `<p class="meta">No entries.</p>` :
|
|
232
|
+
html `<div class="evidence-table"><table><thead><tr><th>#</th><th>Time</th><th>Who</th><th>What</th><th>Outcome</th><th>Seal</th></tr></thead><tbody>${decisions.map(one => html `<tr data-ledger-id="${one.id}"><td>${one.id}</td><td>${when(one.at)}</td><td>${one.actor}</td><td>${one.action}${one.detail ? html `<br><span class="meta">${one.detail}</span>` : ""}</td><td>${one.outcome}</td><td>${short(one.seal?.hash ?? null)}</td></tr>`)}</tbody></table></div>`}${requests > 0 ? html `<p class="meta">Plus ${askedWords}, listed in the JSON.</p>` : ""}${pack.ledger.truncated ? html `<p class="meta">This pack holds ${decisions.length} of the task's entries and the newest ${requests} requests; the ledger export has the rest.</p>` : ""}</section>`;
|
|
239
233
|
const cost = pack.totals.runs === 0 ? "None ran" : `${pack.totals.runs} ${pack.totals.runs === 1 ? "run" : "runs"} · ${usd(pack.totals.costUsd)}`;
|
|
240
234
|
// The rules as they stand, and each change to them (the approval above was under whichever applied then).
|
|
241
|
-
const rules = pack.rules === null ?
|
|
242
|
-
`<ul class="criteria">${pack.rules.changes.slice(-5).map(one => `<li class="meta">${when(one.at)} · ${
|
|
243
|
-
return `<article class="evidence-pack" data-evidence-pack="${
|
|
244
|
-
`<div class="evidence-head"><h1>Evidence pack</h1><p class="evidence-actions"><a href="/t/${encodeURIComponent(task.id)}/evidence?format=json" download>Download JSON</a> · <a href="/t/${encodeURIComponent(task.id)}">Back to task</a></p></div
|
|
245
|
-
`<p><strong>${
|
|
246
|
-
`<dl><dt>Task</dt><dd>${
|
|
247
|
-
`<dt>Approval rules</dt><dd>${rules}</dd><dt>Agents</dt><dd>${cost}</dd
|
|
248
|
-
`<dt>Prepared</dt><dd>${when(pack.generatedAt)} for ${
|
|
249
|
-
versions
|
|
250
|
-
`<p class="evidence-foot">Pack digest <code>${
|
|
235
|
+
const rules = pack.rules === null ? html `None` : html `${pack.rules.now}${pack.rules.changes.length === 0 ? "" :
|
|
236
|
+
html `<ul class="criteria">${pack.rules.changes.slice(-5).map(one => html `<li class="meta">${when(one.at)} · ${one.by}: ${one.change}</li>`)}</ul>`}`;
|
|
237
|
+
return joinHtml([html `<article class="evidence-pack" data-evidence-pack="${task.id}">`,
|
|
238
|
+
html `<div class="evidence-head"><h1>Evidence pack</h1><p class="evidence-actions"><a href="/t/${encodeURIComponent(task.id)}/evidence?format=json" download>Download JSON</a> · <a href="/t/${encodeURIComponent(task.id)}">Back to task</a></p></div>`,
|
|
239
|
+
html `<p><strong>${task.title}</strong></p>`,
|
|
240
|
+
html `<dl><dt>Task</dt><dd>${task.id}${task.project === null ? "" : ` · ${task.project}`} · ${task.state}</dd>`,
|
|
241
|
+
html `<dt>Approval rules</dt><dd>${rules}</dd><dt>Agents</dt><dd>${cost}</dd>`,
|
|
242
|
+
html `<dt>Prepared</dt><dd>${when(pack.generatedAt)} for ${pack.generatedBy}</dd></dl>${chain}`,
|
|
243
|
+
versions, outcome, ledger,
|
|
244
|
+
html `<p class="evidence-foot">Pack digest <code>${pack.digest}</code>. Each ledger entry's seal can be rechecked from the JSON: ${pack.ledger.recipe}.</p></article>`]);
|
|
251
245
|
}
|
|
252
246
|
/** The page as a file on its own (the CLI's --html): the same facts with just enough style to print. */
|
|
253
247
|
export function standaloneEvidenceHtml(pack) {
|
|
254
|
-
|
|
255
|
-
`<style>:root{--muted-foreground:#5c5c5c;--border:#dedede;--success:#1a7f37;--font-mono:ui-monospace,SFMono-Regular,Menlo,monospace}` +
|
|
248
|
+
const css = `:root{--muted-foreground:#5c5c5c;--border:#dedede;--success:#1a7f37;--font-mono:ui-monospace,SFMono-Regular,Menlo,monospace}` +
|
|
256
249
|
`body{font:14px/1.5 system-ui,-apple-system,sans-serif;margin:32px auto;padding:0 16px;max-width:960px;color:#171717;background:#fff}.meta{color:var(--muted-foreground);font-size:.8125rem}.evidence-actions{display:none}` +
|
|
257
|
-
EVIDENCE_PACK_CSS
|
|
250
|
+
EVIDENCE_PACK_CSS;
|
|
251
|
+
// The file is the sink: its bytes.
|
|
252
|
+
return htmlString(html `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Evidence pack · ${pack.task.id}</title>${styleElement(css)}</head><body>${evidencePackHtml(pack)}</body></html>
|
|
253
|
+
`);
|
|
258
254
|
}
|
package/dist/export-ui.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings → Data (v105): an instance operator downloads everything Standing
|
|
3
|
+
* Orders knows as one .zip. One form: the password, typed again, and a button.
|
|
4
|
+
*/
|
|
5
|
+
import { type Html } from "./html.js";
|
|
1
6
|
export declare const EXPORT_CSS: string;
|
|
2
|
-
export declare function dataExportHtml(
|
|
7
|
+
export declare function dataExportHtml(notice: {
|
|
3
8
|
problem?: string | null;
|
|
4
|
-
}):
|
|
9
|
+
}): Html;
|
package/dist/export-ui.js
CHANGED
|
@@ -2,16 +2,10 @@
|
|
|
2
2
|
* Settings → Data (v105): an instance operator downloads everything Standing
|
|
3
3
|
* Orders knows as one .zip. One form: the password, typed again, and a button.
|
|
4
4
|
*/
|
|
5
|
-
|
|
5
|
+
import { html, postForm } from "./html.js";
|
|
6
6
|
export const EXPORT_CSS = `.data-export{max-width:720px;min-width:0}.data-export label{display:grid;gap:4px;margin:14px 0 0;font-size:.875rem}` +
|
|
7
7
|
`.data-export input[type=password]{width:100%;max-width:360px;box-sizing:border-box}.data-export button[type=submit]{margin-top:14px;min-height:44px;white-space:nowrap}`;
|
|
8
|
-
export function dataExportHtml(
|
|
9
|
-
const note = notice.problem ? `<p class="problem" role="alert">${
|
|
10
|
-
return `<section class="data-export">${note}
|
|
11
|
-
`<p>Everything Toolroll knows, as one .zip: projects, tasks, runs and cost, the action ledger, evidence packs, chats, flows, teammates and settings. ` +
|
|
12
|
-
`Passwords, keys and tokens are never included.</p>` +
|
|
13
|
-
`<form method="post" action="/settings/data"><input type="hidden" name="csrf" value="${e(csrf)}">` +
|
|
14
|
-
`<label>Your Toolroll password<input type="password" name="password" autocomplete="current-password" required></label>` +
|
|
15
|
-
`<button type="submit">Download export</button></form>` +
|
|
16
|
-
`<p class="meta">The same export from the command line: <code>toolroll export --out <path> [--zip]</code></p></section>`;
|
|
8
|
+
export function dataExportHtml(notice) {
|
|
9
|
+
const note = notice.problem ? html `<p class="problem" role="alert">${notice.problem}</p>` : "";
|
|
10
|
+
return html `<section class="data-export">${note}<p>Everything Toolroll knows, as one .zip: projects, tasks, runs and cost, the action ledger, evidence packs, chats, flows, teammates and settings. Passwords, keys and tokens are never included.</p>${postForm("/settings/data", html `<label>Your Toolroll password<input type="password" name="password" autocomplete="current-password" required></label><button type="submit">Download export</button>`)}<p class="meta">The same export from the command line: <code>toolroll export --out <path> [--zip]</code></p></section>`;
|
|
17
11
|
}
|
package/dist/export.js
CHANGED
|
@@ -29,20 +29,20 @@ import { readEmailSettings } from "./email-settings.js";
|
|
|
29
29
|
import { SUBSCRIPTION_CAPABLE, readAuthMode } from "./keys.js";
|
|
30
30
|
export const EXPORT_FORMAT = "standing-orders/export/v1";
|
|
31
31
|
/** Tables that hold nothing but secrets or short-lived sign-in material, and derived search indexes. */
|
|
32
|
-
const SECRET_TABLES = new Set(["web_session", "ceremony_nonce", "
|
|
32
|
+
const SECRET_TABLES = new Set(["web_session", "ceremony_nonce", "chat_pair", "oauth_code", "oauth_refresh"]);
|
|
33
33
|
const DERIVED_TABLE = /^(sqlite_|memory_search)/;
|
|
34
34
|
/** Columns that hold a secret (or what stands in for one): never exported. */
|
|
35
35
|
const SECRET_COLUMNS = new Set([
|
|
36
36
|
"api_token.secret_hash", "lead_credential.secret_hash", "approver.credential_hash", "coordinator_credential.credential_hash", "runner.credential_hash", "invite.token_hash",
|
|
37
37
|
"flow_trigger.hook_hash", "push_subscription.endpoint", "push_subscription.p256dh", "push_subscription.auth",
|
|
38
|
-
"chat_turn.credential_key", "mate_session.credential_key", "mate_turn.credential_key", "
|
|
38
|
+
"chat_turn.credential_key", "mate_session.credential_key", "mate_turn.credential_key", "chat_runtime.push_url", "workflow_preview.token",
|
|
39
39
|
"quota.credential_fp", "oauth_grant.renew_hash", "oauth_client.source_hash",
|
|
40
40
|
]);
|
|
41
41
|
/** A column named like this holds a secret unless it is reviewed below; an unreviewed one is dropped. */
|
|
42
42
|
const SECRET_NAME = /hash|secret|token|passw|cookie|csrf|credential|p256dh|^auth$|key|nonce|endpoint|url/i;
|
|
43
43
|
/** Reviewed: named like a secret, but it isn't one (a seal, a digest, a lookup name, an address with no key). */
|
|
44
44
|
const REVIEWED_COLUMNS = new Set([
|
|
45
|
-
"artifact.key", "budget.scope_key", "mate_thread.scope_key", "diff_comment.source_key", "
|
|
45
|
+
"artifact.key", "budget.scope_key", "mate_thread.scope_key", "diff_comment.source_key", "chat_meta.key",
|
|
46
46
|
"flow_trigger_event.key", "installation_fact.key", "mcp_idempotency.key", "memory_gap.key", "mutation.idempotency_key", "notification.dedupe_key",
|
|
47
47
|
"service_cursor.key", "ledger_checkpoint.hash", "ledger_seal.hash", "merge_intent.grant_terms_hash", "plan_revision.parent_hash",
|
|
48
48
|
"publication.body_hash", "publication.pr_url", "team_message.payload_hash", "team_request.payload_hash", "backend_grant.credential_scope",
|
|
@@ -55,7 +55,7 @@ export function categoryOf(table) {
|
|
|
55
55
|
[/^(action_ledger|ledger_seal|ledger_checkpoint|sync_ledger)$/, "ledger"],
|
|
56
56
|
[/^teammate/, "teammates"],
|
|
57
57
|
[/^(flow|workflow_)/, "flows"],
|
|
58
|
-
[/^(chat_|mate_|team_|
|
|
58
|
+
[/^(chat_|mate_|team_|notification|push_)/, "chats"],
|
|
59
59
|
[/^(approver|api_token|lead_credential|invite|sso_identity|coordinator_|oauth_)/, "people"],
|
|
60
60
|
[/^(run|artifact|claim|worktree$|execution_slot|criterion_review|diff_comment|proof_|review_request|repair_chain|incident|publication$|merge_|side_spend|knowledge_snapshot|learning_snapshot|skill_snapshot)/, "runs"],
|
|
61
61
|
[/^(task|hold$|plan_|scope_|tool_seal|decision|external_|skill_test)/, "tasks"],
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type GalleryAnswers, type GalleryPreview, type GalleryTemplate, type GalleryTool } from "./flow-gallery.js";
|
|
2
2
|
import { type FlowDefinition, type FlowStage } from "./flows.js";
|
|
3
|
+
import { type Html } from "./html.js";
|
|
3
4
|
export declare const GALLERY_CSS: string;
|
|
4
5
|
/** A flow's main path, as a person reads it: from the start, each step's first way on (a sort's first answer, a choose's
|
|
5
6
|
* first option that goes somewhere, else next; a check's failure when passing just ends), never back to a step shown. */
|
|
6
7
|
export declare function mainPath(definition: FlowDefinition): FlowStage[];
|
|
7
8
|
/** A flow's steps in order, joined by arrows; a person's decisions carry the accent. */
|
|
8
|
-
export declare function stepStrip(definition: FlowDefinition):
|
|
9
|
+
export declare function stepStrip(definition: FlowDefinition): Html;
|
|
9
10
|
/** The gallery: every template, grouped, each with one "Use this". `repo` carries a chosen project to the template's page. */
|
|
10
11
|
export declare function galleryHtml(input: {
|
|
11
12
|
repo: string | null;
|
|
@@ -14,7 +15,7 @@ export declare function galleryHtml(input: {
|
|
|
14
15
|
id: string;
|
|
15
16
|
state: "connected" | "open" | "taken";
|
|
16
17
|
}[] | null;
|
|
17
|
-
}):
|
|
18
|
+
}): Html;
|
|
18
19
|
/** A template's page: its questions, then what it will do and never do, then Create. */
|
|
19
20
|
export declare function galleryUseHtml(input: {
|
|
20
21
|
template: GalleryTemplate;
|
|
@@ -32,4 +33,4 @@ export declare function galleryUseHtml(input: {
|
|
|
32
33
|
/** The template's tools in this project; `said` is how the last Connect went. */
|
|
33
34
|
tools?: readonly GalleryTool[];
|
|
34
35
|
said?: string | null;
|
|
35
|
-
}):
|
|
36
|
+
}): Html;
|