toolroll 0.9.54 → 0.9.56
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 +19 -0
- package/README.md +29 -12
- package/dist/action-ledger.d.ts +2 -4
- package/dist/action-ledger.js +2 -10
- package/dist/agent-onboard.js +4 -1
- package/dist/approval-policy.d.ts +5 -5
- package/dist/approval-policy.js +4 -4
- package/dist/approval-rules-ui.d.ts +3 -2
- package/dist/approval-rules-ui.js +17 -22
- 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 +14 -15
- package/dist/browser-workspace.js +0 -3
- package/dist/budget-alerts.js +3 -3
- package/dist/chat-actions.d.ts +23 -18
- package/dist/chat-actions.js +110 -88
- package/dist/chat-ask.d.ts +2 -2
- package/dist/chat-ask.js +5 -5
- package/dist/chat-channel.d.ts +15 -15
- package/dist/chat-channel.js +20 -20
- package/dist/chat-controls.d.ts +0 -4
- package/dist/chat-controls.js +0 -1
- package/dist/chat-core.js +1 -1
- package/dist/chat-delivery-state.js +2 -2
- package/dist/chat-delivery.d.ts +2 -2
- package/dist/chat-delivery.js +27 -34
- package/dist/chat-flow.js +3 -3
- package/dist/chat-messages.d.ts +1 -1
- package/dist/chat-messages.js +4 -4
- package/dist/chat-migration.d.ts +1 -1
- package/dist/chat-migration.js +11 -11
- package/dist/chat-polish.d.ts +5 -4
- package/dist/chat-polish.js +9 -10
- package/dist/chat-rooms.js +3 -3
- package/dist/chat-warmth.d.ts +2 -2
- 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 +15 -10
- 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-actions.d.ts +32 -27
- package/dist/contracts/chat-actions.js +29 -11
- package/dist/contracts/chat-content.d.ts +1 -1
- package/dist/contracts/chat-content.js +1 -1
- package/dist/contracts/chat-tables.js +4 -4
- package/dist/contracts/cli.d.ts +1 -3
- package/dist/contracts/console-api.d.ts +7 -7
- package/dist/contracts/console-api.js +13 -12
- package/dist/contracts/flow-propose.d.ts +3 -3
- package/dist/contracts/flow.d.ts +132 -125
- package/dist/contracts/flow.js +27 -12
- package/dist/contracts/health.d.ts +1 -3
- package/dist/contracts/lead-context.d.ts +1 -1
- package/dist/contracts/lead-context.js +2 -2
- package/dist/contracts/lead-tools.d.ts +20 -18
- package/dist/contracts/lead-tools.js +15 -15
- package/dist/contracts/stage-output.d.ts +1 -1
- package/dist/contracts/stage-output.js +1 -1
- package/dist/contracts/store-json.d.ts +23 -22
- package/dist/contracts/store-json.js +17 -17
- package/dist/contracts/{teammate-turn.d.ts → subagent-turn.d.ts} +5 -5
- package/dist/contracts/{teammate-turn.js → subagent-turn.js} +7 -7
- package/dist/control-ui.d.ts +5 -4
- package/dist/control-ui.js +9 -34
- package/dist/converse.d.ts +23 -23
- package/dist/converse.js +28 -28
- package/dist/coordinator-proposals.d.ts +2 -2
- package/dist/coordinator-proposals.js +1 -1
- package/dist/coordinator.js +1 -1
- 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/deprecations.d.ts +12 -0
- package/dist/deprecations.js +20 -0
- package/dist/desktop-update-gate.js +8 -4
- package/dist/desktop-update.js +6 -1
- package/dist/discord-chat.js +6 -6
- package/dist/discord-settings.d.ts +3 -2
- package/dist/discord-settings.js +17 -42
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +1 -1
- package/dist/envelope.js +10 -1
- 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 +7 -7
- package/dist/flow-engine.d.ts +2 -2
- package/dist/flow-engine.js +21 -21
- package/dist/flow-gallery-ui.d.ts +4 -3
- package/dist/flow-gallery-ui.js +28 -43
- package/dist/flow-insights.d.ts +1 -1
- package/dist/flow-share.js +13 -8
- package/dist/flow-starters-ui.d.ts +2 -1
- package/dist/flow-starters-ui.js +12 -15
- package/dist/flow-steps.d.ts +5 -5
- package/dist/flow-steps.js +19 -19
- package/dist/flow-triggers.d.ts +2 -1
- package/dist/flow-triggers.js +17 -15
- package/dist/flows-cli.js +1 -1
- package/dist/flows-ui.d.ts +4 -3
- package/dist/flows-ui.js +38 -49
- package/dist/flows.js +21 -20
- package/dist/guides.js +112 -6
- 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/invoke.js +1 -1
- package/dist/kits-ui.d.ts +5 -4
- package/dist/kits-ui.js +22 -24
- package/dist/kits.d.ts +12 -12
- package/dist/kits.js +31 -31
- package/dist/knowledge-ui.d.ts +6 -5
- package/dist/knowledge-ui.js +26 -25
- package/dist/{mate-cli.d.ts → lead-cli.d.ts} +11 -11
- package/dist/{mate-cli.js → lead-cli.js} +38 -38
- package/dist/lead-commitments.js +4 -4
- package/dist/lead-context.d.ts +12 -2
- package/dist/lead-context.js +15 -17
- package/dist/{mate-contract.d.ts → lead-contract.d.ts} +3 -3
- package/dist/{mate-contract.js → lead-contract.js} +7 -6
- package/dist/{mate-doors.d.ts → lead-doors.d.ts} +6 -6
- package/dist/{mate-doors.js → lead-doors.js} +24 -24
- package/dist/lead-follow.d.ts +5 -5
- package/dist/lead-follow.js +9 -9
- package/dist/lead-people.d.ts +5 -5
- package/dist/lead-people.js +14 -14
- package/dist/{mate-progress.d.ts → lead-progress.d.ts} +9 -9
- package/dist/{mate-progress.js → lead-progress.js} +3 -3
- package/dist/{mate-tools.d.ts → lead-tools.d.ts} +19 -19
- package/dist/{mate-tools.js → lead-tools.js} +109 -101
- package/dist/{mate.d.ts → lead.d.ts} +27 -27
- package/dist/{mate.js → lead.js} +102 -102
- package/dist/ledger-view.d.ts +2 -1
- package/dist/ledger-view.js +27 -29
- package/dist/limits-ui.d.ts +3 -2
- package/dist/limits-ui.js +8 -8
- package/dist/live-bus.d.ts +50 -8
- package/dist/live-bus.js +133 -50
- package/dist/mcp-connect.d.ts +1 -1
- package/dist/mcp-connect.js +3 -3
- package/dist/mcp-core.js +1 -1
- package/dist/mcp-oauth.js +35 -36
- package/dist/memory-pass.d.ts +2 -2
- package/dist/memory-pass.js +7 -7
- package/dist/models-ui.d.ts +2 -1
- package/dist/models-ui.js +25 -35
- package/dist/monitoring-ui.d.ts +4 -3
- package/dist/monitoring-ui.js +13 -26
- package/dist/notification-rows.d.ts +2 -2
- package/dist/openrouter-models.d.ts +2 -1
- package/dist/openrouter-models.js +5 -9
- package/dist/operate.d.ts +3 -3
- package/dist/operate.js +46 -41
- package/dist/people-audit-ui.d.ts +2 -1
- package/dist/people-audit-ui.js +13 -13
- package/dist/policy-ui.d.ts +3 -2
- package/dist/policy-ui.js +17 -23
- package/dist/policy.d.ts +1 -1
- package/dist/principal.d.ts +1 -1
- package/dist/principal.js +2 -2
- package/dist/project-delete-ui.d.ts +4 -3
- package/dist/project-delete-ui.js +16 -28
- package/dist/project-delete.d.ts +1 -1
- package/dist/project-delete.js +26 -26
- package/dist/project-memory.js +1 -1
- package/dist/project-tools.d.ts +1 -1
- package/dist/project-tools.js +1 -1
- 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/retention-ui.d.ts +7 -1
- package/dist/retention-ui.js +7 -11
- package/dist/retention.js +4 -4
- package/dist/scope.d.ts +2 -2
- package/dist/scout-report.d.ts +1 -1
- package/dist/scout-report.js +1 -1
- package/dist/serve.d.ts +11 -2
- package/dist/serve.js +105 -99
- package/dist/server/chat.d.ts +7 -10
- package/dist/server/chat.js +984 -1021
- 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 +854 -773
- package/dist/server/guards.d.ts +1 -1
- package/dist/server/guards.js +8 -5
- 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 +581 -573
- package/dist/server/remote.d.ts +4 -7
- package/dist/server/remote.js +39 -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 +154 -0
- package/dist/server/render-settings.js +580 -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 +167 -77
- package/dist/server/runtime.d.ts +28 -20
- 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 +2186 -2121
- 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 +1489 -1420
- package/dist/server-telemetry.d.ts +4 -3
- package/dist/server-telemetry.js +4 -8
- package/dist/setup-guide.d.ts +2 -2
- 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-chat.d.ts +2 -2
- package/dist/slack-chat.js +6 -6
- package/dist/slack-settings.d.ts +3 -2
- package/dist/slack-settings.js +17 -42
- package/dist/spend-ui.d.ts +5 -4
- package/dist/spend-ui.js +17 -40
- package/dist/spend.d.ts +14 -14
- package/dist/spend.js +31 -31
- 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-baseline.d.ts +19 -0
- package/dist/store-baseline.js +2873 -0
- package/dist/store.d.ts +197 -249
- package/dist/store.js +747 -3084
- package/dist/subagent-admin.d.ts +53 -0
- package/dist/{teammate-admin.js → subagent-admin.js} +37 -37
- package/dist/{teammate-desk.d.ts → subagent-desk.d.ts} +22 -23
- package/dist/{teammate-desk.js → subagent-desk.js} +26 -35
- package/dist/{teammate-memory.d.ts → subagent-memory.d.ts} +17 -17
- package/dist/{teammate-memory.js → subagent-memory.js} +41 -41
- package/dist/{teammate-question.d.ts → subagent-question.d.ts} +6 -6
- package/dist/{teammate-question.js → subagent-question.js} +17 -17
- package/dist/{teammate-tools.d.ts → subagent-tools.d.ts} +18 -18
- package/dist/{teammate-tools.js → subagent-tools.js} +36 -36
- package/dist/{teammate-week.d.ts → subagent-week.d.ts} +10 -10
- package/dist/{teammate-week.js → subagent-week.js} +23 -23
- package/dist/{teammate-work.d.ts → subagent-work.d.ts} +21 -21
- package/dist/{teammate-work.js → subagent-work.js} +50 -50
- package/dist/subagents-ui.d.ts +22 -0
- package/dist/subagents-ui.js +137 -0
- package/dist/{teammates.d.ts → subagents.d.ts} +11 -11
- package/dist/{teammates.js → subagents.js} +21 -21
- package/dist/subscription-chat.d.ts +13 -13
- package/dist/subscription-chat.js +19 -19
- package/dist/surface.d.ts +19 -2
- package/dist/surface.js +20 -33
- 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-cli.d.ts +1 -0
- package/dist/team-cli.js +14 -2
- package/dist/team-http.d.ts +5 -9
- package/dist/team-http.js +5 -63
- package/dist/team-leads.js +13 -13
- package/dist/team-runtime.d.ts +2 -2
- package/dist/team-runtime.js +8 -8
- package/dist/team-ui.d.ts +2 -1
- package/dist/team-ui.js +2 -5
- package/dist/teams-chat.js +6 -6
- package/dist/teams-settings.d.ts +3 -2
- package/dist/teams-settings.js +20 -31
- package/dist/telegram-flow.d.ts +3 -3
- package/dist/telegram-flow.js +3 -3
- package/dist/{telegram-mate.d.ts → telegram-lead.d.ts} +5 -5
- package/dist/{telegram-mate.js → telegram-lead.js} +27 -27
- package/dist/telegram-settings.d.ts +3 -2
- package/dist/telegram-settings.js +14 -25
- package/dist/telegram-status.js +1 -1
- package/dist/telegram-team.d.ts +1 -1
- package/dist/telegram-team.js +5 -5
- package/dist/telegram.d.ts +1 -1
- package/dist/telegram.js +9 -30
- package/dist/text-limits.d.ts +18 -18
- package/dist/text-limits.js +18 -18
- package/dist/toolroll-update-ui.d.ts +3 -2
- package/dist/toolroll-update-ui.js +35 -39
- package/dist/toolroll-update.d.ts +8 -0
- package/dist/toolroll-update.js +45 -8
- 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-ui.d.ts +4 -2
- package/dist/workspace-ui.js +10 -10
- package/package.json +1 -2
- package/dist/server/shared.d.ts +0 -3151
- package/dist/server/shared.js +0 -12001
- package/dist/teammate-admin.d.ts +0 -53
- package/dist/teammates-ui.d.ts +0 -20
- package/dist/teammates-ui.js +0 -166
package/dist/html.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server HTML, escaped by construction.
|
|
3
|
+
*
|
|
4
|
+
* - `Html` is markup this module made. Nothing else can make one: the class
|
|
5
|
+
* is private and branded, so a string never passes for markup.
|
|
6
|
+
* - `html` is the only way to write markup. Its literal text is the
|
|
7
|
+
* author's; every interpolated value is text and is escaped, unless it is
|
|
8
|
+
* already `Html`. Arrays render in order; null, undefined and booleans
|
|
9
|
+
* render nothing, so `${cond && html`…`}` reads naturally.
|
|
10
|
+
* - `postForm` is the only way to write a POST form. It adds the request's
|
|
11
|
+
* CSRF field itself, so a form cannot forget it.
|
|
12
|
+
* - Page sinks (the shell, `respond` for text/html, the workspace's page
|
|
13
|
+
* snapshot) take `Html` and turn it into bytes with `htmlString`.
|
|
14
|
+
*
|
|
15
|
+
* The script, style and JSON constructors exist for the page shell: their
|
|
16
|
+
* sources are code, and each refuses content that could close its element.
|
|
17
|
+
*
|
|
18
|
+
* This module has no server dependencies (the browser bundle shares time
|
|
19
|
+
* helpers that use it); the server's request context supplies the CSRF token.
|
|
20
|
+
*/
|
|
21
|
+
const MADE_HERE = Symbol("html");
|
|
22
|
+
/** Markup this module made. Only this module holds the token its constructor asks for. */
|
|
23
|
+
export class Html {
|
|
24
|
+
#markup;
|
|
25
|
+
constructor(token, markup) {
|
|
26
|
+
if (token !== MADE_HERE)
|
|
27
|
+
throw new TypeError("Html is made by html`…`");
|
|
28
|
+
this.#markup = markup;
|
|
29
|
+
}
|
|
30
|
+
/** The bytes, for a sink. */
|
|
31
|
+
static markupOf(fragment) { return fragment.#markup; }
|
|
32
|
+
// Coercing markup to a string (`${fragment}` in a plain template, `+`, join) would lose the brand and
|
|
33
|
+
// be escaped again downstream. Under test that is an error; a running server keeps serving the markup.
|
|
34
|
+
toString() {
|
|
35
|
+
if (STRICT)
|
|
36
|
+
throw new Error("Html coerced to a string: interpolate it with html`…` or hand it to a sink");
|
|
37
|
+
return this.#markup;
|
|
38
|
+
}
|
|
39
|
+
toJSON() {
|
|
40
|
+
if (STRICT)
|
|
41
|
+
throw new Error("Html serialized as JSON: convert it with htmlString() first");
|
|
42
|
+
return this.#markup;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Under test, a coercion is an error to fix; a running server keeps serving the markup.
|
|
46
|
+
const STRICT = typeof process !== "undefined" && process.env["VITEST"] !== undefined;
|
|
47
|
+
const make = (markup) => new Html(MADE_HERE, markup);
|
|
48
|
+
export const isHtml = (value) => value instanceof Html;
|
|
49
|
+
/** The markup of an `Html` value, for a sink that writes bytes. */
|
|
50
|
+
export function htmlString(fragment) {
|
|
51
|
+
if (!(fragment instanceof Html))
|
|
52
|
+
throw new TypeError("htmlString needs Html");
|
|
53
|
+
return Html.markupOf(fragment);
|
|
54
|
+
}
|
|
55
|
+
const ENTITIES = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
|
|
56
|
+
const escapeText = (text) => text.replace(/[&<>"']/g, char => ENTITIES[char]);
|
|
57
|
+
function render(value) {
|
|
58
|
+
if (value === null || value === undefined || typeof value === "boolean")
|
|
59
|
+
return "";
|
|
60
|
+
if (value instanceof Html)
|
|
61
|
+
return Html.markupOf(value);
|
|
62
|
+
if (Array.isArray(value))
|
|
63
|
+
return value.map(render).join("");
|
|
64
|
+
if (typeof value === "string")
|
|
65
|
+
return escapeText(value);
|
|
66
|
+
if (typeof value === "number" || typeof value === "bigint")
|
|
67
|
+
return String(value);
|
|
68
|
+
throw new TypeError(`html: cannot interpolate ${typeof value}`);
|
|
69
|
+
}
|
|
70
|
+
// A template's literal text is checked once per call site: POST forms and CSRF fields come only from postForm.
|
|
71
|
+
const checked = new WeakSet();
|
|
72
|
+
const POST_FORM = /<form\b[^>]*\bmethod\s*=\s*["']?post\b/i;
|
|
73
|
+
const CSRF_FIELD = /\bname\s*=\s*["']?csrf\b/i;
|
|
74
|
+
function checkLiteral(strings) {
|
|
75
|
+
if (checked.has(strings))
|
|
76
|
+
return;
|
|
77
|
+
const text = strings.join("\u0000");
|
|
78
|
+
if (POST_FORM.test(text))
|
|
79
|
+
throw new Error("html: write POST forms with postForm, which adds the CSRF field");
|
|
80
|
+
if (CSRF_FIELD.test(text))
|
|
81
|
+
throw new Error("html: postForm adds the CSRF field; don't write one");
|
|
82
|
+
checked.add(strings);
|
|
83
|
+
}
|
|
84
|
+
/** Markup with every interpolation escaped as text, except values that are already `Html`. */
|
|
85
|
+
export function html(strings, ...values) {
|
|
86
|
+
checkLiteral(strings);
|
|
87
|
+
let out = strings[0];
|
|
88
|
+
for (let index = 0; index < values.length; index++)
|
|
89
|
+
out += render(values[index]) + strings[index + 1];
|
|
90
|
+
return make(out);
|
|
91
|
+
}
|
|
92
|
+
/** Parts in order, with an optional separator between them. */
|
|
93
|
+
export function joinHtml(parts, separator = "") {
|
|
94
|
+
const between = render(separator);
|
|
95
|
+
return make([...parts].map(render).join(between));
|
|
96
|
+
}
|
|
97
|
+
/** Plain text, escaped (for a sink that takes Html when the content is only words). */
|
|
98
|
+
export const textHtml = (text) => make(escapeText(text));
|
|
99
|
+
/** Attributes as `name="value"` pairs: true is a bare attribute; false, null and undefined are left out. */
|
|
100
|
+
export function attributes(values) {
|
|
101
|
+
let out = "";
|
|
102
|
+
for (const [name, value] of Object.entries(values)) {
|
|
103
|
+
if (!/^[a-z][a-z0-9-]*$/.test(name))
|
|
104
|
+
throw new Error(`attributes: bad name ${name}`);
|
|
105
|
+
if (value === false || value === null || value === undefined)
|
|
106
|
+
continue;
|
|
107
|
+
out += value === true ? ` ${name}` : ` ${name}="${escapeText(String(value))}"`;
|
|
108
|
+
}
|
|
109
|
+
return make(out);
|
|
110
|
+
}
|
|
111
|
+
let formToken = () => undefined;
|
|
112
|
+
/** The server's request context supplies the request's CSRF token (server/request-context.ts). */
|
|
113
|
+
export function provideFormToken(source) { formToken = source; }
|
|
114
|
+
/**
|
|
115
|
+
* A POST form: the action, the request's one CSRF field, the optional
|
|
116
|
+
* project revision and return fields, other hidden fields, then the body.
|
|
117
|
+
* Outside a console request there is no CSRF to give, so it refuses.
|
|
118
|
+
*/
|
|
119
|
+
export function postForm(action, body, options = {}) {
|
|
120
|
+
let csrf = null;
|
|
121
|
+
if (options.signedOut !== true) {
|
|
122
|
+
const token = formToken();
|
|
123
|
+
if (token === undefined)
|
|
124
|
+
throw new Error(`postForm(${action}) outside a console request`);
|
|
125
|
+
csrf = token;
|
|
126
|
+
}
|
|
127
|
+
const hidden = [];
|
|
128
|
+
if (csrf !== null)
|
|
129
|
+
hidden.push(["csrf", csrf]);
|
|
130
|
+
if (options.projectRevision !== undefined && options.projectRevision !== null)
|
|
131
|
+
hidden.push(["projectRevision", options.projectRevision]);
|
|
132
|
+
if (options.returnTo !== undefined && options.returnTo !== null)
|
|
133
|
+
hidden.push(["return", options.returnTo]);
|
|
134
|
+
for (const [name, value] of Object.entries(options.hidden ?? {})) {
|
|
135
|
+
if (name === "csrf")
|
|
136
|
+
throw new Error("postForm adds the CSRF field itself");
|
|
137
|
+
if (value !== null && value !== undefined)
|
|
138
|
+
hidden.push([name, value]);
|
|
139
|
+
}
|
|
140
|
+
const fields = hidden.map(([name, value]) => `<input type="hidden" name="${escapeText(name)}" value="${escapeText(String(value))}">`).join("");
|
|
141
|
+
return make(`<form method="post" action="${escapeText(action)}"${htmlString(attributes(options.attrs ?? {}))}>${fields}${render(body)}</form>`);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Markup edited after rendering: each match of `pattern` in the page is replaced by markup the replacer builds
|
|
145
|
+
* (with html`…`), or kept as it was (null), so nothing unescaped can enter. For whole-page passes (a sign-in that
|
|
146
|
+
* stands in for passwords, the browser shell's insertions).
|
|
147
|
+
*/
|
|
148
|
+
export function replaceMarkup(page, pattern, replace) {
|
|
149
|
+
const groups = new RegExp(`${pattern.source}|`).exec("").length - 1;
|
|
150
|
+
return make(htmlString(page).replace(pattern, (match, ...rest) => {
|
|
151
|
+
const replaced = replace(match, ...rest.slice(0, groups).map(one => typeof one === "string" ? one : ""));
|
|
152
|
+
return replaced === null ? match : htmlString(replaced);
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
// ---- the page shell's own elements ---------------------------------------------------------------------------
|
|
156
|
+
/** An inline script whose source is code. Its text may not close the element or open a comment. */
|
|
157
|
+
export function scriptElement(source, attrs = {}) {
|
|
158
|
+
if (/<\/script|<!--/i.test(source))
|
|
159
|
+
throw new Error("scriptElement: the source would end its element");
|
|
160
|
+
return make(`<script${htmlString(attributes(attrs))}>${source}</script>`);
|
|
161
|
+
}
|
|
162
|
+
/** A style element whose rules are code. */
|
|
163
|
+
export function styleElement(css, attrs = {}) {
|
|
164
|
+
if (/<\/style/i.test(css))
|
|
165
|
+
throw new Error("styleElement: the rules would end their element");
|
|
166
|
+
return make(`<style${htmlString(attributes(attrs))}>${css}</style>`);
|
|
167
|
+
}
|
|
168
|
+
/** Inert JSON for a page script to read: no character of it can end the element. */
|
|
169
|
+
export function jsonScript(value, attrs = {}) {
|
|
170
|
+
// An escaped "<" can neither close the element nor open a comment; the line separators keep it valid script.
|
|
171
|
+
const json = JSON.stringify(value).replace(/[<\u2028\u2029]/g, char => `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
172
|
+
return make(`<script type="application/json"${htmlString(attributes(attrs))}>${json}</script>`);
|
|
173
|
+
}
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
* failed, what uses it, and one action. The list comes from saved checks; a
|
|
5
5
|
* render never waits on one (see integrations.ts).
|
|
6
6
|
*/
|
|
7
|
+
import { type Html } from "./html.js";
|
|
7
8
|
import { type Integration } from "./integrations.js";
|
|
8
9
|
export declare const INTEGRATIONS_CSS: string;
|
|
9
10
|
export declare function integrationsHtml(list: readonly Integration[], csrf: string, notice: {
|
|
10
11
|
said?: string | null;
|
|
11
12
|
problem?: string | null;
|
|
12
13
|
checking?: boolean;
|
|
13
|
-
}):
|
|
14
|
+
}): Html;
|
package/dist/integrations-ui.js
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* failed, what uses it, and one action. The list comes from saved checks; a
|
|
5
5
|
* render never waits on one (see integrations.ts).
|
|
6
6
|
*/
|
|
7
|
+
import { html, joinHtml, postForm } from "./html.js";
|
|
7
8
|
import { STATE_WORDS } from "./integrations.js";
|
|
8
9
|
import { whenUtc } from "./when-html.js";
|
|
9
10
|
import { brandMarkHtml } from "./brand-mark.js";
|
|
10
|
-
const e = (value) => String(value ?? "").replace(/[&<>"']/g, c => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
11
11
|
const when = (at) => whenUtc(at);
|
|
12
12
|
export const INTEGRATIONS_CSS = `.integrations{max-width:760px;min-width:0}.integrations h2{margin:24px 0 6px;font-size:.9375rem}` +
|
|
13
13
|
`.integrations .integration{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:4px 16px;align-items:start;padding:12px 0;border-top:1px solid var(--so-line);min-width:0}` +
|
|
@@ -31,13 +31,12 @@ const GROUPS = [
|
|
|
31
31
|
function action(one, csrf) {
|
|
32
32
|
const a = one.action;
|
|
33
33
|
if (a.kind === "test") {
|
|
34
|
-
return csrf === "" ? "" :
|
|
35
|
-
`<input type="hidden" name="key" value="${e(one.key)}"><button type="submit">Send test</button></form>`;
|
|
34
|
+
return csrf === "" ? "" : postForm("/settings/integrations/test", html `<button type="submit">Send test</button>`, { attrs: { class: "integration-action" }, hidden: { key: one.key } });
|
|
36
35
|
}
|
|
37
36
|
// A command is run where Toolroll runs; a page is linked. Fix and Set up both say which.
|
|
38
37
|
// Fix is the one filled action on the page; Set up stays quiet so a list of unused services doesn't shout.
|
|
39
38
|
if (a.href !== null)
|
|
40
|
-
return `<p class="integration-action"><a class="button-link${a.kind === "setup" ? " integration-quiet" : ""}" href="${
|
|
39
|
+
return html `<p class="integration-action"><a class="button-link${a.kind === "setup" ? " integration-quiet" : ""}" href="${a.href}">${a.label}</a></p>`;
|
|
41
40
|
return "";
|
|
42
41
|
}
|
|
43
42
|
/** Which mark a row shows: email is Gmail's only when the account is a Gmail address; a custom tool is a letter. */
|
|
@@ -48,30 +47,27 @@ function markFor(one) {
|
|
|
48
47
|
}
|
|
49
48
|
function row(one, csrf) {
|
|
50
49
|
const badge = !one.checked && one.state === "connected"
|
|
51
|
-
? `<span class="integration-state integration-state--checking"><i aria-hidden="true"></i>Checking</span>`
|
|
52
|
-
: `<span class="integration-state integration-state--${one.state}"><i aria-hidden="true"></i>${STATE_WORDS[one.state]}</span>`;
|
|
53
|
-
const facts = [one.account, one.detail].filter(Boolean).
|
|
50
|
+
? html `<span class="integration-state integration-state--checking"><i aria-hidden="true"></i>Checking</span>`
|
|
51
|
+
: html `<span class="integration-state integration-state--${one.state}"><i aria-hidden="true"></i>${STATE_WORDS[one.state]}</span>`;
|
|
52
|
+
const facts = [one.account, one.detail].filter(Boolean).join(" · ");
|
|
54
53
|
const command = one.action.kind !== "test" ? one.action.command : null;
|
|
55
|
-
const fix = one.action.kind === "fix" ? `<p class="integration-fix" role="status">${
|
|
56
|
-
const run = command === null || (one.action.kind === "fix" && one.action.words.includes(command)) ? "" : `<p class="meta">Run <code>${
|
|
57
|
-
const seen = [one.lastSuccessAt === null ? null : `Last success ${when(one.lastSuccessAt)}`, one.usedBy.length === 0 ? null : `Used by ${
|
|
58
|
-
const history = one.state === "not-set-up" || seen.length === 0 ? "" : `<p class="meta">${seen
|
|
54
|
+
const fix = one.action.kind === "fix" ? html `<p class="integration-fix" role="status">${one.action.words}</p>` : "";
|
|
55
|
+
const run = command === null || (one.action.kind === "fix" && one.action.words.includes(command)) ? "" : html `<p class="meta">Run <code>${command}</code> on the computer running Toolroll.</p>`;
|
|
56
|
+
const seen = [one.lastSuccessAt === null ? null : html `Last success ${when(one.lastSuccessAt)}`, one.usedBy.length === 0 ? null : html `Used by ${one.usedBy.join(", ")}`].filter(Boolean);
|
|
57
|
+
const history = one.state === "not-set-up" || seen.length === 0 ? "" : html `<p class="meta">${joinHtml(seen, " · ")}</p>`;
|
|
59
58
|
const lastError = one.lastError === null || one.state === "not-set-up" ? "" : one.state === "broken" && one.action.kind === "fix" && one.action.words === one.lastError
|
|
60
|
-
? `<p class="meta">Failed ${when(one.lastErrorAt ?? "")}</p>`
|
|
61
|
-
: `<details><summary>Last error ${one.lastErrorAt === null ? "" : when(one.lastErrorAt)}</summary><p class="meta">${
|
|
62
|
-
return `<div class="integration" data-integration="${
|
|
63
|
-
`<p class="integration-head">${brandMarkHtml(markFor(one), one.name, one.state === "connected")}<strong>${e(one.name)}</strong> ${badge}</p>` +
|
|
64
|
-
action(one, csrf) +
|
|
65
|
-
`<div class="integration-body">${facts === "" ? "" : `<p class="meta">${facts}</p>`}${fix}${run}${history}${lastError}</div></div>`;
|
|
59
|
+
? html `<p class="meta">Failed ${when(one.lastErrorAt ?? "")}</p>`
|
|
60
|
+
: html `<details><summary>Last error ${one.lastErrorAt === null ? "" : when(one.lastErrorAt)}</summary><p class="meta">${one.lastError}</p></details>`;
|
|
61
|
+
return html `<div class="integration" data-integration="${one.key}" data-state="${one.state}"><p class="integration-head">${brandMarkHtml(markFor(one), one.name, one.state === "connected")}<strong>${one.name}</strong> ${badge}</p>${action(one, csrf)}<div class="integration-body">${facts === "" ? "" : html `<p class="meta">${facts}</p>`}${fix}${run}${history}${lastError}</div></div>`;
|
|
66
62
|
}
|
|
67
63
|
export function integrationsHtml(list, csrf, notice) {
|
|
68
|
-
const note = notice.problem ? `<p class="problem" role="alert">${
|
|
64
|
+
const note = notice.problem ? html `<p class="problem" role="alert">${notice.problem}</p>` : notice.said ? html `<p role="status">${notice.said}</p>` : "";
|
|
69
65
|
const broken = list.filter(one => one.state === "broken").length;
|
|
70
|
-
const summary = broken > 0 ? `<p><strong>${broken} need${broken === 1 ? "s" : ""} fixing.</strong></p>`
|
|
71
|
-
: notice.checking || list.some(one => !one.checked) ? `<p class="meta" role="status">Checking in the background. Refresh in a few seconds.</p>` : "";
|
|
66
|
+
const summary = broken > 0 ? html `<p><strong>${broken} need${broken === 1 ? "s" : ""} fixing.</strong></p>`
|
|
67
|
+
: notice.checking || list.some(one => !one.checked) ? html `<p class="meta" role="status">Checking in the background. Refresh in a few seconds.</p>` : "";
|
|
72
68
|
const sections = GROUPS.map(([group, title]) => {
|
|
73
69
|
const rows = list.filter(one => one.group === group);
|
|
74
|
-
return rows.length === 0 ? "" : `<h2>${title}</h2>${rows.map(one => row(one, csrf))
|
|
75
|
-
})
|
|
76
|
-
return `<section class="integrations">${note}${summary}${sections}</section>`;
|
|
70
|
+
return rows.length === 0 ? "" : html `<h2>${title}</h2>${rows.map(one => row(one, csrf))}`;
|
|
71
|
+
});
|
|
72
|
+
return html `<section class="integrations">${note}${summary}${sections}</section>`;
|
|
77
73
|
}
|
package/dist/invoke.js
CHANGED
|
@@ -353,7 +353,7 @@ export async function invokeAgent(store, runId, spec, invocation, options) {
|
|
|
353
353
|
const evidence = spec.provider === "claude" ? claudeBillingFrom(billingSeen)
|
|
354
354
|
// Codex signed in with ChatGPT is its plan, unless its plan is itself billed by use (seen by the console's probe).
|
|
355
355
|
: codexLogin === "subscription" ? seenBilling(store.handle, "codex") ?? "subscription" : codexLogin;
|
|
356
|
-
// What this computer's Claude does with no key from us (
|
|
356
|
+
// What this computer's Claude does with no key from us (subagents and drafts bill the same way): learnt only from a
|
|
357
357
|
// run we gave no key that got as far as an answer.
|
|
358
358
|
if (spec.provider === "claude" && managedKey === null && authMode === "subscription" && billingSeen.answered && evidence !== null) {
|
|
359
359
|
store.recordProviderBilling("claude", evidence, new Date());
|
package/dist/kits-ui.d.ts
CHANGED
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { Store } from "./store.js";
|
|
7
7
|
import { type Kit } from "./kits.js";
|
|
8
|
+
import { type Html } from "./html.js";
|
|
8
9
|
export declare const KITS_CSS: string;
|
|
9
10
|
/** The gallery: every kit, set up in a project with one button (or opened, when it's already there). */
|
|
10
|
-
export declare function kitsGalleryHtml(store: Store, projects: readonly string[], projectName: (repo: string) => string,
|
|
11
|
+
export declare function kitsGalleryHtml(store: Store, projects: readonly string[], projectName: (repo: string) => string, canSetUp: boolean, notice: {
|
|
11
12
|
problem?: string | null;
|
|
12
|
-
}):
|
|
13
|
+
}): Html;
|
|
13
14
|
/** A kit's own page: what's set up, and the next step on each line. */
|
|
14
|
-
export declare function kitPageHtml(store: Store, kit: Kit, repo: string, dir: string | null,
|
|
15
|
+
export declare function kitPageHtml(store: Store, kit: Kit, repo: string, dir: string | null, canSetUp: boolean, notice: {
|
|
15
16
|
said?: string | null;
|
|
16
17
|
problem?: string | null;
|
|
17
|
-
}):
|
|
18
|
+
}): Html;
|
package/dist/kits-ui.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { kitChecklist, kitInstalled, KITS } from "./kits.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const RING = `<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8"/></svg>`;
|
|
2
|
+
import { SUBAGENT_TEMPLATES } from "./subagents.js";
|
|
3
|
+
import { html, postForm } from "./html.js";
|
|
4
|
+
const CHECK = html `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>`;
|
|
5
|
+
const RING = html `<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8"/></svg>`;
|
|
7
6
|
export const KITS_CSS = `.kits{max-width:960px;min-width:0}.kits-lede{color:var(--so-muted);margin:.25rem 0 1.5rem;max-width:40rem}.kit-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px}` +
|
|
8
7
|
`.kit{display:flex;flex-direction:column;gap:10px;padding:20px;border:1px solid var(--so-line);border-radius:12px;background:var(--so-paper)}.kit h2{font-size:1.1rem;margin:0}.kit p{margin:0}.kit .kit-promise{line-height:1.5}` +
|
|
9
8
|
`.kit .kit-gets{font-size:.85rem;color:var(--so-muted);line-height:1.5}.kit form{display:flex;flex-wrap:wrap;gap:8px;align-items:flex-end;margin-top:auto;padding-top:6px}.kit form label{display:grid;gap:4px;font-size:.85rem;flex:1 1 140px}` +
|
|
@@ -13,34 +12,33 @@ export const KITS_CSS = `.kits{max-width:960px;min-width:0}.kits-lede{color:var(
|
|
|
13
12
|
`.kit-steps .kit-step-said{flex:1;min-width:0}.kit-steps li[data-done="true"] .kit-step-said{color:var(--so-muted)}.kit-steps form{margin:0}.kit-steps button,.kit-steps .kit-step-go{min-height:40px;white-space:nowrap}` +
|
|
14
13
|
`.kit-steps .kit-step-go{display:inline-flex;align-items:center;padding:0 12px;border:1px solid var(--so-line);border-radius:8px;text-decoration:none;font-size:.9rem}` +
|
|
15
14
|
`@media(max-width:600px){.kit-steps li{flex-wrap:wrap}.kit-steps .kit-step-said{flex-basis:calc(100% - 40px)}.kit form select{font-size:16px}}`;
|
|
16
|
-
const
|
|
15
|
+
const subagentWords = (kit) => { const template = SUBAGENT_TEMPLATES.find(one => one.id === kit.subagent.template); return template === undefined ? kit.subagent.handle : template.label.toLowerCase(); };
|
|
17
16
|
/** The gallery: every kit, set up in a project with one button (or opened, when it's already there). */
|
|
18
|
-
export function kitsGalleryHtml(store, projects, projectName,
|
|
17
|
+
export function kitsGalleryHtml(store, projects, projectName, canSetUp, notice) {
|
|
19
18
|
const cards = KITS.map(kit => {
|
|
20
19
|
const here = projects.filter(repo => kitInstalled(store, kit, repo) !== null);
|
|
21
20
|
const open = projects.length === 1 && here.length === 1;
|
|
22
|
-
const name = kit.
|
|
23
|
-
const gets = `${name} (${
|
|
24
|
-
const action = open ? `<a class="kit-open" href="/kits/${kit.id}?repo=${encodeURIComponent(projects[0])}">Open ${
|
|
21
|
+
const name = kit.subagent.handle.charAt(0).toUpperCase() + kit.subagent.handle.slice(1);
|
|
22
|
+
const gets = `${name} (${subagentWords(kit)}) · the ${kit.flowName} flow${kit.tools.length === 0 ? "" : ` · works with ${kit.tools.map(one => one.label).join(" and ")}`}`;
|
|
23
|
+
const action = open ? html `<a class="kit-open" href="/kits/${kit.id}?repo=${encodeURIComponent(projects[0])}">Open ${kit.name} →</a>`
|
|
25
24
|
: !canSetUp || projects.length === 0 ? ""
|
|
26
|
-
:
|
|
27
|
-
: `<label>Project<select name="repo">${projects.map(repo => `<option value="${
|
|
28
|
-
return `<article class="kit" data-kit="${kit.id}"><h2>${
|
|
29
|
-
})
|
|
30
|
-
return `<section class="kits">${notice.problem ? `<p class="problem" role="alert">${
|
|
25
|
+
: postForm(`/kits/${kit.id}/setup`, html `${projects.length === 1 ? ""
|
|
26
|
+
: html `<label>Project<select name="repo">${projects.map(repo => html `<option value="${repo}">${projectName(repo)}${here.includes(repo) ? " (set up)" : ""}</option>`)}</select></label>`}<button>Set it up</button>`, { hidden: { repo: projects.length === 1 ? projects[0] : null } });
|
|
27
|
+
return html `<article class="kit" data-kit="${kit.id}"><h2>${kit.name}</h2><p class="kit-promise">${kit.promise}</p><p class="kit-gets">${gets}</p>${action}</article>`;
|
|
28
|
+
});
|
|
29
|
+
return html `<section class="kits">${notice.problem ? html `<p class="problem" role="alert">${notice.problem}</p>` : ""}<p class="kits-lede">A working setup in one click: a subagent, the flow it works, and what to connect next. Nothing goes out without you approving it.</p><div class="kit-grid">${cards}</div></section>`;
|
|
31
30
|
}
|
|
32
31
|
/** A kit's own page: what's set up, and the next step on each line. */
|
|
33
|
-
export function kitPageHtml(store, kit, repo, dir,
|
|
32
|
+
export function kitPageHtml(store, kit, repo, dir, canSetUp, notice) {
|
|
34
33
|
const steps = kitChecklist(store, kit, repo, dir);
|
|
35
34
|
const set = kitInstalled(store, kit, repo);
|
|
36
35
|
const rows = steps.map(step => {
|
|
37
|
-
const go = step.done ? (step.href === null ? "" : `<a class="kit-step-go" href="${
|
|
36
|
+
const go = step.done ? (step.href === null ? "" : html `<a class="kit-step-go" href="${step.href}">Open</a>`)
|
|
38
37
|
: step.action === "sample" || step.action === "github"
|
|
39
|
-
? (canSetUp ?
|
|
40
|
-
: step.href === null ? "" : `<a class="kit-step-go" href="${
|
|
41
|
-
return `<li data-step="${
|
|
42
|
-
})
|
|
43
|
-
const setUp = set === null && canSetUp ?
|
|
44
|
-
return `<section class="kits">${notice.problem ? `<p class="problem" role="alert">${
|
|
45
|
-
`<p class="kits-lede">${e(kit.promise)}</p>${setUp}${rows === "" ? "" : `<ol class="kit-steps">${rows}</ol>`}<p class="meta"><a href="/kits">All starter kits</a></p></section>`;
|
|
38
|
+
? (canSetUp ? postForm(`/kits/${kit.id}/${step.action}`, html `<button${step.action === "sample" ? "" : html ` class="secondary"`}>${step.action === "sample" ? "Try it" : "Bring them in"}</button>`, { hidden: { repo } }) : "")
|
|
39
|
+
: step.href === null ? "" : html `<a class="kit-step-go" href="${step.href}">${step.action === "connect" ? "Connect" : "Set up"}</a>`;
|
|
40
|
+
return html `<li data-step="${step.id}" data-done="${String(step.done)}">${step.done ? CHECK : RING}<span class="kit-step-said">${step.said}</span>${go}</li>`;
|
|
41
|
+
});
|
|
42
|
+
const setUp = set === null && canSetUp ? postForm(`/kits/${kit.id}/setup`, html `<button>Set up ${kit.name}</button>`, { hidden: { repo } }) : "";
|
|
43
|
+
return html `<section class="kits">${notice.problem ? html `<p class="problem" role="alert">${notice.problem}</p>` : ""}${notice.said ? html `<p class="said" role="status">${notice.said}</p>` : ""}<p class="kits-lede">${kit.promise}</p>${setUp}${rows.length === 0 ? "" : html `<ol class="kit-steps">${rows}</ol>`}<p class="meta"><a href="/kits">All starter kits</a></p></section>`;
|
|
46
44
|
}
|
package/dist/kits.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type FlowStepInput } from "./flows.js";
|
|
2
|
-
import type { FlowRow, Store,
|
|
3
|
-
import { type ToolIo } from "./
|
|
4
|
-
/** A tool a kit's
|
|
2
|
+
import type { FlowRow, Store, SubagentRow } from "./store.js";
|
|
3
|
+
import { type ToolIo } from "./subagent-tools.js";
|
|
4
|
+
/** A tool a kit's subagent works better with: the catalog entry that connects it, and why. */
|
|
5
5
|
export type KitTool = {
|
|
6
6
|
tool: string;
|
|
7
7
|
label: string;
|
|
@@ -11,8 +11,8 @@ export type Kit = {
|
|
|
11
11
|
id: string;
|
|
12
12
|
name: string;
|
|
13
13
|
promise: string;
|
|
14
|
-
/** The
|
|
15
|
-
|
|
14
|
+
/** The subagent it hires: a template, and the name it starts with. */
|
|
15
|
+
subagent: {
|
|
16
16
|
template: string;
|
|
17
17
|
handle: string;
|
|
18
18
|
};
|
|
@@ -38,23 +38,23 @@ type Done = {
|
|
|
38
38
|
ok: true;
|
|
39
39
|
said: string;
|
|
40
40
|
flow: number;
|
|
41
|
-
|
|
41
|
+
subagent: number;
|
|
42
42
|
} | {
|
|
43
43
|
ok: false;
|
|
44
44
|
said: string;
|
|
45
45
|
};
|
|
46
|
-
/** A kit set up in a project: its flow (by name) and its
|
|
46
|
+
/** A kit set up in a project: its flow (by name) and its subagent, when both are there. */
|
|
47
47
|
export declare function kitInstalled(store: Store, kit: Kit, repo: string): {
|
|
48
48
|
flow: FlowRow;
|
|
49
|
-
mate:
|
|
49
|
+
mate: SubagentRow;
|
|
50
50
|
} | null;
|
|
51
51
|
/**
|
|
52
|
-
* Set a kit up: its
|
|
53
|
-
* flow, and its button triggers; then its
|
|
52
|
+
* Set a kit up: its subagent (kept if one of that name is already there), its
|
|
53
|
+
* flow, and its button triggers; then its subagent may use whichever of the
|
|
54
54
|
* kit's tools the project already has (reading free, writing asking first).
|
|
55
55
|
*/
|
|
56
56
|
export declare function setUpKit(store: Store, kit: Kit, repo: string, by: string, now: Date, dir: string | null, io?: ToolIo): Promise<Done>;
|
|
57
|
-
/** The same, all at once (a chat confirmation can't wait on a tool): its
|
|
57
|
+
/** The same, all at once (a chat confirmation can't wait on a tool): its subagent is given the kit's connected tools by the names their last test found, and each is listed properly on its first turn. */
|
|
58
58
|
export declare function setUpKitNow(store: Store, kit: Kit, repo: string, by: string, now: Date, dir: string | null, grantByName?: boolean): Done;
|
|
59
59
|
/** One line of a kit's checklist: done, or the next step with where to take it. */
|
|
60
60
|
export type KitStep = {
|
|
@@ -67,7 +67,7 @@ export type KitStep = {
|
|
|
67
67
|
};
|
|
68
68
|
/** What's set up and what's left, read fresh each time. */
|
|
69
69
|
export declare function kitChecklist(store: Store, kit: Kit, repo: string, dir: string | null): KitStep[];
|
|
70
|
-
/** A sample card in the kit's first zone, so its
|
|
70
|
+
/** A sample card in the kit's first zone, so its subagent starts on it at once. */
|
|
71
71
|
export declare function addKitSample(store: Store, kit: Kit, repo: string, by: string, now: Date): {
|
|
72
72
|
ok: true;
|
|
73
73
|
href: string;
|
package/dist/kits.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Starter kits: a working setup in one click —
|
|
2
|
+
* Starter kits: a working setup in one click — a subagent, the flow
|
|
3
3
|
* it works, and the triggers that are safe to add straight away — plus a
|
|
4
4
|
* checklist of what's left (connect email, connect a tool) and a sample card
|
|
5
|
-
* that shows the
|
|
5
|
+
* that shows the subagent at work within seconds.
|
|
6
6
|
*
|
|
7
7
|
* A kit adds nothing a person couldn't add by hand, and nothing that acts
|
|
8
8
|
* outside: replies go out only after you approve them, a code change is filed
|
|
@@ -16,17 +16,17 @@ import { googleConnected } from "./google-mail.js";
|
|
|
16
16
|
import { readEmailSettings } from "./email-settings.js";
|
|
17
17
|
import { mailboxReady } from "./mailbox.js";
|
|
18
18
|
import { projectToolsOf } from "./project-tools.js";
|
|
19
|
-
import {
|
|
20
|
-
import { grantListed, grantTool } from "./
|
|
21
|
-
import {
|
|
19
|
+
import { createSubagentFrom, nameOf } from "./subagent-admin.js";
|
|
20
|
+
import { grantListed, grantTool } from "./subagent-tools.js";
|
|
21
|
+
import { SUBAGENT_TEMPLATES } from "./subagents.js";
|
|
22
22
|
export const KITS = [
|
|
23
23
|
{
|
|
24
24
|
id: "support-desk", name: "Support desk",
|
|
25
25
|
promise: "Maya answers customer emails, looks orders up and refunds within limits you set. You approve each reply before it goes out.",
|
|
26
|
-
|
|
26
|
+
subagent: { template: "support", handle: "maya" },
|
|
27
27
|
flowName: "Support desk",
|
|
28
28
|
steps: [
|
|
29
|
-
{ id: "answer", title: "Maya answers", kind: "
|
|
29
|
+
{ id: "answer", title: "Maya answers", kind: "subagent", subagent: "maya",
|
|
30
30
|
instructions: "Read the customer's message. Look things up (and refund, within your rules) with your tools when it helps. Write the reply we'd send in \"text\". Pick Reply when it's ready for the customer, or Needs a person when your rules say to ask first or you can't help.",
|
|
31
31
|
routes: [{ answer: "Reply", goesTo: "You check the reply" }, { answer: "Needs a person", goesTo: "For you" }], ifFails: "For you" },
|
|
32
32
|
{ id: "check", title: "You check the reply", kind: "approval", decider: "owner", next: "Send it", ifFails: "Maya answers" },
|
|
@@ -44,10 +44,10 @@ export const KITS = [
|
|
|
44
44
|
{
|
|
45
45
|
id: "bug-triage", name: "Bug triage",
|
|
46
46
|
promise: "Theo reads every new issue, says what it is and how urgent, answers questions, and turns real bugs into fixes you approve.",
|
|
47
|
-
|
|
47
|
+
subagent: { template: "triage", handle: "theo" },
|
|
48
48
|
flowName: "Bug triage",
|
|
49
49
|
steps: [
|
|
50
|
-
{ id: "sort", title: "Theo sorts it", kind: "
|
|
50
|
+
{ id: "sort", title: "Theo sorts it", kind: "subagent", subagent: "theo",
|
|
51
51
|
instructions: "Read the report. In \"text\", say in one or two sentences what it is and how urgent (for a bug, add what you'd check first; for a question, write the answer). Pick Bug for something broken in this project's code, Question for someone asking how something works, or Duplicate or noise for anything else, saying why.",
|
|
52
52
|
routes: [{ answer: "Bug", goesTo: "Fix it" }, { answer: "Question", goesTo: "Answered" }, { answer: "Duplicate or noise", goesTo: "Closed" }], ifFails: "For you" },
|
|
53
53
|
{ id: "fix", title: "Fix it", kind: "task", planning: "auto", instructions: "Fix the bug this card reports. Theo's notes on it: {{stage.sort}}", next: "You review the fix" },
|
|
@@ -67,10 +67,10 @@ export const KITS = [
|
|
|
67
67
|
{
|
|
68
68
|
id: "sales-follow-up", name: "Sales follow-up",
|
|
69
69
|
promise: "Leo writes back to every new lead within minutes, you approve the email, and it waits for their answer, so no lead goes cold.",
|
|
70
|
-
|
|
70
|
+
subagent: { template: "sales", handle: "leo" },
|
|
71
71
|
flowName: "Sales follow-up",
|
|
72
72
|
steps: [
|
|
73
|
-
{ id: "write", title: "Leo writes back", kind: "
|
|
73
|
+
{ id: "write", title: "Leo writes back", kind: "subagent", subagent: "leo",
|
|
74
74
|
instructions: "Read the lead. In \"text\", write a short, friendly first reply that answers what they asked and suggests one clear next step. Pick Write back, or Not a fit (say why).",
|
|
75
75
|
routes: [{ answer: "Write back", goesTo: "You check it" }, { answer: "Not a fit", goesTo: "Not a fit" }], ifFails: "Not a fit" },
|
|
76
76
|
{ id: "check", title: "You check it", kind: "approval", decider: "owner", next: "Send it", ifFails: "Leo writes back" },
|
|
@@ -87,10 +87,10 @@ export const KITS = [
|
|
|
87
87
|
{
|
|
88
88
|
id: "ops-requests", name: "Ops requests",
|
|
89
89
|
promise: "Ada approves routine requests under $200 without you and brings you everything else, so the team gets answers the same day.",
|
|
90
|
-
|
|
90
|
+
subagent: { template: "ops", handle: "ada" },
|
|
91
91
|
flowName: "Ops requests",
|
|
92
92
|
steps: [
|
|
93
|
-
{ id: "decide", title: "Ada decides", kind: "approval",
|
|
93
|
+
{ id: "decide", title: "Ada decides", kind: "approval", subagent: "ada", decider: "owner", next: "Tell the team", ifFails: "Needs more" },
|
|
94
94
|
{ id: "tell", title: "Tell the team", kind: "notify", message: "Approved: {{card.title}}", next: "Done" },
|
|
95
95
|
{ id: "more", title: "Needs more", kind: "inbox" },
|
|
96
96
|
{ id: "done", title: "Done", kind: "done" },
|
|
@@ -104,22 +104,22 @@ export const KITS = [
|
|
|
104
104
|
},
|
|
105
105
|
];
|
|
106
106
|
export const kitOf = (id) => KITS.find(one => one.id === id) ?? null;
|
|
107
|
-
/** A kit set up in a project: its flow (by name) and its
|
|
107
|
+
/** A kit set up in a project: its flow (by name) and its subagent, when both are there. */
|
|
108
108
|
export function kitInstalled(store, kit, repo) {
|
|
109
|
-
const mate = store.
|
|
109
|
+
const mate = store.subagentByHandle(repo, kit.subagent.handle);
|
|
110
110
|
const flow = store.listFlows([repo]).find(one => one.name === kit.flowName && one.state === "active") ?? null;
|
|
111
111
|
return mate === null || flow === null ? null : { flow, mate };
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
114
|
-
* Set a kit up: its
|
|
115
|
-
* flow, and its button triggers; then its
|
|
114
|
+
* Set a kit up: its subagent (kept if one of that name is already there), its
|
|
115
|
+
* flow, and its button triggers; then its subagent may use whichever of the
|
|
116
116
|
* kit's tools the project already has (reading free, writing asking first).
|
|
117
117
|
*/
|
|
118
118
|
export async function setUpKit(store, kit, repo, by, now, dir, io = {}) {
|
|
119
119
|
const made = setUpKitNow(store, kit, repo, by, now, dir);
|
|
120
120
|
if (!made.ok || made.said.endsWith("already set up here."))
|
|
121
121
|
return made;
|
|
122
|
-
const mate = store.
|
|
122
|
+
const mate = store.getSubagent(made.subagent);
|
|
123
123
|
const present = new Set(projectToolsOf(store, repo).map(one => one.name));
|
|
124
124
|
for (const tool of kit.tools.filter(one => present.has(one.tool))) {
|
|
125
125
|
try {
|
|
@@ -129,20 +129,20 @@ export async function setUpKit(store, kit, repo, by, now, dir, io = {}) {
|
|
|
129
129
|
}
|
|
130
130
|
return made;
|
|
131
131
|
}
|
|
132
|
-
/** The same, all at once (a chat confirmation can't wait on a tool): its
|
|
132
|
+
/** The same, all at once (a chat confirmation can't wait on a tool): its subagent is given the kit's connected tools by the names their last test found, and each is listed properly on its first turn. */
|
|
133
133
|
export function setUpKitNow(store, kit, repo, by, now, dir, grantByName = false) {
|
|
134
134
|
const had = kitInstalled(store, kit, repo);
|
|
135
135
|
if (had !== null)
|
|
136
|
-
return { ok: true, said: `${kit.name} is already set up here.`, flow: had.flow.id,
|
|
137
|
-
let mate = store.
|
|
136
|
+
return { ok: true, said: `${kit.name} is already set up here.`, flow: had.flow.id, subagent: had.mate.id };
|
|
137
|
+
let mate = store.subagentByHandle(repo, kit.subagent.handle);
|
|
138
138
|
if (mate === null) {
|
|
139
|
-
const template =
|
|
139
|
+
const template = SUBAGENT_TEMPLATES.find(one => one.id === kit.subagent.template);
|
|
140
140
|
if (template === undefined)
|
|
141
|
-
return { ok: false, said: "That kit's
|
|
142
|
-
const made =
|
|
141
|
+
return { ok: false, said: "That kit's subagent template is missing." };
|
|
142
|
+
const made = createSubagentFrom(store, { repo, template: template.id, by }, now);
|
|
143
143
|
if (!made.ok)
|
|
144
144
|
return { ok: false, said: made.said };
|
|
145
|
-
mate = store.
|
|
145
|
+
mate = store.getSubagent(made.id);
|
|
146
146
|
}
|
|
147
147
|
let definition;
|
|
148
148
|
try {
|
|
@@ -158,7 +158,7 @@ export function setUpKitNow(store, kit, repo, by, now, dir, grantByName = false)
|
|
|
158
158
|
if (grantByName)
|
|
159
159
|
for (const tool of projectToolsOf(store, repo).filter(one => kit.tools.some(each => each.tool === one.name)))
|
|
160
160
|
grantListed(store, mate, tool, null, by, now);
|
|
161
|
-
return { ok: true, said: `${kit.name} is ready: ${nameOf(mate)} works its ${kit.flowName} flow.`, flow: flowId,
|
|
161
|
+
return { ok: true, said: `${kit.name} is ready: ${nameOf(mate)} works its ${kit.flowName} flow.`, flow: flowId, subagent: mate.id };
|
|
162
162
|
}
|
|
163
163
|
/** What's set up and what's left, read fresh each time. */
|
|
164
164
|
export function kitChecklist(store, kit, repo, dir) {
|
|
@@ -167,7 +167,7 @@ export function kitChecklist(store, kit, repo, dir) {
|
|
|
167
167
|
return [];
|
|
168
168
|
const name = nameOf(set.mate);
|
|
169
169
|
const steps = [
|
|
170
|
-
{ id: "
|
|
170
|
+
{ id: "subagent", done: true, said: `${name} joined the team`, href: `/settings/lead/subagents/${set.mate.id}` },
|
|
171
171
|
{ id: "flow", done: true, said: `The ${kit.flowName} flow is ready`, href: `/flows/${set.flow.id}` },
|
|
172
172
|
];
|
|
173
173
|
if (kit.email === true) {
|
|
@@ -184,18 +184,18 @@ export function kitChecklist(store, kit, repo, dir) {
|
|
|
184
184
|
const tools = projectToolsOf(store, repo);
|
|
185
185
|
for (const tool of kit.tools) {
|
|
186
186
|
const connected = tools.some(one => one.name === tool.tool);
|
|
187
|
-
const granted = store.
|
|
187
|
+
const granted = store.subagentGrant(set.mate.id, tool.tool) !== null;
|
|
188
188
|
steps.push(connected && granted
|
|
189
|
-
? { id: `tool-${tool.tool}`, done: true, said: `${name} can use ${tool.label}`, href: `/
|
|
189
|
+
? { id: `tool-${tool.tool}`, done: true, said: `${name} can use ${tool.label}`, href: `/settings/lead/subagents/${set.mate.id}#tools` }
|
|
190
190
|
: connected
|
|
191
|
-
? { id: `tool-${tool.tool}`, done: false, said: `Let ${name} use ${tool.label}, ${tool.why}`, href: `/
|
|
191
|
+
? { id: `tool-${tool.tool}`, done: false, said: `Let ${name} use ${tool.label}, ${tool.why}`, href: `/settings/lead/subagents/${set.mate.id}#tools` }
|
|
192
192
|
: { id: `tool-${tool.tool}`, done: false, said: `Connect ${tool.label}, ${tool.why}`, href: `/settings/tools?repo=${encodeURIComponent(repo)}&kit=${kit.id}&connect=${tool.tool}#connect`, action: "connect", tool: tool.tool });
|
|
193
193
|
}
|
|
194
194
|
const tried = store.flowCards(set.flow.id, true).length > 0;
|
|
195
195
|
steps.push({ id: "sample", done: tried, said: tried ? `${name} has worked a card` : `See ${name} work a sample card`, href: tried ? `/flows/${set.flow.id}` : null, ...(tried ? {} : { action: "sample" }) });
|
|
196
196
|
return steps;
|
|
197
197
|
}
|
|
198
|
-
/** A sample card in the kit's first zone, so its
|
|
198
|
+
/** A sample card in the kit's first zone, so its subagent starts on it at once. */
|
|
199
199
|
export function addKitSample(store, kit, repo, by, now) {
|
|
200
200
|
const set = kitInstalled(store, kit, repo);
|
|
201
201
|
if (set === null)
|