@illuminis/comprism 0.1.5 → 0.1.7

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 (107) hide show
  1. package/out/agent/command.js +4 -4
  2. package/out/agent/render.d.ts +24 -0
  3. package/out/agent/render.js +35 -3
  4. package/out/agent/session.d.ts +6 -1
  5. package/out/agent/session.js +17 -8
  6. package/out/commands/agents.js +3 -3
  7. package/out/commands/ask.js +8 -8
  8. package/out/commands/codemap.d.ts +1 -1
  9. package/out/commands/codemap.js +3 -3
  10. package/out/commands/commands-thin.js +4 -4
  11. package/out/commands/config.js +1 -1
  12. package/out/commands/cost.js +4 -4
  13. package/out/commands/decisionEngine.d.ts +1 -0
  14. package/out/commands/decisionEngine.js +66 -0
  15. package/out/commands/hooks.js +1 -1
  16. package/out/commands/install.d.ts +1 -1
  17. package/out/commands/install.js +3 -2
  18. package/out/commands/instructions.js +1 -1
  19. package/out/commands/integrations.js +4 -4
  20. package/out/commands/keys.js +6 -6
  21. package/out/commands/login.js +37 -21
  22. package/out/commands/permissions.js +2 -2
  23. package/out/commands/plugins.js +3 -3
  24. package/out/commands/privacy.js +2 -2
  25. package/out/commands/repl.js +107 -83
  26. package/out/commands/review.js +6 -6
  27. package/out/commands/settings.js +30 -123
  28. package/out/commands/skills.js +2 -2
  29. package/out/commands/unattended.js +6 -6
  30. package/out/commands/update.js +1 -1
  31. package/out/commands/welcome.js +20 -29
  32. package/out/commands/worktrees.d.ts +1 -1
  33. package/out/graph/sync.d.ts +1 -1
  34. package/out/graph/sync.js +6 -6
  35. package/out/lib/attach.js +4 -3
  36. package/out/lib/commandlist.d.ts +1 -1
  37. package/out/lib/commandlist.js +2 -2
  38. package/out/lib/config.d.ts +3 -2
  39. package/out/lib/config.js +7 -8
  40. package/out/lib/connection.js +1 -1
  41. package/out/lib/gateway.d.ts +5 -5
  42. package/out/lib/machine.js +1 -1
  43. package/out/lib/project-ops.d.ts +1 -1
  44. package/out/lib/project-ops.js +4 -4
  45. package/out/lib/queue.js +1 -1
  46. package/out/lib/readiness.d.ts +3 -30
  47. package/out/lib/readiness.js +13 -82
  48. package/out/lib/servicecommand.d.ts +14 -0
  49. package/out/lib/servicecommand.js +74 -0
  50. package/out/lib/sessions.js +3 -3
  51. package/out/lib/ui.d.ts +9 -6
  52. package/out/lib/ui.js +20 -26
  53. package/out/lib/voice.d.ts +2 -39
  54. package/out/lib/voice.js +7 -148
  55. package/out/lib/words.d.ts +20 -0
  56. package/out/lib/words.js +27 -0
  57. package/out/machine/codemap/build.d.ts +45 -0
  58. package/out/machine/codemap/build.js +91 -0
  59. package/out/machine/codemap/facts.d.ts +47 -0
  60. package/out/machine/codemap/facts.js +12 -0
  61. package/out/machine/codemap/files.d.ts +45 -0
  62. package/out/machine/codemap/files.js +207 -0
  63. package/out/machine/codemap/read-locales.d.ts +29 -0
  64. package/out/machine/codemap/read-locales.js +246 -0
  65. package/out/machine/codemap/read-python.d.ts +11 -0
  66. package/out/machine/codemap/read-python.js +116 -0
  67. package/out/machine/codemap/read-typescript.d.ts +16 -0
  68. package/out/machine/codemap/read-typescript.js +292 -0
  69. package/out/machine/executor/browser.d.ts +14 -0
  70. package/out/machine/executor/browser.js +270 -0
  71. package/out/machine/executor/diagnostics.d.ts +2 -0
  72. package/out/machine/executor/diagnostics.js +181 -0
  73. package/out/machine/executor/documents.d.ts +40 -0
  74. package/out/machine/executor/documents.js +170 -0
  75. package/out/machine/executor/files.d.ts +2 -0
  76. package/out/machine/executor/files.js +590 -0
  77. package/out/machine/executor/git.d.ts +48 -0
  78. package/out/machine/executor/git.js +145 -0
  79. package/out/machine/executor/hooks.d.ts +51 -0
  80. package/out/machine/executor/hooks.js +154 -0
  81. package/out/machine/executor/index.d.ts +45 -0
  82. package/out/machine/executor/index.js +367 -0
  83. package/out/machine/executor/notebook.d.ts +2 -0
  84. package/out/machine/executor/notebook.js +147 -0
  85. package/out/machine/executor/paths.d.ts +20 -0
  86. package/out/machine/executor/paths.js +154 -0
  87. package/out/machine/executor/sandbox.d.ts +40 -0
  88. package/out/machine/executor/sandbox.js +299 -0
  89. package/out/machine/executor/shell.d.ts +86 -0
  90. package/out/machine/executor/shell.js +582 -0
  91. package/out/machine/executor/toolservers.d.ts +20 -0
  92. package/out/machine/executor/toolservers.js +189 -0
  93. package/out/machine/executor/worktree.d.ts +9 -0
  94. package/out/machine/executor/worktree.js +119 -0
  95. package/out/machine/folder/project.d.ts +28 -0
  96. package/out/machine/folder/project.js +114 -0
  97. package/out/machine/runtime/home.d.ts +2 -0
  98. package/out/machine/runtime/home.js +48 -0
  99. package/out/machine/runtime/needs.d.ts +30 -0
  100. package/out/machine/runtime/needs.js +89 -0
  101. package/out/machine/runtime/self.d.ts +23 -0
  102. package/out/machine/runtime/self.js +124 -0
  103. package/out/machine/voice/record.d.ts +38 -0
  104. package/out/machine/voice/record.js +208 -0
  105. package/out/providers/index.js +2 -1
  106. package/out/thin.js +18 -10
  107. package/package.json +4 -4
