toolroll 0.9.43 → 0.9.45

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/action-ledger.d.ts +5 -2
  3. package/dist/action-ledger.js +13 -3
  4. package/dist/cli-http.d.ts +53 -0
  5. package/dist/cli-http.js +143 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.js +7 -0
  8. package/dist/contracts/cli.d.ts +13 -0
  9. package/dist/contracts/cli.js +7 -0
  10. package/dist/contracts/gateway-tools.d.ts +82 -0
  11. package/dist/contracts/gateway-tools.js +33 -0
  12. package/dist/desktop-update.d.ts +4 -2
  13. package/dist/desktop-update.js +7 -6
  14. package/dist/dispatch.d.ts +2 -2
  15. package/dist/dispatch.js +8 -4
  16. package/dist/lead-status.d.ts +7 -3
  17. package/dist/lead-status.js +21 -12
  18. package/dist/mcp-core.d.ts +110 -0
  19. package/dist/mcp-core.js +311 -0
  20. package/dist/mcp-http.d.ts +26 -0
  21. package/dist/mcp-http.js +151 -0
  22. package/dist/mcp-person.d.ts +31 -0
  23. package/dist/mcp-person.js +96 -0
  24. package/dist/mcp.d.ts +1 -8
  25. package/dist/mcp.js +21 -322
  26. package/dist/operate-remote-arguments.d.ts +36 -0
  27. package/dist/operate-remote-arguments.js +185 -0
  28. package/dist/operate-remote.d.ts +98 -0
  29. package/dist/operate-remote.js +145 -0
  30. package/dist/operate.d.ts +21 -1
  31. package/dist/operate.js +206 -19
  32. package/dist/remote-command.d.ts +45 -0
  33. package/dist/remote-command.js +124 -0
  34. package/dist/remote-exec.d.ts +18 -0
  35. package/dist/remote-exec.js +241 -0
  36. package/dist/remote-run.d.ts +20 -0
  37. package/dist/remote-run.js +13 -0
  38. package/dist/scope.js +10 -0
  39. package/dist/serve.d.ts +7 -0
  40. package/dist/serve.js +24 -0
  41. package/dist/store.d.ts +15 -1
  42. package/dist/store.js +34 -7
  43. package/dist/surface.d.ts +10 -3
  44. package/dist/surface.js +67 -2
  45. package/dist/team-cli.d.ts +19 -0
  46. package/dist/team-cli.js +20 -3
  47. package/dist/toolroll-update.js +4 -1
  48. package/dist/work-summary.d.ts +4 -1
  49. package/dist/work-summary.js +1 -1
  50. package/package.json +1 -1
package/dist/team-cli.js CHANGED
@@ -8,9 +8,12 @@ import { envelopeJson } from './envelope.js';
8
8
  import { sessionServiceOrigin } from './session-cli.js';
9
9
  import { databasePath } from './store.js';
10
10
  import { configBase, namedPath } from './names.js';
11
+ import { parseApiToken } from './api-tokens.js';
11
12
  export const TEAM_CLI_ACTIONS = ['connect', 'lead list', 'lead create', 'lead update', 'lead member', 'lead transfer', 'conversation list', 'conversation create', 'conversation show', 'conversation member', 'conversation edit', 'conversation withdraw', 'conversation read', 'conversation follow', 'conversation stop'];
12
13
  const HELP = `toolroll connect <HTTPS-origin> --as <account> --token-stdin
13
- Use --token-file <private-file> or --local-login for your existing local sign-in.
14
+ Pipe your API token (so_…, from Settings → API tokens) or use --token-file <private-file>;
15
+ --local-login uses your existing local sign-in for chat only.
16
+ With an API token saved, other commands run on that server as you (--local runs here).
14
17
  Optional: --profile <name> (saved as the active profile).
15
18
  toolroll lead list|create|update|member|transfer [--profile <name>] [--json]
16
19
  create: --name <name> --instructions <text> --project <server-path> (repeatable)
@@ -36,7 +39,7 @@ toolroll brief --lead <id> --conversation <id> [--json]
36
39
  Saved profiles select the central service for chat and brief. Use --local for the
37
40
  existing local commands. Credentials stay in a private file, never in URLs or arguments.
38
41
  Reads and attachment do not start model work. Server permissions remain authoritative.`;
