toolroll 0.9.67 → 0.9.68
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 +36 -0
- package/dist/acp-client.js +1 -3
- package/dist/agent-board.d.ts +0 -1
- package/dist/agent-board.js +0 -1
- package/dist/agent-fence.js +3 -0
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +31 -29
- package/dist/builder.js +3 -26
- package/dist/canonical-json.d.ts +14 -0
- package/dist/canonical-json.js +36 -0
- package/dist/chat-actions.js +1 -2
- package/dist/chat-admission.d.ts +19 -0
- package/dist/chat-admission.js +22 -0
- package/dist/chat-channel.js +1 -1
- package/dist/chat-delivery.js +0 -1
- package/dist/chat-follow.d.ts +17 -0
- package/dist/chat-follow.js +32 -0
- package/dist/chat-messages.js +0 -1
- package/dist/chat-settings-page.d.ts +23 -0
- package/dist/chat-settings-page.js +41 -0
- package/dist/claim.js +3 -6
- package/dist/claude-print.d.ts +12 -0
- package/dist/claude-print.js +34 -0
- package/dist/cli-http.d.ts +0 -7
- package/dist/cli-http.js +0 -21
- package/dist/cli.d.ts +3 -10
- package/dist/cli.js +15 -24
- package/dist/coding-provider.js +2 -1
- package/dist/coding-workspace.js +1 -1
- package/dist/{team-cli.d.ts → connect-cli.d.ts} +5 -9
- package/dist/connect-cli.js +331 -0
- package/dist/containment.d.ts +0 -2
- package/dist/containment.js +2 -6
- package/dist/contracts/chat-actions.d.ts +0 -1
- package/dist/contracts/chat-content.d.ts +0 -4
- package/dist/contracts/chat-content.js +0 -2
- package/dist/contracts/chat-tables.d.ts +0 -1
- package/dist/contracts/cli.d.ts +0 -62
- package/dist/contracts/cli.js +0 -4
- package/dist/contracts/coding-workspace.js +1 -1
- package/dist/contracts/contract.js +1 -1
- package/dist/contracts/flow-card.d.ts +0 -1
- package/dist/contracts/flow-propose.d.ts +0 -1
- package/dist/contracts/flow-send.js +1 -1
- package/dist/contracts/gateway-tools.d.ts +0 -1
- package/dist/contracts/integration-metadata.js +1 -1
- package/dist/contracts/lead-tools.d.ts +0 -1
- package/dist/contracts/memory-pass.d.ts +0 -1
- package/dist/contracts/memory-pass.js +1 -1
- package/dist/contracts/plan.js +1 -1
- package/dist/contracts/project-knowledge.js +1 -1
- package/dist/contracts/project-skills.d.ts +0 -1
- package/dist/contracts/proof.js +1 -1
- package/dist/contracts/recipes.js +1 -1
- package/dist/contracts/scope.js +1 -1
- package/dist/contracts/scout-report.js +1 -1
- package/dist/contracts/teams-callback.d.ts +0 -1
- package/dist/converse.d.ts +0 -6
- package/dist/converse.js +0 -19
- package/dist/daemon.js +3 -2
- package/dist/decision.d.ts +1 -3
- package/dist/deprecations.d.ts +12 -4
- package/dist/deprecations.js +27 -4
- package/dist/desktop-update-recovery.js +1 -1
- package/dist/desktop-update.js +3 -2
- package/dist/discord-chat.js +4 -15
- package/dist/discord-settings.d.ts +2 -6
- package/dist/discord-settings.js +10 -38
- package/dist/discord.js +12 -23
- package/dist/evidence.d.ts +0 -1
- package/dist/evidence.js +0 -3
- package/dist/exec.d.ts +0 -9
- package/dist/exec.js +3 -81
- package/dist/flow-actions.js +0 -3
- package/dist/flow-draft.d.ts +1 -3
- package/dist/flow-draft.js +15 -28
- package/dist/flow-scripts.d.ts +1 -8
- package/dist/flow-scripts.js +0 -13
- package/dist/flow-send.js +1 -1
- package/dist/flow-share.d.ts +0 -2
- package/dist/flow-share.js +1 -6
- package/dist/flow-steps.js +1 -1
- package/dist/flow-triggers.d.ts +0 -1
- package/dist/flow-triggers.js +1 -2
- package/dist/flow-unattended.js +1 -12
- package/dist/flows.js +1 -1
- package/dist/health-cli.d.ts +2 -2
- package/dist/health-cli.js +1 -1
- package/dist/invoke.d.ts +0 -3
- package/dist/invoke.js +5 -3
- package/dist/is-record.d.ts +2 -0
- package/dist/is-record.js +2 -0
- package/dist/lead-doors.d.ts +1 -5
- package/dist/lead-doors.js +0 -4
- package/dist/lead-tools.js +3 -6
- package/dist/live-detail.d.ts +0 -1
- package/dist/live-detail.js +0 -6
- package/dist/live-terminal.js +2 -1
- package/dist/live.d.ts +0 -1
- package/dist/live.js +1 -2
- package/dist/mcp-core.js +0 -2
- package/dist/memory-pass.js +1 -1
- package/dist/model-catalog.js +1 -1
- package/dist/modes.js +1 -12
- package/dist/operate.js +383 -750
- package/dist/password-bearer.d.ts +0 -2
- package/dist/password-bearer.js +0 -4
- package/dist/peek-cli.js +2 -1
- package/dist/peek.d.ts +2 -26
- package/dist/peek.js +2 -168
- package/dist/phase-routing.d.ts +0 -4
- package/dist/phase-routing.js +2 -21
- package/dist/planner-source.d.ts +2 -1
- package/dist/planner-source.js +2 -12
- package/dist/planner.js +2 -14
- package/dist/process-tree.d.ts +0 -2
- package/dist/process-tree.js +0 -10
- package/dist/project-delete.d.ts +0 -2
- package/dist/project-delete.js +1 -3
- package/dist/project-learning.d.ts +0 -14
- package/dist/project-skills.d.ts +0 -1
- package/dist/project-skills.js +0 -14
- package/dist/project-tools.js +2 -10
- package/dist/provider.d.ts +0 -17
- package/dist/provider.js +0 -26
- package/dist/pulls.d.ts +0 -2
- package/dist/pulls.js +0 -9
- package/dist/release-adapters.d.ts +3 -0
- package/dist/release-adapters.js +15 -5
- package/dist/remote-exec.d.ts +3 -2
- package/dist/remote-exec.js +3 -2
- package/dist/repos.d.ts +0 -5
- package/dist/repos.js +2 -10
- package/dist/repository-context.d.ts +0 -2
- package/dist/repository-context.js +0 -4
- package/dist/restart-certification.d.ts +0 -2
- package/dist/restart-certification.js +1 -5
- package/dist/result-follow-ups.d.ts +1 -4
- package/dist/result-follow-ups.js +1 -6
- package/dist/result-review.d.ts +0 -2
- package/dist/result-review.js +0 -2
- package/dist/retention.d.ts +0 -1
- package/dist/retention.js +0 -1
- package/dist/review-context.d.ts +0 -1
- package/dist/review-context.js +1 -12
- package/dist/reviewer.d.ts +0 -16
- package/dist/reviewer.js +0 -18
- package/dist/run-closeout.js +2 -1
- package/dist/scope.js +1 -13
- package/dist/scout-browser.js +2 -1
- package/dist/serve.js +2 -2
- package/dist/server/console.css +5 -133
- package/dist/server/flows.js +2 -2
- package/dist/server/http.d.ts +2 -0
- package/dist/server/http.js +5 -0
- package/dist/server/pages.js +2 -2
- package/dist/server/people-tokens.js +3 -4
- package/dist/server/remote.js +1 -6
- package/dist/server/render-chat.d.ts +9 -2
- package/dist/server/render-chat.js +53 -7
- package/dist/server/render-results.d.ts +0 -6
- package/dist/server/render-results.js +3 -9
- package/dist/server/render-tasks.js +4 -5
- package/dist/server/route-table.d.ts +0 -4
- package/dist/server/route-table.js +0 -6
- package/dist/server/settings.js +23 -23
- package/dist/server/task-views.js +1 -1
- package/dist/server/tasks.js +3 -3
- package/dist/setup-guide.d.ts +2 -2
- package/dist/skills.d.ts +0 -6
- package/dist/skills.js +0 -8
- package/dist/slack-chat.d.ts +1 -7
- package/dist/slack-chat.js +5 -18
- package/dist/slack-settings.d.ts +2 -6
- package/dist/slack-settings.js +10 -38
- package/dist/slack.js +12 -23
- package/dist/sleep.d.ts +2 -0
- package/dist/sleep.js +2 -0
- package/dist/store.d.ts +0 -73
- package/dist/store.js +3 -177
- package/dist/structured-output.d.ts +0 -1
- package/dist/structured-output.js +0 -1
- package/dist/subagent-work.d.ts +0 -2
- package/dist/subagent-work.js +0 -2
- package/dist/subagents.d.ts +1 -3
- package/dist/subagents.js +20 -33
- package/dist/subscription-chat.d.ts +1 -3
- package/dist/subscription-chat.js +2 -3
- package/dist/task-sizing.d.ts +1 -4
- package/dist/task-sizing.js +3 -10
- package/dist/teams-chat.js +4 -11
- package/dist/teams.js +12 -19
- package/dist/telegram-lead.js +2 -2
- package/dist/telegram.js +1 -1
- package/dist/token-notices.d.ts +0 -11
- package/dist/token-notices.js +1 -5
- package/dist/tool-secret-store.d.ts +0 -3
- package/dist/tool-secret-store.js +2 -11
- package/dist/toolroll-update.d.ts +0 -2
- package/dist/toolroll-update.js +2 -2
- package/dist/verification-evidence.d.ts +0 -1
- package/dist/verification-evidence.js +0 -1
- package/dist/webhooks.d.ts +0 -1
- package/dist/webhooks.js +0 -1
- package/dist/workspace-ui.d.ts +0 -8
- package/dist/workspace-ui.js +1 -6
- package/package.json +1 -1
- package/dist/contracts/native-census.d.ts +0 -42
- package/dist/contracts/native-census.js +0 -36
- package/dist/process-recovery-anchor.d.ts +0 -75
- package/dist/process-recovery-anchor.js +0 -188
- package/dist/process-recovery-coalition.d.ts +0 -25
- package/dist/process-recovery-coalition.js +0 -105
- package/dist/process-recovery-eligibility.d.ts +0 -179
- package/dist/process-recovery-eligibility.js +0 -174
- package/dist/process-recovery-native.d.ts +0 -161
- package/dist/process-recovery-native.js +0 -595
- package/dist/process-recovery-provenance.d.ts +0 -163
- package/dist/process-recovery-provenance.js +0 -292
- package/dist/process-recovery-services.d.ts +0 -56
- package/dist/process-recovery-services.js +0 -191
- package/dist/process-recovery-settlement.d.ts +0 -32
- package/dist/process-recovery-settlement.js +0 -133
- package/dist/process-recovery.d.ts +0 -41
- package/dist/process-recovery.js +0 -91
- package/dist/session-brief.d.ts +0 -8
- package/dist/session-brief.js +0 -24
- package/dist/session-cli.d.ts +0 -42
- package/dist/session-cli.js +0 -271
- package/dist/session-contract.d.ts +0 -183
- package/dist/session-contract.js +0 -122
- package/dist/session-remote.d.ts +0 -19
- package/dist/session-remote.js +0 -86
- package/dist/session-service.d.ts +0 -22
- package/dist/session-service.js +0 -157
- package/dist/team-cli.js +0 -692
|
@@ -95,9 +95,6 @@ export declare function withRefusal(href: string, code: keyof typeof RESULT_REFU
|
|
|
95
95
|
/** The queue reads at most this many completed tasks — the store's own
|
|
96
96
|
* page ceiling, admission bound before it. */
|
|
97
97
|
export declare const REVIEW_QUEUE_CAP = 100;
|
|
98
|
-
/** The one return road the comment endpoint honors besides its own run
|
|
99
|
-
* page: the cockpit's selected-result link, exactly. */
|
|
100
|
-
export declare const REVIEW_RETURN: RegExp;
|
|
101
98
|
/** Review priority: a deterministic, LABELED presentation aid over facts
|
|
102
99
|
* the done list already carries. Band 0 goes first; the words name why.
|
|
103
100
|
* It is never a verdict — the stored proof verdict stays the authority,
|
|
@@ -287,9 +284,6 @@ export declare function reviewCockpitPage(chrome: Chrome, data: {
|
|
|
287
284
|
user: string;
|
|
288
285
|
now: Date;
|
|
289
286
|
}): Screen;
|
|
290
|
-
/** The selected result: intent → proof → changes → publication → acts,
|
|
291
|
-
* one scan path, every fact labeled by its source. */
|
|
292
|
-
export declare function reviewCockpitDetail(view: ReviewCockpitView, csrf: string, noted: boolean, canRetryReview?: boolean, tab?: ResultTab, user?: string): Html;
|
|
293
287
|
/** The selected result's HTML and, for the rebuilt page, the same result
|
|
294
288
|
* as data: every form and panel body is the one the HTML carries. */
|
|
295
289
|
/** Where a result is shown: its own page (/review), or the Result tab of its task's page (docs/plans/one-page-task-and-agent.md),
|
|
@@ -238,9 +238,6 @@ export function withRefusal(href, code) {
|
|
|
238
238
|
/** The queue reads at most this many completed tasks — the store's own
|
|
239
239
|
* page ceiling, admission bound before it. */
|
|
240
240
|
export const REVIEW_QUEUE_CAP = 100;
|
|
241
|
-
/** The one return road the comment endpoint honors besides its own run
|
|
242
|
-
* page: the cockpit's selected-result link, exactly. */
|
|
243
|
-
export const REVIEW_RETURN = /^\/review\?result=[A-Za-z0-9._~%-]{1,200}$/;
|
|
244
241
|
export const PRIORITY_LABELS = { 0: "needs action", 1: "review", 2: "no flags" };
|
|
245
242
|
export function reviewPriorityOf(row) {
|
|
246
243
|
if (row.assignment != null && (row.assignment.receipt?.runId ?? null) === row.runId) {
|
|
@@ -568,11 +565,6 @@ export function reviewCockpitPage(chrome, data) {
|
|
|
568
565
|
functional: { script: reviewEvidenceScript() + (selected !== null && selected.detail !== null ? RESULT_REVIEW_SCRIPT : ""), fetches: false },
|
|
569
566
|
});
|
|
570
567
|
}
|
|
571
|
-
/** The selected result: intent → proof → changes → publication → acts,
|
|
572
|
-
* one scan path, every fact labeled by its source. */
|
|
573
|
-
export function reviewCockpitDetail(view, csrf, noted, canRetryReview = false, tab = "summary", user = "") {
|
|
574
|
-
return reviewCockpitDetailParts(view, csrf, noted, canRetryReview, tab, user).html;
|
|
575
|
-
}
|
|
576
568
|
export function reviewCockpitDetailParts(view, csrf, noted, canRetryReview, tab, user, host = { kind: "review" }) {
|
|
577
569
|
const parts = [];
|
|
578
570
|
const run = view.run;
|
|
@@ -1268,7 +1260,9 @@ export function reviewDiffHtml(patch, stat, runId, commentable, anchors = new Ma
|
|
|
1268
1260
|
? html `<span class="diff-annotate-space"></span>`
|
|
1269
1261
|
: html `<button type="button" class="diff-annotate pick-line" data-path="${one.path}" data-line="${lineNumber}" data-side="${side}" aria-label="Annotate ${one.path}, ${side} line ${lineNumber}" title="Annotate this line">${annotateIcon}</button>`;
|
|
1270
1262
|
const marker = line.kind === "addition" ? "+" : line.kind === "deletion" ? "−" : line.kind === "context" ? " " : "·";
|
|
1271
|
-
|
|
1263
|
+
// Each real line names its file and line, so a comment (or a test) can find the exact row.
|
|
1264
|
+
const named = lineNumber === null || line.kind === "meta" ? "" : html ` data-diff-file="${one.path}" data-diff-line="${lineNumber}"`;
|
|
1265
|
+
return html `<div class="diff-line diff-${line.kind}"${named}>${annotate}<span class="diff-gutter">${line.oldLine ?? ""}</span><span class="diff-gutter">${line.newLine ?? ""}</span><code><b aria-hidden="true">${marker}</b>${line.text}</code></div>`;
|
|
1272
1266
|
})}</div></section>`;
|
|
1273
1267
|
});
|
|
1274
1268
|
const anchor = anchors.get(one.path);
|
|
@@ -22,17 +22,16 @@ import { starterForWork } from "../flow-starters.js";
|
|
|
22
22
|
import {} from "../gaps.js";
|
|
23
23
|
import { readAuthModeStrict } from "../keys.js";
|
|
24
24
|
import { failedAttemptSentence, retryNoteOf } from "../needs-you.js";
|
|
25
|
-
import {
|
|
25
|
+
import { chosenWords, makesNoPlan, PHASES as ROUTE_PHASES, TASK_SIZES } from "../phase-routing.js";
|
|
26
26
|
import { parseExecutionPlanDocument } from "../plan.js";
|
|
27
27
|
import {} from "../planner-source.js";
|
|
28
28
|
import { projectName } from "../project.js";
|
|
29
29
|
import { dispatchStatusToken, manualReviewOnly, plainReasonWords, reviewConflict } from "../proof.js";
|
|
30
30
|
import { providerName } from "../provider-auth.js";
|
|
31
|
-
import {
|
|
31
|
+
import {} from "../provider.js";
|
|
32
32
|
import { qualityModeTitle } from "../quality.js";
|
|
33
|
-
import {} from "../result-evidence-readers.js";
|
|
34
33
|
import { evidenceShortenedWords } from "../result-review.js";
|
|
35
|
-
import { acceptanceToLines, approvalOf,
|
|
34
|
+
import { acceptanceToLines, approvalOf, scopeAuthorityOf } from "../scope.js";
|
|
36
35
|
import {} from "../store.js";
|
|
37
36
|
import { runCostWords } from "../summary.js";
|
|
38
37
|
import {} from "../task-control.js";
|
|
@@ -44,7 +43,7 @@ import { isOverdue, oneLineOf, projectChip, regionScript, safePrUrl, screen, SEN
|
|
|
44
43
|
import { steerDestinationWords, steerStandingParts } from "../steer-status.js";
|
|
45
44
|
import { reviewHref, taskChatHref, taskHref } from "./http.js";
|
|
46
45
|
import { coordinatorProposalsSection } from "./render-chat.js";
|
|
47
|
-
import { completionReceiptCard, criterionMatrixHtml, evidenceWords, homePhaseWords, incidentWords, needsCheck, pullRequestCardHtml, reasonWords, receiptStatusOf, repairChainHtml, reportItemsHtml, reviewRetryPanel, runNoun, runOutcomeBadge, semanticCoverageHtml,
|
|
46
|
+
import { completionReceiptCard, criterionMatrixHtml, evidenceWords, homePhaseWords, incidentWords, needsCheck, pullRequestCardHtml, reasonWords, receiptStatusOf, repairChainHtml, reportItemsHtml, reviewRetryPanel, runNoun, runOutcomeBadge, semanticCoverageHtml, verificationExplanation, verificationRecovered } from "./render-results.js";
|
|
48
47
|
import {} from "./session.js";
|
|
49
48
|
import { linkWords, originNote } from "../task-source.js";
|
|
50
49
|
export function contractChangeHtml(change) {
|
|
@@ -91,9 +91,5 @@ export interface RouteDeclaration {
|
|
|
91
91
|
export declare const ROUTES: readonly RouteDeclaration[];
|
|
92
92
|
/** The first row that declares this method and path, or null: an undeclared request never reaches a handler. */
|
|
93
93
|
export declare function matchRoute(method: string, pathname: string, stage?: Stage): RouteDeclaration | null;
|
|
94
|
-
/** True when some row declares this path for another method (a wrong-method request). */
|
|
95
|
-
export declare function declaredForAnotherMethod(method: string, pathname: string, stage?: Stage): boolean;
|
|
96
94
|
/** Refuses a malformed table at startup: duplicate ids, duplicate method+pattern rows, or a row shadowed by an earlier one for its own sample. */
|
|
97
95
|
export declare function assertRouteTable(routes?: readonly RouteDeclaration[]): void;
|
|
98
|
-
/** The effective policy of every row, as plain data — what the snapshot test compares. */
|
|
99
|
-
export declare const routePolicySnapshot: () => Array<Omit<RouteDeclaration, "sample">>;
|
|
@@ -431,10 +431,6 @@ export function matchRoute(method, pathname, stage) {
|
|
|
431
431
|
}
|
|
432
432
|
return null;
|
|
433
433
|
}
|
|
434
|
-
/** True when some row declares this path for another method (a wrong-method request). */
|
|
435
|
-
export function declaredForAnotherMethod(method, pathname, stage) {
|
|
436
|
-
return compiled.some(({ route, test }) => (stage === undefined || route.stage === stage) && !methodMatches(route.method, method) && test.test(pathname));
|
|
437
|
-
}
|
|
438
434
|
/** Refuses a malformed table at startup: duplicate ids, duplicate method+pattern rows, or a row shadowed by an earlier one for its own sample. */
|
|
439
435
|
export function assertRouteTable(routes = ROUTES) {
|
|
440
436
|
const ids = new Set(), keys = new Set();
|
|
@@ -466,5 +462,3 @@ export function assertRouteTable(routes = ROUTES) {
|
|
|
466
462
|
}
|
|
467
463
|
}
|
|
468
464
|
assertRouteTable();
|
|
469
|
-
/** The effective policy of every row, as plain data — what the snapshot test compares. */
|
|
470
|
-
export const routePolicySnapshot = () => ROUTES.map(({ sample: _sample, ...rest }) => ({ ...rest }));
|
package/dist/server/settings.js
CHANGED
|
@@ -32,7 +32,7 @@ import { budgetLabel, monthOf } from "../spend.js";
|
|
|
32
32
|
import { PACKAGE_VERSION } from "../version.js";
|
|
33
33
|
import { handlersOf } from './handler-registry.js';
|
|
34
34
|
import { refuse, screen } from "./chrome.js";
|
|
35
|
-
import { HANDOFF_STYLE, redirect, respond, safeReturn, signInSpent, startedHere } from "./http.js";
|
|
35
|
+
import { HANDOFF_STYLE, redirect, respond, safeReturn, sayTo, signInSpent, startedHere } from "./http.js";
|
|
36
36
|
import { html, htmlString, joinHtml, postForm, reauth } from "../html.js";
|
|
37
37
|
import { browserCaller } from "./session.js";
|
|
38
38
|
/** settings handlers, moved without changing their route bodies. */
|
|
@@ -127,7 +127,7 @@ import { personChip, ROLE_TITLES } from "./chrome.js";
|
|
|
127
127
|
import { chatReturnWithSaid, goOutside } from "./http.js";
|
|
128
128
|
import { latestReleaseFor, leadSettingsHtml, notificationProjects, SETTINGS_AUTOSAVE_SCRIPT, settingsPage, settingsWorkers, telegramDeliveryWords, telegramTrouble } from "./render-settings.js";
|
|
129
129
|
export function createSettingsHandlers(runtime) {
|
|
130
|
-
const { store, providerHome, managedRepos, consoleProjects, sendScreen, chromeFor, connectionCheck, clock, modelCatalog, modelSeams, toolsViewOf, codexServers, projectViewOf, sessions, restricted, options, integrations, consoleOrigin, evidenceRoot, storagePool, visible, ssoSettings, checkLocalAgents, chatKeyFor, chatCatalog, leadWords, agentSignInCommand, settingsUpdates, phoneSetup, authenticateApprover, authenticateCredential, deletedRepos,
|
|
130
|
+
const { store, providerHome, managedRepos, consoleProjects, sendScreen, chromeFor, connectionCheck, clock, modelCatalog, modelSeams, toolsViewOf, codexServers, projectViewOf, sessions, restricted, options, integrations, consoleOrigin, evidenceRoot, storagePool, visible, ssoSettings, checkLocalAgents, chatKeyFor, chatCatalog, leadWords, agentSignInCommand, settingsUpdates, phoneSetup, authenticateApprover, authenticateCredential, deletedRepos, projectOf, providerFor, admissionList, toolHome, connectVisits, mintApprovalNonce, consumeApprovalNonce, defaultProject, googleVisits } = runtime;
|
|
131
131
|
// v105: what agent work cost, by month, and the monthly budgets. An instance operator's page.
|
|
132
132
|
async function spendPage(ctx) {
|
|
133
133
|
const { url, who, response, now } = ctx;
|
|
@@ -451,7 +451,7 @@ ${connectionHtml(asked, connection, task)}${command === null ? "" : html `<p>On
|
|
|
451
451
|
// v105: Settings → Data, where an instance operator downloads everything. The download is a POST with the password.
|
|
452
452
|
async function dataPage(ctx) {
|
|
453
453
|
const { url, response, project } = ctx;
|
|
454
|
-
|
|
454
|
+
browserCaller(ctx.who);
|
|
455
455
|
return sendScreen(response, 200, screen("Data", html `<p><a href="/settings">Settings</a></p><h1>Data</h1>${(dataExportHtml({ problem: url.searchParams.get("problem") }))}`, { chrome: chromeFor(project, "settings") }));
|
|
456
456
|
}
|
|
457
457
|
// v100: Settings → Sign-in, the identity provider people sign in with. An instance operator's page.
|
|
@@ -486,7 +486,7 @@ ${connectionHtml(asked, connection, task)}${command === null ? "" : html `<p>On
|
|
|
486
486
|
const { response, now, posted } = ctx;
|
|
487
487
|
const who = browserCaller(ctx.who);
|
|
488
488
|
const body = readForm(posted, CONSOLE_FORMS.passwordless);
|
|
489
|
-
const back = (
|
|
489
|
+
const back = sayTo(response, "/settings/passwordless");
|
|
490
490
|
const action = body.get("action");
|
|
491
491
|
if (action === "off") {
|
|
492
492
|
setPasswordless(store, false, who.name, now);
|
|
@@ -812,7 +812,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
812
812
|
async function spendBudget(ctx) {
|
|
813
813
|
const { who, response, now, posted } = ctx;
|
|
814
814
|
const body = readForm(posted, CONSOLE_FORMS.spendBudget);
|
|
815
|
-
const back = (
|
|
815
|
+
const back = sayTo(response, "/spend");
|
|
816
816
|
if (!authenticateApprover(who, body.get("password") ?? "").ok)
|
|
817
817
|
return back("problem", "Enter your Toolroll password to change a budget.");
|
|
818
818
|
const target = /^(installation|project|person|subagent):(.+)$/.exec(body.get("target") ?? "");
|
|
@@ -848,7 +848,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
848
848
|
return refuse(response, who, 403, "That project is outside your access.", "/settings/project");
|
|
849
849
|
if (who.via !== "cookie" || who.role !== "approver" || !store.accountCanAccess(who.name, repo))
|
|
850
850
|
return refuse(response, who, 403, "An approver for this project sets how many tasks build at once.", "/settings/project");
|
|
851
|
-
const back = (
|
|
851
|
+
const back = sayTo(response, `/settings/project?repo=${encodeURIComponent(repo)}`);
|
|
852
852
|
const n = parseProjectConcurrency(body.get("concurrency") ?? "");
|
|
853
853
|
if (n === null)
|
|
854
854
|
return back("problem", "Builds at once is a whole number from 1 to 64. Nothing changed.");
|
|
@@ -870,7 +870,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
870
870
|
return refuse(response, who, 403, "That project is outside your access.", "/settings/project");
|
|
871
871
|
if (who.via !== "cookie" || who.role !== "approver" || store.isDemo() || !store.accountCanAccess(who.name, repo))
|
|
872
872
|
return refuse(response, who, 403, "An approver for this project changes the live view.", "/settings/project");
|
|
873
|
-
const back = (
|
|
873
|
+
const back = sayTo(response, `/settings/project?repo=${encodeURIComponent(repo)}`);
|
|
874
874
|
const act = body.get("act");
|
|
875
875
|
if (act === "off")
|
|
876
876
|
return back("said", setLiveCommands(store, { repo, commands: false, by: who.name, via: "the console" }, now).said);
|
|
@@ -893,7 +893,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
893
893
|
return refuse(response, who, 403, "That project is outside your access.", "/settings/project");
|
|
894
894
|
if (who.via !== "cookie" || who.role !== "approver" || store.isDemo() || !store.accountCanAccess(who.name, repo))
|
|
895
895
|
return refuse(response, who, 403, "An approver for this project changes what its builds may reach.", "/settings/project");
|
|
896
|
-
const back = (
|
|
896
|
+
const back = sayTo(response, `/settings/project?repo=${encodeURIComponent(repo)}`, "#network");
|
|
897
897
|
const hosts = (body.get("hosts") ?? "").split(/[\s,]+/).filter(Boolean).map(normalizeHost);
|
|
898
898
|
if (hosts.some(one => one === null))
|
|
899
899
|
return back("problem", "A host is a name like api.stripe.com or *.stripe.com: no address, port or path. Nothing changed.");
|
|
@@ -942,7 +942,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
942
942
|
async function policySave(ctx) {
|
|
943
943
|
const { who, response, now, posted } = ctx;
|
|
944
944
|
const body = readForm(posted, CONSOLE_FORMS.policy);
|
|
945
|
-
const back = (
|
|
945
|
+
const back = sayTo(response, "/settings/policy");
|
|
946
946
|
if (!authenticateApprover(who, body.get("password") ?? "").ok)
|
|
947
947
|
return back("problem", "That password didn't match. Nothing changed.");
|
|
948
948
|
const chosen = body.getAll("provider");
|
|
@@ -962,7 +962,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
962
962
|
const known = consoleProjects();
|
|
963
963
|
if (!known.includes(repo))
|
|
964
964
|
return refuse(response, who, 403, "That project is outside your access.", "/settings/approval");
|
|
965
|
-
const back = (
|
|
965
|
+
const back = sayTo(response, `/settings/approval?repo=${encodeURIComponent(repo)}`);
|
|
966
966
|
if (!authenticateApprover(who, body.get("password") ?? "").ok)
|
|
967
967
|
return back("problem", "That password didn't match. Nothing changed.");
|
|
968
968
|
const protect = body.get("protect") ?? "none";
|
|
@@ -980,7 +980,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
980
980
|
async function requestLimitsSave(ctx) {
|
|
981
981
|
const { who, response, now, posted } = ctx;
|
|
982
982
|
const body = readForm(posted, CONSOLE_FORMS.requestLimits);
|
|
983
|
-
const back = (
|
|
983
|
+
const back = sayTo(response, "/settings/sessions");
|
|
984
984
|
const target = body.get("target") ?? "";
|
|
985
985
|
const token = target === "*" ? null : store.apiTokens(null).find(one => one.id === target && one.revokedAt === null) ?? null;
|
|
986
986
|
if (target !== "*" && token === null)
|
|
@@ -1004,7 +1004,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1004
1004
|
const body = readForm(posted, CONSOLE_FORMS.signIn);
|
|
1005
1005
|
if (!options.configDir)
|
|
1006
1006
|
return refuse(response, who, 403, "An instance operator sets up sign-in.", "/settings");
|
|
1007
|
-
const back = (
|
|
1007
|
+
const back = sayTo(response, "/settings/sign-in");
|
|
1008
1008
|
const action = body.get("action") ?? "";
|
|
1009
1009
|
const before = ssoSettings();
|
|
1010
1010
|
if (action === "test") {
|
|
@@ -1046,7 +1046,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1046
1046
|
if (databaseFile === null)
|
|
1047
1047
|
return refuse(response, who, 403, "An instance operator updates Toolroll.", "/settings");
|
|
1048
1048
|
const stateDir = dirname(databaseFile);
|
|
1049
|
-
const back = (
|
|
1049
|
+
const back = sayTo(response, "/settings/updates");
|
|
1050
1050
|
if (op === "seen") {
|
|
1051
1051
|
markWhatsNewSeen(stateDir);
|
|
1052
1052
|
return redirect(response, "/settings/updates");
|
|
@@ -1090,7 +1090,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1090
1090
|
async function retentionSave(ctx) {
|
|
1091
1091
|
const { who, response, now, posted } = ctx;
|
|
1092
1092
|
const body = readForm(posted, CONSOLE_FORMS.retention);
|
|
1093
|
-
const back = (
|
|
1093
|
+
const back = sayTo(response, "/settings/retention");
|
|
1094
1094
|
if (!authenticateApprover(who, body.get("password") ?? "").ok)
|
|
1095
1095
|
return back("problem", "Enter your Toolroll password to change retention.");
|
|
1096
1096
|
const before = store.retentionPeriods();
|
|
@@ -1112,7 +1112,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1112
1112
|
const databaseFile = store.databaseFile();
|
|
1113
1113
|
if (databaseFile === null)
|
|
1114
1114
|
return refuse(response, who, 403, "An instance operator looks after storage.", "/settings");
|
|
1115
|
-
const back = (
|
|
1115
|
+
const back = sayTo(response, "/settings/storage");
|
|
1116
1116
|
const what = op === "clean" ? "clean up" : op === "discard" ? "discard a checkout" : "change checkout cleanup";
|
|
1117
1117
|
if (!authenticateApprover(who, body.get("password") ?? "").ok)
|
|
1118
1118
|
return back("problem", `Enter your Toolroll password to ${what}.`);
|
|
@@ -1152,7 +1152,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1152
1152
|
return refuse(response, who, 404, "No such project.", "/projects");
|
|
1153
1153
|
if (who.role !== "approver" || store.isDemo())
|
|
1154
1154
|
return refuse(response, who, 403, "An approver sets up pull requests.", "/projects");
|
|
1155
|
-
const back = (
|
|
1155
|
+
const back = sayTo(response, `/settings/pull-requests?repo=${encodeURIComponent(repo)}`);
|
|
1156
1156
|
const act = body.get("act");
|
|
1157
1157
|
if (!authenticateApprover(who, body.get("password") ?? "", repo).ok)
|
|
1158
1158
|
return back("problem", "Enter your Toolroll password to change pull requests.");
|
|
@@ -1193,7 +1193,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1193
1193
|
return refuse(response, who, 404, "No such project.", "/projects");
|
|
1194
1194
|
if (who.role !== "approver" || store.isDemo())
|
|
1195
1195
|
return refuse(response, who, 403, "An approver sets a project's checks.", "/projects");
|
|
1196
|
-
const back = (
|
|
1196
|
+
const back = sayTo(response, `/settings/checks?repo=${encodeURIComponent(repo)}`);
|
|
1197
1197
|
if (!authenticateApprover(who, body.get("password") ?? "", repo).ok)
|
|
1198
1198
|
return back("problem", "Enter your Toolroll password to change checks.");
|
|
1199
1199
|
const act = body.get("act");
|
|
@@ -1256,7 +1256,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1256
1256
|
const databaseFile = store.databaseFile();
|
|
1257
1257
|
if (databaseFile === null)
|
|
1258
1258
|
return refuse(response, who, 403, "An instance operator looks after backups.", "/settings");
|
|
1259
|
-
const back = (
|
|
1259
|
+
const back = sayTo(response, "/settings/backups");
|
|
1260
1260
|
if (!authenticateApprover(who, body.get("password") ?? "").ok)
|
|
1261
1261
|
return back("problem", "Enter your Toolroll password to change backups.");
|
|
1262
1262
|
const every = body.get("every") ?? "";
|
|
@@ -1296,7 +1296,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1296
1296
|
const { who, response, posted } = ctx;
|
|
1297
1297
|
const body = readForm(posted, CONSOLE_FORMS.flowsOn);
|
|
1298
1298
|
const repo = body.get("repo") ?? "";
|
|
1299
|
-
const back = (
|
|
1299
|
+
const back = sayTo(response, `/settings/flows?repo=${encodeURIComponent(repo)}`);
|
|
1300
1300
|
const known = [...new Set([...(admissionList() ?? []), ...managedRepos(), ...store.knownRepos()])];
|
|
1301
1301
|
if (store.isDemo())
|
|
1302
1302
|
return refuse(response, who, 403, "An approver switches starter flows on.", "/settings/flows");
|
|
@@ -1315,7 +1315,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1315
1315
|
if (!options.configDir)
|
|
1316
1316
|
return refuse(response, who, 403, "An installation approver tests integrations.", "/settings");
|
|
1317
1317
|
const key = body.get("key") ?? "";
|
|
1318
|
-
const back = (
|
|
1318
|
+
const back = sayTo(response, "/settings/integrations");
|
|
1319
1319
|
const checked = (await integrations.check([key])).find(one => one.key === key);
|
|
1320
1320
|
if (checked === undefined)
|
|
1321
1321
|
return back("problem", "That integration isn't here any more.");
|
|
@@ -1328,7 +1328,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1328
1328
|
const body = readForm(posted, CONSOLE_FORMS.monitoring);
|
|
1329
1329
|
if (!options.configDir)
|
|
1330
1330
|
return refuse(response, who, 403, "An instance operator sets up monitoring.", "/settings");
|
|
1331
|
-
const back = (
|
|
1331
|
+
const back = sayTo(response, "/settings/monitoring");
|
|
1332
1332
|
if (!authenticateApprover(who, body.get("password") ?? "").ok)
|
|
1333
1333
|
return back("problem", "Enter your Toolroll password to change monitoring.");
|
|
1334
1334
|
const before = readMonitoring(options.configDir);
|
|
@@ -1429,7 +1429,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1429
1429
|
const repo = body.get("repo") ?? "";
|
|
1430
1430
|
if (!visible(repo) || !consoleProjects().includes(repo))
|
|
1431
1431
|
return refuse(response, who, 403, "That project is outside your access.", "/projects");
|
|
1432
|
-
const back = (
|
|
1432
|
+
const back = sayTo(response, `/settings/tools?repo=${encodeURIComponent(repo)}`);
|
|
1433
1433
|
// As for Connect: a post from a page that showed another project is refused, never applied here.
|
|
1434
1434
|
const shown = body.get("shown") ?? "";
|
|
1435
1435
|
if (shown !== repo) {
|
|
@@ -2578,7 +2578,7 @@ ${modeWords(terms)
|
|
|
2578
2578
|
return refuse(response, who, 403, "That project is outside your access.", "/settings/project");
|
|
2579
2579
|
if (who.via !== "cookie" || who.role !== "approver" || store.isDemo() || !store.accountCanAccess(who.name, repo))
|
|
2580
2580
|
return refuse(response, who, 403, "An approver for this project changes where its builds may write.", "/settings/project");
|
|
2581
|
-
const back = (
|
|
2581
|
+
const back = sayTo(response, `/settings/project?repo=${encodeURIComponent(repo)}`, "#writes");
|
|
2582
2582
|
const wanted = body.get("writes");
|
|
2583
2583
|
if (!isWritesMode(wanted))
|
|
2584
2584
|
return back("problem", "Choose where builds may write. Nothing changed.");
|
|
@@ -3,7 +3,7 @@ import { liveAttemptOf, steerControlsFor } from "../steer-controls.js";
|
|
|
3
3
|
import { originOf } from "../task-source.js";
|
|
4
4
|
import { taskBaseOf } from "../task-base.js";
|
|
5
5
|
import { assignmentPresentationOf, historicalAssessmentReason } from '../assignment-presentation.js';
|
|
6
|
-
import { html,
|
|
6
|
+
import { html, joinHtml } from "../html.js";
|
|
7
7
|
import { assignmentStatusOf, assignmentWithEvidence } from '../assignment-ui.js';
|
|
8
8
|
import { assignmentOf } from '../assignment.js';
|
|
9
9
|
import { knowledgeContextHtml } from "../knowledge-ui.js";
|
package/dist/server/tasks.js
CHANGED
|
@@ -11,7 +11,7 @@ import { handlersOf } from './handler-registry.js';
|
|
|
11
11
|
import { closedQuestionWords } from "../decision.js";
|
|
12
12
|
import { DEPRECATED_PAGE } from "../deprecations.js";
|
|
13
13
|
/** tasks handlers, moved without changing their route bodies. */
|
|
14
|
-
import { html, htmlString, joinHtml,
|
|
14
|
+
import { html, htmlString, joinHtml, textHtml } from "../html.js";
|
|
15
15
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
16
16
|
import { lstatSync, realpathSync } from "node:fs";
|
|
17
17
|
import {} from "node:http";
|
|
@@ -40,7 +40,7 @@ import { isTaskSize, PHASES as ROUTE_PHASES } from "../phase-routing.js";
|
|
|
40
40
|
import { applyModeToNewFiling, authorizePlanUnderMode } from "../plan-auto.js";
|
|
41
41
|
import { milestonesOf, parseExecutionPlanDocument } from "../plan.js";
|
|
42
42
|
import { levelOfProfile } from "../policy.js";
|
|
43
|
-
import { authorizedProject, canonicalProject, projectName,
|
|
43
|
+
import { authorizedProject, canonicalProject, projectName, } from "../project.js";
|
|
44
44
|
import { fileTaskProposal } from "../proposal.js";
|
|
45
45
|
import { isProviderId, PROVIDER_IDS, validateSpec } from "../provider.js";
|
|
46
46
|
import { completeAndOpenPullRequest, mergeAsPerson } from '../pull-request-flow.js';
|
|
@@ -69,7 +69,7 @@ import { ACCEPT_ANYWAY_NEEDS_REASON, editorFileHref, proofBundleView, rankReview
|
|
|
69
69
|
import { approvalFormDigest, approveRefusalWords, checkProgressHtml, consentDoorOf, decisionPage, nonceHashOf, PermissionsWouldChange, requirementsFromEditor, resumeDigestOf, taskOf } from "./render-tasks.js";
|
|
70
70
|
import {} from "./session.js";
|
|
71
71
|
export function createTasksHandlers(runtime) {
|
|
72
|
-
const { store, peekSay, peekCache, PEEK_CACHE_TTL_MS, peekInFlight, PEEK_GLOBAL_INFLIGHT, peekBySession, PEEK_SESSION_INFLIGHT, clock, peekName, PEEK_FRAGMENT_BYTES, peekEvict, evidenceRoot, sendScreen, chromeFor, visible, consumeApprovalNonce, authenticateApprover, options, mintApprovalNonce, taskScreen, unscopedMode, admissionList, planViewOf, revisionViewOf, runIsTaskResult, leadPrincipal, familyOf, armTaskResume, runIsLive, revisionLedgerOf, resultDetailOf, reviewFactsFor, taskRepoOf, runVisible, restricted, codingActorAllowed, codingProjectAllowed, managedRepos, routeViewOf, workAccess, firstRunStepsNow, revisionDocOf, failureOf, dockedConversation, planContractViewOf, familiesInView, explainAttempt, taskChatFocus, familyTasksInView,
|
|
72
|
+
const { store, peekSay, peekCache, PEEK_CACHE_TTL_MS, peekInFlight, PEEK_GLOBAL_INFLIGHT, peekBySession, PEEK_SESSION_INFLIGHT, clock, peekName, PEEK_FRAGMENT_BYTES, peekEvict, evidenceRoot, sendScreen, chromeFor, visible, consumeApprovalNonce, authenticateApprover, options, mintApprovalNonce, taskScreen, unscopedMode, admissionList, planViewOf, revisionViewOf, runIsTaskResult, leadPrincipal, familyOf, armTaskResume, runIsLive, revisionLedgerOf, resultDetailOf, reviewFactsFor, taskRepoOf, runVisible, restricted, codingActorAllowed, codingProjectAllowed, managedRepos, routeViewOf, workAccess, firstRunStepsNow, revisionDocOf, failureOf, dockedConversation, planContractViewOf, familiesInView, explainAttempt, taskChatFocus, familyTasksInView, liveCeiling, projectOf, ceiling, bustBadge, revisionDestination } = runtime;
|
|
73
73
|
/** A row admitted the address but its path parameter does not parse: the console's unknown page. */
|
|
74
74
|
function noPage(who, response) {
|
|
75
75
|
return refuse(response, who, 404, "There's no page at this address.", "/chat");
|
package/dist/setup-guide.d.ts
CHANGED
|
@@ -15,10 +15,10 @@ export declare function detectPreparation(repo: string): {
|
|
|
15
15
|
} | null;
|
|
16
16
|
/** Browser installation may write only this known file inside its project. */
|
|
17
17
|
export declare function previewProjectInstructions(repo: string): {
|
|
18
|
-
content?: never;
|
|
19
18
|
ok: false;
|
|
20
19
|
message: string;
|
|
21
20
|
plan?: never;
|
|
21
|
+
content?: never;
|
|
22
22
|
fingerprint?: never;
|
|
23
23
|
installed?: never;
|
|
24
24
|
} | {
|
|
@@ -30,10 +30,10 @@ export declare function previewProjectInstructions(repo: string): {
|
|
|
30
30
|
installed: boolean;
|
|
31
31
|
};
|
|
32
32
|
export declare function addProjectInstructions(repo: string, fingerprint: string): {
|
|
33
|
-
content?: never;
|
|
34
33
|
ok: false;
|
|
35
34
|
message: string;
|
|
36
35
|
plan?: never;
|
|
36
|
+
content?: never;
|
|
37
37
|
fingerprint?: never;
|
|
38
38
|
installed?: never;
|
|
39
39
|
} | import("./skills.js").InstallResult;
|
package/dist/skills.d.ts
CHANGED
|
@@ -33,12 +33,6 @@ export declare const CONTEXT_END = "<!-- standing-orders:end -->";
|
|
|
33
33
|
export declare function skillContent(): string;
|
|
34
34
|
/** The AGENTS.md block, marker-fenced so reinstalls replace only themselves. */
|
|
35
35
|
export declare function contextBlock(): string;
|
|
36
|
-
/**
|
|
37
|
-
* Whether a repository has the Toolroll skill: a SKILL.md in the current
|
|
38
|
-
* folder, or in the one installs before the rename used. The setup guide
|
|
39
|
-
* and the console's first-run checklist both ask this, so they agree.
|
|
40
|
-
*/
|
|
41
|
-
export declare function projectSkillInstalled(repo: string): boolean;
|
|
42
36
|
export type InstallPlan = {
|
|
43
37
|
skillPath: string;
|
|
44
38
|
skillAction: "create" | "replace" | "refuse-foreign";
|
package/dist/skills.js
CHANGED
|
@@ -72,14 +72,6 @@ in the plane: read \`skills get knowledge\`, search it with
|
|
|
72
72
|
merge anything yourself.
|
|
73
73
|
${CONTEXT_END}`;
|
|
74
74
|
}
|
|
75
|
-
/**
|
|
76
|
-
* Whether a repository has the Toolroll skill: a SKILL.md in the current
|
|
77
|
-
* folder, or in the one installs before the rename used. The setup guide
|
|
78
|
-
* and the console's first-run checklist both ask this, so they agree.
|
|
79
|
-
*/
|
|
80
|
-
export function projectSkillInstalled(repo) {
|
|
81
|
-
return [SKILL_DIR, LEGACY_SKILL_DIR].some(dir => existsSync(join(repo, dir, SKILL_FILE)));
|
|
82
|
-
}
|
|
83
75
|
/** What install would do, computed without writing anything. */
|
|
84
76
|
export function planInstall(repo, writeContext) {
|
|
85
77
|
const skillPath = join(repo, SKILL_DIR, SKILL_FILE);
|
package/dist/slack-chat.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChatState
|
|
1
|
+
import { ChatState } from "./chat-delivery-state.js";
|
|
2
2
|
import { uploadSlackBytes, type SlackApi, type SlackIdentity } from "./slack-api.js";
|
|
3
3
|
import { type Store } from "./store.js";
|
|
4
4
|
import type { SubscriptionLeadRunner } from "./subscription-chat.js";
|
|
@@ -19,12 +19,6 @@ export type SlackChatOptions = {
|
|
|
19
19
|
/** Synchronous receipt before ACK. Never stores response_url, raw tokens, or a pairing code. */
|
|
20
20
|
export declare function receiveSlack(state: ChatState, identity: SlackIdentity, type: string, raw: unknown, now: Date): boolean;
|
|
21
21
|
export declare const processSlackEvent: (options: SlackChatOptions) => Promise<boolean>;
|
|
22
|
-
export declare const applySlackAction: (options: SlackChatOptions, event: ChatEvent, binding: ChatBinding, repos: readonly string[]) => {
|
|
23
|
-
merge: import("./chat-decide.js").ChatMerge;
|
|
24
|
-
part: number;
|
|
25
|
-
shown: string;
|
|
26
|
-
content: ChatContent;
|
|
27
|
-
} | null;
|
|
28
22
|
export declare const planSlackNotifications: (options: SlackChatOptions) => Promise<void>;
|
|
29
23
|
export declare const planSlackRooms: (options: SlackChatOptions) => Promise<void>;
|
|
30
24
|
export declare function slackBlocks(text: string, buttons?: Record<string, unknown>[]): Record<string, unknown>[];
|
package/dist/slack-chat.js
CHANGED
|
@@ -2,9 +2,8 @@ import { chatFlowButtons } from "./chat-flow.js";
|
|
|
2
2
|
import { refuseResultShots, resultShotsPruned, shotWaitsUntil } from "./result-shots.js";
|
|
3
3
|
import { chatQuestionButtons } from "./subagent-question.js";
|
|
4
4
|
import { chatAskButtons } from "./chat-ask.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { processChatEvent, applyChatAction, planChatNotifications, channelAccess, planRoomMessages, heldForWatch, } from "./chat-delivery.js";
|
|
5
|
+
import { admitChatEvent } from "./chat-admission.js";
|
|
6
|
+
import { processChatEvent, planChatNotifications, channelAccess, planRoomMessages, heldForWatch, } from "./chat-delivery.js";
|
|
8
7
|
/** Slack is a transport for the same saved assistant and confirmation doors. */
|
|
9
8
|
import { proposalPreview, proposalLink, proposalOutcomeText, armedCardText, armedYesLabel, } from "./chat-channel.js";
|
|
10
9
|
import { LEAD_MESSAGE_MAX_CHARS } from "./lead.js";
|
|
@@ -100,21 +99,10 @@ export function receiveSlack(state, identity, type, raw, now) {
|
|
|
100
99
|
// A channel or private group is a room: accepted only while it follows a
|
|
101
100
|
// conversation, or for the `/team` words that make it follow one.
|
|
102
101
|
const isRoom = !slackId(channel, "D");
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
const inbox = isRoom && kind === "message" ? channelInbox(state.store, "slack", identity.installation, String(channel), words) : { watched: false, command: false };
|
|
106
|
-
const roomish = isRoom && (state.room(identity.installation, String(channel)) !== null || (kind === "message" && roomCommand(words) !== null) || inbox.watched || inbox.command);
|
|
107
|
-
if (isRoom && !roomish)
|
|
108
|
-
return false;
|
|
109
|
-
const binding = state.bindingFor(identity.installation, member);
|
|
110
|
-
const open = inbox.watched && !inbox.command;
|
|
111
|
-
if (kind !== "pair" && !open &&
|
|
112
|
-
(!binding ||
|
|
113
|
-
!state.live(binding) ||
|
|
114
|
-
(binding.channel !== channel && !roomish)))
|
|
115
|
-
return false;
|
|
116
|
-
if (kind === "pair" && (binding || isRoom))
|
|
102
|
+
const admitted = admitChatEvent(state, "slack", identity.installation, { kind, member, channel, isRoom, payload });
|
|
103
|
+
if (admitted === null)
|
|
117
104
|
return false;
|
|
105
|
+
const { binding } = admitted;
|
|
118
106
|
return state.enqueue({
|
|
119
107
|
id,
|
|
120
108
|
installation: identity.installation,
|
|
@@ -139,7 +127,6 @@ const delivery = (options) => ({
|
|
|
139
127
|
}),
|
|
140
128
|
});
|
|
141
129
|
export const processSlackEvent = (options) => processChatEvent(delivery(options));
|
|
142
|
-
export const applySlackAction = (options, event, binding, repos) => applyChatAction(delivery(options), event, binding, repos);
|
|
143
130
|
export const planSlackNotifications = (options) => planChatNotifications(delivery(options));
|
|
144
131
|
export const planSlackRooms = (options) => planRoomMessages(delivery(options));
|
|
145
132
|
const access = (options, binding, ceiling) => channelAccess(delivery(options), binding, ceiling);
|
package/dist/slack-settings.d.ts
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import type { Store } from "./store.js";
|
|
2
2
|
import { type Html } from "./html.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
problem?: string;
|
|
6
|
-
now?: Date;
|
|
7
|
-
who?: string;
|
|
8
|
-
}): Html;
|
|
3
|
+
import { type ChatSettingsOptions } from "./chat-settings-page.js";
|
|
4
|
+
export declare function slackSettingsHtml(store: Store, dir: string, options?: ChatSettingsOptions): Html;
|
package/dist/slack-settings.js
CHANGED
|
@@ -1,41 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { html, postForm } from "./html.js";
|
|
1
|
+
import { html } from "./html.js";
|
|
3
2
|
import { loadSlackCredentials } from "./slack-api.js";
|
|
4
|
-
import {
|
|
3
|
+
import { chatSettingsHtml } from "./chat-settings-page.js";
|
|
5
4
|
export function slackSettingsHtml(store, dir, options = {}) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const parts = [];
|
|
15
|
-
if (!credentials) {
|
|
16
|
-
parts.push(html `<p>Manage projects and review results in a private Slack conversation.</p>`, html `<details open><summary>Create your Slack app</summary><ol><li>Download the <a href="/settings/slack/manifest">app manifest</a>, then <a href="https://api.slack.com/apps" target="_blank" rel="noopener noreferrer">create an app</a> in your workspace using that manifest.</li><li>Under Basic Information, create an app-level token with <code>connections:write</code>.</li><li>Under OAuth & Permissions, install the app and copy its bot token.</li></ol></details>`, post("connect", html `<label>App token<input style="min-height:44px" type="password" name="app-token" autocomplete="off" placeholder="xapp-…" required></label><label>Bot token<input style="min-height:44px" type="password" name="bot-token" autocomplete="off" placeholder="xoxb-…" required></label>${password}<p class="meta">Tokens stay on this installation. Next, pair your own Slack account.</p><button type="submit">Connect Slack</button>`));
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
parts.push(html `<p><strong>${credentials.workspace}</strong> · ${live ? "Connected" : "Waiting for connection"}</p>`);
|
|
20
|
-
if (runtime?.problem)
|
|
21
|
-
parts.push(html `<p role="status">${String(runtime.problem)}</p>`);
|
|
22
|
-
if (!live && !runtime?.problem)
|
|
23
|
-
parts.push(html `<p>Keep the Toolroll worker running to receive Slack messages.</p>`);
|
|
24
|
-
if (options.code) {
|
|
25
|
-
parts.push(html `<h2>Pair your account</h2><p>Send this in a direct message to your Toolroll Slack app. It expires in 10 minutes.</p>`, html `<label>Pairing message<input type="text" readonly value="pair ${options.code}" autocomplete="off" style="width:100%;max-width:100%;font-size:14px;min-height:44px"></label>`, html `<a class="button-link" style="min-height:44px;white-space:nowrap" href="/settings/slack">Check connection</a>`);
|
|
26
|
-
}
|
|
27
|
-
else if (binding && state.live(binding)) {
|
|
28
|
-
const { pending, dropped: failed } = state.partCounts(binding.id);
|
|
29
|
-
parts.push(html `<p>Your Slack account is paired.${others ? ` ${others} teammate${others === 1 ? " is" : "s are"} paired too.` : ""}${pending ? ` ${pending} replies waiting to send.` : ""}${failed ? ` ${failed} replies could not be delivered. Open the saved chat to recover them.` : ""}</p>`, html `<p>Try “What needs my attention?” or “Show the evidence for the latest result.”</p>`, html `<p><a href="/chat">Open saved chat</a></p>`, loadPrimary(process.env, dir) === "slack"
|
|
30
|
-
? html `<p>Task updates are sent here.</p>`
|
|
31
|
-
: post("alerts", html `<button type="submit">Send task updates here</button>`));
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
parts.push(post("pair", html `<h2>Pair your account</h2><p>Your paired Slack account can read your connected projects and confirm proposed changes. Password approvals still open in Toolroll.${others ? ` ${others} teammate${others === 1 ? " is" : "s are"} already paired.` : ""}</p>${password}<button type="submit">Create pairing code</button>`));
|
|
35
|
-
}
|
|
36
|
-
if (binding && state.live(binding) && !options.code)
|
|
37
|
-
parts.push(post("unpair", html `${password}<p class="meta">Unpairing ends every open button in your chat. Teammates are unaffected.</p><button type="submit">Unpair my account</button>`));
|
|
38
|
-
parts.push(html `<details><summary>Connection settings</summary>${post("disconnect", html `${password}<button type="submit">Disconnect Slack</button>`)}</details>`);
|
|
39
|
-
}
|
|
40
|
-
return html `<section style="max-width:42rem;overflow-wrap:anywhere"><p><a href="/settings">Settings</a></p><h1>Slack</h1>${options.problem ? html `<p class="problem" role="alert">${options.problem}</p>` : ""}${parts}<details><summary>Access and setup</summary><p>Private messages and threads use your Toolroll permissions. Shared channels are not supported. Secure review links use this installation’s configured HTTPS console address.</p><p>Socket Mode connects out to Slack. It does not need a public webhook or a Tailscale account.</p></details></section>`;
|
|
5
|
+
return chatSettingsHtml(store, dir, options, {
|
|
6
|
+
provider: "slack",
|
|
7
|
+
label: "Slack",
|
|
8
|
+
credentials: loadSlackCredentials(dir),
|
|
9
|
+
setup: html `<details open><summary>Create your Slack app</summary><ol><li>Download the <a href="/settings/slack/manifest">app manifest</a>, then <a href="https://api.slack.com/apps" target="_blank" rel="noopener noreferrer">create an app</a> in your workspace using that manifest.</li><li>Under Basic Information, create an app-level token with <code>connections:write</code>.</li><li>Under OAuth & Permissions, install the app and copy its bot token.</li></ol></details>`,
|
|
10
|
+
connect: password => html `<label>App token<input style="min-height:44px" type="password" name="app-token" autocomplete="off" placeholder="xapp-…" required></label><label>Bot token<input style="min-height:44px" type="password" name="bot-token" autocomplete="off" placeholder="xoxb-…" required></label>${password}<p class="meta">Tokens stay on this installation. Next, pair your own Slack account.</p><button type="submit">Connect Slack</button>`,
|
|
11
|
+
access: html `<p>Private messages and threads use your Toolroll permissions. Shared channels are not supported. Secure review links use this installation’s configured HTTPS console address.</p><p>Socket Mode connects out to Slack. It does not need a public webhook or a Tailscale account.</p>`,
|
|
12
|
+
});
|
|
41
13
|
}
|
package/dist/slack.js
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
/** One leased Socket Mode connection, hosted by the ordinary worker. */
|
|
2
2
|
import { randomBytes } from "node:crypto";
|
|
3
|
-
import {
|
|
3
|
+
import { abortablePause, workChatQueue } from "./chat-follow.js";
|
|
4
4
|
import { loadSlackCredentials, slackApi } from "./slack-api.js";
|
|
5
5
|
import { ChatState } from "./chat-delivery-state.js";
|
|
6
6
|
import { receiveSlack, processSlackEvent, deliverSlackPart, planSlackNotifications, planSlackRooms, } from "./slack-chat.js";
|
|
7
7
|
export async function followSlack(options) {
|
|
8
8
|
const owner = randomBytes(16).toString("hex"), state = new ChatState(options.store, "slack");
|
|
9
|
-
const pause =
|
|
10
|
-
try {
|
|
11
|
-
await sleep(ms, undefined, { signal: options.signal });
|
|
12
|
-
}
|
|
13
|
-
catch {
|
|
14
|
-
/* shutdown */
|
|
15
|
-
}
|
|
16
|
-
};
|
|
9
|
+
const pause = abortablePause(options.signal);
|
|
17
10
|
while (!options.signal.aborted) {
|
|
18
11
|
const credentials = loadSlackCredentials(options.dir);
|
|
19
12
|
if (!credentials ||
|
|
@@ -104,20 +97,16 @@ export async function followSlack(options) {
|
|
|
104
97
|
};
|
|
105
98
|
try {
|
|
106
99
|
await client.start();
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (same() && options.notifications())
|
|
118
|
-
await planSlackNotifications(chat);
|
|
119
|
-
await pause(1000);
|
|
120
|
-
}
|
|
100
|
+
await workChatQueue({
|
|
101
|
+
same,
|
|
102
|
+
ready: () => connected && !(state.retryAt(credentials.installation) > new Date().toISOString()),
|
|
103
|
+
pause,
|
|
104
|
+
notifications: () => options.notifications(),
|
|
105
|
+
process: () => processSlackEvent(chat),
|
|
106
|
+
rooms: () => planSlackRooms(chat),
|
|
107
|
+
deliver: () => deliverSlackPart(chat),
|
|
108
|
+
notify: () => planSlackNotifications(chat),
|
|
109
|
+
});
|
|
121
110
|
}
|
|
122
111
|
catch {
|
|
123
112
|
if (alive)
|