toolroll 0.9.89 → 0.9.91
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 +63 -0
- package/README.md +16 -10
- package/THIRD_PARTY_NOTICES.md +37 -0
- package/dist/agent-board.d.ts +9 -0
- package/dist/agent-board.js +20 -0
- package/dist/agent-fence.js +20 -2
- package/dist/approval-authority.d.ts +7 -0
- package/dist/approval-authority.js +13 -2
- package/dist/browser/THIRD_PARTY_NOTICES.txt +692 -28
- package/dist/browser/terminal.js +42 -42
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +32 -32
- package/dist/browser-workspace.d.ts +13 -0
- package/dist/builder.js +5 -2
- package/dist/chat-actions.d.ts +5 -0
- package/dist/chat-actions.js +7 -1
- package/dist/chat-delivery.js +2 -0
- package/dist/chat-task-actions.d.ts +1 -0
- package/dist/check-access.d.ts +3 -0
- package/dist/check-access.js +4 -1
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +6 -2
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +23 -0
- package/dist/coding-ui.js +4 -2
- package/dist/connect-cli.js +2 -1
- package/dist/console-instance.d.ts +34 -1
- package/dist/console-instance.js +124 -15
- package/dist/contracts/cli.d.ts +72 -0
- package/dist/contracts/cli.js +13 -0
- package/dist/contracts/console-api.d.ts +9 -2
- package/dist/contracts/console-api.js +9 -2
- package/dist/desktop-host.js +8 -3
- package/dist/desktop-update.js +2 -1
- package/dist/dispatch.d.ts +3 -0
- package/dist/dispatch.js +22 -2
- package/dist/easy-start-args.d.ts +13 -0
- package/dist/easy-start-args.js +24 -0
- package/dist/easy-start.d.ts +77 -0
- package/dist/easy-start.js +321 -0
- package/dist/first-run.d.ts +77 -0
- package/dist/first-run.js +45 -0
- package/dist/guides.js +1 -1
- package/dist/invoke.js +11 -6
- package/dist/laptop-background.d.ts +171 -0
- package/dist/laptop-background.js +550 -0
- package/dist/laptop-service.d.ts +111 -0
- package/dist/laptop-service.js +268 -0
- package/dist/lead-cli.js +2 -1
- package/dist/lead-doors.d.ts +6 -0
- package/dist/lead-doors.js +20 -2
- package/dist/live-records.d.ts +1 -1
- package/dist/live-records.js +3 -12
- package/dist/local-owner.d.ts +124 -0
- package/dist/local-owner.js +212 -0
- package/dist/login-secrets.d.ts +60 -0
- package/dist/login-secrets.js +195 -0
- package/dist/mcp-oauth.d.ts +4 -0
- package/dist/mcp-oauth.js +13 -7
- package/dist/model-choices.d.ts +43 -0
- package/dist/model-choices.js +80 -0
- package/dist/operate.d.ts +46 -1
- package/dist/operate.js +461 -40
- package/dist/person-proof.js +3 -0
- package/dist/project-knowledge.d.ts +7 -7
- package/dist/quick-start.d.ts +3 -1
- package/dist/quick-start.js +6 -1
- package/dist/release-adapters.d.ts +2 -0
- package/dist/release-adapters.js +30 -1
- package/dist/release-cli.js +2 -1
- package/dist/runner-process.d.ts +9 -0
- package/dist/runner-process.js +18 -0
- package/dist/sample-project.d.ts +31 -0
- package/dist/sample-project.js +141 -0
- package/dist/scope.d.ts +24 -2
- package/dist/scope.js +74 -3
- package/dist/serve.js +129 -22
- package/dist/server/chat.js +10 -3
- package/dist/server/chrome.d.ts +10 -2
- package/dist/server/chrome.js +7 -5
- package/dist/server/console.css +10 -0
- package/dist/server/guards.d.ts +3 -0
- package/dist/server/guards.js +22 -2
- package/dist/server/http.d.ts +22 -0
- package/dist/server/http.js +58 -0
- package/dist/server/live.js +4 -14
- package/dist/server/mutation-key.d.ts +70 -0
- package/dist/server/mutation-key.js +142 -0
- package/dist/server/pages.js +10 -7
- package/dist/server/people-tokens.js +240 -20
- package/dist/server/remote.d.ts +2 -0
- package/dist/server/remote.js +19 -4
- package/dist/server/render-new-task.js +5 -1
- package/dist/server/render-pages.d.ts +2 -1
- package/dist/server/render-pages.js +1 -1
- package/dist/server/render-people.d.ts +36 -0
- package/dist/server/render-people.js +65 -1
- package/dist/server/render-projects.js +4 -2
- package/dist/server/render-tasks.d.ts +12 -0
- package/dist/server/render-tasks.js +12 -4
- package/dist/server/request-context.d.ts +6 -0
- package/dist/server/route-table.d.ts +16 -0
- package/dist/server/route-table.js +121 -0
- package/dist/server/runtime.d.ts +6 -4
- package/dist/server/session.d.ts +31 -1
- package/dist/server/session.js +12 -2
- package/dist/server/settings.js +63 -8
- package/dist/server/task-views.js +10 -1
- package/dist/server/tasks.d.ts +10 -0
- package/dist/server/tasks.js +59 -9
- package/dist/server/terminals.js +15 -2
- package/dist/server/welcome.d.ts +22 -16
- package/dist/server/welcome.js +150 -52
- package/dist/settings-groups-ui.d.ts +7 -1
- package/dist/settings-groups-ui.js +23 -2
- package/dist/settings-proofs.js +1 -0
- package/dist/setup-guide.d.ts +2 -2
- package/dist/store.d.ts +124 -1
- package/dist/store.js +245 -12
- package/dist/surface.d.ts +10 -0
- package/dist/surface.js +40 -1
- package/dist/task-text.d.ts +6 -0
- package/dist/task-text.js +17 -0
- package/dist/telegram-lead.js +2 -0
- package/dist/terminal-text.d.ts +11 -0
- package/dist/terminal-text.js +26 -0
- package/dist/toolroll-update.d.ts +8 -1
- package/dist/toolroll-update.js +151 -4
- package/dist/tui-contract.d.ts +118 -0
- package/dist/tui-contract.js +9 -0
- package/dist/tui-source.d.ts +14 -0
- package/dist/tui-source.js +182 -0
- package/dist/tui.js +129 -0
- package/dist/up-processes.d.ts +13 -1
- package/dist/up-processes.js +13 -3
- package/dist/welcome-facts.d.ts +18 -0
- package/dist/welcome-facts.js +79 -0
- package/package.json +4 -2
package/dist/contracts/cli.d.ts
CHANGED
|
@@ -235,6 +235,16 @@ export declare const COMMAND_OUTPUTS: {
|
|
|
235
235
|
error: z.ZodNullable<z.ZodString>;
|
|
236
236
|
retry: z.ZodString;
|
|
237
237
|
}, z.core.$loose>>>;
|
|
238
|
+
laptop: z.ZodOptional<z.ZodObject<{
|
|
239
|
+
running: z.ZodBoolean;
|
|
240
|
+
url: z.ZodNullable<z.ZodString>;
|
|
241
|
+
pid: z.ZodNullable<z.ZodNumber>;
|
|
242
|
+
background: z.ZodEnum<{
|
|
243
|
+
off: "off";
|
|
244
|
+
on: "on";
|
|
245
|
+
"other-service": "other-service";
|
|
246
|
+
}>;
|
|
247
|
+
}, z.core.$loose>>;
|
|
238
248
|
}, z.core.$loose>, z.ZodObject<{
|
|
239
249
|
envelopeVersion: z.ZodLiteral<1>;
|
|
240
250
|
ok: z.ZodLiteral<false>;
|
|
@@ -838,6 +848,68 @@ export declare const COMMAND_OUTPUTS: {
|
|
|
838
848
|
reason: z.ZodString;
|
|
839
849
|
message: z.ZodOptional<z.ZodString>;
|
|
840
850
|
}, z.core.$loose>], "ok">;
|
|
851
|
+
readonly welcome: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
852
|
+
envelopeVersion: z.ZodLiteral<1>;
|
|
853
|
+
ok: z.ZodLiteral<true>;
|
|
854
|
+
command: z.ZodLiteral<"welcome">;
|
|
855
|
+
agent: z.ZodObject<{
|
|
856
|
+
done: z.ZodBoolean;
|
|
857
|
+
checking: z.ZodBoolean;
|
|
858
|
+
agents: z.ZodArray<z.ZodObject<{
|
|
859
|
+
id: z.ZodString;
|
|
860
|
+
name: z.ZodString;
|
|
861
|
+
state: z.ZodString;
|
|
862
|
+
command: z.ZodNullable<z.ZodString>;
|
|
863
|
+
inUse: z.ZodBoolean;
|
|
864
|
+
canUse: z.ZodBoolean;
|
|
865
|
+
}, z.core.$loose>>;
|
|
866
|
+
inUse: z.ZodNullable<z.ZodString>;
|
|
867
|
+
buildsWith: z.ZodNullable<z.ZodString>;
|
|
868
|
+
}, z.core.$loose>;
|
|
869
|
+
project: z.ZodObject<{
|
|
870
|
+
done: z.ZodBoolean;
|
|
871
|
+
projects: z.ZodArray<z.ZodObject<{
|
|
872
|
+
name: z.ZodString;
|
|
873
|
+
path: z.ZodString;
|
|
874
|
+
}, z.core.$loose>>;
|
|
875
|
+
sample: z.ZodNullable<z.ZodString>;
|
|
876
|
+
notTakingWork: z.ZodBoolean;
|
|
877
|
+
}, z.core.$loose>;
|
|
878
|
+
task: z.ZodObject<{
|
|
879
|
+
done: z.ZodBoolean;
|
|
880
|
+
open: z.ZodBoolean;
|
|
881
|
+
starters: z.ZodArray<z.ZodObject<{
|
|
882
|
+
label: z.ZodString;
|
|
883
|
+
draft: z.ZodString;
|
|
884
|
+
href: z.ZodString;
|
|
885
|
+
}, z.core.$loose>>;
|
|
886
|
+
}, z.core.$loose>;
|
|
887
|
+
next: z.ZodNullable<z.ZodEnum<{
|
|
888
|
+
agent: "agent";
|
|
889
|
+
project: "project";
|
|
890
|
+
task: "task";
|
|
891
|
+
}>>;
|
|
892
|
+
left: z.ZodNumber;
|
|
893
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
894
|
+
envelopeVersion: z.ZodLiteral<1>;
|
|
895
|
+
ok: z.ZodLiteral<false>;
|
|
896
|
+
command: z.ZodLiteral<"welcome">;
|
|
897
|
+
reason: z.ZodString;
|
|
898
|
+
message: z.ZodOptional<z.ZodString>;
|
|
899
|
+
}, z.core.$loose>], "ok">;
|
|
900
|
+
readonly "password set": z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
901
|
+
envelopeVersion: z.ZodLiteral<1>;
|
|
902
|
+
ok: z.ZodLiteral<true>;
|
|
903
|
+
command: z.ZodLiteral<"password set">;
|
|
904
|
+
account: z.ZodString;
|
|
905
|
+
generation: z.ZodNumber;
|
|
906
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
907
|
+
envelopeVersion: z.ZodLiteral<1>;
|
|
908
|
+
ok: z.ZodLiteral<false>;
|
|
909
|
+
command: z.ZodLiteral<"password set">;
|
|
910
|
+
reason: z.ZodString;
|
|
911
|
+
message: z.ZodOptional<z.ZodString>;
|
|
912
|
+
}, z.core.$loose>], "ok">;
|
|
841
913
|
};
|
|
842
914
|
/** The schema for `command`'s answer: its own when it names its fields, else the envelope under its own name. */
|
|
843
915
|
export declare function commandOutputSchema(command: string): z.ZodType;
|
package/dist/contracts/cli.js
CHANGED
|
@@ -90,6 +90,9 @@ const statusOutput = answers("status", {
|
|
|
90
90
|
integrations: z.looseObject({ broken: rows({ key: str, name: str, fix: nullable(str) }) }).optional(),
|
|
91
91
|
updateWaiting: z.looseObject({}).optional(),
|
|
92
92
|
unsentReplies: rows({ since: str, error: nullable(str), retry: str }).optional(),
|
|
93
|
+
// This computer's console (easy-start D): proven live or not, its address and console process, and whether a
|
|
94
|
+
// background service runs it — ours, another on this database or the desktop app's, or none. Not for a person limited to some projects.
|
|
95
|
+
laptop: z.looseObject({ running: z.boolean(), url: nullable(str), pid: nullable(int), background: z.enum(["on", "off", "other-service"]) }).optional(),
|
|
93
96
|
});
|
|
94
97
|
/** One integration (`integrations.ts` Integration): never a secret. */
|
|
95
98
|
const integrationRow = z.looseObject({
|
|
@@ -203,6 +206,16 @@ export const COMMAND_OUTPUTS = {
|
|
|
203
206
|
reap: answers("reap", { count: int, released: list }),
|
|
204
207
|
audit: auditOutput,
|
|
205
208
|
"skills list": answers("skills list", { guides: rows({ name: str, title: str, oneLiner: str }) }),
|
|
209
|
+
// The welcome's three steps (first-run.ts welcomeModel), the same model the console's /welcome shows.
|
|
210
|
+
welcome: answers("welcome", {
|
|
211
|
+
agent: z.looseObject({ done: z.boolean(), checking: z.boolean(), agents: rows({ id: str, name: str, state: str, command: nullable(str), inUse: z.boolean(), canUse: z.boolean() }), inUse: nullable(str), buildsWith: nullable(str) }),
|
|
212
|
+
project: z.looseObject({ done: z.boolean(), projects: rows({ name: str, path: str }), sample: nullable(str), notTakingWork: z.boolean() }),
|
|
213
|
+
task: z.looseObject({ done: z.boolean(), open: z.boolean(), starters: rows({ label: str, draft: str, href: str }) }),
|
|
214
|
+
next: nullable(z.enum(["agent", "project", "task"])),
|
|
215
|
+
left: int,
|
|
216
|
+
}),
|
|
217
|
+
// The first password set: whose, and the account generation every older session was bound to before it.
|
|
218
|
+
"password set": answers("password set", { account: str, generation: int }),
|
|
206
219
|
};
|
|
207
220
|
const declared = new Map(Object.entries(COMMAND_OUTPUTS));
|
|
208
221
|
/** The schema for `command`'s answer: its own when it names its fields, else the envelope under its own name. */
|
|
@@ -61,7 +61,9 @@ export declare const CONSOLE_FORMS: {
|
|
|
61
61
|
readonly signup: FormContract<"code" | "name" | "password">;
|
|
62
62
|
readonly login: FormContract<"name" | "return" | "token">;
|
|
63
63
|
readonly join: FormContract<"name" | "password">;
|
|
64
|
-
readonly
|
|
64
|
+
readonly rekey: FormContract<"return" | "token">;
|
|
65
|
+
readonly passwordSet: FormContract<"again" | "csrf" | "next" | "password" | "return" | "toolroll-key">;
|
|
66
|
+
readonly mutationGuard: FormContract<"confirm" | "csrf" | "digest" | "nonce" | "token" | "toolroll-key">;
|
|
65
67
|
readonly ledgerTarget: FormContract<"repo">;
|
|
66
68
|
readonly projectCeiling: FormContract<"repo">;
|
|
67
69
|
readonly demo: FormContract<"message" | "note">;
|
|
@@ -72,6 +74,7 @@ export declare const CONSOLE_FORMS: {
|
|
|
72
74
|
readonly code: FormContract<"acceptance" | "answers" | "base" | "candidate" | "decision" | "goal" | "model" | "password" | "prompt" | "repo" | "requestId" | "title" | "visual" | `question:${string}`>;
|
|
73
75
|
readonly sweep: FormContract<"confirm" | "snapshot">;
|
|
74
76
|
readonly passwordless: FormContract<"action" | "password">;
|
|
77
|
+
readonly localModeOff: FormContract<"password">;
|
|
75
78
|
readonly releaseApproval: FormContract<"action" | "password" | "repo">;
|
|
76
79
|
readonly term: FormContract<"password" | "program" | "repo">;
|
|
77
80
|
readonly skillsRevise: FormContract<"feedback" | "nonce" | "repo" | "run">;
|
|
@@ -156,12 +159,16 @@ export declare const CONSOLE_FORMS: {
|
|
|
156
159
|
readonly leadAbout: FormContract<"about">;
|
|
157
160
|
readonly leadPromiseCancel: FormContract<"promise">;
|
|
158
161
|
readonly leadCheck: FormContract<"return">;
|
|
162
|
+
readonly leadOn: FormContract<"agent" | "return">;
|
|
163
|
+
readonly welcomeSample: FormContract<never>;
|
|
159
164
|
readonly chatConfig: FormContract<"daily-turns" | "forget-key" | "key" | "model" | "off" | "provider" | "return" | "token" | "weekly-usd">;
|
|
160
165
|
readonly leadMint: FormContract<"ceiling-usd" | "follow" | "return" | "token">;
|
|
161
166
|
readonly leadFollow: FormContract<"enabled" | "return">;
|
|
162
167
|
readonly leadEnd: FormContract<"return">;
|
|
163
168
|
readonly leadStop: FormContract<"return" | "turn">;
|
|
164
169
|
readonly leadQueuedRemove: FormContract<"return">;
|
|
170
|
+
readonly agentSeen: FormContract<"run">;
|
|
171
|
+
readonly agentWatch: FormContract<"op" | "run" | "view">;
|
|
165
172
|
readonly leadProposal: FormContract<"confirm" | "nonce" | "return" | "token">;
|
|
166
173
|
readonly coordinatorProposal: FormContract<"confirm" | "return">;
|
|
167
174
|
readonly chat: FormContract<"message" | "mode" | "project" | "request" | "request-session" | "result" | "task" | "token">;
|
|
@@ -200,7 +207,7 @@ export declare const CONSOLE_FORMS: {
|
|
|
200
207
|
};
|
|
201
208
|
export type ConsoleFormName = keyof typeof CONSOLE_FORMS;
|
|
202
209
|
/** The console POSTs whose handlers read no field (sign-out not even the shared guard): their contract is the empty form. */
|
|
203
|
-
export declare const BODILESS_POSTS: readonly ["POST /settings/models/<unknown action>", "POST /
|
|
210
|
+
export declare const BODILESS_POSTS: readonly ["POST /settings/models/<unknown action>", "POST /r/<run>/draft-repair", "POST /ledger/checkpoint", "POST /settings/backups/now", "POST /mode/revoke", "POST /settings/telegram/retry", "POST /providers/<provider>/resume", "POST /logout", "POST /t/<task>/unhold", "POST /t/<task>/stop-carrying", "POST /t/<task>/plan", "POST /t/<task>/retry-review", "POST /settings/updates/check", "POST /settings/storage/check", "POST /projects/github", "POST /code/<session>/ship/preview"];
|
|
204
211
|
/** The field names a console form's handler may read. */
|
|
205
212
|
export type FormFieldOf<N extends ConsoleFormName> = (typeof CONSOLE_FORMS)[N] extends FormContract<infer K> ? K : never;
|
|
206
213
|
/** `GET /ledger?format=json`: the chain's state, a page of entries (newest first) and the cursor for the next. */
|
|
@@ -69,8 +69,10 @@ export const CONSOLE_FORMS = {
|
|
|
69
69
|
signup: formContract("POST /signup", ["code", "name", "password"]),
|
|
70
70
|
login: formContract("POST /login", ["name", "token", "return"]),
|
|
71
71
|
join: formContract("POST /join/<invite>", ["name", "password"]),
|
|
72
|
+
rekey: formContract("POST /login/rekey", ["token", "return"]),
|
|
73
|
+
passwordSet: formContract("POST /password/set", ["csrf", "password", "again", "next", "return", "toolroll-key"]),
|
|
72
74
|
// Shared by every signed-in POST.
|
|
73
|
-
mutationGuard: formContract("POST (every signed-in form)", ["csrf", "token", "digest", "nonce", "confirm"]),
|
|
75
|
+
mutationGuard: formContract("POST (every signed-in form)", ["csrf", "token", "digest", "nonce", "confirm", "toolroll-key"]),
|
|
74
76
|
ledgerTarget: formContract("POST (the action ledger's project)", ["repo"]),
|
|
75
77
|
projectCeiling: formContract("POST (project ceiling)", ["repo"]),
|
|
76
78
|
demo: formContract("POST /chat/demo/ask, /chat/demo/<id>/approve|change|revise|complete", ["message", "note"]),
|
|
@@ -81,6 +83,7 @@ export const CONSOLE_FORMS = {
|
|
|
81
83
|
code: formContract("POST /code, /code/start, /code/<session>/<act>, /code/<anything else>", ["repo", "password", "title", "model", "prompt", "requestId", "base", "candidate", "goal", "acceptance", "visual", "decision", "answers"], { prefixes: ["question:"] }),
|
|
82
84
|
sweep: formContract("POST /sweeps/<sweep>/cancel-rest", ["confirm", "snapshot"]),
|
|
83
85
|
passwordless: formContract("POST /settings/passwordless", ["action", "password"]),
|
|
86
|
+
localModeOff: formContract("POST /settings/local-mode/off", ["password"]),
|
|
84
87
|
releaseApproval: formContract("POST /settings/release-approval", ["repo", "action", "password"]),
|
|
85
88
|
term: formContract("POST /term/start, /term/<terminal>/close, /term/<terminal>/remove", ["repo", "program", "password"]),
|
|
86
89
|
skillsRevise: formContract("POST /settings/skills/revise", ["run", "repo", "feedback", "nonce"]),
|
|
@@ -169,12 +172,17 @@ export const CONSOLE_FORMS = {
|
|
|
169
172
|
leadAbout: formContract("POST /settings/lead/about", ["about"]),
|
|
170
173
|
leadPromiseCancel: formContract("POST /settings/lead/promise/cancel", ["promise"]),
|
|
171
174
|
leadCheck: formContract("POST /lead/check", ["return"]),
|
|
175
|
+
leadOn: formContract("POST /settings/lead/on", ["agent", "return"]),
|
|
176
|
+
welcomeSample: formContract("POST /welcome/sample", []),
|
|
172
177
|
chatConfig: formContract("POST /chat/config", ["return", "token", "off", "forget-key", "provider", "model", "weekly-usd", "daily-turns", "key"]),
|
|
173
178
|
leadMint: formContract("POST /chat/mate/mint", ["return", "ceiling-usd", "token", "follow"]),
|
|
174
179
|
leadFollow: formContract("POST /chat/mate/follow", ["enabled", "return"]),
|
|
175
180
|
leadEnd: formContract("POST /chat/mate/end", ["return"]),
|
|
176
181
|
leadStop: formContract("POST /chat/mate/stop", ["return", "turn"]),
|
|
177
182
|
leadQueuedRemove: formContract("POST /chat/queued/<message>/remove", ["return"]),
|
|
183
|
+
// Easy-start C1 (N1): what a page load used to write, now a keyed POST from the shell.
|
|
184
|
+
agentSeen: formContract("POST /agents/seen", ["run"]),
|
|
185
|
+
agentWatch: formContract("POST /agents/watch", ["run", "view", "op"]),
|
|
178
186
|
leadProposal: formContract("POST /chat/proposal/<id>/confirm|dismiss", ["return", "nonce", "confirm", "token"]),
|
|
179
187
|
coordinatorProposal: formContract("POST /proposals/<id>/confirm|dismiss", ["return", "confirm"]),
|
|
180
188
|
chat: formContract("POST /chat", ["task", "project", "result", "mode", "request", "request-session", "message", "token"]),
|
|
@@ -218,7 +226,6 @@ export const CONSOLE_FORMS = {
|
|
|
218
226
|
/** The console POSTs whose handlers read no field (sign-out not even the shared guard): their contract is the empty form. */
|
|
219
227
|
export const BODILESS_POSTS = [
|
|
220
228
|
"POST /settings/models/<unknown action>",
|
|
221
|
-
"POST /settings/lead/on",
|
|
222
229
|
"POST /r/<run>/draft-repair",
|
|
223
230
|
"POST /ledger/checkpoint",
|
|
224
231
|
"POST /settings/backups/now",
|
package/dist/desktop-host.js
CHANGED
|
@@ -9,6 +9,7 @@ import { openStore, openStoreNoMigrate, databasePath, readSchemaVersion, SCHEMA_
|
|
|
9
9
|
import { createRequire } from "node:module";
|
|
10
10
|
import { authenticateApprover, hashPassword } from "./scope.js";
|
|
11
11
|
import { runOperate, writeLoginFileDurably } from "./operate.js";
|
|
12
|
+
import { ensureSecretsDir, migrateLegacyLogin, savedLoginFile } from "./login-secrets.js";
|
|
12
13
|
import { daemonStatus, installLaunchdService, planDesktopService, stopLaunchdService } from "./daemon.js";
|
|
13
14
|
import { run as execRun } from "./exec.js";
|
|
14
15
|
import { containmentStatus, effectiveContainment, parseContainmentPolicy, probeContainmentCapability } from "./containment.js";
|
|
@@ -172,7 +173,7 @@ export function pairDesktopLogin(store, file, login) {
|
|
|
172
173
|
writeLoginFileDurably(file, login.name, login.password);
|
|
173
174
|
}
|
|
174
175
|
else if (!(first && remembered.name === login.name && remembered.password === login.password) && !authenticateApprover(store, remembered.name, remembered.password).ok) {
|
|
175
|
-
throw new Error("The
|
|
176
|
+
throw new Error("The saved login (up-login.txt) is stale. Restore its valid login before starting the desktop service.");
|
|
176
177
|
}
|
|
177
178
|
if (first)
|
|
178
179
|
store.bootstrapApproverIfNone(login.name, hashPassword(login.password), new Date());
|
|
@@ -371,7 +372,8 @@ export async function desktopMain(argv, supervise = execRun) {
|
|
|
371
372
|
}
|
|
372
373
|
// Inspection and launch must never migrate another running controller.
|
|
373
374
|
const store = openConfiguredDesktopStore(stateDir);
|
|
374
|
-
|
|
375
|
+
// The saved login: in the reserved folder every agent fence denies (login-secrets.ts), else where older versions kept it.
|
|
376
|
+
const legacyFile = savedLoginFile(dirname(config.databaseFile));
|
|
375
377
|
if (argv[0] !== "serve-controller") {
|
|
376
378
|
try {
|
|
377
379
|
if (argv[0] === "inspect") {
|
|
@@ -387,7 +389,10 @@ export async function desktopMain(argv, supervise = execRun) {
|
|
|
387
389
|
}
|
|
388
390
|
else if (argv[0] === "pair") {
|
|
389
391
|
const login = await readInput();
|
|
390
|
-
|
|
392
|
+
// A login saved here goes into the reserved folder, made and proven first; an older one moves in.
|
|
393
|
+
migrateLegacyLogin(dirname(config.databaseFile));
|
|
394
|
+
ensureSecretsDir(dirname(config.databaseFile));
|
|
395
|
+
pairDesktopLogin(store, savedLoginFile(dirname(config.databaseFile)), login);
|
|
391
396
|
console.log(JSON.stringify({ ok: true }));
|
|
392
397
|
}
|
|
393
398
|
else if (argv[0] === "add-repo" || argv[0] === "add-repos") {
|
package/dist/desktop-update.js
CHANGED
|
@@ -3,6 +3,7 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
3
3
|
import { closeSync, chmodSync, copyFileSync, cpSync, existsSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
6
|
+
import { savedLoginFile } from "./login-secrets.js";
|
|
6
7
|
import { createRequire } from "node:module";
|
|
7
8
|
import { bundleHash, readDesktopBundle, verifyDesktopUpdateBundles } from "./desktop-bundle.js";
|
|
8
9
|
import { readDesktopConfig, desktopServiceCommand, desktopServiceDefinition, verifyDesktopLiveness } from "./desktop-host.js";
|
|
@@ -535,7 +536,7 @@ async function verifiedBackup(j) {
|
|
|
535
536
|
const backupHash = await verifiedDatabaseBackup(j.databaseFile, backupPath, j.id, async () => {
|
|
536
537
|
await verifiedCodingBackup(j);
|
|
537
538
|
}, () => {
|
|
538
|
-
for (const [source, name] of [[join(j.stateDir, "desktop.json"), "desktop.json"], [join(dirname(j.databaseFile), "repos.json"), "repos.json"], [
|
|
539
|
+
for (const [source, name] of [[join(j.stateDir, "desktop.json"), "desktop.json"], [join(dirname(j.databaseFile), "repos.json"), "repos.json"], [savedLoginFile(dirname(j.databaseFile)), "up-login.txt"]]) {
|
|
539
540
|
if (source && name && existsSync(source)) {
|
|
540
541
|
if (!lstatSync(source).isFile() || lstatSync(source).isSymbolicLink())
|
|
541
542
|
throw Error("A saved configuration file is linked or not a regular file. Nothing was replaced.");
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -118,3 +118,6 @@ export declare function diagnosisIsDispatchable(diagnosis: DispatchDiagnosis | n
|
|
|
118
118
|
/** Attach the same lifecycle answer to the bounded fleet snapshot used by
|
|
119
119
|
* both direct chat and the long-running lead. */
|
|
120
120
|
export declare function withDispatchDiagnoses(store: Store, snapshot: ChatSnapshot, now: Date): ChatSnapshot;
|
|
121
|
+
/** When a backed-off task tries again, as a person reads it: "in under a minute", "in 3 minutes", or a later day's
|
|
122
|
+
* time in UTC (the console's own clock words, when-html.ts shortWhen). */
|
|
123
|
+
export declare function retryWhenWords(until: string, now: Date): string;
|
package/dist/dispatch.js
CHANGED
|
@@ -17,6 +17,7 @@ import { replacedWords } from "./task-status.js";
|
|
|
17
17
|
import { failedAttemptSentence, latestFinishedAttempt } from "./needs-you.js";
|
|
18
18
|
import { buildReviewOf } from "./review-switch.js";
|
|
19
19
|
import { BUILT_IN, refArchived, parseCapabilityKey } from "./store.js";
|
|
20
|
+
import { shortWhen } from "./when-html.js";
|
|
20
21
|
export const DEFAULT_MAX_OPEN_DECISIONS = 5;
|
|
21
22
|
/**
|
|
22
23
|
* Task state, active holds, and unfinished dependencies in the exact order
|
|
@@ -260,10 +261,11 @@ export function diagnoseTaskDispatch(store, taskId, now, readableRepos = null) {
|
|
|
260
261
|
if (local.ownerKind === "backoff") {
|
|
261
262
|
// Run 2085: an attempt that stopped before its handoff says so plainly.
|
|
262
263
|
const last = store.runsFor(ref.id).find(one => one.role === "builder" && one.finishedAt !== null);
|
|
263
|
-
|
|
264
|
+
// Said once, with a time a person reads (nextAt keeps the exact stamp for machines).
|
|
265
|
+
const when = local.until === null ? "" : ` It tries again ${retryWhenWords(local.until, now)}.`;
|
|
264
266
|
if (last?.reason === "no-handoff")
|
|
265
267
|
return answer("retry-scheduled", "retrying", "Retry scheduled", `The agent stopped before handing off; its work was kept and it is being resumed.${when}`, { nextAt: local.until });
|
|
266
|
-
return answer("retry-scheduled", "retrying", "Retry scheduled", local.until === null ? "The failed attempt is backing off before retrying." : `
|
|
268
|
+
return answer("retry-scheduled", "retrying", "Retry scheduled", local.until === null ? "The failed attempt is backing off before retrying." : `It tries again ${retryWhenWords(local.until, now)}.`, { nextAt: local.until });
|
|
267
269
|
}
|
|
268
270
|
if (local.ownerKind === "decision")
|
|
269
271
|
return answer("waiting-decision", "waiting", "Waiting on your answer", "An agent parked a question; answering it resumes the task.", { action: "answer-decision" });
|
|
@@ -358,3 +360,21 @@ export function withDispatchDiagnoses(store, snapshot, now) {
|
|
|
358
360
|
tasks: snapshot.tasks.map(one => ({ ...one, dispatch: diagnoseTaskDispatch(store, one.id, now) })),
|
|
359
361
|
};
|
|
360
362
|
}
|
|
363
|
+
/** When a backed-off task tries again, as a person reads it: "in under a minute", "in 3 minutes", or a later day's
|
|
364
|
+
* time in UTC (the console's own clock words, when-html.ts shortWhen). */
|
|
365
|
+
export function retryWhenWords(until, now) {
|
|
366
|
+
const ms = Date.parse(until) - now.getTime();
|
|
367
|
+
if (Number.isNaN(ms))
|
|
368
|
+
return "soon";
|
|
369
|
+
if (ms <= 60_000)
|
|
370
|
+
return "in under a minute";
|
|
371
|
+
const minutes = Math.ceil(ms / 60_000);
|
|
372
|
+
if (minutes < 60)
|
|
373
|
+
return `in ${minutes} minutes`;
|
|
374
|
+
const at = shortWhen(until, now, "UTC", true);
|
|
375
|
+
if (/^\d/.test(at))
|
|
376
|
+
return `at ${at} UTC`;
|
|
377
|
+
if (at.startsWith("Tomorrow "))
|
|
378
|
+
return `tomorrow at ${at.slice("Tomorrow ".length)} UTC`;
|
|
379
|
+
return `on ${at} UTC`;
|
|
380
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Which invocations are an easy start (easy-start.ts), kept apart so the CLI's entry decides without loading it. */
|
|
2
|
+
export type EasyStartArgs = {
|
|
3
|
+
web: boolean;
|
|
4
|
+
projectRoot: string | null;
|
|
5
|
+
};
|
|
6
|
+
/** The flags bare `toolroll` takes at a terminal (--web, --project-root <dir>), or null: not an easy start. */
|
|
7
|
+
export declare function easyStartArgs(argv: readonly string[]): EasyStartArgs | null;
|
|
8
|
+
/** Whether this invocation is an easy start: only those flags, a person at a terminal, no coding agent. */
|
|
9
|
+
export declare function easyStartApplies(argv: readonly string[], facts: {
|
|
10
|
+
stdin: boolean;
|
|
11
|
+
stdout: boolean;
|
|
12
|
+
agent: boolean;
|
|
13
|
+
}): boolean;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Which invocations are an easy start (easy-start.ts), kept apart so the CLI's entry decides without loading it. */
|
|
2
|
+
/** The flags bare `toolroll` takes at a terminal (--web, --project-root <dir>), or null: not an easy start. */
|
|
3
|
+
export function easyStartArgs(argv) {
|
|
4
|
+
let web = false, projectRoot = null;
|
|
5
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
6
|
+
const one = argv[index];
|
|
7
|
+
if (one === "--web" && !web)
|
|
8
|
+
web = true;
|
|
9
|
+
else if (one === "--project-root" && projectRoot === null) {
|
|
10
|
+
const value = argv[index + 1];
|
|
11
|
+
if (value === undefined || value === "" || value.startsWith("-"))
|
|
12
|
+
return null;
|
|
13
|
+
projectRoot = value;
|
|
14
|
+
index += 1;
|
|
15
|
+
}
|
|
16
|
+
else
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return { web, projectRoot };
|
|
20
|
+
}
|
|
21
|
+
/** Whether this invocation is an easy start: only those flags, a person at a terminal, no coding agent. */
|
|
22
|
+
export function easyStartApplies(argv, facts) {
|
|
23
|
+
return facts.stdin && facts.stdout && !facts.agent && easyStartArgs(argv) !== null;
|
|
24
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { type AttachProof } from "./console-instance.js";
|
|
2
|
+
import type { TuiExit, TuiSession } from "./tui-contract.js";
|
|
3
|
+
export { easyStartApplies, easyStartArgs, type EasyStartArgs } from "./easy-start-args.js";
|
|
4
|
+
import type { EasyStartArgs } from "./easy-start-args.js";
|
|
5
|
+
/** The `up` this process started and owns. */
|
|
6
|
+
export type OwnedUp = {
|
|
7
|
+
pid: number;
|
|
8
|
+
exited: Promise<number | null>;
|
|
9
|
+
running(): boolean;
|
|
10
|
+
stop(): void;
|
|
11
|
+
tail(): readonly string[];
|
|
12
|
+
};
|
|
13
|
+
export type EasyStartSeams = {
|
|
14
|
+
databaseFile: string;
|
|
15
|
+
cwd: string;
|
|
16
|
+
write: (line: string) => void;
|
|
17
|
+
/** Prove this database's console live (read only). */
|
|
18
|
+
prove: () => Promise<AttachProof>;
|
|
19
|
+
/** Local mode as saved now: on, off, or no database yet (a new install). */
|
|
20
|
+
localMode: () => "on" | "off" | "absent";
|
|
21
|
+
serviceInstalled: () => boolean;
|
|
22
|
+
/** Start the installed background service through its supervisor and prove its console. */
|
|
23
|
+
startService: () => Promise<AttachProof>;
|
|
24
|
+
/** `up` (with these arguments) as an ordinary child of this process. */
|
|
25
|
+
spawnUp: (args: readonly string[]) => OwnedUp;
|
|
26
|
+
/** `up` (with these arguments) in this terminal, until it stops: today's foreground start. */
|
|
27
|
+
runUpForeground: (args: readonly string[]) => Promise<number>;
|
|
28
|
+
openBrowser: (url: string) => void | Promise<void>;
|
|
29
|
+
/** A local owner's one-time sign-in address from the live console (the socket `link` verb). */
|
|
30
|
+
requestLink: () => Promise<{
|
|
31
|
+
ok: true;
|
|
32
|
+
url: string;
|
|
33
|
+
} | {
|
|
34
|
+
ok: false;
|
|
35
|
+
message: string;
|
|
36
|
+
}>;
|
|
37
|
+
/** The terminal UI, in this process, as the local owner, until the person quits. */
|
|
38
|
+
openTui: (session: TuiSession) => Promise<TuiExit>;
|
|
39
|
+
/** `background on`, handing over from the `up` this process owns (when it owns one). */
|
|
40
|
+
backgroundOn: (foreground: {
|
|
41
|
+
controllerPid: number;
|
|
42
|
+
restart: () => Promise<void>;
|
|
43
|
+
} | null) => Promise<{
|
|
44
|
+
ok: boolean;
|
|
45
|
+
message: string;
|
|
46
|
+
}>;
|
|
47
|
+
backgroundAsked: () => boolean;
|
|
48
|
+
recordBackgroundAnswer: (yes: boolean) => void;
|
|
49
|
+
/** This folder's repository, whether it is added, and adding it (only when attaching to a running console). */
|
|
50
|
+
findRepo: (cwd: string) => Promise<string | null>;
|
|
51
|
+
enrolled: (repo: string) => Promise<boolean>;
|
|
52
|
+
admit: (repo: string) => Promise<number>;
|
|
53
|
+
sleep?: (ms: number) => Promise<void>;
|
|
54
|
+
startTimeoutMs?: number;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Bare `toolroll` ("start") or `toolroll open` ("open") at a person's terminal. Resolves with the exit code once the
|
|
58
|
+
* screen (or the foreground `up`, or the browser hand-off) is done.
|
|
59
|
+
*/
|
|
60
|
+
export declare function runEasyStart(mode: "start" | "open", args: EasyStartArgs, seams: EasyStartSeams): Promise<number>;
|
|
61
|
+
/** `up` as an ordinary child of this process: its output kept (last lines, for a failed start), never on the screen. */
|
|
62
|
+
export declare function spawnOwnedUp(args: readonly string[], options: {
|
|
63
|
+
cwd: string;
|
|
64
|
+
databaseFile: string;
|
|
65
|
+
entry?: string;
|
|
66
|
+
}): OwnedUp;
|
|
67
|
+
/** The real seams, for this installation's database. */
|
|
68
|
+
export declare function defaultEasyStartSeams(options: {
|
|
69
|
+
databaseFile: string;
|
|
70
|
+
write: (line: string) => void;
|
|
71
|
+
build: string;
|
|
72
|
+
cwd?: string;
|
|
73
|
+
operate?: import("./operate.js").OperateOptions;
|
|
74
|
+
admit: (repo: string) => Promise<number>;
|
|
75
|
+
}): EasyStartSeams;
|
|
76
|
+
/** Local mode as saved, read only: "absent" with no database yet; an older database has none ("off"). */
|
|
77
|
+
export declare function localModeRead(file: string): "on" | "off" | "absent";
|