@@ -78,6 +78,7 @@ const connection_1 = require("../lib/connection");
78
78
  const gateway_1 = require("../lib/gateway");
79
79
  const ui = __importStar(require("../lib/ui"));
80
80
  const prompt_1 = require("../lib/prompt");
81
+ const words_1 = require("../lib/words");
81
82
  const out = (s = '') => process.stdout.write(s + '\n');
82
83
  /** Where a named workspace lives. One format, stated once. */
83
84
  const WORKSPACE_DOMAIN = 'completionprism.illuminis.ai';
@@ -103,6 +104,21 @@ function workspaceUrl(typed) {
103
104
  return `https://${value}`;
104
105
  return `https://${value}.${WORKSPACE_DOMAIN}`;
105
106
  }
107
+ /**
108
+ * Where an organization signs in. A full address is used as typed. A bare name
109
+ * is turned into its address by the service, so every client follows one rule
110
+ * (service catalog 1).
111
+ */
112
+ async function addressOf(typed) {
113
+ const value = typed.trim().replace(/\/+$/, '');
114
+ if (/^[A-Za-z0-9][A-Za-z0-9-]{0,62}$/.test(value) && value.toLowerCase() !== 'localhost') {
115
+ const reply = await (0, gateway_1.askAt)(`https://${WORKSPACE_DOMAIN}`, { intent: 'resolve_workspace', prompt: value });
116
+ const url = reply.session?.workspace_url;
117
+ if (url)
118
+ return url.replace(/\/+$/, '');
119
+ }
120
+ return workspaceUrl(value);
121
+ }
106
122
  /** Localhost needs the tenant named, because there is no subdomain to read. */
107
123
  function tenantFor(url, typed, flagged) {
108
124
  if (flagged)
@@ -168,8 +184,8 @@ async function approveInBrowser(url, tenant, noBrowser) {
168
184
  const res = await fetch(`${url}/oauth/device_authorization`, form({ client_id: CLIENT_ID, scope: 'api:read api:write' }));
169
185
  if (!res.ok) {
170
186
  out(ui.fail(res.status === 404 || res.status === 405
171
- ? 'That workspace is running a build without browser sign in.'
172
- : 'The workspace would not start a browser sign in.'));
187
+ ? 'That organization is running a build without browser sign in.'
188
+ : 'The organization would not start a browser sign in.'));
173
189
  return '';
174
190
  }
175
191
  started = await res.json();
@@ -253,7 +269,7 @@ async function runLogin(args) {
253
269
  out();
254
270
  return;
255
271
  }
