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/mcp-oauth.js
CHANGED
|
@@ -4,6 +4,8 @@ import { hashSecret, mintApiToken } from "./api-tokens.js";
|
|
|
4
4
|
import { limitWords } from "./request-budget.js";
|
|
5
5
|
import { sourceKey } from "./source-key.js";
|
|
6
6
|
import { PKCE_CHALLENGE, readOAuthRegistration, readOAuthTokenRequest } from "./contracts/mcp-oauth.js";
|
|
7
|
+
import { html, htmlString, joinHtml, postForm, styleElement } from "./html.js";
|
|
8
|
+
import { withFormToken } from "./server/request-context.js";
|
|
7
9
|
/** How long each piece lives. A code is spent at once; a grant ends after 30 days and the person consents again. */
|
|
8
10
|
export const OAUTH_TIMES = { requestMs: 10 * 60_000, codeMs: 60_000, accessSeconds: 3600, grantDays: 30 };
|
|
9
11
|
/** The most sign-ins waiting at once, and registered clients kept. */
|
|
@@ -33,7 +35,6 @@ export function oauthTokenAllowed(store, tokenId, path, now) {
|
|
|
33
35
|
export const oauthProjects = (store, tokenId) => store.oauthGrant(tokenId)?.projects ?? null;
|
|
34
36
|
const sha256 = (text) => createHash("sha256").update(text, "utf8").digest("hex");
|
|
35
37
|
const same = (a, b) => { const x = Buffer.from(a), y = Buffer.from(b); return x.length === y.length && timingSafeEqual(x, y); };
|
|
36
|
-
const escape = (text) => text.replace(/[&<>"']/g, one => `&#${one.charCodeAt(0)};`);
|
|
37
38
|
const clean = (text) => text.replace(/[\u0000-\u001f\u007f-\u009f\u200b-\u200f\u202a-\u202e\u2066-\u2069]+/g, " ").replace(/\s+/g, " ").trim();
|
|
38
39
|
/**
|
|
39
40
|
* A redirect a client may register: https anywhere, or http on 127.0.0.1 / [::1] (RFC 8252 §7.3), with no
|
|
@@ -80,10 +81,10 @@ export function createOAuthHttp(options) {
|
|
|
80
81
|
};
|
|
81
82
|
const oauthError = (response, status, error, description) => json(response, status, { error, error_description: description });
|
|
82
83
|
/** A page of this sign-in: script-free, never framed, posting only to this console. */
|
|
83
|
-
const page = (response, status,
|
|
84
|
+
const page = (response, status, document) => {
|
|
84
85
|
response.writeHead(status, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store", "referrer-policy": "no-referrer", "x-content-type-options": "nosniff", "x-frame-options": "DENY",
|
|
85
86
|
"content-security-policy": "default-src 'none'; style-src 'unsafe-inline'; form-action 'self'; frame-ancestors 'none'; base-uri 'none'" });
|
|
86
|
-
response.end(
|
|
87
|
+
response.end(htmlString(document));
|
|
87
88
|
};
|
|
88
89
|
/**
|
|
89
90
|
* On to an address by a page that moves on by itself, never a redirect: a redirect answering a form is held to
|
|
@@ -93,9 +94,9 @@ export function createOAuthHttp(options) {
|
|
|
93
94
|
const moveOn = (response, to, words) => {
|
|
94
95
|
response.writeHead(200, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store", "referrer-policy": "no-referrer", "x-content-type-options": "nosniff", "x-frame-options": "DENY",
|
|
95
96
|
"content-security-policy": "default-src 'none'; style-src 'unsafe-inline'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'" });
|
|
96
|
-
response.end(`<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta http-equiv="refresh" content="0;url=${
|
|
97
|
+
response.end(htmlString(html `<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta http-equiv="refresh" content="0;url=${to}"><title>Toolroll</title>${STYLE}<main><p>${words} <a href="${to}">Continue</a></p></main></html>`));
|
|
97
98
|
};
|
|
98
|
-
const problemPage = (response, status, title, said) => page(response, status, shell(title, `<h1>${
|
|
99
|
+
const problemPage = (response, status, title, said) => page(response, status, shell(title, html `<h1>${title}</h1><p>${said}</p>`));
|
|
99
100
|
/** Back to the client with an answer: the code, or an error, and its state; `iss` names this console (RFC 9207). */
|
|
100
101
|
const backToClient = (response, origin, redirectUri, state, answer, words) => {
|
|
101
102
|
const to = new URL(redirectUri);
|
|
@@ -234,34 +235,34 @@ export function createOAuthHttp(options) {
|
|
|
234
235
|
const title = "Connect to Toolroll";
|
|
235
236
|
const projectList = projects.length === 0 ? "" : projects.map(path => {
|
|
236
237
|
const name = path.split("/").filter(part => part !== "").pop() ?? path;
|
|
237
|
-
return `<label class="pick"><input type="checkbox" name="project" value="${
|
|
238
|
-
})
|
|
238
|
+
return html `<label class="pick"><input type="checkbox" name="project" value="${path}"${checked.has(path) ? " checked" : ""}><span><b>${name}</b><small>${path}</small></span></label>`;
|
|
239
|
+
});
|
|
239
240
|
const step = session.sso?.fresh === true
|
|
240
|
-
? `<p class="meta">Confirmed with ${
|
|
241
|
-
: `<label class="field">Your Toolroll password<input type="password" name="password" autocomplete="current-password" required></label>${session.sso === null ? "" : `<p class="meta"><a href="/login/sso?reauth=1&return=${
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
241
|
+
? html `<p class="meta">Confirmed with ${session.sso.label}.</p><input type="hidden" name="password" value="">`
|
|
242
|
+
: html `<label class="field">Your Toolroll password<input type="password" name="password" autocomplete="current-password" required></label>${session.sso === null ? "" : html `<p class="meta"><a href="/login/sso?reauth=1&return=${encodeURIComponent(`/oauth/authorize?request=${id}`)}">Confirm with ${session.sso.label}</a> instead.</p>`}`;
|
|
243
|
+
// The consent form carries the console session's own CSRF token (checked against session.csrf on the post).
|
|
244
|
+
const consentForm = (body) => withFormToken(session.csrf, () => postForm("/oauth/authorize", body, { hidden: { request: id } }));
|
|
245
|
+
const body = joinHtml([
|
|
246
|
+
html `<h1>${title}</h1>`,
|
|
247
|
+
html `<p class="client"><b>${client.name}</b> <span class="tag">unverified</span> <span class="meta">returns to ${host}</span></p>`,
|
|
248
|
+
problem === null ? "" : html `<div class="problem" role="alert">${problem}</div>`,
|
|
246
249
|
projects.length === 0
|
|
247
|
-
? `<p>You don't have access to any projects. Ask your Toolroll administrator to add you to one.</p>`
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
`<details><summary>Client details</summary><p class="meta">Client ID <code>${escape(client.id)}</code><br>Returns to <code>${escape(one.redirectUri)}</code><br>Toolroll doesn't know who made this client; its name is what it says about itself.</p></details>`,
|
|
264
|
-
].join("");
|
|
250
|
+
? html `<p>You don't have access to any projects. Ask your Toolroll administrator to add you to one.</p>${consentForm(html `<button name="decision" value="deny" class="secondary">Cancel</button>`)}`
|
|
251
|
+
: joinHtml([
|
|
252
|
+
html `<p>Connects as ${session.name} in your selected projects. It can't approve work or change people or policy.</p>`,
|
|
253
|
+
consentForm(joinHtml([
|
|
254
|
+
html `<fieldset><legend>Access</legend>`,
|
|
255
|
+
html `<label class="pick"><input type="radio" name="access" value="read"${access === "read" ? " checked" : ""}><span><b>Read</b><small>See tasks, results and status.</small></span></label>`,
|
|
256
|
+
mayAct ? html `<label class="pick"><input type="radio" name="access" value="act"${access === "act" ? " checked" : ""}><span><b>Act</b><small>Also file tasks under your name.</small></span></label>` : "",
|
|
257
|
+
html `</fieldset>`,
|
|
258
|
+
html `<fieldset><legend>Projects</legend>${projectList}</fieldset>`,
|
|
259
|
+
html `<p class="meta">Access lasts up to 30 days. Revoke it anytime in Settings → Sessions & tokens.</p>`,
|
|
260
|
+
step,
|
|
261
|
+
html `<div class="actions"><button name="decision" value="allow">Allow access</button><button name="decision" value="deny" class="secondary" formnovalidate>Cancel</button></div>`,
|
|
262
|
+
])),
|
|
263
|
+
]),
|
|
264
|
+
html `<details><summary>Client details</summary><p class="meta">Client ID <code>${client.id}</code><br>Returns to <code>${one.redirectUri}</code><br>Toolroll doesn't know who made this client; its name is what it says about itself.</p></details>`,
|
|
265
|
+
]);
|
|
265
266
|
return shell(title, body);
|
|
266
267
|
};
|
|
267
268
|
async function authorizeConsent(request, response, url, origin) {
|
|
@@ -449,8 +450,7 @@ export function createOAuthHttp(options) {
|
|
|
449
450
|
};
|
|
450
451
|
}
|
|
451
452
|
/** The sign-in's pages stand alone: no stylesheet, script or font loads here, so the palette rides inline. */
|
|
452
|
-
const STYLE = [
|
|
453
|
-
`<style>`,
|
|
453
|
+
const STYLE = styleElement([
|
|
454
454
|
`:root{color-scheme:light dark;--ground:#efefef;--paper:#fff;--ink:#171717;--muted:#666;--line:#e6e6e6;--soft:#f2f2f2;--accent:#171717;--on-accent:#fff;--problem:#b42318}`,
|
|
455
455
|
`@media (prefers-color-scheme:dark){:root{--ground:#0b0b0b;--paper:#161616;--ink:#ededed;--muted:#a1a1a1;--line:#262626;--soft:#1f1f1f;--accent:#ededed;--on-accent:#0b0b0b;--problem:#ff8a80}}`,
|
|
456
456
|
`*{box-sizing:border-box}body{margin:0;min-height:100vh;display:grid;place-items:center;padding:24px 16px;background:var(--ground);color:var(--ink);font:15px/1.55 ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}`,
|
|
@@ -467,6 +467,5 @@ const STYLE = [
|
|
|
467
467
|
`.problem{border:1px solid var(--problem);color:var(--problem);border-radius:8px;padding:10px 12px;margin-bottom:14px}a{color:inherit;text-underline-offset:3px}`,
|
|
468
468
|
`details{border-top:1px solid var(--line)}summary{cursor:pointer;color:var(--muted);font-size:13px;min-height:44px;padding:12px 0}code{font:12.5px/1.5 ui-monospace,"SF Mono",Menlo,monospace;background:var(--soft);border-radius:5px;padding:1px 5px;overflow-wrap:anywhere}`,
|
|
469
469
|
`@media (max-width:480px){main{padding:20px}.field input{font-size:16px}.actions button{flex:1 1 auto}}`,
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
const shell = (title, body) => `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="color-scheme" content="light dark"><title>${escape(title)}</title>${STYLE}</head><body><main>${body}</main></body></html>`;
|
|
470
|
+
].join(""));
|
|
471
|
+
const shell = (title, body) => html `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="color-scheme" content="light dark"><title>${title}</title>${STYLE}</head><body><main>${body}</main></body></html>`;
|
package/dist/memory-pass.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Store } from './store.js';
|
|
2
|
-
import {
|
|
2
|
+
import { performSubscriptionLeadRequest } from './subscription-chat.js';
|
|
3
3
|
import { type MemoryProposal, type Verdict } from './contracts/memory-pass.js';
|
|
4
4
|
export type { MemoryProposal, Verdict };
|
|
5
5
|
/** One session's trace, in characters (TEXT_LIMITS.memoryTrace). */
|
|
@@ -66,7 +66,7 @@ export declare function defaultAnalyzer(store: Store, options: {
|
|
|
66
66
|
env?: NodeJS.ProcessEnv;
|
|
67
67
|
fetcher?: typeof fetch;
|
|
68
68
|
timeoutMs?: number;
|
|
69
|
-
runner?: Parameters<typeof
|
|
69
|
+
runner?: Parameters<typeof performSubscriptionLeadRequest>[1];
|
|
70
70
|
}): MemoryAnalyzer;
|
|
71
71
|
/**
|
|
72
72
|
* Read the analyser's answer with the tolerant parse-side schema, then keep only claims whose quote
|
package/dist/memory-pass.js
CHANGED
|
@@ -17,8 +17,8 @@ import { basename, join } from 'node:path';
|
|
|
17
17
|
import { scanForSecrets, readVerifiedArtifact } from './evidence.js';
|
|
18
18
|
import { changeKnowledge, knowledgeView } from './project-knowledge.js';
|
|
19
19
|
import { listDecisions, recordDecision } from './project-memory.js';
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
20
|
+
import { composeLeadRequest, performLeadRequest, isDirectChatProvider, CHAT_KEY_ENV } from './converse.js';
|
|
21
|
+
import { performSubscriptionLeadRequest } from './subscription-chat.js';
|
|
22
22
|
import { TEXT_LIMITS } from './text-limits.js';
|
|
23
23
|
import { contractError, parseContract } from './contracts/contract.js';
|
|
24
24
|
import { MEMORY_VERDICT_MODEL_SCHEMA, MEMORY_VERDICT_VERSION, memoryEvidenceSchema, memoryProposalSchema, memoryVerdictReadSchema, readSavedVerdict, savedVerdictSchema } from './contracts/memory-pass.js';
|
|
@@ -138,11 +138,11 @@ function fileSources(repo, kind, home) {
|
|
|
138
138
|
/** The plane's own sessions: lead threads that touched the project, and finished crew runs with their handoff and check output. */
|
|
139
139
|
function planeSources(store, repo, evidenceRoot) {
|
|
140
140
|
const out = [];
|
|
141
|
-
const threads = store.handle.prepare(`SELECT c.thread AS thread, c.id AS conversation, MAX(m.id) AS last, MAX(m.created_at) AS at FROM team_conversation c JOIN
|
|
141
|
+
const threads = store.handle.prepare(`SELECT c.thread AS thread, c.id AS conversation, MAX(m.id) AS last, MAX(m.created_at) AS at FROM team_conversation c JOIN lead_message m ON m.thread=c.thread
|
|
142
142
|
WHERE EXISTS (SELECT 1 FROM json_each(c.projects_json) WHERE value=?) GROUP BY c.thread`).all(repo);
|
|
143
143
|
for (const row of threads) {
|
|
144
144
|
const thread = Number(row['thread']);
|
|
145
|
-
out.push({ id: `lead:${thread}:${Number(row['last'])}`, repo, kind: 'lead', source: `conversation:${String(row['conversation'])}`, at: String(row['at']), trace: () => redact(store.handle.prepare('SELECT role,text FROM
|
|
145
|
+
out.push({ id: `lead:${thread}:${Number(row['last'])}`, repo, kind: 'lead', source: `conversation:${String(row['conversation'])}`, at: String(row['at']), trace: () => redact(store.handle.prepare('SELECT role,text FROM lead_message WHERE thread=? ORDER BY id LIMIT 400').all(thread).map(m => `${m['role']}: ${clip(String(m['text']), 4000)}`).join('\n')) });
|
|
146
146
|
}
|
|
147
147
|
const runs = store.handle.prepare(`SELECT r.id, r.role, r.finished_at, r.handoff, t.external_id FROM run r JOIN task_ref t ON t.id=r.task_ref WHERE t.repo=? AND r.role IN ('builder','repair') AND r.outcome IS NOT NULL ORDER BY r.id DESC LIMIT 100`).all(repo);
|
|
148
148
|
for (const row of runs) {
|
|
@@ -207,12 +207,12 @@ export function defaultAnalyzer(store, options) {
|
|
|
207
207
|
}
|
|
208
208
|
if (!key)
|
|
209
209
|
return { ok: false, problem: `No ${config.provider} key is available for the backward pass.` };
|
|
210
|
-
const request =
|
|
211
|
-
const answer = await
|
|
210
|
+
const request = composeLeadRequest({ provider: config.provider, model: config.model, key, system, dataDocument, history, tools: [] });
|
|
211
|
+
const answer = await performLeadRequest(request, config.provider, AbortSignal.timeout(options.timeoutMs ?? 120_000), options.fetcher);
|
|
212
212
|
return answer.ok ? { ok: true, text: answer.answer.text } : { ok: false, problem: answer.problem };
|
|
213
213
|
}
|
|
214
214
|
// The verdict's own schema is the harness's structured output, so the answer is the verdict itself.
|
|
215
|
-
const answer = await
|
|
215
|
+
const answer = await performSubscriptionLeadRequest({ provider: config.provider, model: config.model, system, dataDocument, history, tools: [], outputSchema: MEMORY_VERDICT_MODEL_SCHEMA, timeoutMs: options.timeoutMs ?? 180_000 }, options.runner);
|
|
216
216
|
return answer.ok ? { ok: true, text: answer.answer.text } : { ok: false, problem: answer.problem };
|
|
217
217
|
};
|
|
218
218
|
}
|
package/dist/models-ui.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Settings → Models: the CLIs on this computer, the default agent for each
|
|
2
2
|
* role picked from live lists, and the models that just arrived. */
|
|
3
|
+
import { type Html } from "./html.js";
|
|
3
4
|
import type { ModelOption, RuntimeState, SeenModel, WatchState } from "./model-catalog.js";
|
|
4
5
|
export declare const MODELS_CSS = ".models{max-width:780px;min-width:0;overflow-wrap:anywhere}.models .card{padding:16px 20px;margin:12px 0;min-width:0}.models h2{margin:24px 0 8px}.models form{margin:0}.models label{display:grid;gap:6px}.models select,.models input[type=search]{box-sizing:border-box;width:100%;max-width:100%;min-width:0}.models button,.models summary,.models .button-link{min-height:44px}.models button{white-space:nowrap}.models summary{padding:12px 0;cursor:pointer}.models .tool{display:flex;gap:12px;align-items:center;justify-content:space-between;flex-wrap:wrap}.models .role{display:grid;grid-template-columns:minmax(0,1fr);gap:8px}.models .role-row{display:flex;gap:12px;align-items:end;flex-wrap:wrap}.models .role-row select{flex:1 1 240px;width:auto;min-width:0}.models .role-name{font-weight:600}.models .new{font-weight:600}.models ul{padding-left:20px}.models .meta{font-size:.86rem}@media(max-width:600px){.models .card{padding:14px}}";
|
|
5
6
|
export type RoleView = {
|
|
@@ -29,6 +30,6 @@ export type ModelsView = {
|
|
|
29
30
|
said: string | null;
|
|
30
31
|
problem: string | null;
|
|
31
32
|
};
|
|
32
|
-
export declare function modelsHtml(view: ModelsView):
|
|
33
|
+
export declare function modelsHtml(view: ModelsView): Html;
|
|
33
34
|
/** Filters one long picker as you type; the saved choice always stays listed. */
|
|
34
35
|
export declare function modelsScript(): string;
|
package/dist/models-ui.js
CHANGED
|
@@ -1,60 +1,50 @@
|
|
|
1
|
+
/** Settings → Models: the CLIs on this computer, the default agent for each
|
|
2
|
+
* role picked from live lists, and the models that just arrived. */
|
|
3
|
+
import { html, postForm } from "./html.js";
|
|
1
4
|
import { priceWords } from "./model-catalog.js";
|
|
2
5
|
import { whenUtc } from "./when-html.js";
|
|
3
|
-
const e = (s) => String(s ?? "").replace(/[&<>"']/g, c => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]));
|
|
4
|
-
const hidden = (data) => Object.entries(data).map(([k, v]) => `<input type="hidden" name="${e(k)}" value="${e(v)}">`).join("");
|
|
5
6
|
const when = (iso) => iso === null ? "never" : whenUtc(iso);
|
|
6
7
|
export const MODELS_CSS = `.models{max-width:780px;min-width:0;overflow-wrap:anywhere}.models .card{padding:16px 20px;margin:12px 0;min-width:0}.models h2{margin:24px 0 8px}.models form{margin:0}.models label{display:grid;gap:6px}.models select,.models input[type=search]{box-sizing:border-box;width:100%;max-width:100%;min-width:0}.models button,.models summary,.models .button-link{min-height:44px}.models button{white-space:nowrap}.models summary{padding:12px 0;cursor:pointer}.models .tool{display:flex;gap:12px;align-items:center;justify-content:space-between;flex-wrap:wrap}.models .role{display:grid;grid-template-columns:minmax(0,1fr);gap:8px}.models .role-row{display:flex;gap:12px;align-items:end;flex-wrap:wrap}.models .role-row select{flex:1 1 240px;width:auto;min-width:0}.models .role-name{font-weight:600}.models .new{font-weight:600}.models ul{padding-left:20px}.models .meta{font-size:.86rem}@media(max-width:600px){.models .card{padding:14px}}`;
|
|
7
|
-
function toolRow(one, manage
|
|
8
|
+
function toolRow(one, manage) {
|
|
8
9
|
const state = one.installed === null ? "Version unknown"
|
|
9
|
-
: one.behind ? `${
|
|
10
|
-
: `${
|
|
10
|
+
: one.behind ? html `${one.installed} · <strong>${one.latest} available</strong>`
|
|
11
|
+
: `${one.installed} · Up to date`;
|
|
11
12
|
const action = manage && one.behind && one.updateCommand !== null
|
|
12
|
-
?
|
|
13
|
-
: one.behind ? `<span class="meta">Update it the way you installed it</span>` : "";
|
|
14
|
-
return `<div class="card tool"><span><strong>${
|
|
13
|
+
? postForm("/settings/models/update", html `<button type="submit">Update ${one.name}</button>`, { hidden: { tool: one.tool } })
|
|
14
|
+
: one.behind ? html `<span class="meta">Update it the way you installed it</span>` : "";
|
|
15
|
+
return html `<div class="card tool"><span><strong>${one.name}</strong> ${state}${one.problem === null ? "" : html `<br><span class="meta">${one.problem}</span>`}</span>${action}</div>`;
|
|
15
16
|
}
|
|
16
|
-
function roleForm(role, manage
|
|
17
|
+
function roleForm(role, manage) {
|
|
17
18
|
if (!manage)
|
|
18
|
-
return `<div class="card role"><strong>${
|
|
19
|
+
return html `<div class="card role"><strong>${role.label}</strong><span>${role.words}</span></div>`;
|
|
19
20
|
const known = role.groups.some(group => group.options.some(option => `${group.provider}|${option.value}` === role.current));
|
|
20
|
-
const saved = !known && role.current !== "inherit" && role.current !== "" ? `<option value="${
|
|
21
|
-
const groups = role.groups.map(group => `<optgroup label="${
|
|
21
|
+
const saved = !known && role.current !== "inherit" && role.current !== "" ? html `<option value="${role.current}" selected>${role.words} · current</option>` : "";
|
|
22
|
+
const groups = role.groups.map(group => html `<optgroup label="${group.label}">${group.options.map(option => {
|
|
22
23
|
const value = `${group.provider}|${option.value}`;
|
|
23
|
-
return `<option value="${
|
|
24
|
-
})
|
|
25
|
-
const inherit = role.inherit ? `<option value="inherit"${role.current === "inherit" ?
|
|
24
|
+
return html `<option value="${value}"${value === role.current ? html ` selected` : ""}>${option.label}</option>`;
|
|
25
|
+
})}</optgroup>`);
|
|
26
|
+
const inherit = role.inherit ? html `<option value="inherit"${role.current === "inherit" ? html ` selected` : ""}>Same as the builder</option>` : "";
|
|
26
27
|
const long = role.groups.reduce((sum, group) => sum + group.options.length, 0) > 40;
|
|
27
28
|
const count = role.groups.reduce((sum, group) => sum + group.options.length, 0);
|
|
28
29
|
const id = `role-${role.phase}`;
|
|
29
|
-
return `<
|
|
30
|
-
`<label class="role-name" for="${id}">${e(role.label)}</label>` +
|
|
31
|
-
(long ? `<input type="search" data-model-filter hidden aria-label="Search ${e(role.label.toLowerCase())} models" placeholder="Search ${count} models" autocomplete="off">` : "") +
|
|
32
|
-
`<div class="role-row"><select id="${id}" name="agent">${inherit}${saved}${groups}</select><button type="submit" class="secondary">Save</button></div></form>`;
|
|
30
|
+
return postForm("/settings/models/agent", html `<label class="role-name" for="${id}">${role.label}</label>${long ? html `<input type="search" data-model-filter hidden aria-label="Search ${role.label.toLowerCase()} models" placeholder="Search ${count} models" autocomplete="off">` : ""}<div class="role-row"><select id="${id}" name="agent">${inherit}${saved}${groups}</select><button type="submit" class="secondary">Save</button></div>`, { attrs: { class: "card role" }, hidden: { phase: role.phase } });
|
|
33
31
|
}
|
|
34
32
|
export function modelsHtml(view) {
|
|
35
33
|
const manage = view.canManage && view.csrf !== "";
|
|
36
|
-
const status = view.said === null ? "" : `<p role="status">${
|
|
37
|
-
const problem = view.problem === null ? "" : `<p class="problem" role="alert">${
|
|
34
|
+
const status = view.said === null ? "" : html `<p role="status">${view.said}</p>`;
|
|
35
|
+
const problem = view.problem === null ? "" : html `<p class="problem" role="alert">${view.problem}</p>`;
|
|
38
36
|
const tools = view.runtimes.length === 0
|
|
39
|
-
? `<p class="meta">No Claude, Codex or Gemini CLI found on this computer yet.</p>`
|
|
40
|
-
: view.runtimes.map(one => toolRow(one, manage
|
|
37
|
+
? html `<p class="meta">No Claude, Codex or Gemini CLI found on this computer yet.</p>`
|
|
38
|
+
: view.runtimes.map(one => toolRow(one, manage));
|
|
41
39
|
const check = manage
|
|
42
|
-
?
|
|
40
|
+
? postForm("/settings/models/check", html `<button type="submit" class="secondary">Check now</button>`, { attrs: { class: "inline" } })
|
|
43
41
|
: "";
|
|
44
42
|
const fresh = view.fresh.length === 0 ? "" :
|
|
45
|
-
`<h2>New models</h2><ul>${view.fresh.map(model => `<li><span class="new">${
|
|
43
|
+
html `<h2>New models</h2><ul>${view.fresh.map(model => html `<li><span class="new">${model.name}</span> <span class="meta">${priceWords(model)}${model.releasedAt === null ? "" : ` · released ${model.releasedAt.slice(0, 10)}`}</span></li>`)}</ul>`;
|
|
46
44
|
const watch = manage
|
|
47
|
-
? `<details><summary>Automatic checks · ${view.watch.enabled ? "On" : "Off"}</summary><
|
|
48
|
-
`<p>Check every 6 hours and send a message when a new model or CLI update appears. It uses public lists and sends nothing about you or your projects.</p>` +
|
|
49
|
-
`<button type="submit" class="secondary">${view.watch.enabled ? "Turn off" : "Turn on"}</button></form></details>`
|
|
45
|
+
? html `<details><summary>Automatic checks · ${view.watch.enabled ? "On" : "Off"}</summary>${postForm("/settings/models/watch", html `<p>Check every 6 hours and send a message when a new model or CLI update appears. It uses public lists and sends nothing about you or your projects.</p><button type="submit" class="secondary">${view.watch.enabled ? "Turn off" : "Turn on"}</button>`, { hidden: { enabled: view.watch.enabled ? "0" : "1" } })}</details>`
|
|
50
46
|
: "";
|
|
51
|
-
return `<section class="models">${status}${problem}
|
|
52
|
-
`<h2>AI tools</h2>${tools}<p class="meta">Checked ${when(view.watch.checkedAt)} ${check}</p>` +
|
|
53
|
-
fresh +
|
|
54
|
-
`<h2>Default agents</h2><p class="meta">New tasks use these. Approved tasks keep the agents they were approved with.</p>` +
|
|
55
|
-
view.roles.map(role => roleForm(role, manage, view.csrf)).join("") +
|
|
56
|
-
(view.chat === null ? "" : `<h2>Chat</h2><p>${e(view.chat.words)} · <a href="/settings/lead">Change</a></p>`) +
|
|
57
|
-
watch + `</section>`;
|
|
47
|
+
return html `<section class="models">${status}${problem}<h2>AI tools</h2>${tools}<p class="meta">Checked ${when(view.watch.checkedAt)} ${check}</p>${fresh}<h2>Default agents</h2><p class="meta">New tasks use these. Approved tasks keep the agents they were approved with.</p>${view.roles.map(role => roleForm(role, manage))}${view.chat === null ? "" : html `<h2>Chat</h2><p>${view.chat.words} · <a href="/settings/lead">Change</a></p>`}${watch}</section>`;
|
|
58
48
|
}
|
|
59
49
|
/** Filters one long picker as you type; the saved choice always stays listed. */
|
|
60
50
|
export function modelsScript() {
|
package/dist/monitoring-ui.d.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* the company's tools. One form, the status of each destination beside it,
|
|
4
4
|
* and how Prometheus reads `/metrics`. The signing secret is shown once.
|
|
5
5
|
*/
|
|
6
|
+
import { type Html } from "./html.js";
|
|
6
7
|
import type { MonitoringStatus } from "./store.js";
|
|
7
8
|
import type { MonitoringSettings } from "./monitoring-settings.js";
|
|
8
9
|
export declare const MONITORING_CSS: string;
|
|
@@ -12,9 +13,9 @@ export type MonitoringView = {
|
|
|
12
13
|
head: number;
|
|
13
14
|
origin: string | null;
|
|
14
15
|
};
|
|
15
|
-
export declare function monitoringHtml(view: MonitoringView,
|
|
16
|
+
export declare function monitoringHtml(view: MonitoringView, notice: {
|
|
16
17
|
said?: string | null;
|
|
17
18
|
problem?: string | null;
|
|
18
|
-
}):
|
|
19
|
+
}): Html;
|
|
19
20
|
/** The webhook's signing secret, once: a focused page with no script. */
|
|
20
|
-
export declare function signingSecretHtml(url: string, secret: string):
|
|
21
|
+
export declare function signingSecretHtml(url: string, secret: string): Html;
|
package/dist/monitoring-ui.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings → Monitoring (v104): an instance operator points Toolroll at
|
|
3
|
+
* the company's tools. One form, the status of each destination beside it,
|
|
4
|
+
* and how Prometheus reads `/metrics`. The signing secret is shown once.
|
|
5
|
+
*/
|
|
6
|
+
import { html, postForm } from "./html.js";
|
|
1
7
|
import { targetOf } from "./monitoring.js";
|
|
2
8
|
import { whenUtc } from "./when-html.js";
|
|
3
|
-
const e = (value) => String(value ?? "").replace(/[&<>"']/g, c => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
4
9
|
const when = (at) => whenUtc(at);
|
|
5
10
|
export const MONITORING_CSS = `.monitoring{max-width:720px;min-width:0}.monitoring fieldset{border:0;padding:0;margin:20px 0 0;min-width:0}` +
|
|
6
11
|
`.monitoring legend{font-weight:600;font-size:.9375rem;margin-bottom:6px}.monitoring label{display:grid;gap:4px;margin:10px 0 0;font-size:.875rem}` +
|
|
@@ -15,38 +20,20 @@ function statusLine(view, sink, address) {
|
|
|
15
20
|
// A status for another address (it just changed) says nothing about this one yet.
|
|
16
21
|
const one = view.status.find(row => row.sink === sink && row.target === targetOf(address));
|
|
17
22
|
if (one === undefined || (one.lastOkAt === null && one.lastError === null))
|
|
18
|
-
return `<p class="status meta" data-monitoring="${sink}" data-state="starting">Starting: the first delivery goes out within a few seconds.</p>`;
|
|
23
|
+
return html `<p class="status meta" data-monitoring="${sink}" data-state="starting">Starting: the first delivery goes out within a few seconds.</p>`;
|
|
19
24
|
if (one.failures > 0 && one.lastError !== null) {
|
|
20
|
-
return `<p class="status problem" role="alert" data-monitoring="${sink}" data-state="failing">Failing: ${
|
|
25
|
+
return html `<p class="status problem" role="alert" data-monitoring="${sink}" data-state="failing">Failing: ${one.lastError}${one.nextTryAt === null ? "" : html ` · trying again ${when(one.nextTryAt)}`}</p>`;
|
|
21
26
|
}
|
|
22
27
|
const behind = Math.max(0, view.head - one.through);
|
|
23
|
-
return `<p class="status meta" data-monitoring="${sink}" data-state="ok">${sink === "traces" ? "Sending" : `Sent through entry #${one.through}`}${one.lastOkAt === null ? "" : ` · last ${when(one.lastOkAt)}`}${behind > 0 && sink !== "traces" ? ` · ${behind} to go` : ""}</p>`;
|
|
28
|
+
return html `<p class="status meta" data-monitoring="${sink}" data-state="ok">${sink === "traces" ? "Sending" : `Sent through entry #${one.through}`}${one.lastOkAt === null ? "" : html ` · last ${when(one.lastOkAt)}`}${behind > 0 && sink !== "traces" ? ` · ${behind} to go` : ""}</p>`;
|
|
24
29
|
}
|
|
25
|
-
export function monitoringHtml(view,
|
|
30
|
+
export function monitoringHtml(view, notice) {
|
|
26
31
|
const s = view.settings;
|
|
27
|
-
const note = notice.problem ? `<p class="problem" role="alert">${
|
|
32
|
+
const note = notice.problem ? html `<p class="problem" role="alert">${notice.problem}</p>` : notice.said ? html `<p role="status">${notice.said}</p>` : "";
|
|
28
33
|
const metrics = view.origin === null ? "/metrics" : `${view.origin}/metrics`;
|
|
29
|
-
return `<section class="monitoring">${note}
|
|
30
|
-
`<form method="post" action="/settings/monitoring"><input type="hidden" name="csrf" value="${e(csrf)}">` +
|
|
31
|
-
`<fieldset><legend>Audit stream</legend><p class="meta">Every ledger entry with its seal, as it happens, to your log system.</p>` +
|
|
32
|
-
`<label>Webhook<input type="url" name="webhook" value="${e(s.webhook?.url ?? "")}" placeholder="https://logs.example.com/standing-orders" spellcheck="false"></label>` +
|
|
33
|
-
statusLine(view, "webhook", s.webhook?.url ?? null) +
|
|
34
|
-
(s.webhook === null ? "" : `<label class="choice"><input type="checkbox" name="rotate" value="1"> Make a new signing secret</label>`) +
|
|
35
|
-
`<label>Folder (JSON Lines, a file per day)<input type="text" name="folder" value="${e(s.folder?.path ?? "")}" placeholder="/var/log/standing-orders" spellcheck="false"></label>` +
|
|
36
|
-
statusLine(view, "folder", s.folder?.path ?? null) + `</fieldset>` +
|
|
37
|
-
`<fieldset><legend>Traces</legend><p class="meta">Each run as an OpenTelemetry span: timings, model, tokens and cost. Never a prompt or code.</p>` +
|
|
38
|
-
`<label>Collector (OTLP over HTTP)<input type="url" name="traces" value="${e(s.traces?.endpoint ?? "")}" placeholder="https://otel.example.com:4318" spellcheck="false"></label>` +
|
|
39
|
-
statusLine(view, "traces", s.traces?.endpoint ?? null) +
|
|
40
|
-
`<label>Header name (optional)<input type="text" name="header-name" value="${e(s.traces?.header?.name ?? "")}" placeholder="x-honeycomb-team" spellcheck="false"></label>` +
|
|
41
|
-
`<label>Header value<input type="password" name="header-value" autocomplete="off" placeholder="${s.traces?.header ? "Saved; leave blank to keep" : "API key"}"></label></fieldset>` +
|
|
42
|
-
`<div class="step-up"><label>Your Toolroll password<input type="password" name="password" autocomplete="current-password"></label></div>` +
|
|
43
|
-
`<button type="submit">Save</button></form>` +
|
|
44
|
-
`<fieldset><legend>Metrics</legend><p class="meta">Prometheus reads <code>${e(metrics)}</code> with an instance operator's API token (<a href="/settings/sessions">Sessions & tokens</a>).</p>` +
|
|
45
|
-
`<details><summary>Example</summary><pre>curl -H "Authorization: Bearer so_…" ${e(metrics)}</pre></details></fieldset></section>`;
|
|
34
|
+
return html `<section class="monitoring">${note}${postForm("/settings/monitoring", html `<fieldset><legend>Audit stream</legend><p class="meta">Every ledger entry with its seal, as it happens, to your log system.</p><label>Webhook<input type="url" name="webhook" value="${s.webhook?.url ?? ""}" placeholder="https://logs.example.com/standing-orders" spellcheck="false"></label>${statusLine(view, "webhook", s.webhook?.url ?? null)}${s.webhook === null ? "" : html `<label class="choice"><input type="checkbox" name="rotate" value="1"> Make a new signing secret</label>`}<label>Folder (JSON Lines, a file per day)<input type="text" name="folder" value="${s.folder?.path ?? ""}" placeholder="/var/log/standing-orders" spellcheck="false"></label>${statusLine(view, "folder", s.folder?.path ?? null)}</fieldset><fieldset><legend>Traces</legend><p class="meta">Each run as an OpenTelemetry span: timings, model, tokens and cost. Never a prompt or code.</p><label>Collector (OTLP over HTTP)<input type="url" name="traces" value="${s.traces?.endpoint ?? ""}" placeholder="https://otel.example.com:4318" spellcheck="false"></label>${statusLine(view, "traces", s.traces?.endpoint ?? null)}<label>Header name (optional)<input type="text" name="header-name" value="${s.traces?.header?.name ?? ""}" placeholder="x-honeycomb-team" spellcheck="false"></label><label>Header value<input type="password" name="header-value" autocomplete="off" placeholder="${s.traces?.header ? "Saved; leave blank to keep" : "API key"}"></label></fieldset><div class="step-up"><label>Your Toolroll password<input type="password" name="password" autocomplete="current-password"></label></div><button type="submit">Save</button>`)}<fieldset><legend>Metrics</legend><p class="meta">Prometheus reads <code>${metrics}</code> with an instance operator's API token (<a href="/settings/sessions">Sessions & tokens</a>).</p><details><summary>Example</summary><pre>curl -H "Authorization: Bearer so_…" ${metrics}</pre></details></fieldset></section>`;
|
|
46
35
|
}
|
|
47
36
|
/** The webhook's signing secret, once: a focused page with no script. */
|
|
48
37
|
export function signingSecretHtml(url, secret) {
|
|
49
|
-
return `<h1>Signing secret</h1><div class="card"><p>Give this to the receiver at <span class="mono">${
|
|
50
|
-
`<p class="meta">Each request carries <code>x-standing-orders-signature: t=<time>,v1=<HMAC-SHA256 of "time.body"></code>. Check it, and that the time is recent.</p></div>` +
|
|
51
|
-
`<p class="meta"><a href="/settings/monitoring">Back to Monitoring</a></p>`;
|
|
38
|
+
return html `<h1>Signing secret</h1><div class="card"><p>Give this to the receiver at <span class="mono">${url}</span>. It's shown once.</p><p class="mono secret-value">${secret}</p><p class="meta">Each request carries <code>x-standing-orders-signature: t=<time>,v1=<HMAC-SHA256 of "time.body"></code>. Check it, and that the time is recent.</p></div><p class="meta"><a href="/settings/monitoring">Back to Monitoring</a></p>`;
|
|
52
39
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/** Saved notifications, read and told apart without a store: the row readers, the lifecycle key and the predicates
|
|
2
2
|
* the store and the chat core (chat-messages.ts) share. A leaf: it imports types only. */
|
|
3
|
-
import type {
|
|
3
|
+
import type { LeadProposal, Notification, NotificationReceipt } from "./store.js";
|
|
4
4
|
/** The one task a card is about: a task its confirmation created (a new
|
|
5
5
|
* task, a revision), else the task it names (and, for feedback on a result,
|
|
6
6
|
* that result). Null for a card about no single task. A pointer for
|
|
7
7
|
* replies, never an authority. */
|
|
8
|
-
export declare function proposalTaskOf(proposal:
|
|
8
|
+
export declare function proposalTaskOf(proposal: LeadProposal | null): {
|
|
9
9
|
task: string;
|
|
10
10
|
run: number | null;
|
|
11
11
|
} | null;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Html } from "./html.js";
|
|
1
2
|
export type OpenRouterModel = {
|
|
2
3
|
id: string;
|
|
3
4
|
name: string;
|
|
@@ -25,6 +26,6 @@ export declare function fetchOpenRouterModels(key: string | null, fetcher?: type
|
|
|
25
26
|
/** One bounded cache per console. A changed/removed key cannot reuse another account's list. */
|
|
26
27
|
export declare function openRouterModelsCache(fetcher?: typeof fetch): (key: string | null, refresh?: boolean) => Promise<OpenRouterModels>;
|
|
27
28
|
export declare function modelDollars(amount: number | null): string;
|
|
28
|
-
export declare function openRouterPicker(catalog: OpenRouterModels, selected: string | null, refreshUrl: string):
|
|
29
|
+
export declare function openRouterPicker(catalog: OpenRouterModels, selected: string | null, refreshUrl: string): Html;
|
|
29
30
|
/** Only filters already-rendered options; credentials never enter this script. */
|
|
30
31
|
export declare function openRouterPickerScript(): string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Live discovery for the UI. Prices are USD, separate from rounded budget pins. */
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
+
import { html } from "./html.js";
|
|
3
4
|
import { readCappedBody } from "./converse.js";
|
|
4
5
|
import { validModelId } from "./provider.js";
|
|
5
6
|
const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
@@ -76,7 +77,6 @@ export function openRouterModelsCache(fetcher = fetch) {
|
|
|
76
77
|
return value;
|
|
77
78
|
};
|
|
78
79
|
}
|
|
79
|
-
const escape = (s) => s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
80
80
|
export function modelDollars(amount) {
|
|
81
81
|
if (amount === null)
|
|
82
82
|
return "Not reported";
|
|
@@ -96,14 +96,10 @@ function details(model) {
|
|
|
96
96
|
export function openRouterPicker(catalog, selected, refreshUrl) {
|
|
97
97
|
const rows = catalog.ok ? catalog.models : [];
|
|
98
98
|
const current = rows.find(row => row.id === selected);
|
|
99
|
-
const manual = selected && !current ? `<option selected value="${
|
|
100
|
-
return `<div data-openrouter-picker><p class="meta">${catalog.ok
|
|
101
|
-
? `${rows.length} text models · ${catalog.source === "account" ? "Filtered for your OpenRouter account" : "Public catalog · Connect a key to check your account"} · Updated ${
|
|
102
|
-
:
|
|
103
|
-
`<label data-model-search hidden>Search models<input type="search" placeholder="Search by name or provider" autocomplete="off"></label>` +
|
|
104
|
-
`<label>Model<select name="model" aria-describedby="openrouter-price"><option value="__custom__"${selected === null ? " selected" : ""}>Choose a model</option>${manual}${rows.map(row => `<option value="${escape(row.id)}" data-detail="${escape(details(row))}"${row.id === selected ? " selected" : ""}>${escape(row.name)} · ${modelDollars(row.input)} in / ${modelDollars(row.output)} out per 1M</option>`).join("")}</select></label>` +
|
|
105
|
-
`<p id="openrouter-price" class="meta" aria-live="polite">${current ? escape(details(current)) : selected ? "This saved model is not in the loaded catalog. Availability and pricing are unverified." : "Choose a model to see its token prices and context size."}</p>` +
|
|
106
|
-
`<p data-model-count class="meta" aria-live="polite"></p><p class="meta"><a href="https://openrouter.ai/models" target="_blank" rel="noreferrer">Compare models on OpenRouter ↗</a> · Prices in USD; catalog browsing uses no model tokens.</p></div>`;
|
|
99
|
+
const manual = selected && !current ? html `<option selected value="${selected}" data-detail="This saved model is not in the loaded catalog. Availability and pricing are unverified.">${selected} · Saved choice</option>` : "";
|
|
100
|
+
return html `<div data-openrouter-picker><p class="meta">${catalog.ok
|
|
101
|
+
? `${rows.length} text models · ${catalog.source === "account" ? "Filtered for your OpenRouter account" : "Public catalog · Connect a key to check your account"} · Updated ${catalog.checkedAt.slice(11, 16)} UTC`
|
|
102
|
+
: catalog.problem} <a href="${refreshUrl}">Refresh models</a></p><label data-model-search hidden>Search models<input type="search" placeholder="Search by name or provider" autocomplete="off"></label><label>Model<select name="model" aria-describedby="openrouter-price"><option value="__custom__"${selected === null ? html ` selected` : ""}>Choose a model</option>${manual}${rows.map(row => html `<option value="${row.id}" data-detail="${details(row)}"${row.id === selected ? html ` selected` : ""}>${row.name} · ${modelDollars(row.input)} in / ${modelDollars(row.output)} out per 1M</option>`)}</select></label><p id="openrouter-price" class="meta" aria-live="polite">${current ? details(current) : selected ? "This saved model is not in the loaded catalog. Availability and pricing are unverified." : "Choose a model to see its token prices and context size."}</p><p data-model-count class="meta" aria-live="polite"></p><p class="meta"><a href="https://openrouter.ai/models" target="_blank" rel="noreferrer">Compare models on OpenRouter ↗</a> · Prices in USD; catalog browsing uses no model tokens.</p></div>`;
|
|
107
103
|
}
|
|
108
104
|
/** Only filters already-rendered options; credentials never enter this script. */
|
|
109
105
|
export function openRouterPickerScript() {
|
package/dist/operate.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { FetchLike } from "./flow-share.js";
|
|
|
7
7
|
import { type Store } from "./store.js";
|
|
8
8
|
import { type DispatchAdapter } from "./sync.js";
|
|
9
9
|
import { type TelegramTransport } from "./telegram.js";
|
|
10
|
-
import { type
|
|
10
|
+
import { type LeadCliSeams } from "./lead-cli.js";
|
|
11
11
|
import { type Principal, type RemoteSource } from "./operate-remote.js";
|
|
12
12
|
import { type PublishExec } from "./publish.js";
|
|
13
13
|
import { type Runner as CommandRunner } from "./builder.js";
|
|
@@ -71,8 +71,8 @@ export type OperateOptions = {
|
|
|
71
71
|
flowFetch?: FetchLike;
|
|
72
72
|
/** Injected by tests: how `integrations` checks reach services (fetch, gh, sign-in checks, mail servers). */
|
|
73
73
|
integrationIo?: Partial<IntegrationIo>;
|
|
74
|
-
/** Injected by tests: the
|
|
75
|
-
|
|
74
|
+
/** Injected by tests: the lead's provider fetch, key environment, and stdin lines. */
|
|
75
|
+
leadSeams?: LeadCliSeams;
|
|
76
76
|
/** Test seam for the short indexed wait loop. */
|
|
77
77
|
waitSleep?: (milliseconds: number) => Promise<void>;
|
|
78
78
|
/** Injected by tests: `onboard`'s home folder, terminal and probes. */
|