39
- class UsageError extends Error {
42
+ export class UsageError extends Error {
40
43
  }
41
44
  const value = (flags, name) => typeof flags[name] === 'string' ? flags[name] : '';
42
45
  const required = (flags, name) => { const result = value(flags, name); if (!result)
@@ -128,6 +131,20 @@ function saveProfile(options, name, profile) {
128
131
  unlinkSync(temporary);
129
132
  }
130
133
  }
134
+ /** An API token (so_…) is sent bare; an older saved sign-in keeps its account:secret form. */
135
+ export const bearerOf = (profile) => parseApiToken(profile.token) !== null ? `Bearer ${profile.token}` : `Bearer ${profile.account}:${profile.token}`;
136
+ /** The saved central profile remote commands use: the named one (or the active one), and whether it holds an API token.
137
+ * null when nothing is saved. Reads only the private profile file — never a local login or the database. */
138
+ export function centralProfile(options, name) {
139
+ const saved = profiles(options);
140
+ if (saved === null)
141
+ return null;
142
+ const chosen = name ?? saved.active;
143
+ if (!/^[A-Za-z0-9_-]{1,64}$/.test(chosen) || !Object.hasOwn(saved.profiles, chosen))
144
+ throw new UsageError(`No saved connection named ${chosen}. Use connect with your API token first.`);
145
+ const profile = saved.profiles[chosen];
146
+ return { name: chosen, ...profile, apiToken: parseApiToken(profile.token) !== null };
147
+ }
131
148
  async function stdinSecret() {
132
149
  if (process.stdin.isTTY)
133
150
  throw new UsageError('Pipe your sign-in secret into --token-stdin, or use --token-file.');
@@ -229,7 +246,7 @@ async function perform(profile, request, options) {
229
246
  const reading = ['list', 'show'].includes(request.operation);
230
247
  const unconfirmed = () => failure(reading ? 'service-unavailable' : 'delivery-unconfirmed', reading ? 'The service response could not be read.' : 'The response could not be confirmed. Inspect saved messages before continuing; this request was not retried.', { operation: request.operation, ...(request.args.conversationId ? { conversationId: request.args.conversationId } : {}), ...(request.args.requestId ? { requestId: request.args.requestId } : {}) });
231
248
  try {
232
- const response = await (options.fetch ?? fetch)(`${profile.origin}/api/team`, { method: 'POST', redirect: 'manual', credentials: 'omit', signal: options.signal ? AbortSignal.any([options.signal, AbortSignal.timeout(30_000)]) : AbortSignal.timeout(30_000), headers: { authorization: `Bearer ${profile.account}:${profile.token}`, 'content-type': 'application/json', accept: 'application/json' }, body: JSON.stringify(request) });
249
+ const response = await (options.fetch ?? fetch)(`${profile.origin}/api/team`, { method: 'POST', redirect: 'manual', credentials: 'omit', signal: options.signal ? AbortSignal.any([options.signal, AbortSignal.timeout(30_000)]) : AbortSignal.timeout(30_000), headers: { authorization: bearerOf(profile), 'content-type': 'application/json', accept: 'application/json' }, body: JSON.stringify(request) });
233
250
  if (response.status >= 300 && response.status < 400) {
234
251
  await response.body?.cancel();
235
252
  return unconfirmed();
@@ -35,6 +35,7 @@ import { processMayBeAlive } from "./process-liveness.js";
35
35
  import { assertCodingUpdateStopped, backupCodingCatalog, codingCatalogExists, releaseStaleCodingOwner, removeCodingUpdateGate } from "./coding-update.js";
36
36
  import { installLaunchdService, launchdPlist, stopLaunchdService, writeFileDurably } from "./daemon.js";
37
37
  import { NAME } from "./names.js";
38
+ import { updateSafeSchema } from "./store.js";
38
39
  import { isNewer, REGISTRY } from "./releases.js";
39
40
  import { markNeverIndex } from "./never-index.js";
40
41
  import { readRuntimeUpdateJournal, RUNTIME_PHASES, RUNTIME_UPDATE_STEPS, stagedStartedAt, updaterStartingOf } from "./contracts/update-journal.js";
@@ -911,7 +912,9 @@ async function driveRuntimeUpdate(j, system) {
911
912
  db.close();
912
913
  }
913
914
  const backup = join(j.stageDir, existsSync(join(j.stageDir, "orders.backup.db")) ? `orders.backup.${randomUUID()}.db` : "orders.backup.db");
914
- j.backupHash = await verifiedDatabaseBackup(j.databaseFile, backup, j.id, async () => { await codingBackup(j); });
915
+ // A database one or more update-safe migrations behind is backed up as it is: the rehearsal below proves the
916
+ // migration on a copy, and the new runtime runs it. Any other schema needs the separate migration procedure.
917
+ j.backupHash = await verifiedDatabaseBackup(j.databaseFile, backup, j.id, async () => { await codingBackup(j); }, undefined, updateSafeSchema);
915
918
  j.backupPath = backup;
916
919
  save(j, "backing-up", "Database and coding catalog backed up.", system.now());
917
920
  step("rehearsing", `Rehearsing ${j.to.version} on a copy of the database.`);
@@ -41,7 +41,7 @@ export declare function openWorkDecisionOf(store: Store, taskRef: number, now: D
41
41
  export declare function workDecisionAction(taskId: string, decision: NonNullable<WorkFacts["openDecision"]>, principal: WorkPrincipal): WorkAction;
42
42
  /** Pure counterpart for callers already holding the same facts. */
43
43
  export declare function workSummaryOf(facts: WorkFacts, principal: WorkPrincipal, resultDisplay?: DisplayStatus): WorkSummary;
44
- export type WorkSummaryAccess =
44
+ export type WorkSummaryAccess = (
45
45
  /** `viewer`: the person reading, whose own lead's claim (lead-voice.ts) a task reads as "<name> is on it". */
46
46
  {
47
47
  principal: "operator";
@@ -51,6 +51,9 @@ export type WorkSummaryAccess =
51
51
  } | {
52
52
  principal: "coordinator";
53
53
  repos: readonly string[];
54
+ }) & {
55
+ /** Remote task reads also restrict dependency details; omitted preserves existing local output. */
56
+ dependencyRepos?: readonly string[] | null;
54
57
  };
55
58
  /** Admission precedes reading question/proof bodies. Missing and foreign
56
59
  * tasks share the same result. The caller must authenticate/revalidate
@@ -94,7 +94,7 @@ export function taskWorkSummaryOf(store, taskId, now, access) {
94
94
  const publication = result === null ? null : store.publicationForRun(result.id);
95
95
  const facts = {
96
96
  id: task.id, title: task.title, repo: ref.repo, state: task.state, updatedAt: task.updatedAt,
97
- dispatch: diagnoseTaskDispatch(store, taskId, now), liveRunId: live?.id ?? null,
97
+ dispatch: diagnoseTaskDispatch(store, taskId, now, access.dependencyRepos ?? null), liveRunId: live?.id ?? null,
98
98
  unfinishedRunId: runs.find(one => one.outcome === null && one.role !== "reviewer")?.id ?? null,
99
99
  control: taskControlOf(store, ref.id, now), openDecision: openWorkDecisionOf(store, ref.id, now),
100
100
  result: result === null ? null : {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolroll",
3
- "version": "0.9.43",
3
+ "version": "0.9.45",
4
4
  "description": "A control plane for unattended coding agents — queue tasks, walk away, come back to pull requests. Agents build in leased worktrees, never touch your default branch, and interrupt you only for decisions that need a human.",
5
5
  "directories": {
6
6
  "doc": "docs"