toolroll 0.9.54 → 0.9.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +5 -4
- package/dist/agent-onboard.js +4 -1
- package/dist/approval-rules-ui.d.ts +3 -2
- package/dist/approval-rules-ui.js +15 -20
- package/dist/assignment-ui.d.ts +3 -2
- package/dist/assignment-ui.js +4 -11
- package/dist/backup-ui.d.ts +2 -1
- package/dist/backup-ui.js +16 -27
- package/dist/brand-mark.d.ts +3 -2
- package/dist/brand-mark.js +4 -4
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +25 -25
- package/dist/browser-shell.d.ts +2 -1
- package/dist/browser-shell.js +9 -9
- package/dist/browser-workspace.d.ts +2 -3
- package/dist/browser-workspace.js +0 -3
- package/dist/chat-polish.d.ts +5 -4
- package/dist/chat-polish.js +9 -10
- package/dist/check-levels-ui.d.ts +2 -1
- package/dist/check-levels-ui.js +26 -53
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +9 -3
- package/dist/coding-shipping-ui.d.ts +2 -1
- package/dist/coding-shipping-ui.js +6 -6
- package/dist/coding-ui.d.ts +3 -1
- package/dist/coding-ui.js +30 -29
- package/dist/contracts/cli.d.ts +1 -3
- package/dist/contracts/console-api.d.ts +1 -1
- package/dist/contracts/console-api.js +6 -5
- package/dist/contracts/health.d.ts +1 -3
- package/dist/control-ui.d.ts +5 -4
- package/dist/control-ui.js +9 -34
- package/dist/credentials-ui.d.ts +5 -4
- package/dist/credentials-ui.js +23 -24
- package/dist/demo-chat.d.ts +3 -3
- package/dist/demo-chat.js +29 -76
- package/dist/discord-settings.d.ts +3 -2
- package/dist/discord-settings.js +17 -42
- package/dist/envelope.js +10 -0
- package/dist/evidence-pack.d.ts +2 -1
- package/dist/evidence-pack.js +45 -49
- package/dist/export-ui.d.ts +7 -2
- package/dist/export-ui.js +4 -10
- package/dist/flow-gallery-ui.d.ts +4 -3
- package/dist/flow-gallery-ui.js +28 -43
- package/dist/flow-starters-ui.d.ts +2 -1
- package/dist/flow-starters-ui.js +12 -15
- package/dist/flow-triggers.d.ts +1 -0
- package/dist/flow-triggers.js +16 -14
- package/dist/flows-ui.d.ts +4 -3
- package/dist/flows-ui.js +19 -30
- package/dist/flows.js +2 -1
- package/dist/guides.js +108 -3
- package/dist/html.d.ts +76 -0
- package/dist/html.js +173 -0
- package/dist/integrations-ui.d.ts +2 -1
- package/dist/integrations-ui.js +19 -23
- package/dist/kits-ui.d.ts +5 -4
- package/dist/kits-ui.js +18 -20
- package/dist/knowledge-ui.d.ts +6 -5
- package/dist/knowledge-ui.js +26 -25
- package/dist/lead-context.d.ts +11 -1
- package/dist/lead-context.js +6 -8
- package/dist/ledger-view.d.ts +2 -1
- package/dist/ledger-view.js +27 -29
- package/dist/limits-ui.d.ts +2 -1
- package/dist/limits-ui.js +7 -7
- package/dist/live-bus.d.ts +50 -8
- package/dist/live-bus.js +133 -50
- package/dist/mcp-oauth.js +35 -36
- 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/openrouter-models.d.ts +2 -1
- package/dist/openrouter-models.js +5 -9
- package/dist/operate.js +11 -7
- 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/project-delete-ui.d.ts +4 -3
- package/dist/project-delete-ui.js +15 -27
- 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/serve.d.ts +11 -2
- package/dist/serve.js +69 -58
- package/dist/server/chat.d.ts +3 -6
- package/dist/server/chat.js +969 -1006
- package/dist/server/chrome.d.ts +291 -0
- package/dist/server/chrome.js +747 -0
- package/dist/server/console.css +2819 -0
- package/dist/server/flows.d.ts +2 -6
- package/dist/server/flows.js +840 -766
- package/dist/server/guards.d.ts +1 -1
- package/dist/server/guards.js +5 -2
- package/dist/server/handler-context.d.ts +1 -1
- package/dist/server/handler-context.js +1 -1
- package/dist/server/handler-registry.d.ts +8 -6
- package/dist/server/handler-registry.js +14 -2
- package/dist/server/http.d.ts +274 -0
- package/dist/server/http.js +239 -0
- package/dist/server/live.d.ts +34 -0
- package/dist/server/live.js +309 -0
- package/dist/server/pages.d.ts +2 -6
- package/dist/server/pages.js +808 -789
- package/dist/server/people-tokens.d.ts +1 -6
- package/dist/server/people-tokens.js +580 -572
- package/dist/server/remote.d.ts +4 -7
- package/dist/server/remote.js +37 -70
- package/dist/server/render-chat.d.ts +402 -0
- package/dist/server/render-chat.js +927 -0
- package/dist/server/render-pages.d.ts +535 -0
- package/dist/server/render-pages.js +1277 -0
- package/dist/server/render-people.d.ts +32 -0
- package/dist/server/render-people.js +121 -0
- package/dist/server/render-results.d.ts +744 -0
- package/dist/server/render-results.js +2237 -0
- package/dist/server/render-settings.d.ts +147 -0
- package/dist/server/render-settings.js +577 -0
- package/dist/server/render-tasks.d.ts +632 -0
- package/dist/server/render-tasks.js +2345 -0
- package/dist/server/request-context.d.ts +46 -0
- package/dist/server/request-context.js +17 -0
- package/dist/server/route-table.d.ts +25 -3
- package/dist/server/route-table.js +163 -75
- package/dist/server/runtime.d.ts +15 -7
- package/dist/server/runtime.js +7 -1
- package/dist/server/session.d.ts +138 -0
- package/dist/server/session.js +94 -0
- package/dist/server/settings.d.ts +1 -5
- package/dist/server/settings.js +2181 -2117
- package/dist/server/task-views.d.ts +11 -4
- package/dist/server/task-views.js +26 -22
- package/dist/server/tasks.d.ts +9 -9
- package/dist/server/tasks.js +1486 -1418
- package/dist/server-telemetry.d.ts +4 -3
- package/dist/server-telemetry.js +4 -8
- package/dist/skills-ui.d.ts +4 -3
- package/dist/skills-ui.js +14 -17
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +2 -1
- package/dist/slack-settings.d.ts +3 -2
- package/dist/slack-settings.js +17 -42
- package/dist/spend-ui.d.ts +2 -1
- package/dist/spend-ui.js +9 -32
- package/dist/sso-ui.d.ts +3 -2
- package/dist/sso-ui.js +26 -27
- package/dist/storage-ui.d.ts +2 -1
- package/dist/storage-ui.js +14 -33
- package/dist/surface.js +14 -3
- package/dist/task-composer.d.ts +2 -1
- package/dist/task-composer.js +7 -7
- package/dist/task-status.d.ts +4 -3
- package/dist/task-status.js +15 -16
- package/dist/team-http.d.ts +5 -9
- package/dist/team-http.js +5 -63
- package/dist/team-ui.d.ts +2 -1
- package/dist/team-ui.js +2 -5
- package/dist/teammates-ui.d.ts +5 -4
- package/dist/teammates-ui.js +61 -90
- package/dist/teams-settings.d.ts +3 -2
- package/dist/teams-settings.js +20 -31
- package/dist/telegram-settings.d.ts +3 -2
- package/dist/telegram-settings.js +14 -25
- package/dist/toolroll-update-ui.d.ts +3 -2
- package/dist/toolroll-update-ui.js +35 -39
- 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 -1
- package/dist/server/shared.d.ts +0 -3151
- package/dist/server/shared.js +0 -12001
package/dist/coding-ui.js
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
|
|
3
|
-
const hidden = (values) => Object.entries(values).map(([name, value]) => `<input type="hidden" name="${escape(name)}" value="${escape(value)}">`).join('');
|
|
2
|
+
import { html, joinHtml, postForm } from './html.js';
|
|
4
3
|
function linkedText(text) {
|
|
5
4
|
const pattern = /\[([^\]\n]+)\]\((https?:\/\/[^\s)]+)\)|\x60([^\x60\n]+)\x60|\*\*([^*\n]+)\*\*|(https?:\/\/[^\s<>]+)/g;
|
|
6
5
|
let end = 0;
|
|
7
|
-
|
|
6
|
+
const output = [];
|
|
8
7
|
for (const match of text.matchAll(pattern)) {
|
|
9
|
-
output
|
|
8
|
+
output.push(html `${text.slice(end, match.index)}`);
|
|
10
9
|
if (match[3] !== undefined)
|
|
11
|
-
output
|
|
10
|
+
output.push(html `<code>${match[3]}</code>`);
|
|
12
11
|
else if (match[4] !== undefined)
|
|
13
|
-
output
|
|
12
|
+
output.push(html `<strong>${match[4]}</strong>`);
|
|
14
13
|
else {
|
|
15
14
|
const raw = match[2] ?? match[5];
|
|
16
15
|
const url = raw.replace(/[.,;!?]+$/, '');
|
|
17
|
-
output
|
|
16
|
+
output.push(html `<a href="${url}" target="_blank" rel="noopener noreferrer">${match[1] ?? url}</a>${raw.slice(url.length)}`);
|
|
18
17
|
}
|
|
19
18
|
end = match.index + match[0].length;
|
|
20
19
|
}
|
|
21
|
-
return output
|
|
20
|
+
return html `${output}${text.slice(end)}`;
|
|
22
21
|
}
|
|
23
22
|
function friendlyCodingError(message) {
|
|
24
23
|
return /already has an active writer/i.test(message)
|
|
@@ -27,57 +26,59 @@ function friendlyCodingError(message) {
|
|
|
27
26
|
}
|
|
28
27
|
function noticeHtml(message) {
|
|
29
28
|
const friendly = friendlyCodingError(message);
|
|
30
|
-
return
|
|
29
|
+
return html `${friendly}${friendly === message ? '' : html `<details><summary>Technical details</summary><pre>${message}</pre></details>`}`;
|
|
31
30
|
}
|
|
32
31
|
const sessionUrl = (id) => `/code/${encodeURIComponent(id)}`;
|
|
33
32
|
const active = (status) => ['starting', 'working', 'needs-input', 'stopping'].includes(status);
|
|
34
33
|
const stateLabel = (session, requests = []) => ({ starting: 'Starting Codex', ready: 'Ready', working: 'Working', 'needs-input': requests.some(r => r.kind !== 'questions') ? 'Permission needed' : 'Needs your answer', stopping: 'Stopping', interrupted: 'Stopped', failed: 'Stopped with an error', uncertain: 'Delivery not confirmed', closed: 'Session closed' }[session.status]);
|
|
35
34
|
function itemHtml(item) {
|
|
36
35
|
if (item.type === 'reasoning')
|
|
37
|
-
return
|
|
36
|
+
return html ``;
|
|
38
37
|
const role = ['user', 'userMessage'].includes(item.type) ? 'You' : ['agent', 'assistant', 'agentMessage'].includes(item.type) ? 'Codex' : null;
|
|
39
38
|
if (role)
|
|
40
|
-
return `<article class="coding-message${role === 'You' ? ' coding-user' : ''}" data-coding-item="${
|
|
39
|
+
return html `<article class="coding-message${role === 'You' ? ' coding-user' : ''}" data-coding-item="${item.id}"><strong>${role}</strong><div class="coding-text">${linkedText(item.text)}</div></article>`;
|
|
41
40
|
const title = { commandExecution: 'Command', fileChange: 'File changes', mcpToolCall: 'Tool', reasoning: 'Reasoning', plan: 'Plan', error: 'Error' }[item.type] ?? 'Activity';
|
|
42
|
-
return `<details class="coding-tool" data-coding-item="${
|
|
41
|
+
return html `<details class="coding-tool" data-coding-item="${item.id}"><summary>${title}${item.status ? html ` <span class="coding-meta">${item.status}</span>` : ''}</summary><pre>${item.text}</pre></details>`;
|
|
43
42
|
}
|
|
44
|
-
function requestHtml(request, session
|
|
43
|
+
function requestHtml(request, session) {
|
|
45
44
|
const questions = request.kind === 'questions';
|
|
46
|
-
const body = questions ? request.questions.map((q, index) => `<label>${
|
|
47
|
-
return `<section class="coding-request" data-coding-request="${
|
|
45
|
+
const body = questions ? request.questions.map((q, index) => html `<label>${q.question}<input type="text" name="question:${q.id}"${q.options.length ? html ` list="coding-options-${request.id}-${index}"` : ''} required autocomplete="off">${q.options.length ? html `<datalist id="coding-options-${request.id}-${index}">${q.options.map(o => html `<option value="${o.label}">${o.description}</option>`)}</datalist>` : ''}</label>`) : html `<pre class="coding-permission-detail">${request.detail}</pre><label>Password to approve this request<input type="password" name="password" autocomplete="current-password" required></label>`;
|
|
46
|
+
return html `<section class="coding-request" data-coding-request="${request.id}"><h3>${request.title || (questions ? 'Your answer' : 'Allow extra access?')}</h3>${postForm(`${sessionUrl(session)}/answer`, html `${body}<div class="coding-actions"><button type="submit" name="decision" value="accept">${questions ? 'Send answer' : 'Approve access'}</button><button type="submit" name="decision" value="decline" formnovalidate class="coding-secondary">${questions ? 'Cancel question' : 'Decline'}</button></div>`, { attrs: { 'data-coding-form': 'answer' }, hidden: { requestId: request.id } })}</section>`;
|
|
48
47
|
}
|
|
49
|
-
function controlsHtml(session
|
|
48
|
+
function controlsHtml(session) {
|
|
50
49
|
if (active(session.status) && session.turnId)
|
|
51
|
-
return
|
|
50
|
+
return postForm(`${sessionUrl(session.id)}/stop`, html `<button class="coding-secondary"${session.status === 'stopping' ? html ` disabled` : ''}>Stop</button>`, { attrs: { 'data-coding-form': 'stop' } });
|
|
52
51
|
if (session.status === 'uncertain')
|
|
53
|
-
return
|
|
52
|
+
return postForm(`${sessionUrl(session.id)}/${session.deliveryReviewRequired ? 'continue' : 'recover'}`, html `<button>${session.deliveryReviewRequired ? (session.nativeThreadId ? 'Continue with saved work' : 'Keep work and close session') : 'Check saved session'}</button>`, { attrs: { 'data-coding-form': 'recover' } });
|
|
54
53
|
if (!session.nativeThreadId && ['closed', 'failed', 'interrupted'].includes(session.status))
|
|
55
|
-
return
|
|
54
|
+
return html ``;
|
|
56
55
|
if (['failed', 'interrupted'].includes(session.status))
|
|
57
|
-
return
|
|
58
|
-
return
|
|
56
|
+
return postForm(`${sessionUrl(session.id)}/resume`, html `<button>Resume session</button>`, { attrs: { 'data-coding-form': 'resume' } });
|
|
57
|
+
return html ``;
|
|
59
58
|
}
|
|
60
59
|
export function codingWorkspaceHtml(input) {
|
|
61
60
|
const { selected, csrf } = input;
|
|
62
61
|
const selectedRepo = selected?.session.repo ?? input.project;
|
|
63
62
|
const projectName = (repo) => input.projects.find(p => p.path === repo)?.name ?? repo.split('/').filter(Boolean).at(-1) ?? repo;
|
|
64
63
|
const newUrl = `/code${selectedRepo ? `?project=${encodeURIComponent(selectedRepo)}` : ''}`;
|
|
65
|
-
const sessions = input.sessions.map(session => `<li><a href="${sessionUrl(session.id)}"${selected?.session.id === session.id ?
|
|
66
|
-
const
|
|
67
|
-
const
|
|
64
|
+
const sessions = joinHtml(input.sessions.map(session => html `<li><a href="${sessionUrl(session.id)}"${selected?.session.id === session.id ? html ` aria-current="page"` : ''}><span>${session.title}</span><small>${projectName(session.repo)} · <span data-coding-status-for="${session.id}">${stateLabel(session)}</span></small></a></li>`));
|
|
65
|
+
const sessionItems = input.sessions.length ? sessions : html `<li class="coding-meta">No sessions yet.</li>`;
|
|
66
|
+
const sidebar = html `<aside class="coding-sidebar"><div class="coding-sidebar-top"><h2>Sessions</h2>${selected ? html `<a class="coding-link-button" href="${newUrl}">New session</a>` : ''}</div><nav class="coding-session-list" aria-label="Coding sessions"><ul>${sessionItems}</ul></nav><details class="coding-mobile-sessions"><summary>Sessions${input.sessions.length ? ` (${input.sessions.length})` : ''}</summary><nav aria-label="Coding sessions"><ul>${sessionItems}</ul></nav></details></aside>`;
|
|
67
|
+
const warning = !input.available ? html `<p class="coding-notice">Codex is unavailable on this installation. Install and sign in to Codex on the host to start coding.</p>` : '';
|
|
68
68
|
const error = input.error ?? selected?.session.error ?? '';
|
|
69
69
|
let main;
|
|
70
70
|
if (!selected) {
|
|
71
|
-
const options = input.projects.map(p => `<option value="${
|
|
72
|
-
main = `<header class="coding-header"><h1>Build with Codex</h1></header>${warning}${input.projects.length ?
|
|
71
|
+
const options = input.projects.map(p => html `<option value="${p.path}"${selectedRepo === p.path ? html ` selected` : ''}>${p.name}</option>`);
|
|
72
|
+
main = html `<header class="coding-header"><h1>Build with Codex</h1></header>${warning}${input.projects.length ? postForm('/code/start', html `<label>Project<select name="repo" required>${options}</select></label><label>What would you like to change?<textarea name="prompt" rows="5" required maxlength="24000" placeholder="Describe the result you want…">${input.draft ?? ''}</textarea></label><details class="coding-options"><summary>Session options</summary><label>Title <span class="coding-meta">Optional</span><input name="title" maxlength="160" autocomplete="off"></label><label>Model <span class="coding-meta">Optional; uses the installed default</span><input name="model" maxlength="120" placeholder="Installed default" autocomplete="off"></label></details><p class="coding-terms">Codex uses this installation’s login and edits a separate copy of this project. It can run commands and read files outside the project. Connected tools (MCP servers) keep their own access to this computer and connected services. Additional command and file access requires your approval.</p><label>Password to start<input type="password" name="password" autocomplete="current-password" required></label><button type="submit"${input.available ? '' : html ` disabled`}>Start coding</button>`, { attrs: { 'data-coding-form': 'start', class: 'coding-start' }, hidden: { requestId: randomUUID() } }) : html `<div class="coding-empty"><p>Add a project to start coding.</p><a class="coding-link-button" href="/projects">Open projects</a></div>`}`;
|
|
73
73
|
}
|
|
74
74
|
else {
|
|
75
75
|
const s = selected.session;
|
|
76
76
|
const blocked = !s.nativeThreadId || !['ready', 'working', 'failed', 'interrupted'].includes(s.status);
|
|
77
|
-
const messages = selected.items.filter(item => item.type !== 'reasoning').map(itemHtml)
|
|
78
|
-
|
|
77
|
+
const messages = selected.items.filter(item => item.type !== 'reasoning').map(itemHtml);
|
|
78
|
+
const composer = postForm(`${sessionUrl(s.id)}/send`, html `<label for="coding-prompt">Message Codex</label><textarea id="coding-prompt" name="prompt" rows="2" maxlength="24000" required placeholder="Describe a change or ask about the result…">${input.draft ?? ''}</textarea><div class="coding-actions"><button type="submit"${blocked ? html ` disabled` : ''}>${s.status === 'working' ? 'Send update' : 'Send message'}</button></div>`, { attrs: { 'data-coding-form': 'send', class: 'coding-composer', hidden: s.status === 'closed' }, hidden: { requestId: randomUUID() } });
|
|
79
|
+
main = html `<header class="coding-header"><div><h1>${s.title}</h1><p class="coding-meta">${projectName(s.repo)} · Codex${s.model ? html ` · ${s.model}` : ''}</p></div><details class="coding-session-detail"><summary aria-label="Session details">Details</summary><dl><dt>Branch</dt><dd>${s.branch}</dd><dt>Worktree</dt><dd>${s.worktree}</dd><dt>Native session</dt><dd>${s.nativeThreadId ?? 'Not established'}</dd>${s.initialRequest ? html `<dt>Initial request</dt><dd class="coding-text">${s.initialRequest.prompt}</dd>` : ''}</dl></details></header><div class="coding-state-row"><p id="coding-state" role="status">${stateLabel(s, selected.requests)}</p><div id="coding-controls">${controlsHtml(s)}</div></div><div id="coding-requests">${selected.requests.map(r => requestHtml(r, s.id))}</div><div id="coding-conversation" class="coding-conversation" tabindex="0" aria-label="Conversation"${s.status === 'closed' && messages.length === 0 ? html ` hidden` : ''}>${messages.length ? messages : html `<p class="coding-meta coding-no-messages">No messages yet.</p>`}</div><button type="button" id="coding-latest" class="coding-secondary" hidden>Latest update ↓</button>${composer}<details id="coding-changes" class="coding-changes"><summary>Changes</summary><div id="coding-change-content"><p class="coding-meta">Open to load the current changes.</p></div><button type="button" id="coding-refresh-changes" class="coding-secondary">Refresh changes</button><noscript><a href="${sessionUrl(s.id)}/changes">View changes</a></noscript><p id="coding-shipping"${['closed', 'uncertain'].includes(s.status) ? html ` hidden` : ''}><a class="coding-link-button" href="${sessionUrl(s.id)}/ship">Review for shipping</a></p></details>`;
|
|
79
80
|
}
|
|
80
|
-
return `<section class="coding-workspace" data-coding-session="${
|
|
81
|
+
return html `<section class="coding-workspace" data-coding-session="${selected?.session.id ?? ''}" data-coding-owner="${selected?.session.owner ?? input.owner ?? ''}" data-coding-project="${selectedRepo ?? input.projects[0]?.path ?? ''}" data-coding-status="${selected?.session.status ?? ''}" data-coding-native-thread="${String(Boolean(selected?.session.nativeThreadId))}" data-coding-revision="${selected?.revision ?? -1}" data-coding-csrf="${csrf}">${sidebar}<div class="coding-main"><div id="coding-error" class="coding-notice" role="alert"${error ? '' : html ` hidden`}>${noticeHtml(error)}</div><p id="coding-connection" class="coding-meta" role="status" hidden></p>${main}</div></section>`;
|
|
81
82
|
}
|
|
82
83
|
export const CODING_CSS = `
|
|
83
84
|
.coding-workspace{display:grid;grid-template-columns:224px minmax(0,1fr);gap:24px;max-width:1220px;min-width:0;margin:0 auto;overflow-wrap:anywhere}
|
package/dist/contracts/cli.d.ts
CHANGED
|
@@ -323,13 +323,12 @@ export declare const COMMAND_OUTPUTS: {
|
|
|
323
323
|
assets: "assets";
|
|
324
324
|
auth: "auth";
|
|
325
325
|
chat: "chat";
|
|
326
|
-
"chat-stream": "chat-stream";
|
|
327
326
|
cli: "cli";
|
|
328
327
|
console: "console";
|
|
329
328
|
decisions: "decisions";
|
|
330
|
-
"flow-stream": "flow-stream";
|
|
331
329
|
flows: "flows";
|
|
332
330
|
health: "health";
|
|
331
|
+
live: "live";
|
|
333
332
|
mcp: "mcp";
|
|
334
333
|
metrics: "metrics";
|
|
335
334
|
other: "other";
|
|
@@ -339,7 +338,6 @@ export declare const COMMAND_OUTPUTS: {
|
|
|
339
338
|
settings: "settings";
|
|
340
339
|
tasks: "tasks";
|
|
341
340
|
team: "team";
|
|
342
|
-
"team-stream": "team-stream";
|
|
343
341
|
}> & z.core.$partial, z.ZodObject<{
|
|
344
342
|
count: z.ZodNumber;
|
|
345
343
|
p50Ms: z.ZodNullable<z.ZodNumber>;
|
|
@@ -177,7 +177,7 @@ export declare const CONSOLE_FORMS: {
|
|
|
177
177
|
};
|
|
178
178
|
export type ConsoleFormName = keyof typeof CONSOLE_FORMS;
|
|
179
179
|
/** The console POSTs whose handlers read no field (sign-out not even the shared guard): their contract is the empty form. */
|
|
180
|
-
export declare const BODILESS_POSTS: readonly ["POST /settings/lead/on", "POST /r/<run>/draft-repair", "POST /ledger/checkpoint", "POST /settings/backups/now", "POST /mode/revoke", "POST /settings/telegram/retry", "POST /providers/<provider>/resume", "POST /logout", "POST /t/<task>/unhold", "POST /t/<task>/plan", "POST /t/<task>/retry-review"];
|
|
180
|
+
export declare const BODILESS_POSTS: readonly ["POST /settings/models/<unknown action>", "POST /settings/lead/on", "POST /r/<run>/draft-repair", "POST /ledger/checkpoint", "POST /settings/backups/now", "POST /mode/revoke", "POST /settings/telegram/retry", "POST /providers/<provider>/resume", "POST /logout", "POST /t/<task>/unhold", "POST /t/<task>/plan", "POST /t/<task>/retry-review"];
|
|
181
181
|
/** The field names a console form's handler may read. */
|
|
182
182
|
export type FormFieldOf<N extends ConsoleFormName> = (typeof CONSOLE_FORMS)[N] extends FormContract<infer K> ? K : never;
|
|
183
183
|
/** `GET /ledger?format=json`: the chain's state, a page of entries (newest first) and the cursor for the next. */
|
|
@@ -73,10 +73,10 @@ export const CONSOLE_FORMS = {
|
|
|
73
73
|
mutationGuard: formContract("POST (every signed-in form)", ["csrf", "token", "digest", "nonce", "confirm"]),
|
|
74
74
|
ledgerTarget: formContract("POST (the action ledger's project)", ["repo"]),
|
|
75
75
|
projectCeiling: formContract("POST (project ceiling)", ["repo"]),
|
|
76
|
-
demo: formContract("POST /chat/demo/<
|
|
76
|
+
demo: formContract("POST /chat/demo/ask, /chat/demo/<id>/approve|change|revise|complete", ["message", "note"]),
|
|
77
77
|
updatesDismiss: formContract("POST /settings/updates/dismiss", ["version", "quiet", "return"]),
|
|
78
78
|
updatesChecks: formContract("POST /settings/updates/checks", ["check"]),
|
|
79
|
-
code: formContract("POST /code, /code/start, /code/<session>/<act>", ["repo", "password", "title", "model", "prompt", "requestId", "base", "candidate", "goal", "acceptance", "visual", "decision", "answers"], { prefixes: ["question:"] }),
|
|
79
|
+
code: formContract("POST /code, /code/start, /code/<session>/<act>, /code/<anything else>", ["repo", "password", "title", "model", "prompt", "requestId", "base", "candidate", "goal", "acceptance", "visual", "decision", "answers"], { prefixes: ["question:"] }),
|
|
80
80
|
skillsRevise: formContract("POST /settings/skills/revise", ["run", "repo", "feedback", "nonce"]),
|
|
81
81
|
skillsChange: formContract("POST /settings/skills/import, /settings/skills/change", ["repo", "identity", "revision", "method", "content", "files", "url", "action", "sha", "sample", "nonce", "restore"]),
|
|
82
82
|
kit: formContract("POST /kits/<kit>/<act>", ["repo"]),
|
|
@@ -111,11 +111,11 @@ export const CONSOLE_FORMS = {
|
|
|
111
111
|
knowledgeChange: formContract("POST /settings/knowledge/change", ["repo", "action", "identity", "revision", "instructions", "title", "content", "path", "id", "restore"]),
|
|
112
112
|
learningChange: formContract("POST /settings/learning/change", ["repo", "action", "identity", "revision", "lesson", "version", "sha"]),
|
|
113
113
|
projectSetup: formContract("POST /control/setup-preview, /control/setup-approve, /control/instructions-preview, /control/instructions-approve", ["repo", "nonce", "fingerprint", "token", "provider", "model", "command", "seconds"]),
|
|
114
|
-
slack: formContract("POST /settings/slack
|
|
114
|
+
slack: formContract("POST /settings/slack/connect|pair|unpair|disconnect|alerts", ["password", "app-token", "bot-token"]),
|
|
115
115
|
chatApproval: formContract("POST /settings/chat-approval/confirm, /settings/chat-approval/save, /settings/chat-approval/off", ["scope", "full-access", "cap-usd", "nonce", "digest", "token"]),
|
|
116
116
|
telegramPair: formContract("POST /settings/telegram/pair, /settings/telegram/unpair", ["password"]),
|
|
117
|
-
teams: formContract("POST /settings/teams
|
|
118
|
-
discord: formContract("POST /settings/discord
|
|
117
|
+
teams: formContract("POST /settings/teams/connect|pair|unpair|disconnect|alerts", ["password", "app-id", "tenant", "secret"]),
|
|
118
|
+
discord: formContract("POST /settings/discord/connect|pair|unpair|disconnect|alerts", ["password", "bot-token"]),
|
|
119
119
|
messaging: formContract("POST /settings/messaging", ["primary"]),
|
|
120
120
|
permissionDefault: formContract("POST /settings/permission-default", ["permission-mode"]),
|
|
121
121
|
qualityDefault: formContract("POST /settings/quality-default", ["quality-mode"]),
|
|
@@ -188,6 +188,7 @@ export const CONSOLE_FORMS = {
|
|
|
188
188
|
};
|
|
189
189
|
/** The console POSTs whose handlers read no field (sign-out not even the shared guard): their contract is the empty form. */
|
|
190
190
|
export const BODILESS_POSTS = [
|
|
191
|
+
"POST /settings/models/<unknown action>",
|
|
191
192
|
"POST /settings/lead/on",
|
|
192
193
|
"POST /r/<run>/draft-repair",
|
|
193
194
|
"POST /ledger/checkpoint",
|
|
@@ -21,13 +21,12 @@ export declare const healthSnapshotSchema: z.ZodObject<{
|
|
|
21
21
|
assets: "assets";
|
|
22
22
|
auth: "auth";
|
|
23
23
|
chat: "chat";
|
|
24
|
-
"chat-stream": "chat-stream";
|
|
25
24
|
cli: "cli";
|
|
26
25
|
console: "console";
|
|
27
26
|
decisions: "decisions";
|
|
28
|
-
"flow-stream": "flow-stream";
|
|
29
27
|
flows: "flows";
|
|
30
28
|
health: "health";
|
|
29
|
+
live: "live";
|
|
31
30
|
mcp: "mcp";
|
|
32
31
|
metrics: "metrics";
|
|
33
32
|
other: "other";
|
|
@@ -37,7 +36,6 @@ export declare const healthSnapshotSchema: z.ZodObject<{
|
|
|
37
36
|
settings: "settings";
|
|
38
37
|
tasks: "tasks";
|
|
39
38
|
team: "team";
|
|
40
|
-
"team-stream": "team-stream";
|
|
41
39
|
}> & z.core.$partial, z.ZodObject<{
|
|
42
40
|
count: z.ZodNumber;
|
|
43
41
|
p50Ms: z.ZodNullable<z.ZodNumber>;
|
package/dist/control-ui.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/** Shared setup views over the existing project configuration and approval doors. */
|
|
2
|
+
import { type Html } from "./html.js";
|
|
2
3
|
import type { ProviderId } from "./provider.js";
|
|
3
4
|
import type { ProviderConnection } from "./provider-connection.js";
|
|
4
5
|
import { type ModelChoice } from "./setup-guide.js";
|
|
5
6
|
import type { SetupInputs } from "./control-setup.js";
|
|
6
7
|
import { type OpenRouterModels } from "./openrouter-models.js";
|
|
7
|
-
export declare function hiddenFields(values: Record<string, string>): string;
|
|
8
8
|
export declare function connectionWords(connection: ProviderConnection): string;
|
|
9
|
-
export declare function connectionHtml(provider: ProviderId, connection: ProviderConnection, task?: string):
|
|
9
|
+
export declare function connectionHtml(provider: ProviderId, connection: ProviderConnection, task?: string): Html;
|
|
10
10
|
export declare function controlSetupHtml(data: {
|
|
11
11
|
repo: string;
|
|
12
12
|
csrf: string;
|
|
@@ -25,5 +25,6 @@ export declare function controlSetupHtml(data: {
|
|
|
25
25
|
installed: boolean;
|
|
26
26
|
message?: string;
|
|
27
27
|
};
|
|
28
|
-
}):
|
|
29
|
-
|
|
28
|
+
}): Html;
|
|
29
|
+
/** `fields` may still carry the session's csrf (a caller's habit): postForm writes the one CSRF field. */
|
|
30
|
+
export declare function setupPreviewHtml(inputs: SetupInputs, fields: Record<string, string>): Html;
|
package/dist/control-ui.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
+
/** Shared setup views over the existing project configuration and approval doors. */
|
|
2
|
+
import { html, joinHtml, postForm } from "./html.js";
|
|
1
3
|
import { ASSISTANTS } from "./setup-guide.js";
|
|
2
4
|
import { openRouterPicker } from "./openrouter-models.js";
|
|
3
|
-
const escape = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
4
|
-
export function hiddenFields(values) {
|
|
5
|
-
return Object.entries(values).map(([name, value]) => `<input type="hidden" name="${escape(name)}" value="${escape(value)}">`).join("");
|
|
6
|
-
}
|
|
7
5
|
export function connectionWords(connection) {
|
|
8
6
|
return {
|
|
9
7
|
connected: "Connected", "signed-out": "Not signed in", "not-installed": "CLI not installed",
|
|
@@ -12,42 +10,19 @@ export function connectionWords(connection) {
|
|
|
12
10
|
}
|
|
13
11
|
export function connectionHtml(provider, connection, task = "") {
|
|
14
12
|
const query = new URLSearchParams({ provider, ...(task ? { task } : {}) });
|
|
15
|
-
return `<div class="card assistant-account"><p><strong>${
|
|
16
|
-
`<p class="meta">${[connection.email, connection.plan, connection.method].filter(Boolean).map(one => escape(one)).join(" · ")}</p>` +
|
|
17
|
-
`<p class="meta">Checked ${escape(new Date(connection.checkedAt).toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", timeZoneName: "short" }))} on the computer running this console. Sign-in does not prove model access or remaining quota.</p>` +
|
|
18
|
-
`<a class="button-link" href="/control/connection?${escape(query.toString())}">${connection.state === "connected" ? "Manage connection" : "Connect account"}</a> · ` +
|
|
19
|
-
`<a href="/control?${escape(query.toString())}&check-connection=1">Check again</a></div>`;
|
|
13
|
+
return html `<div class="card assistant-account"><p><strong>${ASSISTANTS[provider].name} · ${connectionWords(connection)}</strong></p><p class="meta">${[connection.email, connection.plan, connection.method].filter(Boolean).join(" · ")}</p><p class="meta">Checked ${new Date(connection.checkedAt).toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", timeZoneName: "short" })} on the computer running this console. Sign-in does not prove model access or remaining quota.</p><a class="button-link" href="/control/connection?${query.toString()}">${connection.state === "connected" ? "Manage connection" : "Connect account"}</a> · <a href="/control?${query.toString()}&check-connection=1">Check again</a></div>`;
|
|
20
14
|
}
|
|
21
15
|
export function controlSetupHtml(data) {
|
|
22
16
|
const { provider, inputs, task = "" } = data;
|
|
23
17
|
const query = new URLSearchParams({ provider, ...(task ? { task } : {}) });
|
|
24
18
|
const model = data.catalog !== null
|
|
25
19
|
? openRouterPicker(data.catalog, inputs.model || null, `/control?${query}&refresh-models=1`)
|
|
26
|
-
: `<label>Model<input type="text" name="model" value="${
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
`<p class="meta">Choose the default builder for new tasks and the preparation command for this project.</p>` +
|
|
30
|
-
`<nav class="assistant-picker row" aria-label="Choose provider">${Object.keys(ASSISTANTS).map(id => `<a class="button-link" href="/control?${escape(new URLSearchParams({ provider: id, ...(task ? { task } : {}) }).toString())}"${id === provider ? ' aria-current="page"' : ""}>${ASSISTANTS[id].name}</a>`).join(" ")}</nav>` +
|
|
31
|
-
connectionHtml(provider, data.connection, task) +
|
|
32
|
-
`<form method="post" action="/control/setup-preview" class="card">${hiddenFields({ csrf: data.csrf, repo: data.repo, provider, task })}` + model +
|
|
33
|
-
`<details class="setup-advanced"><summary>Project preparation</summary>` +
|
|
34
|
-
(data.preparation === null ? "" : `<p class="meta">Suggested from ${escape(data.preparation.evidence)}: ${escape(data.preparation.label)} — <code>${escape(data.preparation.command)}</code>. Review before approving.</p>`) +
|
|
35
|
-
`<label>Preparation command<textarea name="command" rows="2" maxlength="2000">${escape(inputs.command)}</textarea></label>` +
|
|
36
|
-
`<label>Preparation timeout in seconds<input name="seconds" type="number" min="1" max="3600" value="${escape(inputs.seconds)}" required></label>` +
|
|
37
|
-
`<p class="meta">Preparation applies to subsequent runs, including already approved tasks. Saving setup does not execute it.</p></details>` +
|
|
38
|
-
`<button type="submit">Review project setup</button></form>` +
|
|
39
|
-
`<section class="card"><h2>Agent instructions</h2>` +
|
|
40
|
-
(data.instructions.message ? `<p>${escape(data.instructions.message)}</p>` : data.instructions.installed ? `<p>Toolroll instructions are installed.</p>` :
|
|
41
|
-
`<p>Give the agent the project's Toolroll handoff instructions.</p><form method="post" action="/control/instructions-preview">${hiddenFields({ csrf: data.csrf, repo: data.repo })}<button>Review instructions</button></form>`) +
|
|
42
|
-
`</section><p><a href="/fleet">Configure planning, repair, and review agents</a> · <a href="/tasks/new">Describe a task</a></p>`;
|
|
20
|
+
: html `<label>Model<input type="text" name="model" value="${inputs.model}" list="setup-models" required autocomplete="off" placeholder="Exact model id or supported alias"></label><datalist id="setup-models">${data.models.map(one => html `<option value="${one.value}">${one.label}</option>`)}</datalist>`;
|
|
21
|
+
return html `<h1>Project setup</h1><p>${data.repo}</p><p class="meta">Choose the default builder for new tasks and the preparation command for this project.</p><nav class="assistant-picker row" aria-label="Choose provider">${joinHtml(Object.keys(ASSISTANTS).map(id => html `<a class="button-link" href="/control?${new URLSearchParams({ provider: id, ...(task ? { task } : {}) }).toString()}"${id === provider ? html ` aria-current="page"` : ""}>${ASSISTANTS[id].name}</a>`), " ")}</nav>${connectionHtml(provider, data.connection, task)}${postForm("/control/setup-preview", html `${model}<details class="setup-advanced"><summary>Project preparation</summary>${data.preparation === null ? "" : html `<p class="meta">Suggested from ${data.preparation.evidence}: ${data.preparation.label} — <code>${data.preparation.command}</code>. Review before approving.</p>`}<label>Preparation command<textarea name="command" rows="2" maxlength="2000">${inputs.command}</textarea></label><label>Preparation timeout in seconds<input name="seconds" type="number" min="1" max="3600" value="${inputs.seconds}" required></label><p class="meta">Preparation applies to subsequent runs, including already approved tasks. Saving setup does not execute it.</p></details><button type="submit">Review project setup</button>`, { attrs: { class: "card" }, hidden: { repo: data.repo, provider, task } })}<section class="card"><h2>Agent instructions</h2>${data.instructions.message ? html `<p>${data.instructions.message}</p>` : data.instructions.installed ? html `<p>Toolroll instructions are installed.</p>` :
|
|
22
|
+
html `<p>Give the agent the project's Toolroll handoff instructions.</p>${postForm("/control/instructions-preview", html `<button>Review instructions</button>`, { hidden: { repo: data.repo } })}`}</section><p><a href="/fleet">Configure planning, repair, and review agents</a> · <a href="/tasks/new">Describe a task</a></p>`;
|
|
43
23
|
}
|
|
24
|
+
/** `fields` may still carry the session's csrf (a caller's habit): postForm writes the one CSRF field. */
|
|
44
25
|
export function setupPreviewHtml(inputs, fields) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
`<p><strong>Preparation:</strong></p><pre class="recap">${escape(inputs.command || "No preparation command")}</pre>` +
|
|
48
|
-
`<p>Preparation timeout: ${escape(inputs.seconds)} seconds.</p>` +
|
|
49
|
-
`<p>The builder default applies to new tasks. Preparation applies to subsequent runs, including already approved tasks. Existing signed task settings and verification rules remain in force.</p>` +
|
|
50
|
-
`<form method="post" action="/control/setup-approve">${hiddenFields({ ...fields, ...inputs })}` +
|
|
51
|
-
`<label>Your password<input type="password" name="token" autocomplete="current-password" required></label>` +
|
|
52
|
-
`<button>Approve project setup</button></form></div>`;
|
|
26
|
+
const { csrf: _csrf, ...rest } = fields;
|
|
27
|
+
return html `<h1>Approve project setup</h1><div class="card"><p>${fields.repo ?? ""}</p><p><strong>Builder:</strong> ${inputs.provider} · ${inputs.model}</p><p><strong>Preparation:</strong></p><pre class="recap">${inputs.command || "No preparation command"}</pre><p>Preparation timeout: ${inputs.seconds} seconds.</p><p>The builder default applies to new tasks. Preparation applies to subsequent runs, including already approved tasks. Existing signed task settings and verification rules remain in force.</p>${postForm("/control/setup-approve", html `<label>Your password<input type="password" name="token" autocomplete="current-password" required></label><button>Approve project setup</button>`, { hidden: { ...rest, ...inputs } })}</div>`;
|
|
53
28
|
}
|
package/dist/credentials-ui.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* can see and end everyone's.
|
|
5
5
|
*/
|
|
6
6
|
import type { ApiTokenRow, WebSessionRow } from "./store.js";
|
|
7
|
+
import { type Html } from "./html.js";
|
|
7
8
|
export declare const CREDENTIALS_CSS: string;
|
|
8
9
|
/** "Chrome on macOS", from a user agent, for a person to recognise a session. */
|
|
9
10
|
export declare function deviceWords(agent: string | null): string;
|
|
@@ -19,12 +20,12 @@ export type CredentialsView = {
|
|
|
19
20
|
/** Request limits (request-budget-ui.ts): words for each token's line, and the operator's disclosure. */
|
|
20
21
|
limits?: {
|
|
21
22
|
note: (token: ApiTokenRow) => string;
|
|
22
|
-
section:
|
|
23
|
+
section: Html;
|
|
23
24
|
};
|
|
24
25
|
};
|
|
25
|
-
export declare function credentialsHtml(view: CredentialsView,
|
|
26
|
+
export declare function credentialsHtml(view: CredentialsView, notice: {
|
|
26
27
|
said?: string | null;
|
|
27
28
|
problem?: string | null;
|
|
28
|
-
}):
|
|
29
|
+
}): Html;
|
|
29
30
|
/** The token, once: a focused page with no script. */
|
|
30
|
-
export declare function tokenShownHtml(name: string, token: string, access: "read" | "act", expiresAt: string):
|
|
31
|
+
export declare function tokenShownHtml(name: string, token: string, access: "read" | "act", expiresAt: string): Html;
|
package/dist/credentials-ui.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { daysLeft, expiryNoticeDue, TOKEN_DAYS, tokenLive } from "./api-tokens.js";
|
|
2
2
|
import { projectName } from "./project.js";
|
|
3
|
-
|
|
3
|
+
import { html, postForm } from "./html.js";
|
|
4
4
|
export const CREDENTIALS_CSS = `.credentials{max-width:820px;min-width:0}.credentials .card{padding:14px 16px;margin:12px 0}.credentials h2{font-size:1.05rem;margin:0 0 8px}` +
|
|
5
5
|
`.credentials .rows{display:grid;gap:0;border:1px solid var(--so-line);border-radius:10px;overflow:hidden}.credentials .row-item{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 14px;border-bottom:1px solid var(--so-line)}` +
|
|
6
6
|
`.credentials .row-item:last-child{border-bottom:0}.credentials .row-item p{margin:0}.credentials .row-item .meta{font-size:.85rem}.credentials form{margin:0}.credentials button{min-height:40px;white-space:nowrap}` +
|
|
@@ -22,39 +22,38 @@ const when = (value, now) => {
|
|
|
22
22
|
const minutes = Math.round((now - at) / 60_000);
|
|
23
23
|
return minutes < 1 ? "just now" : minutes < 60 ? `${minutes} min ago` : minutes < 60 * 24 ? `${Math.round(minutes / 60)} h ago` : new Date(at).toISOString().slice(0, 10);
|
|
24
24
|
};
|
|
25
|
-
export function credentialsHtml(view,
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
`${one.here ? "" : post({ action: "end-session", session: one.idHash }, "Sign out")}</div>`).join("")}</div>`;
|
|
25
|
+
export function credentialsHtml(view, notice) {
|
|
26
|
+
const post = (fields, label, style = "secondary") => postForm("/settings/sessions", html `<button class="${style}">${label}</button>`, { hidden: { ...fields, ...(view.everyone ? { everyone: "1" } : {}) } });
|
|
27
|
+
const note = notice.problem ? html `<p class="problem" role="alert">${notice.problem}</p>` : notice.said ? html `<p role="status">${notice.said}</p>` : "";
|
|
28
|
+
const toggle = view.canSeeEveryone ? html `<p class="meta"><a href="/settings/sessions${view.everyone ? "" : "?everyone=1"}">${view.everyone ? "Show only mine" : "Show everyone's"}</a></p>` : "";
|
|
29
|
+
const sessions = view.sessions.length === 0 ? html `<p class="meta">No one else is signed in.</p>` : html `<div class="rows">${view.sessions.map(one => html `<div class="row-item" data-session="${one.idHash.slice(0, 12)}"><div><p>${view.everyone ? html `<strong>${one.account}</strong> · ` : ""}${deviceWords(one.agent)}${one.here ? html `<span class="badge-here">This browser</span>` : ""}</p>\
|
|
30
|
+
<p class="meta">${one.ssoAt === null ? "Password" : "Identity provider"} · ${one.address ?? "unknown address"} · active ${when(one.lastSeen, view.now)} · signed in ${when(one.createdAt, view.now)}</p></div>\
|
|
31
|
+
${one.here ? "" : post({ action: "end-session", session: one.idHash }, "Sign out")}</div>`)}</div>`;
|
|
33
32
|
const others = view.sessions.filter(one => !one.here && one.account === view.who).length;
|
|
34
33
|
const endOthers = others > 0 && !view.everyone ? post({ action: "end-others" }, `Sign out everywhere else (${others})`) : "";
|
|
35
34
|
const live = view.tokens.filter(one => tokenLive(one, view.now));
|
|
36
35
|
// v111: its projects, and one state: when it stops (soon, said once and marked) or, once replaced, when it ends.
|
|
37
36
|
const ends = (one) => {
|
|
38
37
|
if (one.replacedBy !== null && one.overlapUntil !== null)
|
|
39
|
-
return `<span class="soon">Replaced · stops ${
|
|
38
|
+
return html `<span class="soon">Replaced · stops ${one.overlapUntil.slice(11, 16)} UTC</span>`;
|
|
40
39
|
if (expiryNoticeDue(one, view.now) === null)
|
|
41
|
-
return `Expires ${
|
|
40
|
+
return html `Expires ${one.expiresAt.slice(0, 10)}`;
|
|
42
41
|
const days = daysLeft(one.expiresAt, view.now);
|
|
43
|
-
return `<span class="soon">Expires ${days <= 1 ? "within a day" : `in ${days} days`}</span>`;
|
|
42
|
+
return html `<span class="soon">Expires ${days <= 1 ? "within a day" : `in ${days} days`}</span>`;
|
|
44
43
|
};
|
|
45
|
-
const scope = (one) => one.projects === null ? "All projects" : one.projects.length === 0 ? "No projects" : one.projects.map(repo =>
|
|
46
|
-
const tokens = live.length === 0 ? `<p class="meta">No API tokens.</p>` : `<div class="rows">${live.map(one => `<div class="row-item" data-token="${
|
|
47
|
-
|
|
48
|
-
const create = `<details class="card"><summary>New API token</summary
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return `<section class="credentials">${note}${toggle}<h2>Signed in</h2>${sessions}${endOthers}<h2 style="margin-top:1.5rem">API tokens</h2
|
|
54
|
-
|
|
44
|
+
const scope = (one) => one.projects === null ? "All projects" : one.projects.length === 0 ? "No projects" : one.projects.map(repo => projectName(repo)).join(", ");
|
|
45
|
+
const tokens = live.length === 0 ? html `<p class="meta">No API tokens.</p>` : html `<div class="rows">${live.map(one => html `<div class="row-item" data-token="${one.id}"><div><p>${view.everyone ? html `<strong>${one.account}</strong> · ` : ""}${one.name} · ${one.access === "act" ? "Can act" : "Read only"}</p>\
|
|
46
|
+
<p class="meta">${scope(one)} · ${ends(one)} · last used ${when(one.lastUsedAt, view.now)}${view.limits === undefined ? "" : ` · ${view.limits.note(one)}`}</p></div>${post({ action: "revoke-token", token: one.id }, "Revoke")}</div>`)}</div>`;
|
|
47
|
+
const create = html `<details class="card"><summary>New API token</summary>${postForm("/settings/sessions", html `\
|
|
48
|
+
<label>Name<input type="text" name="name" maxlength="60" required placeholder="for example: CI"></label>\
|
|
49
|
+
<label>It can<select name="access"><option value="read">Read (tasks, results, the ledger)</option><option value="act">Act as you (file and manage work; never approve)</option></select></label>\
|
|
50
|
+
<label>Expires in<select name="days">${TOKEN_DAYS.map(days => html `<option value="${days}"${days === 90 ? " selected" : ""}>${days} days</option>`)}</select></label>\
|
|
51
|
+
<label>Your Toolroll password<input type="password" name="password" autocomplete="current-password" required></label><button>Make the token</button>`, { attrs: { class: "create" }, hidden: { action: "create-token" } })}</details>`;
|
|
52
|
+
return html `<section class="credentials">${note}${toggle}<h2>Signed in</h2>${sessions}${endOthers}<h2 style="margin-top:1.5rem">API tokens</h2>\
|
|
53
|
+
<p class="meta">For scripts and CI: send <code>Authorization: Bearer <token></code>. A token can't approve anything.</p>${tokens}${view.everyone ? "" : create}${view.limits?.section ?? ""}</section>`;
|
|
55
54
|
}
|
|
56
55
|
/** The token, once: a focused page with no script. */
|
|
57
56
|
export function tokenShownHtml(name, token, access, expiresAt) {
|
|
58
|
-
return `<h1>${
|
|
59
|
-
|
|
57
|
+
return html `<h1>${name}</h1><div class="card"><p>Your API token, shown once. Only a hash is kept, so copy it now.</p><p class="mono secret-value">${token}</p>\
|
|
58
|
+
<p class="meta">${access === "act" ? "It acts as you (never approves)" : "It reads"} until ${expiresAt.slice(0, 10)}. Revoke it any time on Sessions & tokens.</p></div><p class="meta"><a href="/settings/sessions">Back to Sessions & tokens</a></p>`;
|
|
60
59
|
}
|
package/dist/demo-chat.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Html } from "./html.js";
|
|
1
2
|
import type { DemoExchange } from "./demo.js";
|
|
2
3
|
/** What a finished exchange's stored result holds, read back from its evidence. */
|
|
3
4
|
export type DemoResultView = {
|
|
@@ -17,14 +18,13 @@ export type DemoResultView = {
|
|
|
17
18
|
taskHref: string;
|
|
18
19
|
};
|
|
19
20
|
/** The conversation itself; the live script swaps this region in place. */
|
|
20
|
-
export declare function demoThreadHtml(exchanges: readonly DemoExchange[],
|
|
21
|
+
export declare function demoThreadHtml(exchanges: readonly DemoExchange[], resultOf: (exchange: DemoExchange) => DemoResultView | null): Html;
|
|
21
22
|
export declare function demoChatHtml(input: {
|
|
22
23
|
exchanges: readonly DemoExchange[];
|
|
23
|
-
csrf: string;
|
|
24
24
|
version: number;
|
|
25
25
|
problem: string | null;
|
|
26
26
|
resultOf: (exchange: DemoExchange) => DemoResultView | null;
|
|
27
|
-
}):
|
|
27
|
+
}): Html;
|
|
28
28
|
/**
|
|
29
29
|
* Keeps a running build live: polls while one is working and swaps the
|
|
30
30
|
* thread in place, unless the visitor is typing inside it. Enter sends.
|