256
- const last = flag('--username') || flag('--email') || flag('--workspace') ? null : readLast();
272
+ const last = flag('--username') || flag('--email') || (flag('--organization') || flag('--workspace')) ? null : readLast();
257
273
  if (last) {
258
274
  out(` ${ui.c.dim('last time')} ${ui.c.bold(last.email)} ${ui.c.dim('at')} ${ui.c.bold(last.workspace)}`);
259
275
  out(` ${ui.c.dim('press Enter to sign in as them, or type another email')}`);
@@ -275,15 +291,15 @@ async function runLogin(args) {
275
291
  return;
276
292
  }
277
293
  // Where this person's workspace is. Asked of the product, not of them.
278
- let typedWorkspace = flag('--workspace') || (again ? last?.workspace : undefined);
294
+ let typedWorkspace = (flag('--organization') || flag('--workspace')) || (again ? last?.workspace : undefined);
279
295
  // Captured BEFORE the email lookup can fill it in. A workspace the product
280
296
  // worked out is not a workspace the person typed, and only the second may be
281
297
  // a local address. See the fence in `writeConnection`. The last sign in
282
298
  // counts as typed only if it was typed then.
283
- const typedByHand = Boolean(flag('--workspace')) || Boolean(again && last?.typed);
299
+ const typedByHand = Boolean((flag('--organization') || flag('--workspace'))) || Boolean(again && last?.typed);
284
300
  if (!typedWorkspace) {
285
301
  const live = new ui.LiveIndicator();
286
- live.start('finding your workspace');
302
+ live.start('finding your organization');
287
303
  try {
288
304
  typedWorkspace = (await resolveWorkspace(username)) || undefined;
289
305
  }
@@ -295,18 +311,18 @@ async function runLogin(args) {
295
311
  process.stdout.write('\r');
296
312
  if (!typedWorkspace) {
297
313
  out();
298
- out(ui.fail('No workspace is registered for that email address.'));
299
- out(` ${ui.c.dim('if your company uses a different domain, pass --workspace <name>')}`);
314
+ out(ui.fail('No organization is registered for that email address.'));
315
+ out(` ${ui.c.dim('if your company uses a different domain, pass --organization <name>')}`);
300
316
  out();
301
317
  process.exitCode = 1;
302
318
  return;
303
319
  }
304
320
  }
305
- const url = workspaceUrl(typedWorkspace);
321
+ const url = await addressOf(typedWorkspace);
306
322
  const tenant = tenantFor(url, typedWorkspace, flag('--tenant'));
307
323
  // Said before the password is asked for, so a person can see which company
308
324
  // they are about to sign in to (manual 1.3).
309
- out(` workspace: ${url}`);
325
+ out(` organization: ${url}`);
310
326
  // COMPANY LOGIN (manual 1.10). A workspace whose company signs in through
311
327
  // its own identity provider is signed in to in a browser; this tool never
312
328
  // sees that password. `--device` is the same thing for a machine with no
@@ -346,8 +362,8 @@ async function runLogin(args) {
346
362
  }, tenant);
347
363
  if (reply.served === false) {
348
364
  out();
349
- out(ui.fail('That workspace is running a build that cannot sign you in yet.'));
350
- out(` ${ui.c.dim(reply.message || 'the workspace is behind this copy of the tool')}`);
365
+ out(ui.fail('That organization is running a build that cannot sign you in yet.'));
366
+ out(` ${ui.c.dim(reply.message || 'the organization is behind this copy of the tool')}`);
351
367
  out();
352
368
  process.exitCode = 1;
353
369
  return;
@@ -394,7 +410,7 @@ async function runLogin(args) {
394
410
  forgetPassword(username, typedWorkspace);
395
411
  out();
396
412
  out(ui.fail(reply.message || 'That username and password were not accepted.'));
397
- out(` ${ui.c.dim('check the workspace name too: a right password in the wrong workspace looks the same')}`);
413
+ out(` ${ui.c.dim('check the organization name too: a right password in the wrong organization looks the same')}`);
398
414
  out();
399
415
  process.exitCode = 1;
400
416
  return;
@@ -447,7 +463,7 @@ async function runLogin(args) {
447
463
  }
448
464
  catch (err) {
449
465
  out();
450
- out(ui.fail(err instanceof Error ? err.message : 'the workspace address was refused'));
466
+ out(ui.fail(err instanceof Error ? err.message : (0, words_1.word)('addressRefused')));
451
467
  out();
452
468
  process.exitCode = 1;
453
469
  return;
@@ -489,7 +505,7 @@ async function runLogin(args) {
489
505
  // The workspace's OWN reason, carried back with the sign in. It used to
490
506
  // come from a local variable set by a separate call, which meant a blank
491
507
  // line whenever that call had not been the thing that failed.
492
- out(` ${ui.c.dim(signInMessage || 'the workspace did not say why')}`);
508
+ out(` ${ui.c.dim(signInMessage || 'the organization did not say why')}`);
493
509
  out(` ${ui.c.dim('you are signed in; this machine cannot ask for work until it is enrolled')}`);
494
510
  }
495
511
  out();
@@ -500,13 +516,13 @@ async function loginWithKey(key, args) {
500
516
  const i = args.indexOf(name);
501
517
  return i >= 0 ? args[i + 1] : undefined;
502
518
  };
503
- const typed = flag('--workspace');
519
+ const typed = (flag('--organization') || flag('--workspace'));
504
520
  if (!key || !typed) {
505
- out(ui.fail('Signing in with a key needs the key and the workspace: comprism login --key <key> --workspace <address>'));
521
+ out(ui.fail('Signing in with a key needs the key and the organization: comprism login --key <key> --organization <address>'));
506
522
  process.exitCode = 1;
507
523
  return;
508
524
  }
509
- const url = workspaceUrl(typed);
525
+ const url = await addressOf(typed);
510
526
  const tenant = tenantFor(url, typed, flag('--tenant'));
511
527
  const { ask: askWith } = await Promise.resolve().then(() => __importStar(require('../lib/gateway')));
512
528
  const existing = (0, connection_1.readConnection)();
@@ -518,7 +534,7 @@ async function loginWithKey(key, args) {
518
534
  }, true);
519
535
  }
520
536
  catch (err) {
521
- out(ui.fail(err instanceof Error ? err.message : 'the workspace address was refused'));
537
+ out(ui.fail(err instanceof Error ? err.message : (0, words_1.word)('addressRefused')));
522
538
  process.exitCode = 1;
523
539
  return;
524
540
  }
@@ -555,7 +571,7 @@ async function resolveWorkspace(email) {
555
571
  //
556
572
  // Working against a local server is still supported and is now something a
557
573
  // person SAYS rather than something an environment does to them:
558
- // `comprism login --workspace localhost:8009`.
574
+ // `comprism login --organization localhost:8009`.
559
575
  // ASKED OF THE ONE SERVICE, at the product's own address.
560
576
  //
561
577
  // This used to post to the authentication route directly, which made the
@@ -565,7 +581,7 @@ async function resolveWorkspace(email) {
565
581
  const root = `https://${WORKSPACE_DOMAIN}`;
566
582
  const reply = await (0, gateway_1.askAt)(root, { intent: 'resolve_workspace', prompt: email });
567
583
  if (!reply.ok || reply.served === false || reply.complete === false) {
568
- throw new Error(reply.message || 'Your workspace could not be looked up. Try again later.');
584
+ throw new Error(reply.message || 'Your organization could not be looked up. Try again later.');
569
585
  }
570
586
  const url = reply.session?.workspace_url;
571
587
  if (!url)
@@ -64,7 +64,7 @@ async function cmdPermissions(args) {
64
64
  process.stderr.write(` ${USAGE}\n`);
65
65
  return 2;
66
66
  }
67
- const { resolveProject, asWire } = await Promise.resolve().then(() => __importStar(require('../lib/project')));
67
+ const { resolveProject, asWire } = await Promise.resolve().then(() => __importStar(require('../machine/folder/project')));
68
68
  const { currentWorkspace } = await Promise.resolve().then(() => __importStar(require('./settings')));
69
69
  const facts = resolveProject(currentWorkspace().path, []);
70
70
  const files = {};
@@ -85,7 +85,7 @@ async function cmdPermissions(args) {
85
85
  const lines = reply.permissions?.lines;
86
86
  if (!reply.ok || !Array.isArray(lines)) {
87
87
  process.stderr.write(ui.fail(reply.served === false
88
- ? 'Your workspace is running a build without permission answers yet.'
88
+ ? 'Your organization is running a build without permission answers yet.'
89
89
  : (0, sessions_1.failure)(reply)) + '\n');
90
90
  return (0, sessions_1.exitFor)(reply);
91
91
  }
@@ -48,7 +48,7 @@ const fs = __importStar(require("fs"));
48
48
  const path = __importStar(require("path"));
49
49
  const readline = __importStar(require("readline"));
50
50
  const gateway_1 = require("../lib/gateway");
51
- const project_1 = require("../lib/project");
51
+ const project_1 = require("../machine/folder/project");
52
52
  const ui = __importStar(require("../lib/ui"));
53
53
  const out = (s = "") => process.stdout.write(`${s}\n`);
54
54
  const MAX_FILES = 200;
@@ -81,7 +81,7 @@ async function call(payload) {
81
81
  const reply = await (0, gateway_1.ask)({ intent: "plugins", prompt: "", pluginsPayload: payload });
82
82
  const body = reply.plugins;
83
83
  if (!reply.ok || reply.served === false || !body) {
84
- out(` ${ui.fail(reply.message || "Your plugins could not be read from your workspace.")}`);
84
+ out(` ${ui.fail(reply.message || "Your plugins could not be read from your organization.")}`);
85
85
  return null;
86
86
  }
87
87
  if (body.error) {
@@ -136,7 +136,7 @@ async function cmdPlugins(args) {
136
136
  out(` ${l}`);
137
137
  let scope = args.indexOf("--scope") >= 0 ? args[args.indexOf("--scope") + 1] : undefined;
138
138
  if (!scope) {
139
- const said = await question(` Install for: 1 this project 2 just me 3 cancel `);
139
+ const said = await question(` Install for: 1 this workspace 2 just me 3 cancel `);
140
140
  scope = said === "1" ? "project" : said === "2" ? "personal" : undefined;
141
141
  if (!scope) {
142
142
  out(" Nothing was installed.");
@@ -45,8 +45,8 @@ async function cmdPrivacy() {
45
45
  const reply = await (0, gateway_1.ask)({ intent: "privacy", prompt: "" });
46
46
  if (!reply.ok || reply.served === false) {
47
47
  out(ui.fail(reply.served === false
48
- ? "Your workspace is running an older build that does not answer this yet."
49
- : `Your workspace could not be reached: ${reply.message || "no answer"}`));
48
+ ? "Your organization is running an older build that does not answer this yet."
49
+ : `Your organization could not be reached: ${reply.message || "no answer"}`));
50
50
  return 1;
51
51
  }
52
52
  out();
@@ -80,7 +80,7 @@ const keyboard_1 = require("../lib/keyboard");
80
80
  const machine_1 = require("../lib/machine");
81
81
  const ui = __importStar(require("../lib/ui"));
82
82
  const render_1 = require("../agent/render");
83
- const shell_1 = require("../executor/shell");
83
+ const shell_1 = require("../machine/executor/shell");
84
84
  const voice = __importStar(require("../lib/voice"));
85
85
  const out = (s = '') => process.stdout.write(s + '\n');
86
86
  /**
@@ -285,7 +285,7 @@ function sessionWords() {
285
285
  return [...COMMANDS, 'project', 'folders', 'instructions', 'remember', 'detach', 'output', 'processes', 'stop', 'agents'];
286
286
  }
287
287
  async function refreshList() {
288
- const { resolveProject } = await Promise.resolve().then(() => __importStar(require('../lib/project')));
288
+ const { resolveProject } = await Promise.resolve().then(() => __importStar(require('../machine/folder/project')));
289
289
  const root = resolveProject(await sessionFolder(), place.added).root;
290
290
  const got = await (0, commandlist_1.fetchCommandList)(root, sessionWords());
291
291
  // A failed refresh keeps the list already held rather than emptying it.
@@ -356,11 +356,12 @@ async function showHelpers() {
356
356
  out();
357
357
  }
358
358
  /** The permission modes, in the order Shift+Tab moves through them (4.5). */
359
+ /** The modes Shift+Tab steps through, as the service last listed them. */
359
360
  const MODES = ['read_only', 'approve_writes', 'auto_edit', 'full_auto'];
360
361
  /** How the next job in this session runs: its mode and whether it plans first
361
362
  * (manual 4.1, 4.5). Changed by `mode`, Shift+Tab, `plan` and `plan off`, and
362
363
  * applied from the next job; the service still decides what each mode allows. */
363
- const steering_ = { mode: 'approve_writes', plan: false, pin: '', effort: '' };
364
+ const steering_ = { mode: 'approve_writes', plan: false, pin: '', effort: '', modes: MODES };
364
365
  /**
365
366
  * The line as a command, with a leading slash taken off.
366
367
  *
@@ -623,7 +624,7 @@ function printHelp() {
623
624
  out();
624
625
  drawList(listedItems());
625
626
  if (!listed?.ok) {
626
- out(ui.c.amber(' Your workspace could not be asked for its skills, so only this tool\'s own words are shown.'));
627
+ out(ui.c.amber(' Your organization could not be asked for its skills, so only this tool\'s own words are shown.'));
627
628
  out();
628
629
  }
629
630
  else if (listed.needsUpdate.length > 0) {
@@ -645,9 +646,42 @@ function printHelp() {
645
646
  /**
646
647
  * The folders this session works in (manual 2.1 and 2.3). `project <path>`
647
648
  * changes the folder for the rest of the session; `folders add <path>` allows
648
- * another. Neither changes the saved default, which is `comprism project`.
649
+ * another. Neither changes the saved default, which is `comprism workspace`.
649
650
  */
650
- const place = { folder: null, added: [] };
651
+ const place = { folder: null, added: [], workspace: '' };
652
+ /**
653
+ * One command the service runs itself (web manual 13.2, 13.3). The service says
654
+ * what it means and writes every line; this answers whether a folder exists on
655
+ * this computer when asked, prints the lines, and carries out the one action it
656
+ * names. True when the session's workspace or folders changed; the request to
657
+ * run when a next step was chosen.
658
+ */
659
+ async function serviceCommand(words) {
660
+ const { runServiceCommand } = await Promise.resolve().then(() => __importStar(require('../lib/servicecommand')));
661
+ const said = await runServiceCommand(words, { cwd: await sessionFolder(), current: place.workspace });
662
+ if (!said.reached) {
663
+ out(ui.fail(said.message || 'Your organization could not answer that.'));
664
+ return false;
665
+ }
666
+ for (const line of said.lines)
667
+ out(line);
668
+ const a = said.action;
669
+ if (!a)
670
+ return false;
671
+ if (a.run)
672
+ return a.run;
673
+ if (a.open_workspace)
674
+ place.workspace = a.open_workspace;
675
+ if (a.use_folder) {
676
+ place.folder = a.use_folder;
677
+ (await Promise.resolve().then(() => __importStar(require('../lib/project-ops')))).useSessionFolder(a.use_folder);
678
+ }
679
+ if (a.add_folder && !place.added.includes(a.add_folder))
680
+ place.added.push(a.add_folder);
681
+ if (a.remove_folder)
682
+ place.added = place.added.filter((f) => f !== a.remove_folder);
683
+ return true;
684
+ }
651
685
  async function sessionFolder() {
652
686
  if (place.folder)
653
687
  return place.folder;
@@ -656,7 +690,7 @@ async function sessionFolder() {
656
690
  }
657
691
  /** The line this session begins with, as the service wrote it (manual 2.2). */
658
692
  async function projectLine() {
659
- const { resolveProject, asWire } = await Promise.resolve().then(() => __importStar(require('../lib/project')));
693
+ const { resolveProject, asWire } = await Promise.resolve().then(() => __importStar(require('../machine/folder/project')));
660
694
  const { ask } = await Promise.resolve().then(() => __importStar(require('../lib/gateway')));
661
695
  const facts = resolveProject(await sessionFolder(), place.added);
662
696
  const reply = await ask({ intent: 'project', prompt: '', project: asWire(facts, 'approve_writes') });
@@ -685,7 +719,7 @@ async function contextOf(intent, jobId, keep = '', sessionKey) {
685
719
  intent, prompt: keep, ...(jobId ? { jobId } : {}), ...(sessionKey ? { spendSession: sessionKey } : {}),
686
720
  });
687
721
  if (!reply.ok || !reply.context) {
688
- out(ui.fail(reply.message || 'Your workspace could not answer that.'));
722
+ out(ui.fail(reply.message || 'Your organization could not answer that.'));
689
723
  }
690
724
  else {
691
725
  for (const line of reply.context.lines)
@@ -704,7 +738,7 @@ async function showTranscript(jobId) {
704
738
  const { ask } = await Promise.resolve().then(() => __importStar(require('../lib/gateway')));
705
739
  const reply = await ask({ intent: 'transcript', prompt: '', jobId });
706
740
  if (!reply.ok || !reply.transcript) {
707
- out(ui.fail(reply.message || 'The transcript could not be read from your workspace.'));
741
+ out(ui.fail(reply.message || 'The transcript could not be read from your organization.'));
708
742
  }
709
743
  else {
710
744
  for (const line of reply.transcript.lines)
@@ -1022,7 +1056,7 @@ async function runRepl(how = {}) {
1022
1056
  });
1023
1057
  }
1024
1058
  if (!state.reached) {
1025
- out(ui.fail(`Your workspace could not be reached, so ${ui.c.bold('nothing is known')} about your keys.`));
1059
+ out(ui.fail(`Your organization could not be reached, so ${ui.c.bold('nothing is known')} about your keys.`));
1026
1060
  out(` ${ui.c.dim(state.why)}`);
1027
1061
  out(` ${ui.c.dim('check the address with')} ${ui.c.bold('comprism status')}${ui.c.dim(', or sign in again with')} ${ui.c.bold('comprism login')}`);
1028
1062
  out();
@@ -1059,6 +1093,14 @@ async function runRepl(how = {}) {
1059
1093
  }
1060
1094
  steering_.pin = said.pin ?? '';
1061
1095
  steering_.effort = said.effort === 'normal' ? '' : said.effort;
1096
+ // Mode and plan are the service's too (CLI manual 4.1, 4.5); kept here only
1097
+ // to send with the next job and to draw the prompt.
1098
+ if (said.mode)
1099
+ steering_.mode = said.mode;
1100
+ if (typeof said.plan === 'boolean')
1101
+ steering_.plan = said.plan;
1102
+ if (said.modes?.length)
1103
+ steering_.modes = said.modes;
1062
1104
  return said;
1063
1105
  };
1064
1106
  if (how.latest || how.resume || how.pick) {
@@ -1346,7 +1388,7 @@ async function runRepl(how = {}) {
1346
1388
  * a drag and drop, and the files wait for the question that follows.
1347
1389
  */
1348
1390
  const gather = async (typed) => {
1349
- const { resolveProject } = await Promise.resolve().then(() => __importStar(require('../lib/project')));
1391
+ const { resolveProject } = await Promise.resolve().then(() => __importStar(require('../machine/folder/project')));
1350
1392
  const here = await sessionFolder();
1351
1393
  const root = resolveProject(here, place.added).root;
1352
1394
  completion.root = root;
@@ -1639,11 +1681,15 @@ async function runRepl(how = {}) {
1639
1681
  keys().on('keypress', (_s, key) => {
1640
1682
  if (!key || key.name !== 'tab' || !key.shift || working || answering || leaving)
1641
1683
  return;
1642
- const next = MODES[(MODES.indexOf(steering_.mode) + 1) % MODES.length];
1643
- steering_.mode = next;
1684
+ // The next mode in the service's own list, set by the service.
1685
+ const modes = steering_.modes;
1686
+ const next = modes[(modes.indexOf(steering_.mode) + 1) % modes.length];
1644
1687
  out();
1645
- out(ui.c.dim(` mode ${ui.c.bold(next)}, from the next job`));
1646
- prompt();
1688
+ void steer({ mode: next }).then((said) => {
1689
+ for (const line of said?.lines ?? [])
1690
+ out(ui.c.dim(line));
1691
+ prompt();
1692
+ });
1647
1693
  });
1648
1694
  async function handle(line) {
1649
1695
  const typed = line.trim();
@@ -1669,38 +1715,27 @@ async function runRepl(how = {}) {
1669
1715
  out();
1670
1716
  return prompt();
1671
1717
  }
1672
- const where = /^(project|workspace)(?:\s+(.+))?$/i.exec(bare);
1673
- const addFolder = /^folders\s+add\s+(.+)$/i.exec(bare);
1674
- if (where || addFolder || /^folders$/i.test(bare)) {
1675
- const { checkFolder, resolveProject } = await Promise.resolve().then(() => __importStar(require('../lib/project')));
1676
- const named = where ? where[2] : addFolder ? addFolder[1] : undefined;
1677
- if (named) {
1678
- const found = checkFolder(named.trim());
1679
- if (!found.ok) {
1680
- out(ui.fail(found.why));
1681
- out();
1682
- return prompt();
1683
- }
1684
- if (where) {
1685
- place.folder = found.path;
1686
- (await Promise.resolve().then(() => __importStar(require('../lib/project-ops')))).useSessionFolder(found.path);
1687
- }
1688
- else if (!place.added.includes(found.path))
1689
- place.added.push(found.path);
1690
- }
1691
- if (/^folders$/i.test(bare)) {
1692
- const facts = resolveProject(await sessionFolder(), place.added);
1693
- out();
1694
- out(` ${ui.c.bold(facts.root)} ${ui.c.dim('project')}`);
1695
- for (const f of facts.added)
1696
- out(` ${ui.c.bold(f)} ${ui.c.dim('added for this session')}`);
1697
- out();
1698
- return prompt();
1699
- }
1718
+ // Commands the service runs itself (web manual 13.2, 13.3): any word the
1719
+ // service lists as its own, and `project`, the old name for `workspace`.
1720
+ // What they mean and every line printed are the service's; this sends what
1721
+ // was typed, answers whether a folder exists, and carries out the action.
1722
+ const head = (bare.split(/\s+/)[0] ?? '').toLowerCase();
1723
+ const { waitingNextSteps } = await Promise.resolve().then(() => __importStar(require('../agent/session')));
1724
+ const serviceWord = head === 'project'
1725
+ || (/^\d+$/.test(bare) && waitingNextSteps > 0)
1726
+ || (listed?.items ?? []).some((i) => i.source === 'service' && i.name === head);
1727
+ if (serviceWord) {
1728
+ const words = head === 'project' ? bare.replace(/^project\b/i, 'workspace') : bare;
1729
+ const acted = await serviceCommand(words);
1700
1730
  out();
1701
- await showProjectLine();
1702
- // Another folder has other skills.
1703
- await refreshList();
1731
+ // A next step chosen: it runs as the request it stands for.
1732
+ if (typeof acted === 'string')
1733
+ return handle(`work ${acted}`);
1734
+ if (acted) {
1735
+ await showProjectLine();
1736
+ // Another folder has other skills.
1737
+ await refreshList();
1738
+ }
1704
1739
  return prompt();
1705
1740
  }
1706
1741
  // `pin <model>` and `effort <level>` (manual 4.17, 4.18), kept by the
@@ -1736,16 +1771,12 @@ async function runRepl(how = {}) {
1736
1771
  }
1737
1772
  const effortTo = /^effort\s+(\S+)$/.exec(word);
1738
1773
  if (effortTo) {
1739
- const level = effortTo[1];
1740
- if (!['low', 'medium', 'normal', 'high'].includes(level))
1741
- out(ui.fail('Effort is low, normal or high.'));
1742
- else {
1743
- const said = await steer({ effort: level === 'medium' ? 'normal' : level });
1744
- if (said) {
1745
- out(ui.c.dim(` effort ${ui.c.bold(said.effort)} for the next requests`));
1746
- if (said.effort_note)
1747
- out(ui.c.dim(` ${said.effort_note}`));
1748
- }
1774
+ // The service checks the word and says what is allowed.
1775
+ const said = await steer({ effort: effortTo[1] });
1776
+ if (said) {
1777
+ out(ui.c.dim(` effort ${ui.c.bold(said.effort)} for the next requests`));
1778
+ if (said.effort_note)
1779
+ out(ui.c.dim(` ${said.effort_note}`));
1749
1780
  }
1750
1781
  out();
1751
1782
  return prompt();
@@ -1753,20 +1784,16 @@ async function runRepl(how = {}) {
1753
1784
  // `mode <name>` and `plan off` (manual 4.1, 4.5).
1754
1785
  const modeTo = /^mode\s+(\S+)$/.exec(word);
1755
1786
  if (modeTo) {
1756
- const named = modeTo[1];
1757
- if (!MODES.includes(named)) {
1758
- out(ui.fail(`${named} is not a permission mode. One of: ${MODES.join(', ')}`));
1759
- }
1760
- else {
1761
- steering_.mode = named;
1762
- out(ui.c.dim(` mode ${ui.c.bold(named)}, from the next job`));
1763
- }
1787
+ const said = await steer({ mode: modeTo[1] });
1788
+ for (const line of said?.lines ?? [])
1789
+ out(ui.c.dim(line));
1764
1790
  out();
1765
1791
  return prompt();
1766
1792
  }
1767
1793
  if (word === 'plan off') {
1768
- steering_.plan = false;
1769
- out(ui.c.dim(' planning off: the next requests are carried out'));
1794
+ const said = await steer({ plan: false });
1795
+ for (const line of said?.lines ?? [])
1796
+ out(ui.c.dim(line));
1770
1797
  out();
1771
1798
  return prompt();
1772
1799
  }
@@ -1784,7 +1811,7 @@ async function runRepl(how = {}) {
1784
1811
  // `output 3`: a command's whole output, kept on this machine (5.12).
1785
1812
  const outputOf = /^output(?:\s+(\d+))?$/.exec(word);
1786
1813
  if (outputOf) {
1787
- const { keptOutput } = await Promise.resolve().then(() => __importStar(require('../executor/shell')));
1814
+ const { keptOutput } = await Promise.resolve().then(() => __importStar(require('../machine/executor/shell')));
1788
1815
  const kept = outputOf[1] ? keptOutput(Number(outputOf[1])) : undefined;
1789
1816
  if (!outputOf[1])
1790
1817
  out(ui.c.dim(' output N shows the whole output of command N, the number on its line.'));
@@ -1893,16 +1920,20 @@ async function runRepl(how = {}) {
1893
1920
  }
1894
1921
  if (COMMANDS.has(word)) {
1895
1922
  switch (word) {
1896
- case 'plan':
1897
- steering_.plan = true;
1898
- out(ui.c.dim(' planning on: the next requests are planned first and change nothing. `plan off` stops.'));
1923
+ case 'plan': {
1924
+ const said = await steer({ plan: true });
1925
+ for (const line of said?.lines ?? [])
1926
+ out(ui.c.dim(line));
1899
1927
  out();
1900
1928
  break;
1901
- case 'mode':
1902
- out(` ${ui.c.dim('mode')} ${ui.c.bold(steering_.mode)}${steering_.plan ? ui.c.dim(', planning first') : ''}`);
1903
- out(ui.c.dim(` one of ${MODES.join(', ')}. \`mode <name>\` or Shift+Tab changes it.`));
1929
+ }
1930
+ case 'mode': {
1931
+ const said = await steer({});
1932
+ for (const line of said?.lines ?? [])
1933
+ out(ui.c.dim(line));
1904
1934
  out();
1905
1935
  break;
1936
+ }
1906
1937
  case 'pin': {
1907
1938
  const said = await steer({});
1908
1939
  out(said?.pin
@@ -2096,17 +2127,10 @@ async function runRepl(how = {}) {
2096
2127
  // session the moment two are open, and this one is on the screen.
2097
2128
  case 'cost':
2098
2129
  case 'spend': {
2099
- // Nothing sent is nothing charged, and the service has no record of
2100
- // a session before its first job, so it would answer "no such
2101
- // session" to a person checking that a refusal cost them nothing
2102
- // (manual 6.1).
2103
- if (session.jobs.length === 0) {
2104
- out(` ${ui.c.dim('session')} ${ui.c.bold(session.id)} ${ui.c.dim('nothing has run yet, so it has cost')} $0.00`);
2105
- out();
2106
- break;
2107
- }
2130
+ // Nothing sent is nothing charged: the service answers even before
2131
+ // the first job, so a refusal can be checked as costing nothing (6.1).
2108
2132
  const { cmdCost } = await Promise.resolve().then(() => __importStar(require('./cost')));
2109
- await cmdCost([session.id]);
2133
+ await cmdCost([session.serverId || session.id]);
2110
2134
  break;
2111
2135
  }
2112
2136
  case 'model': {
@@ -52,8 +52,8 @@ const fs = __importStar(require("fs"));
52
52
  const path = __importStar(require("path"));
53
53
  const ui = __importStar(require("../lib/ui"));
54
54
  const gateway_1 = require("../lib/gateway");
55
- const project_1 = require("../lib/project");
56
- const project_2 = require("../lib/project");
55
+ const project_1 = require("../machine/folder/project");
56
+ const project_2 = require("../machine/folder/project");
57
57
  const out = (s = "") => process.stdout.write(`${s}\n`);
58
58
  const err = (s = "") => process.stderr.write(`${s}\n`);
59
59
  async function review(op, payload, cwd = process.cwd()) {
@@ -62,9 +62,9 @@ async function review(op, payload, cwd = process.cwd()) {
62
62
  }
63
63
  function refused(reply) {
64
64
  if (!reply.ok)
65
- return reply.message || "Your workspace could not be reached.";
65
+ return reply.message || "Your organization could not be reached.";
66
66
  if (reply.served === false)
67
- return "Your workspace is running an older build that does not answer this yet.";
67
+ return "Your organization is running an older build that does not answer this yet.";
68
68
  if (reply.granted === false)
69
69
  return reply.message || "Refused.";
70
70
  return null;
@@ -162,7 +162,7 @@ async function cmdRewind(args, confirmWith) {
162
162
  for (const item of items) {
163
163
  const where = inside(project.root, item.path);
164
164
  if (!where) {
165
- results.push({ path: item.path, result: "outside this project, not restored" });
165
+ results.push({ path: item.path, result: "outside this workspace, not restored" });
166
166
  continue;
167
167
  }
168
168
  // Your later changes are never overwritten (7.4): the file must still be
@@ -190,7 +190,7 @@ async function cmdRewind(args, confirmWith) {
190
190
  else if (item.action === "rename_back" && item.to) {
191
191
  const from = inside(project.root, item.to);
192
192
  if (!from)
193
- throw new Error("the new name is outside this project");
193
+ throw new Error("the new name is outside this workspace");
194
194
  fs.mkdirSync(path.dirname(where), { recursive: true });
195
195
  if (fs.existsSync(from))
196
196
  fs.renameSync(from, where);