toolroll 0.9.85 → 0.9.87
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 +83 -0
- package/dist/agent-fence.d.ts +6 -2
- package/dist/agent-fence.js +3 -2
- package/dist/agents-board.d.ts +12 -0
- package/dist/agents-board.js +26 -0
- package/dist/approval-rules-ui.d.ts +4 -1
- package/dist/approval-rules-ui.js +31 -15
- package/dist/assignment-ui.js +1 -1
- package/dist/assignment.js +4 -3
- package/dist/backup-ui.d.ts +5 -4
- package/dist/backup-ui.js +65 -32
- package/dist/browser/terminal.css +1 -1
- package/dist/browser/terminal.js +57 -54
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +35 -68
- package/dist/browser-crew.js +1 -1
- package/dist/browser-workspace.d.ts +363 -177
- package/dist/browser-workspace.js +10 -9
- package/dist/chat-actions.d.ts +11 -1
- package/dist/chat-actions.js +53 -1
- package/dist/chat-channel.js +1 -0
- package/dist/chat-controls.d.ts +1 -1
- package/dist/chat-controls.js +1 -1
- package/dist/chat-quiet.js +3 -2
- package/dist/chat-settings-page.d.ts +29 -9
- package/dist/chat-settings-page.js +59 -28
- package/dist/check-levels-ui.d.ts +2 -1
- package/dist/check-levels-ui.js +67 -58
- package/dist/claim.d.ts +3 -0
- package/dist/claim.js +4 -1
- package/dist/contracts/chat-actions.d.ts +9 -0
- package/dist/contracts/chat-actions.js +3 -0
- package/dist/contracts/cli.d.ts +43 -0
- package/dist/contracts/cli.js +4 -0
- package/dist/contracts/console-api.d.ts +4 -1
- package/dist/contracts/console-api.js +4 -1
- package/dist/contracts/flow-propose.d.ts +4 -1
- package/dist/contracts/flow-propose.js +1 -1
- package/dist/contracts/flow.d.ts +41 -0
- package/dist/contracts/flow.js +4 -2
- package/dist/contracts/lead-tools.d.ts +21 -0
- package/dist/contracts/lead-tools.js +2 -0
- package/dist/contracts/store-json.d.ts +2 -0
- package/dist/contracts/trigger-payloads.d.ts +20 -0
- package/dist/contracts/trigger-payloads.js +4 -0
- package/dist/coordinator.js +1 -0
- package/dist/credentials-ui.d.ts +5 -3
- package/dist/credentials-ui.js +58 -31
- package/dist/deploy-gate.js +4 -0
- package/dist/design/kit.css +685 -0
- package/dist/design/kit.d.ts +208 -0
- package/dist/design/kit.js +177 -0
- package/dist/design/tokens.css +19 -1
- package/dist/discord-settings.js +8 -4
- package/dist/dispatch.d.ts +3 -0
- package/dist/dispatch.js +4 -1
- package/dist/export-ui.d.ts +6 -4
- package/dist/export-ui.js +18 -8
- package/dist/flow-browser.d.ts +103 -0
- package/dist/flow-browser.js +204 -0
- package/dist/flow-changed.d.ts +14 -0
- package/dist/flow-changed.js +53 -0
- package/dist/flow-code.d.ts +5 -1
- package/dist/flow-code.js +4 -1
- package/dist/flow-engine.js +16 -0
- package/dist/flow-gallery-ui.d.ts +41 -4
- package/dist/flow-gallery-ui.js +95 -63
- package/dist/flow-live.js +9 -1
- package/dist/flow-pull-request.js +15 -7
- package/dist/flow-share.js +3 -1
- package/dist/flow-starters-ui.d.ts +3 -9
- package/dist/flow-starters-ui.js +12 -23
- package/dist/flow-steps.d.ts +9 -1
- package/dist/flow-steps.js +55 -5
- package/dist/flow-triggers.js +55 -0
- package/dist/flow-unattended-words.d.ts +20 -0
- package/dist/flow-unattended-words.js +24 -0
- package/dist/flow-unattended.d.ts +9 -5
- package/dist/flow-unattended.js +45 -23
- package/dist/flows-cli.d.ts +19 -0
- package/dist/flows-cli.js +54 -5
- package/dist/flows-ui.d.ts +12 -6
- package/dist/flows-ui.js +152 -32
- package/dist/flows.js +5 -2
- package/dist/integrations-ui.d.ts +6 -4
- package/dist/integrations-ui.js +34 -40
- package/dist/integrations.d.ts +2 -2
- package/dist/integrations.js +4 -4
- package/dist/kits-ui.d.ts +8 -10
- package/dist/kits-ui.js +29 -33
- package/dist/kits.js +1 -1
- package/dist/knowledge-ui.js +76 -19
- package/dist/lead-tools.js +35 -6
- package/dist/lead-voice.js +16 -5
- package/dist/ledger-view.d.ts +2 -0
- package/dist/ledger-view.js +41 -38
- package/dist/models-ui.d.ts +8 -3
- package/dist/models-ui.js +82 -59
- package/dist/monitoring-ui.d.ts +4 -2
- package/dist/monitoring-ui.js +44 -13
- package/dist/needs-you.d.ts +1 -1
- package/dist/needs-you.js +8 -0
- package/dist/operate-remote-arguments.js +1 -0
- package/dist/operate-remote.js +1 -1
- package/dist/operate.d.ts +1 -1
- package/dist/operate.js +249 -6
- package/dist/passwordless.d.ts +1 -1
- package/dist/passwordless.js +1 -1
- package/dist/people-audit-ui.d.ts +2 -1
- package/dist/people-audit-ui.js +25 -23
- package/dist/policy-ui.d.ts +3 -2
- package/dist/policy-ui.js +27 -21
- package/dist/policy.d.ts +3 -0
- package/dist/policy.js +14 -1
- package/dist/project-delete-ui.d.ts +4 -2
- package/dist/project-delete-ui.js +135 -126
- package/dist/project-delete.js +7 -3
- package/dist/project-knowledge.d.ts +10 -10
- package/dist/publish.d.ts +11 -0
- package/dist/publish.js +16 -3
- package/dist/pull-request-flow.js +35 -4
- package/dist/pull-request-ui.d.ts +2 -1
- package/dist/pull-request-ui.js +39 -28
- package/dist/recipe-ui.d.ts +4 -2
- package/dist/recipe-ui.js +14 -4
- package/dist/release-adapters.d.ts +3 -0
- package/dist/release-adapters.js +22 -2
- package/dist/release-cli.js +81 -9
- package/dist/release.d.ts +21 -0
- package/dist/release.js +40 -4
- package/dist/releases.js +1 -1
- package/dist/request-budget-ui.d.ts +4 -2
- package/dist/request-budget-ui.js +30 -19
- package/dist/result-follow-ups.js +4 -0
- package/dist/retention-ui.d.ts +4 -3
- package/dist/retention-ui.js +15 -12
- package/dist/scope.d.ts +8 -0
- package/dist/scope.js +13 -0
- package/dist/serve.d.ts +0 -1
- package/dist/serve.js +51 -7
- package/dist/server/agents-computers.d.ts +9 -0
- package/dist/server/agents-computers.js +67 -0
- package/dist/server/chat.js +20 -18
- package/dist/server/chrome.d.ts +1 -1
- package/dist/server/chrome.js +19 -9
- package/dist/server/console.css +31 -2
- package/dist/server/flows.js +79 -36
- package/dist/server/home.d.ts +35 -0
- package/dist/server/home.js +104 -0
- package/dist/server/http.d.ts +3 -0
- package/dist/server/http.js +5 -0
- package/dist/server/kit-standalone.d.ts +22 -0
- package/dist/server/kit-standalone.js +69 -0
- package/dist/server/kit.d.ts +405 -0
- package/dist/server/kit.js +361 -0
- package/dist/server/pages.d.ts +2 -1
- package/dist/server/pages.js +213 -174
- package/dist/server/people-tokens.js +26 -85
- package/dist/server/render-history.d.ts +51 -0
- package/dist/server/render-history.js +228 -0
- package/dist/server/render-new-task.d.ts +29 -0
- package/dist/server/render-new-task.js +96 -0
- package/dist/server/render-next.d.ts +63 -0
- package/dist/server/render-next.js +109 -0
- package/dist/server/render-pages.d.ts +35 -190
- package/dist/server/render-pages.js +154 -563
- package/dist/server/render-people.d.ts +24 -3
- package/dist/server/render-people.js +93 -72
- package/dist/server/render-projects.d.ts +103 -0
- package/dist/server/render-projects.js +247 -0
- package/dist/server/render-results.d.ts +5 -24
- package/dist/server/render-results.js +48 -185
- package/dist/server/render-settings.d.ts +5 -74
- package/dist/server/render-settings.js +102 -493
- package/dist/server/render-tasks.d.ts +43 -5
- package/dist/server/render-tasks.js +131 -91
- package/dist/server/route-table.js +31 -5
- package/dist/server/settings.js +276 -255
- package/dist/server/task-views.d.ts +8 -0
- package/dist/server/task-views.js +90 -2
- package/dist/server/tasks.d.ts +18 -0
- package/dist/server/tasks.js +148 -41
- package/dist/server/terminals.js +1 -1
- package/dist/server/welcome.d.ts +22 -0
- package/dist/server/welcome.js +69 -0
- package/dist/settings-groups-ui.d.ts +279 -0
- package/dist/settings-groups-ui.js +615 -0
- package/dist/settings-kit.d.ts +42 -6
- package/dist/settings-kit.js +69 -7
- package/dist/settings-proofs.js +1 -0
- package/dist/setup-guide.d.ts +2 -2
- package/dist/skills-ui.js +59 -7
- package/dist/slack-settings.js +12 -5
- package/dist/spend-ui.d.ts +45 -26
- package/dist/spend-ui.js +124 -56
- package/dist/sso-ui.d.ts +5 -2
- package/dist/sso-ui.js +63 -31
- package/dist/storage-ui.d.ts +3 -2
- package/dist/storage-ui.js +56 -50
- package/dist/store.d.ts +182 -5
- package/dist/store.js +512 -38
- package/dist/surface.js +23 -3
- package/dist/task-archive.d.ts +10 -0
- package/dist/task-archive.js +16 -0
- package/dist/task-control.d.ts +1 -1
- package/dist/teams-settings.d.ts +2 -1
- package/dist/teams-settings.js +24 -41
- package/dist/telegram-settings.d.ts +0 -4
- package/dist/telegram-settings.js +52 -25
- package/dist/tidy.d.ts +130 -0
- package/dist/tidy.js +503 -0
- package/dist/toolroll-update-ui.d.ts +8 -4
- package/dist/toolroll-update-ui.js +55 -45
- package/dist/tools-ui.d.ts +5 -3
- package/dist/tools-ui.js +107 -42
- package/dist/work-index.d.ts +24 -0
- package/dist/work-index.js +56 -30
- package/dist/workspace-ui.d.ts +1 -1
- package/dist/workspace-ui.js +8 -4
- package/package.json +1 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { type FlowDefinition } from "./flows.js";
|
|
2
|
+
import type { FlowRow, FlowScriptRow, Store } from "./store.js";
|
|
3
|
+
/** v144: each approval of browser use, live or ended. `digest` is browserApprovalDigest of what was approved. Additive only. */
|
|
4
|
+
export declare const FLOW_BROWSER_SCHEMA = "\nCREATE TABLE IF NOT EXISTS flow_browser_grant (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n flow INTEGER NOT NULL,\n repo TEXT NOT NULL,\n flow_revision INTEGER NOT NULL,\n flow_digest TEXT NOT NULL,\n digest TEXT NOT NULL,\n zones_json TEXT NOT NULL,\n signed_by TEXT NOT NULL,\n signed_at TEXT NOT NULL,\n proof TEXT NOT NULL,\n revoked_at TEXT,\n revoked_by TEXT\n);\nCREATE UNIQUE INDEX IF NOT EXISTS flow_browser_live ON flow_browser_grant (flow) WHERE revoked_at IS NULL;\n";
|
|
5
|
+
/** One zone an approval covers: the zone, and the script it runs (name, version and content digest). */
|
|
6
|
+
export type BrowserZone = {
|
|
7
|
+
zone: string;
|
|
8
|
+
script: string;
|
|
9
|
+
version: number;
|
|
10
|
+
digest: string;
|
|
11
|
+
};
|
|
12
|
+
/** Everything an approval is about: the flow, its project, the drawing (revision and digest) and every browser zone. */
|
|
13
|
+
export type BrowserTerms = {
|
|
14
|
+
flow: number;
|
|
15
|
+
repo: string;
|
|
16
|
+
revision: number;
|
|
17
|
+
flowDigest: string;
|
|
18
|
+
zones: BrowserZone[];
|
|
19
|
+
};
|
|
20
|
+
export type FlowBrowserGrant = BrowserTerms & {
|
|
21
|
+
id: number;
|
|
22
|
+
digest: string;
|
|
23
|
+
signedBy: string;
|
|
24
|
+
signedAt: string;
|
|
25
|
+
proof: string;
|
|
26
|
+
};
|
|
27
|
+
/** The approval's digest: domain-separated over the whole of its terms, so a changed flow, drawing, revision or any
|
|
28
|
+
* covered script (a restored one is a new version) is a different approval. */
|
|
29
|
+
export declare function browserApprovalDigest(terms: BrowserTerms): string;
|
|
30
|
+
/** The flow's script zones that say they start a browser, each with the script it runs now; null when one's script is
|
|
31
|
+
* missing, waiting for approval, or runs a project file (an approval seals the script's own text, never a file the
|
|
32
|
+
* project can change). */
|
|
33
|
+
export declare function browserZonesOf(store: Store, flow: FlowRow, definition: FlowDefinition): BrowserZone[] | null;
|
|
34
|
+
/** What an approval of this flow would be about now, read from the store; null when it can't be approved. */
|
|
35
|
+
export declare function browserTermsOf(store: Store, flow: FlowRow): BrowserTerms | null;
|
|
36
|
+
/** The flow's live approval, as recorded (whether it still stands is browserStanding's question). */
|
|
37
|
+
export declare function liveBrowserGrant(store: Store, flowId: number): FlowBrowserGrant | null;
|
|
38
|
+
/**
|
|
39
|
+
* Whether `zone` of this flow may start a browser now. `run`, when given, is what the caller is about to run — the
|
|
40
|
+
* drawing it read and the very script object it hands the launch — and the answer is about exactly that. The live
|
|
41
|
+
* approval stands only when its digest is the approval this flow would be now (same flow, project, drawing revision and
|
|
42
|
+
* digest, and every browser zone's script name, version and digest), it was a person's, and its signer is still an
|
|
43
|
+
* approver who reaches the project; `run`'s drawing must be that drawing and its script that zone's script. Anything
|
|
44
|
+
* unreadable, changed or mismatched is a no, with the plain words a card waits with.
|
|
45
|
+
*/
|
|
46
|
+
export declare function browserStanding(store: Store, flowId: number, zone: string, run?: {
|
|
47
|
+
definition: FlowDefinition;
|
|
48
|
+
script: FlowScriptRow;
|
|
49
|
+
}): {
|
|
50
|
+
allowed: true;
|
|
51
|
+
} | {
|
|
52
|
+
allowed: false;
|
|
53
|
+
said: string;
|
|
54
|
+
};
|
|
55
|
+
/** What approving means, in words the person reads before they give their password. */
|
|
56
|
+
export declare function browserTerms(zones: readonly BrowserZone[]): string[];
|
|
57
|
+
/**
|
|
58
|
+
* Approve browser use for the flow as it stands. `proof` is the signer's credential method, proved by the caller just
|
|
59
|
+
* now; only a person's counts (a lead's or remote run's token is refused), from an approver who reaches the project.
|
|
60
|
+
* Re-reads the flow, its zones and scripts in the one transaction that writes; `sawDigest` (required) is the approval
|
|
61
|
+
* (browserApprovalDigest: drawing and scripts) the person read, and anything else is refused. Replaces any live one.
|
|
62
|
+
*/
|
|
63
|
+
export declare function signFlowBrowser(store: Store, input: {
|
|
64
|
+
flow: number;
|
|
65
|
+
signer: string;
|
|
66
|
+
proof: string | null;
|
|
67
|
+
sawDigest: string;
|
|
68
|
+
/** The console and chat: the signer's account must still be this generation (the session that proved it) and reach every project. */
|
|
69
|
+
console?: {
|
|
70
|
+
generation: number;
|
|
71
|
+
};
|
|
72
|
+
}, now: Date): {
|
|
73
|
+
ok: true;
|
|
74
|
+
grant: FlowBrowserGrant;
|
|
75
|
+
} | {
|
|
76
|
+
ok: false;
|
|
77
|
+
message: string;
|
|
78
|
+
};
|
|
79
|
+
/** Any approver on the project ends it, with no password: lowering authority. False when there was none. */
|
|
80
|
+
export declare function revokeFlowBrowser(store: Store, flowId: number, by: string, now: Date): boolean;
|
|
81
|
+
/** What the flow page, `flows show` and chat say about browser use: null when no zone says it starts a browser. */
|
|
82
|
+
export type BrowserView = {
|
|
83
|
+
/** allowed: the approval stands now; ended: there is one, but it no longer stands; none: no one has approved. */
|
|
84
|
+
status: "allowed" | "ended" | "none";
|
|
85
|
+
/** The one line every surface shows. */
|
|
86
|
+
words: string;
|
|
87
|
+
signedBy: string | null;
|
|
88
|
+
signedAt: string | null;
|
|
89
|
+
/** The browser zones as they stand now, each with its title. Empty while one can't be approved (see `problem`). */
|
|
90
|
+
zones: (BrowserZone & {
|
|
91
|
+
title: string;
|
|
92
|
+
})[];
|
|
93
|
+
/** What approving means (browserTerms), and the approval digest the step-up must carry back. */
|
|
94
|
+
terms: string[];
|
|
95
|
+
digest: string | null;
|
|
96
|
+
/** Why it can't be approved as it stands, or null. */
|
|
97
|
+
problem: string | null;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Browser use for one flow, read from the store (no writes: page visits call it). Whether it is allowed is exactly
|
|
101
|
+
* browserStanding's answer for the flow's browser zones; the rest only says why in plain words.
|
|
102
|
+
*/
|
|
103
|
+
export declare function browserViewOf(store: Store, flow: FlowRow): BrowserView | null;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser use in a flow's script zone: a per-flow approval a person gives with their own proof, so that zone's launch
|
|
3
|
+
* gets the one sandbox rule a project check gets for Chromium (sandbox-runtime.ts SEATBELT_CHROMIUM) and nothing more.
|
|
4
|
+
*
|
|
5
|
+
* Two halves, both needed. The flow's drawing says which "Run a script" zones start a browser (`browser: true`, part of
|
|
6
|
+
* the flow's digest and said when the flow is confirmed); anyone who may change the flow can say it. Then a person, with
|
|
7
|
+
* their password or sign-in (never a lead's or remote run's token: person-proof.ts), approves browser use for the flow
|
|
8
|
+
* as it stands. The approval seals the flow's digest and, for each browser zone, its script's name and content digest.
|
|
9
|
+
* At launch (flow-steps.ts) the zone gets the rule only while all of that still holds: the same drawing, the same
|
|
10
|
+
* scripts, a person's proof, a signer who still reaches the project. Anything else gets nothing, and the card waits with
|
|
11
|
+
* a plain line naming the missing approval instead of Chromium crashing in the sandbox. Builds, checks, tool servers and
|
|
12
|
+
* every other flow launch (setup, triggers, zones that don't say browser) are untouched.
|
|
13
|
+
*/
|
|
14
|
+
import { createHash } from "node:crypto";
|
|
15
|
+
import { canonicalJson } from "./canonical-json.js";
|
|
16
|
+
import { personProof } from "./person-proof.js";
|
|
17
|
+
import { flowDigest, validateFlowDefinition } from "./flows.js";
|
|
18
|
+
/** v144: each approval of browser use, live or ended. `digest` is browserApprovalDigest of what was approved. Additive only. */
|
|
19
|
+
export const FLOW_BROWSER_SCHEMA = `
|
|
20
|
+
CREATE TABLE IF NOT EXISTS flow_browser_grant (
|
|
21
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
22
|
+
flow INTEGER NOT NULL,
|
|
23
|
+
repo TEXT NOT NULL,
|
|
24
|
+
flow_revision INTEGER NOT NULL,
|
|
25
|
+
flow_digest TEXT NOT NULL,
|
|
26
|
+
digest TEXT NOT NULL,
|
|
27
|
+
zones_json TEXT NOT NULL,
|
|
28
|
+
signed_by TEXT NOT NULL,
|
|
29
|
+
signed_at TEXT NOT NULL,
|
|
30
|
+
proof TEXT NOT NULL,
|
|
31
|
+
revoked_at TEXT,
|
|
32
|
+
revoked_by TEXT
|
|
33
|
+
);
|
|
34
|
+
CREATE UNIQUE INDEX IF NOT EXISTS flow_browser_live ON flow_browser_grant (flow) WHERE revoked_at IS NULL;
|
|
35
|
+
`;
|
|
36
|
+
function definitionOf(flow) {
|
|
37
|
+
try {
|
|
38
|
+
return validateFlowDefinition(JSON.parse(flow.definitionJson), { stored: true });
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** The approval's digest: domain-separated over the whole of its terms, so a changed flow, drawing, revision or any
|
|
45
|
+
* covered script (a restored one is a new version) is a different approval. */
|
|
46
|
+
export function browserApprovalDigest(terms) {
|
|
47
|
+
return createHash("sha256").update(`toolroll:flow-browser:${canonicalJson(terms)}`, "utf8").digest("hex").slice(0, 32);
|
|
48
|
+
}
|
|
49
|
+
/** The flow's script zones that say they start a browser, each with the script it runs now; null when one's script is
|
|
50
|
+
* missing, waiting for approval, or runs a project file (an approval seals the script's own text, never a file the
|
|
51
|
+
* project can change). */
|
|
52
|
+
export function browserZonesOf(store, flow, definition) {
|
|
53
|
+
const out = [];
|
|
54
|
+
for (const stage of definition.stages) {
|
|
55
|
+
if (stage.kind !== "check" || stage.browser !== true)
|
|
56
|
+
continue;
|
|
57
|
+
const script = stage.script === null ? null : store.flowScript(flow.repo, stage.script);
|
|
58
|
+
if (script === null || script.held !== null || script.file !== null)
|
|
59
|
+
return null;
|
|
60
|
+
out.push({ zone: stage.id, script: script.name, version: script.version, digest: script.digest });
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
/** What an approval of this flow would be about now, read from the store; null when it can't be approved. */
|
|
65
|
+
export function browserTermsOf(store, flow) {
|
|
66
|
+
const definition = definitionOf(flow);
|
|
67
|
+
const zones = definition === null ? null : browserZonesOf(store, flow, definition);
|
|
68
|
+
return definition === null || zones === null ? null : { flow: flow.id, repo: flow.repo, revision: flow.revision, flowDigest: flowDigest(definition), zones };
|
|
69
|
+
}
|
|
70
|
+
function readGrant(row) {
|
|
71
|
+
if (row === undefined)
|
|
72
|
+
return null;
|
|
73
|
+
let zones;
|
|
74
|
+
try {
|
|
75
|
+
zones = JSON.parse(String(row["zones_json"]));
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
if (!Array.isArray(zones) || !zones.every(one => one !== null && typeof one === "object" && typeof one.zone === "string" && typeof one.script === "string" && Number.isInteger(one.version) && typeof one.digest === "string"))
|
|
81
|
+
return null;
|
|
82
|
+
return { id: Number(row["id"]), flow: Number(row["flow"]), repo: String(row["repo"]), revision: Number(row["flow_revision"]), flowDigest: String(row["flow_digest"]), digest: String(row["digest"]),
|
|
83
|
+
zones: zones, signedBy: String(row["signed_by"]), signedAt: String(row["signed_at"]), proof: String(row["proof"]) };
|
|
84
|
+
}
|
|
85
|
+
/** The flow's live approval, as recorded (whether it still stands is browserStanding's question). */
|
|
86
|
+
export function liveBrowserGrant(store, flowId) {
|
|
87
|
+
return readGrant(store.handle.prepare("SELECT * FROM flow_browser_grant WHERE flow = ? AND revoked_at IS NULL").get(flowId));
|
|
88
|
+
}
|
|
89
|
+
/** Whether `name` may approve on `repo` now: a live approver account that reaches the project. */
|
|
90
|
+
function approverNow(store, name, repo) {
|
|
91
|
+
const account = store.accountOf(name);
|
|
92
|
+
return account !== null && account.revokedAt === null && account.role === "approver" && store.accountCanAccess(name, repo);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Whether `zone` of this flow may start a browser now. `run`, when given, is what the caller is about to run — the
|
|
96
|
+
* drawing it read and the very script object it hands the launch — and the answer is about exactly that. The live
|
|
97
|
+
* approval stands only when its digest is the approval this flow would be now (same flow, project, drawing revision and
|
|
98
|
+
* digest, and every browser zone's script name, version and digest), it was a person's, and its signer is still an
|
|
99
|
+
* approver who reaches the project; `run`'s drawing must be that drawing and its script that zone's script. Anything
|
|
100
|
+
* unreadable, changed or mismatched is a no, with the plain words a card waits with.
|
|
101
|
+
*/
|
|
102
|
+
export function browserStanding(store, flowId, zone, run) {
|
|
103
|
+
const flow = store.getFlow(flowId);
|
|
104
|
+
const definition = flow === null ? null : definitionOf(flow);
|
|
105
|
+
const stage = definition?.stages.find(one => one.id === zone);
|
|
106
|
+
if (flow === null || definition === null || stage === undefined || stage.kind !== "check" || stage.browser !== true)
|
|
107
|
+
return { allowed: false, said: "This zone doesn't say it starts a browser." };
|
|
108
|
+
const approve = `Approve it with: toolroll flows browser ${flow.id} on`;
|
|
109
|
+
const grant = liveBrowserGrant(store, flow.id);
|
|
110
|
+
if (grant === null)
|
|
111
|
+
return { allowed: false, said: `${stage.title} starts a browser, and no one has approved browser use for the ${flow.name} flow, so it didn't run. ${approve}` };
|
|
112
|
+
const terms = browserTermsOf(store, flow);
|
|
113
|
+
const now = terms?.zones.find(one => one.zone === zone);
|
|
114
|
+
const runStage = run?.definition.stages.find(one => one.id === zone);
|
|
115
|
+
const runs = run === undefined || (terms !== null && now !== undefined && flowDigest(run.definition) === terms.flowDigest && runStage?.kind === "check" && runStage.browser === true
|
|
116
|
+
&& runStage.script === run.script.name && run.script.repo === flow.repo && run.script.held === null && run.script.file === null
|
|
117
|
+
&& run.script.name === now.script && run.script.version === now.version && run.script.digest === now.digest);
|
|
118
|
+
const stands = runs && terms !== null && now !== undefined && flow.state === "active" && grant.digest === browserApprovalDigest(terms)
|
|
119
|
+
&& browserApprovalDigest({ flow: grant.flow, repo: grant.repo, revision: grant.revision, flowDigest: grant.flowDigest, zones: grant.zones }) === grant.digest
|
|
120
|
+
&& personProof(grant.proof) && approverNow(store, grant.signedBy, flow.repo);
|
|
121
|
+
return stands ? { allowed: true } : { allowed: false, said: `${stage.title} starts a browser, but browser use was approved for an earlier version of the ${flow.name} flow or its scripts, so it didn't run. ${approve}` };
|
|
122
|
+
}
|
|
123
|
+
/** What approving means, in words the person reads before they give their password. */
|
|
124
|
+
export function browserTerms(zones) {
|
|
125
|
+
return [
|
|
126
|
+
`Lets ${zones.map(one => `the ${one.script} script (zone ${one.zone})`).join(", ")} start Playwright's Chromium headless shell inside the flow's sandbox: the same one macOS service a project check gets for it, and nothing else.`,
|
|
127
|
+
"Changing the flow or one of these scripts (even back to how it was) ends it; nothing else gets it.",
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Approve browser use for the flow as it stands. `proof` is the signer's credential method, proved by the caller just
|
|
132
|
+
* now; only a person's counts (a lead's or remote run's token is refused), from an approver who reaches the project.
|
|
133
|
+
* Re-reads the flow, its zones and scripts in the one transaction that writes; `sawDigest` (required) is the approval
|
|
134
|
+
* (browserApprovalDigest: drawing and scripts) the person read, and anything else is refused. Replaces any live one.
|
|
135
|
+
*/
|
|
136
|
+
export function signFlowBrowser(store, input, now) {
|
|
137
|
+
if (!personProof(input.proof))
|
|
138
|
+
return { ok: false, message: "Browser use needs a person's own approval: sign in with your password (a lead's or remote run's token can't give it)." };
|
|
139
|
+
return store.transact(() => {
|
|
140
|
+
const flow = store.getFlow(input.flow);
|
|
141
|
+
if (flow === null || flow.state !== "active")
|
|
142
|
+
return { ok: false, message: "No such flow." };
|
|
143
|
+
if (!approverNow(store, input.signer, flow.repo))
|
|
144
|
+
return { ok: false, message: "Only an approver on this flow's project can approve browser use." };
|
|
145
|
+
if (input.console !== undefined) {
|
|
146
|
+
const account = store.accountOf(input.signer);
|
|
147
|
+
if (account === null || account.generation !== input.console.generation)
|
|
148
|
+
return { ok: false, message: "Your access changed. Sign in again." };
|
|
149
|
+
if (account.projects !== null)
|
|
150
|
+
return { ok: false, message: "Allowing browser use needs an account with access to every project." };
|
|
151
|
+
}
|
|
152
|
+
if (definitionOf(flow) === null)
|
|
153
|
+
return { ok: false, message: "This flow's drawing can't be read." };
|
|
154
|
+
const terms = browserTermsOf(store, flow);
|
|
155
|
+
if (terms === null)
|
|
156
|
+
return { ok: false, message: "A zone that starts a browser has no script it can approve: missing, waiting for approval, or one that runs a project file (write the script on the zone instead)." };
|
|
157
|
+
if (terms.zones.length === 0)
|
|
158
|
+
return { ok: false, message: `No zone in ${flow.name} says it starts a browser. Mark the script zone first (browser: true), then approve.` };
|
|
159
|
+
const digest = browserApprovalDigest(terms);
|
|
160
|
+
if (input.sawDigest !== digest)
|
|
161
|
+
return { ok: false, message: "This flow or its scripts changed while you were reading. Read the terms again." };
|
|
162
|
+
store.handle.prepare("UPDATE flow_browser_grant SET revoked_at = ?, revoked_by = ? WHERE flow = ? AND revoked_at IS NULL").run(now.toISOString(), input.signer, flow.id);
|
|
163
|
+
const id = Number(store.handle.prepare("INSERT INTO flow_browser_grant (flow, repo, flow_revision, flow_digest, digest, zones_json, signed_by, signed_at, proof) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)")
|
|
164
|
+
.run(flow.id, flow.repo, terms.revision, terms.flowDigest, digest, JSON.stringify(terms.zones), input.signer, now.toISOString(), input.proof).lastInsertRowid);
|
|
165
|
+
store.recordAction({ at: now.toISOString(), actor: input.signer, repo: flow.repo, taskId: null, runId: null, source: "policy", action: "flow browser use: approved", outcome: "signed",
|
|
166
|
+
detail: `${flow.name} (flow #${flow.id}) · ${terms.zones.map(one => `${one.zone}: ${one.script} v${one.version}`).join(", ")}` });
|
|
167
|
+
return { ok: true, grant: { ...terms, id, digest, signedBy: input.signer, signedAt: now.toISOString(), proof: input.proof } };
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
/** Any approver on the project ends it, with no password: lowering authority. False when there was none. */
|
|
171
|
+
export function revokeFlowBrowser(store, flowId, by, now) {
|
|
172
|
+
return store.transact(() => {
|
|
173
|
+
const flow = store.getFlow(flowId);
|
|
174
|
+
const ended = store.handle.prepare("UPDATE flow_browser_grant SET revoked_at = ?, revoked_by = ? WHERE flow = ? AND revoked_at IS NULL").run(now.toISOString(), by, flowId).changes > 0;
|
|
175
|
+
if (ended)
|
|
176
|
+
store.recordAction({ at: now.toISOString(), actor: by, repo: flow?.repo ?? null, taskId: null, runId: null, source: "policy", action: "flow browser use: revoked", outcome: "revoked", detail: `flow #${flowId}` });
|
|
177
|
+
return ended;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
const dayWords = (at) => new Date(at).toLocaleDateString("en-US", { month: "short", day: "numeric", timeZone: "UTC" });
|
|
181
|
+
/**
|
|
182
|
+
* Browser use for one flow, read from the store (no writes: page visits call it). Whether it is allowed is exactly
|
|
183
|
+
* browserStanding's answer for the flow's browser zones; the rest only says why in plain words.
|
|
184
|
+
*/
|
|
185
|
+
export function browserViewOf(store, flow) {
|
|
186
|
+
const definition = definitionOf(flow);
|
|
187
|
+
const stages = definition?.stages.filter(stage => stage.kind === "check" && stage.browser === true) ?? [];
|
|
188
|
+
if (definition === null || stages.length === 0)
|
|
189
|
+
return null;
|
|
190
|
+
const terms = browserTermsOf(store, flow);
|
|
191
|
+
const grant = liveBrowserGrant(store, flow.id);
|
|
192
|
+
const allowed = grant !== null && stages.every(stage => browserStanding(store, flow.id, stage.id).allowed);
|
|
193
|
+
const zones = (terms?.zones ?? []).map(zone => ({ ...zone, title: definition.stages.find(one => one.id === zone.zone)?.title ?? zone.zone }));
|
|
194
|
+
const problem = terms === null ? "A zone that starts a browser has no script that can be approved: it's missing, waiting for approval, or runs a project file (write the script on the zone instead)." : null;
|
|
195
|
+
const why = grant === null ? "" : terms === null || canonicalJson(grant.zones) !== canonicalJson(terms.zones) ? "the script changed"
|
|
196
|
+
: grant.flowDigest !== terms.flowDigest || grant.revision !== terms.revision ? "the flow changed"
|
|
197
|
+
: !approverNow(store, grant.signedBy, flow.repo) ? `${grant.signedBy} can no longer approve on this project` : "it no longer matches this flow";
|
|
198
|
+
return {
|
|
199
|
+
status: allowed ? "allowed" : grant === null ? "none" : "ended",
|
|
200
|
+
words: allowed ? `Browser: allowed by ${grant.signedBy}, ${dayWords(grant.signedAt)}` : grant === null ? "Browser: not allowed — runs will wait" : `Browser approval ended: ${why}`,
|
|
201
|
+
signedBy: grant?.signedBy ?? null, signedAt: grant?.signedAt ?? null,
|
|
202
|
+
zones, terms: terms === null ? [] : browserTerms(terms.zones), digest: terms === null ? null : browserApprovalDigest(terms), problem,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Store } from "./store.js";
|
|
2
|
+
export type FlowChange = {
|
|
3
|
+
flow: number;
|
|
4
|
+
} | {
|
|
5
|
+
script: {
|
|
6
|
+
repo: string;
|
|
7
|
+
name: string;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Run `write` (the change itself) and end what it ends, all in one transaction. `write` answers whether it changed
|
|
12
|
+
* anything; when it didn't, nothing else happens. Returns what `write` returned.
|
|
13
|
+
*/
|
|
14
|
+
export declare function flowChanged<T>(store: Store, change: FlowChange, by: string, now: Date, write: () => T, changed?: (result: T) => boolean): T;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one road for a change to what a flow runs: its drawing saved, or a script one of its zones runs saved or removed.
|
|
3
|
+
* Every approval a person gave for the flow as it was ends here, in the same transaction as the write, with its ledger
|
|
4
|
+
* line, so the flow page, `flows show` and chat all say it ended and why:
|
|
5
|
+
* - "Runs on its own" (flow-unattended.ts): its grants end ("flow changed" / "script changed"), and the work they
|
|
6
|
+
* pre-authorized falls back to a person.
|
|
7
|
+
* - Browser use (flow-browser.ts): its approval seals the drawing and each browser zone's script, so the change itself
|
|
8
|
+
* stops it standing (browserStanding re-proves at every launch). Here it is said once in the ledger, for each
|
|
9
|
+
* flow whose approval stood before the write and doesn't after; the page shows it as expired with the reason.
|
|
10
|
+
* Nothing here widens anything: it only ends approvals.
|
|
11
|
+
*/
|
|
12
|
+
import { browserViewOf } from "./flow-browser.js";
|
|
13
|
+
import { endUnattendedGrants } from "./flow-unattended.js";
|
|
14
|
+
/** The flows a change reaches: the flow itself, or every flow in the script's project with a zone that runs it. */
|
|
15
|
+
function flowsOf(store, change) {
|
|
16
|
+
if ("flow" in change) {
|
|
17
|
+
const flow = store.getFlow(change.flow);
|
|
18
|
+
return flow === null ? [] : [flow];
|
|
19
|
+
}
|
|
20
|
+
return store.listFlows([change.script.repo]).filter(flow => {
|
|
21
|
+
try {
|
|
22
|
+
const drawn = JSON.parse(flow.definitionJson);
|
|
23
|
+
return (drawn.stages ?? []).some(stage => stage.kind === "check" && stage.script === change.script.name);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Run `write` (the change itself) and end what it ends, all in one transaction. `write` answers whether it changed
|
|
32
|
+
* anything; when it didn't, nothing else happens. Returns what `write` returned.
|
|
33
|
+
*/
|
|
34
|
+
export function flowChanged(store, change, by, now, write, changed = result => result === true) {
|
|
35
|
+
return store.transact(() => {
|
|
36
|
+
const before = new Map(flowsOf(store, change).map(flow => [flow.id, browserViewOf(store, flow)?.status === "allowed"]));
|
|
37
|
+
const result = write();
|
|
38
|
+
if (!changed(result))
|
|
39
|
+
return result;
|
|
40
|
+
if ("flow" in change)
|
|
41
|
+
endUnattendedGrants(store, { flow: change.flow }, by, "flow changed", now);
|
|
42
|
+
else
|
|
43
|
+
endUnattendedGrants(store, { script: change.script }, by, "script changed", now);
|
|
44
|
+
for (const [id, stood] of before) {
|
|
45
|
+
const flow = store.getFlow(id);
|
|
46
|
+
if (!stood || flow === null || browserViewOf(store, flow)?.status === "allowed")
|
|
47
|
+
continue;
|
|
48
|
+
store.recordAction({ at: now.toISOString(), actor: by, repo: flow.repo, taskId: null, runId: null, source: "policy", action: "flow browser use: ended", outcome: "ended",
|
|
49
|
+
detail: `${flow.name} (flow #${flow.id}) · ${"flow" in change ? "the flow changed" : `the ${change.script.name} script changed`}` });
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
});
|
|
53
|
+
}
|
package/dist/flow-code.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export { LANGUAGE_WORDS, SCRIPT_LANGUAGES, type ScriptLanguage } from "./flows.j
|
|
|
9
9
|
export declare const CODE_OUTPUT_CHARS: 12000;
|
|
10
10
|
export type CodeRun = {
|
|
11
11
|
script: FlowScriptRow;
|
|
12
|
+
/** Asked right before every spawn attempt (retries too); false refuses it. A browser zone's approval rides here. */
|
|
13
|
+
beforeSpawn?: () => boolean;
|
|
12
14
|
/** Where it runs, and the project folder a file script is found in. */
|
|
13
15
|
cwd: string;
|
|
14
16
|
root: string;
|
|
@@ -58,7 +60,8 @@ export declare function cardsFromOutput(stdout: string): {
|
|
|
58
60
|
* of its own for its input, an inline script and its temp files; writes as the project's writes setting allows (its
|
|
59
61
|
* folder, the copy's Git data, its project's caches); the scratch pool denied around its own folder; and its network
|
|
60
62
|
* open, as flows' scripts have always had it (they reach the services their secrets are for; no approval seals a
|
|
61
|
-
* network for them).
|
|
63
|
+
* network for them). `browser` (flow-browser.ts browserStanding, proved by the caller at launch) adds Chromium's one
|
|
64
|
+
* service, as a project check has it; nothing else asks for it. The fence is required: where this machine can't confine it, it runs only when an instance operator
|
|
62
65
|
* allowed unconfined agents, and otherwise nothing runs and the card says why.
|
|
63
66
|
*/
|
|
64
67
|
export type FlowLaunch = {
|
|
@@ -75,6 +78,7 @@ export declare function flowLaunch(store: Store, options: {
|
|
|
75
78
|
available?: () => boolean;
|
|
76
79
|
policy?: FencePolicy;
|
|
77
80
|
home?: string;
|
|
81
|
+
browser?: true;
|
|
78
82
|
}): FlowLaunch | null | {
|
|
79
83
|
problem: string;
|
|
80
84
|
};
|
package/dist/flow-code.js
CHANGED
|
@@ -79,6 +79,7 @@ export async function runCode(run) {
|
|
|
79
79
|
const ran = await run.shell("/bin/sh", ["-c", 'exec "$@" < "$FLOW_INPUT"', "flow-script", runtime.file, ...runtime.args], {
|
|
80
80
|
cwd: run.cwd, timeoutMs: script.timeoutMinutes * 60_000, envAllowlist: SETUP_ENV_ALLOWLIST, omitEnv: SETUP_ENV_DENYLIST, processGroup: true,
|
|
81
81
|
env: { ...run.env, ...run.secrets, ...(run.launch?.env ?? {}), FLOW_INPUT: inputFile }, ...(run.launch?.fence ?? {}),
|
|
82
|
+
...(run.beforeSpawn === undefined ? {} : { beforeSpawn: run.beforeSpawn }),
|
|
82
83
|
});
|
|
83
84
|
const printed = codeOutput(ran.stdout, run.secrets);
|
|
84
85
|
const all = scrubSecrets(ran.stdout.slice(0, 1_000_000), run.secrets);
|
|
@@ -192,7 +193,9 @@ export function flowLaunch(store, options) {
|
|
|
192
193
|
// The project's read denies apply to flows as to builds (read live: a flow seals no list, and these only narrow).
|
|
193
194
|
const readDeny = store.projectSandbox(options.repo).readDeny.map(one => readDenyPath(one, home));
|
|
194
195
|
const fence = fenceFor("flow", { databaseFile, worktree: options.folder, home, boundary, readDeny, policy: options.policy ?? 1, scratchRoots: [options.scratch], binding, runFolders: runFolders(own), poolRoots, extra: allowed?.deny ?? [],
|
|
195
|
-
writes: allowed === null ? null : { writes: allowed.writes, readOnly: allowed.readOnly }, network: { mode: "open" }
|
|
196
|
+
writes: allowed === null ? null : { writes: allowed.writes, readOnly: allowed.readOnly }, network: { mode: "open" },
|
|
197
|
+
// Chromium's one service, only for a script zone whose browser use a person approved, proved by the caller now.
|
|
198
|
+
...(options.browser === true ? { browser: true } : {}) });
|
|
196
199
|
return { folder: own, env: { ...env, ...(allowed === null ? {} : buildCacheEnv(cache)) }, fence, dispose };
|
|
197
200
|
}
|
|
198
201
|
catch {
|
package/dist/flow-engine.js
CHANGED
|
@@ -23,6 +23,7 @@ import { cardFollowers, notifyPeople } from "./flow-people.js";
|
|
|
23
23
|
import { DRAFT_CHARS, keptDraft } from "./flow-draft.js";
|
|
24
24
|
import { chooseStep, flowPersonOf, sendStep } from "./flow-send.js";
|
|
25
25
|
import { parseSoul, subagentLabel } from "./subagents.js";
|
|
26
|
+
import { cardTaskArchived, taskArchived } from "./task-archive.js";
|
|
26
27
|
import { withStageHandoff } from "./contracts/stage-output.js";
|
|
27
28
|
/** The flow's drawing, or null when it can't be read (the card then says so and waits). */
|
|
28
29
|
export function flowDefinitionOf(flow) {
|
|
@@ -90,6 +91,14 @@ function advanceCard(store, flow, definition, card, now, options, outcome) {
|
|
|
90
91
|
return;
|
|
91
92
|
}
|
|
92
93
|
const stage = definition.stages.find(one => one.id === card.stage);
|
|
94
|
+
// A put-away task (v145, task-tidy.md) holds its card where it is, whatever the zone: nothing moves on, files, sends
|
|
95
|
+
// or notifies until the task is restored.
|
|
96
|
+
if (cardTaskArchived(store, card.id)) {
|
|
97
|
+
const waiting = "Its task is put away. Restore the task to continue.";
|
|
98
|
+
if (card.waiting !== waiting)
|
|
99
|
+
store.updateFlowCard(card.id, { waiting }, now);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
93
102
|
if (stage === undefined) {
|
|
94
103
|
// The zone was removed while the card sat in it: back to the start.
|
|
95
104
|
if (store.moveFlowCard(card.id, { to: definition.start, outcome: "moved", actor: "flow", note: null, expectEntry: card.entry }, now))
|
|
@@ -311,6 +320,13 @@ function workStage(store, flow, definition, stage, card, now, options, outcome,
|
|
|
311
320
|
store.updateFlowCard(card.id, { waiting: "Its task is gone. Move the card to try again." }, now);
|
|
312
321
|
return;
|
|
313
322
|
}
|
|
323
|
+
// A put-away task (v145, task-tidy.md) holds its card where it is: nothing moves on, files or sends until it's restored.
|
|
324
|
+
if (taskArchived(store, current) || taskArchived(store, card.task)) {
|
|
325
|
+
const waiting = "Its task is put away. Restore the task to continue.";
|
|
326
|
+
if (card.waiting !== waiting)
|
|
327
|
+
store.updateFlowCard(card.id, { waiting }, now);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
314
330
|
if (task.state === "done") {
|
|
315
331
|
// A build whose project checks failed is done but not good: it takes the failure path, like a failed build.
|
|
316
332
|
if (stage.kind === "task") {
|
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
import { type GalleryAnswers, type GalleryPreview, type GalleryTemplate, type GalleryTool } from "./flow-gallery.js";
|
|
2
2
|
import { type FlowDefinition, type FlowStage } from "./flows.js";
|
|
3
|
-
import { type Html } from "./html.js";
|
|
3
|
+
import { type Html, type HtmlValue } from "./html.js";
|
|
4
4
|
export declare const GALLERY_CSS: string;
|
|
5
5
|
/** A flow's main path, as a person reads it: from the start, each step's first way on (a sort's first answer, a choose's
|
|
6
6
|
* first option that goes somewhere, else next; a check's failure when passing just ends), never back to a step shown. */
|
|
7
7
|
export declare function mainPath(definition: FlowDefinition): FlowStage[];
|
|
8
|
-
/** A flow's steps in order, joined by arrows
|
|
8
|
+
/** A flow's steps in order, joined by arrows. */
|
|
9
9
|
export declare function stepStrip(definition: FlowDefinition): Html;
|
|
10
|
-
/**
|
|
10
|
+
/** One row of New flow: name · promise · one line · one outline action (the same shape for templates, kits, recipes and starters). */
|
|
11
|
+
export declare function templateRow(options: {
|
|
12
|
+
name: string;
|
|
13
|
+
promise: string;
|
|
14
|
+
line?: HtmlValue;
|
|
15
|
+
action?: HtmlValue;
|
|
16
|
+
attrs?: Html;
|
|
17
|
+
}): Html;
|
|
18
|
+
/** The groups New flow filters by: the template groups, then kits, recipes and starter flows. */
|
|
19
|
+
export declare const NEW_FLOW_GROUPS: ReadonlyArray<readonly [key: string, label: string]>;
|
|
20
|
+
/** The templates as rows, grouped (or one group): each with one "Use this". `repo` carries a chosen project to the template's page. */
|
|
11
21
|
export declare function galleryHtml(input: {
|
|
12
22
|
repo: string | null;
|
|
13
23
|
canUse: boolean;
|
|
@@ -15,8 +25,35 @@ export declare function galleryHtml(input: {
|
|
|
15
25
|
id: string;
|
|
16
26
|
state: "connected" | "open" | "taken";
|
|
17
27
|
}[] | null;
|
|
28
|
+
group?: string;
|
|
29
|
+
}): Html;
|
|
30
|
+
/**
|
|
31
|
+
* New flow: describe it (the shared composer; the words go to the lead as a chat draft, the project on its bar), then
|
|
32
|
+
* start from a template, filtered by group. Kits, recipes and starter flows arrive as their own rows.
|
|
33
|
+
*/
|
|
34
|
+
export declare function newFlowHtml(input: {
|
|
35
|
+
repo: string | null;
|
|
36
|
+
projects: readonly {
|
|
37
|
+
path: string;
|
|
38
|
+
name: string;
|
|
39
|
+
}[];
|
|
40
|
+
canUse: boolean;
|
|
41
|
+
group: string;
|
|
42
|
+
connections: readonly {
|
|
43
|
+
id: string;
|
|
44
|
+
state: "connected" | "open" | "taken";
|
|
45
|
+
}[] | null;
|
|
46
|
+
ask: {
|
|
47
|
+
value: string;
|
|
48
|
+
error: string | null;
|
|
49
|
+
};
|
|
50
|
+
kits: Html;
|
|
51
|
+
recipes: Html;
|
|
52
|
+
starters: Html;
|
|
53
|
+
said: string | null;
|
|
54
|
+
problem: string | null;
|
|
18
55
|
}): Html;
|
|
19
|
-
/** A template's page: its questions,
|
|
56
|
+
/** A template's page, a reading page: its steps and tools, its questions, what it will do and never do, then Create. */
|
|
20
57
|
export declare function galleryUseHtml(input: {
|
|
21
58
|
template: GalleryTemplate;
|
|
22
59
|
projects: readonly {
|