@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
package/out/lib/queue.js CHANGED
@@ -51,7 +51,7 @@ const path = __importStar(require("path"));
51
51
  const child_process_1 = require("child_process");
52
52
  const gateway_1 = require("./gateway");
53
53
  const config_1 = require("./config");
54
- const self_1 = require("./self");
54
+ const self_1 = require("../machine/runtime/self");
55
55
  async function queue(op, payload = {}) {
56
56
  return (0, gateway_1.ask)({
57
57
  intent: "queue", prompt: "", op,
@@ -1,33 +1,6 @@
1
- export interface Requirement {
2
- /** What it is for, said as the work it makes possible. */
3
- purpose: string;
4
- /** Any one of these being present is enough. */
5
- anyOf: string[];
6
- /** Work that cannot be done without it, for the message. */
7
- without: string;
8
- /** True when the tool cannot function at all without this. */
9
- essential: boolean;
10
- }
11
- export declare const REQUIREMENTS: Requirement[];
12
- /** Is this program on the machine, by name, without running anything of theirs. */
13
- export declare function present(program: string): boolean;
14
- /**
15
- * The one command that installs what is missing, for the machine it is on.
16
- *
17
- * Named per platform rather than guessed: telling somebody on Ubuntu to run a
18
- * Homebrew command is worse than saying nothing, because they now have to work
19
- * out that the instruction was wrong before they can work out what is right.
20
- */
21
- export declare function howToInstall(missing: string[]): string;
22
- export interface Readiness {
23
- ready: boolean;
24
- /** One line per requirement, ready to print. */
25
- lines: string[];
26
- /** The command to run, empty when nothing is missing. */
27
- fix: string;
28
- }
29
- /** What this machine can and cannot do, and how to close the gap. */
30
- export declare function check(): Readiness;
1
+ import { REQUIREMENTS, check, howToInstall, present } from '../machine/runtime/needs';
2
+ export type { Readiness, Requirement } from '../machine/runtime/needs';
3
+ export { REQUIREMENTS, check, howToInstall, present };
31
4
  /** One checked item: its line, whether it is required, and the fix. */
32
5
  export interface Finding {
33
6
  ok: boolean;
@@ -36,10 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.REQUIREMENTS = void 0;
40
- exports.present = present;
41
- exports.howToInstall = howToInstall;
42
- exports.check = check;
39
+ exports.present = exports.howToInstall = exports.check = exports.REQUIREMENTS = void 0;
43
40
  exports.systemName = systemName;
44
41
  exports.diagnose = diagnose;
45
42
  /**
@@ -72,82 +69,16 @@ exports.diagnose = diagnose;
72
69
  * is not friction worth trading a customer's trust for.
73
70
  */
74
71
  const node_child_process_1 = require("node:child_process");
72
+ const needs_1 = require("../machine/runtime/needs");
73
+ Object.defineProperty(exports, "REQUIREMENTS", { enumerable: true, get: function () { return needs_1.REQUIREMENTS; } });
74
+ Object.defineProperty(exports, "check", { enumerable: true, get: function () { return needs_1.check; } });
75
+ Object.defineProperty(exports, "howToInstall", { enumerable: true, get: function () { return needs_1.howToInstall; } });
76
+ Object.defineProperty(exports, "present", { enumerable: true, get: function () { return needs_1.present; } });
75
77
  const node_os_1 = __importDefault(require("node:os"));
76
78
  const install_1 = require("../commands/install");
77
79
  const connection_1 = require("./connection");
78
80
  const gateway_1 = require("./gateway");
79
81
  const node_fs_1 = __importDefault(require("node:fs"));
80
- exports.REQUIREMENTS = [
81
- {
82
- purpose: 'working with a repository',
83
- anyOf: ['git'],
84
- without: 'reading, branching and committing a project',
85
- essential: false,
86
- },
87
- {
88
- purpose: 'reading a PDF you already have',
89
- // Three ways to do the same job. The first keeps the layout, the second is
90
- // close, the third is usually there on any machine with Python at all.
91
- anyOf: ['pdftotext', 'mutool', 'python3'],
92
- without: 'reading a specification or a contract that arrived as a PDF',
93
- essential: false,
94
- },
95
- ];
96
- /** Is this program on the machine, by name, without running anything of theirs. */
97
- function present(program) {
98
- try {
99
- // Windows has no /usr/bin/env, so every program read as missing there.
100
- if (node_os_1.default.platform() === 'win32')
101
- (0, node_child_process_1.execFileSync)('where', [program], { stdio: 'ignore' });
102
- else
103
- (0, node_child_process_1.execFileSync)('/usr/bin/env', ['which', program], { stdio: 'ignore' });
104
- return true;
105
- }
106
- catch {
107
- return false;
108
- }
109
- }
110
- /**
111
- * The one command that installs what is missing, for the machine it is on.
112
- *
113
- * Named per platform rather than guessed: telling somebody on Ubuntu to run a
114
- * Homebrew command is worse than saying nothing, because they now have to work
115
- * out that the instruction was wrong before they can work out what is right.
116
- */
117
- function howToInstall(missing) {
118
- if (missing.length === 0)
119
- return '';
120
- const platform = node_os_1.default.platform();
121
- const parts = missing.map((m) => (m === 'pdftotext' ? 'poppler' : m));
122
- if (platform === 'darwin')
123
- return `brew install ${parts.join(' ')}`;
124
- if (platform === 'linux') {
125
- const apt = missing.map((m) => (m === 'pdftotext' ? 'poppler-utils' : m));
126
- return `sudo apt-get install -y ${apt.join(' ')}`;
127
- }
128
- if (platform === 'win32')
129
- return `winget install ${parts.join(' ')}`;
130
- return `install: ${parts.join(', ')}`;
131
- }
132
- /** What this machine can and cannot do, and how to close the gap. */
133
- function check() {
134
- const lines = [];
135
- const missing = [];
136
- for (const req of exports.REQUIREMENTS) {
137
- const found = req.anyOf.find(present);
138
- if (found) {
139
- lines.push(` ready ${req.purpose}`);
140
- }
141
- else {
142
- // The FIRST of the alternatives is the one named, because a list of three
143
- // equivalent programs is a decision nobody wants to make about a thing
144
- // they did not know they needed.
145
- missing.push(req.anyOf[0]);
146
- lines.push(` missing ${req.purpose}, so ${req.without} will not work`);
147
- }
148
- }
149
- return { ready: missing.length === 0, lines, fix: howToInstall(missing) };
150
- }
151
82
  /** The system this is, in the words its owner would use. */
152
83
  function systemName() {
153
84
  try {
@@ -205,7 +136,7 @@ async function diagnose() {
205
136
  const major = Number(node.split('.')[0]);
206
137
  // The install script and Python installs carry their own Node (manual 1.1),
207
138
  // so the line says so rather than asking for a Node nobody installed.
208
- const { installMethod, isStandalone } = await Promise.resolve().then(() => __importStar(require('./self')));
139
+ const { installMethod, isStandalone } = await Promise.resolve().then(() => __importStar(require('../machine/runtime/self')));
209
140
  if (isStandalone()) {
210
141
  const how = installMethod() === 'script' ? 'the install script' : 'Python (pip)';
211
142
  found.push({ ok: true, required: true, line: `installed with ${how}; Node ${node} is built in, nothing else needed` });
@@ -260,17 +191,17 @@ async function diagnose() {
260
191
  const git = version('git', ['--version']);
261
192
  found.push(git !== null
262
193
  ? { ok: true, required: false, line: `git ${git}` }
263
- : { ok: false, required: false, line: 'git not found: reading, branching and committing a project will not work',
264
- fix: howToInstall(['git']) });
194
+ : { ok: false, required: false, line: 'git not found: reading, branching and committing a workspace will not work',
195
+ fix: (0, needs_1.howToInstall)(['git']) });
265
196
  const rg = version('rg', ['--version']);
266
197
  found.push(rg !== null
267
198
  ? { ok: true, required: false, line: `ripgrep ${rg}` }
268
199
  : { ok: false, required: false, line: 'ripgrep not found: searches will be slower',
269
- fix: howToInstall(['ripgrep']) });
270
- const pdf = exports.REQUIREMENTS[1];
271
- found.push(pdf.anyOf.some(present)
200
+ fix: (0, needs_1.howToInstall)(['ripgrep']) });
201
+ const pdf = needs_1.REQUIREMENTS[1];
202
+ found.push(pdf.anyOf.some(needs_1.present)
272
203
  ? { ok: true, required: false, line: 'a PDF reader' }
273
204
  : { ok: false, required: false, line: `no PDF reader: ${pdf.without} will not work`,
274
- fix: howToInstall(['pdftotext']) });
205
+ fix: (0, needs_1.howToInstall)(['pdftotext']) });
275
206
  return found;
276
207
  }
@@ -0,0 +1,14 @@
1
+ export interface ServiceAnswer {
2
+ /** False when nobody answered; `lines` then holds nothing. */
3
+ reached: boolean;
4
+ /** Whether the service did what was asked. */
5
+ ok: boolean;
6
+ lines: string[];
7
+ /** The one change to this client's own state the service asked for. */
8
+ action: Record<string, string> | null;
9
+ message?: string;
10
+ }
11
+ export declare function runServiceCommand(words: string, facts: {
12
+ cwd: string;
13
+ current?: string;
14
+ }): Promise<ServiceAnswer>;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.runServiceCommand = runServiceCommand;
37
+ /**
38
+ * Ask the service to run a command it owns (web manual 13.2 to 13.5): what was
39
+ * typed goes up, the service decides what it means and writes every line, and
40
+ * this answers its one question only this computer can, whether a folder exists.
41
+ *
42
+ * Shared by the session and by the commands typed straight at the shell, so a
43
+ * service command behaves the same wherever it is typed.
44
+ */
45
+ const fs = __importStar(require("node:fs"));
46
+ const os = __importStar(require("node:os"));
47
+ const gateway_1 = require("./gateway");
48
+ async function runServiceCommand(words, facts) {
49
+ const exists = {};
50
+ for (let round = 0; round < 4; round += 1) {
51
+ const reply = await (0, gateway_1.ask)({
52
+ intent: 'command', prompt: '',
53
+ payload: { words, cwd: facts.cwd, home: os.homedir(), current: facts.current ?? '', exists },
54
+ });
55
+ if (!reply.lines?.length && !reply.data) {
56
+ return { reached: false, ok: false, lines: [], action: null, message: reply.message ?? undefined };
57
+ }
58
+ const data = (reply.data ?? {});
59
+ const wanted = data.wants?.exists ?? [];
60
+ if (wanted.length) {
61
+ for (const p of wanted) {
62
+ try {
63
+ exists[p] = fs.statSync(p).isDirectory();
64
+ }
65
+ catch {
66
+ exists[p] = false;
67
+ }
68
+ }
69
+ continue;
70
+ }
71
+ return { reached: true, ok: reply.granted !== false, lines: reply.lines ?? [], action: data.action ?? null };
72
+ }
73
+ return { reached: false, ok: false, lines: [], action: null };
74
+ }
@@ -51,7 +51,7 @@ const gateway_1 = require("./gateway");
51
51
  const out = (s = '') => process.stdout.write(s + '\n');
52
52
  /** Ask the service one sessions question, with where this machine is. */
53
53
  async function sessions(op, extra = {}, folder) {
54
- const { resolveProject, asWire } = await Promise.resolve().then(() => __importStar(require('./project')));
54
+ const { resolveProject, asWire } = await Promise.resolve().then(() => __importStar(require('../machine/folder/project')));
55
55
  const { currentWorkspace } = await Promise.resolve().then(() => __importStar(require('../commands/settings')));
56
56
  const facts = resolveProject(folder ?? currentWorkspace().path, []);
57
57
  return (0, gateway_1.ask)({
@@ -69,9 +69,9 @@ async function sessions(op, extra = {}, folder) {
69
69
  /** Why a sessions question got no answer, in the service's words when it has them. */
70
70
  function failure(reply) {
71
71
  if (reply.served === false) {
72
- return 'Your workspace is running a build without sessions yet.';
72
+ return 'Your organization is running a build without sessions yet.';
73
73
  }
74
- return reply.message || 'Your workspace could not be reached.';
74
+ return reply.message || 'Your organization could not be reached.';
75
75
  }
76
76
  /** The exit number for a sessions question that got no answer (Appendix B). */
77
77
  function exitFor(reply) {
package/out/lib/ui.d.ts CHANGED
@@ -36,13 +36,16 @@ export declare const c: {
36
36
  */
37
37
  export declare function spectrumAt(index: number, glyph: string): string;
38
38
  /**
39
- * A small picture, drawn two pixels to a character with the half block glyph:
40
- * the top pixel is the glyph's color and the bottom one its background, which
41
- * makes each pixel square in an ordinary terminal font. Each row of `pixels`
42
- * is six hex digits per pixel, `......` for a clear one, and two rows make one
43
- * line. Returns nothing without color, because the picture is only color.
39
+ * A small picture, one pixel to a character cell, each pixel a colored space.
40
+ *
41
+ * A cell's background is painted edge to edge by every terminal, so the
42
+ * picture has no seams. Half block glyphs pack two pixels into a cell but
43
+ * leave thin gaps and offsets in some fonts, which made the mark look broken
44
+ * in the macOS Terminal (owner, 28 September 2026). Each row of `pixels` is
45
+ * six hex digits per pixel, `......` for a clear one. Returns nothing without
46
+ * color, because the picture is only color.
44
47
  */
45
- export declare function pixelArt(pixels: string[]): string[];
48
+ export declare function cellArt(pixels: string[]): string[];
46
49
  export declare function prismRule(w?: number): string;
47
50
  export declare function banner(subtitle?: string): string;
48
51
  export declare function money(v: number | null): string;
package/out/lib/ui.js CHANGED
@@ -19,7 +19,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.LiveIndicator = exports.c = void 0;
20
20
  exports.width = width;
21
21
  exports.spectrumAt = spectrumAt;
22
- exports.pixelArt = pixelArt;
22
+ exports.cellArt = cellArt;
23
23
  exports.prismRule = prismRule;
24
24
  exports.banner = banner;
25
25
  exports.money = money;
@@ -88,37 +88,31 @@ function spectrumAt(index, glyph) {
88
88
  return `\x1b[38;2;${r};${g};${b}m${glyph}\x1b[0m`;
89
89
  }
90
90
  /**
91
- * A small picture, drawn two pixels to a character with the half block glyph:
92
- * the top pixel is the glyph's color and the bottom one its background, which
93
- * makes each pixel square in an ordinary terminal font. Each row of `pixels`
94
- * is six hex digits per pixel, `......` for a clear one, and two rows make one
95
- * line. Returns nothing without color, because the picture is only color.
91
+ * A small picture, one pixel to a character cell, each pixel a colored space.
92
+ *
93
+ * A cell's background is painted edge to edge by every terminal, so the
94
+ * picture has no seams. Half block glyphs pack two pixels into a cell but
95
+ * leave thin gaps and offsets in some fonts, which made the mark look broken
96
+ * in the macOS Terminal (owner, 28 September 2026). Each row of `pixels` is
97
+ * six hex digits per pixel, `......` for a clear one. Returns nothing without
98
+ * color, because the picture is only color.
96
99
  */
97
- function pixelArt(pixels) {
100
+ function cellArt(pixels) {
98
101
  if (!useColorNow())
99
102
  return [];
100
- const at = (row, x) => {
101
- const hex = row?.slice(x * 6, x * 6 + 6);
102
- return !hex || hex === '......' ? null
103
- : [0, 2, 4].map((k) => parseInt(hex.slice(k, k + 2), 16)).join(';');
104
- };
105
- const lines = [];
106
- for (let y = 0; y < pixels.length; y += 2) {
103
+ return pixels.map((row) => {
107
104
  let line = '';
108
- for (let x = 0; x < (pixels[y]?.length ?? 0) / 6; x++) {
109
- const top = at(pixels[y], x), bottom = at(pixels[y + 1], x);
110
- if (top && bottom)
111
- line += `\x1b[38;2;${top}m\x1b[48;2;${bottom}m▀\x1b[0m`;
112
- else if (top)
113
- line += `\x1b[38;2;${top}m▀\x1b[0m`;
114
- else if (bottom)
115
- line += `\x1b[38;2;${bottom}m▄\x1b[0m`;
116
- else
105
+ for (let x = 0; x < row.length / 6; x++) {
106
+ const hex = row.slice(x * 6, x * 6 + 6);
107
+ if (hex === '......') {
117
108
  line += ' ';
109
+ continue;
110
+ }
111
+ const rgb = [0, 2, 4].map((k) => parseInt(hex.slice(k, k + 2), 16)).join(';');
112
+ line += `\x1b[48;2;${rgb}m \x1b[0m`;
118
113
  }
119
- lines.push(line);
120
- }
121
- return lines;
114
+ return line;
115
+ });
122
116
  }
123
117
  function prismRule(w = width()) {
124
118
  if (!useColorNow())
@@ -1,39 +1,5 @@
1
- /** One recording tool, as this file needs to drive it. */
2
- interface Recorder {
3
- /** What to tell somebody who wants to know what is listening. */
4
- name: string;
5
- command: string;
6
- args: (out: string) => string[];
7
- }
8
- /** The first recording tool this machine has, or null. */
9
- export declare function recorder(): Recorder | null;
10
- /**
11
- * What to tell somebody who has no recording tool, or null when they do.
12
- *
13
- * One sentence and one command. Not a list of three options with trade-offs:
14
- * somebody who just wanted to say a sentence out loud is not shopping for audio
15
- * software.
16
- */
17
- export declare function missing(): string | null;
18
- /** A recording in progress. */
19
- export interface Recording {
20
- /** Where the audio is being written. */
21
- file: string;
22
- /** What is doing the listening, for the line on screen. */
23
- by: string;
24
- /** Stop, wait for the file to be finished properly, and hand it back. */
25
- stop: () => Promise<{
26
- file: string;
27
- bytes: number;
28
- }>;
29
- }
30
- /**
31
- * Start listening.
32
- *
33
- * Throws when there is nothing to listen with, which the caller should have
34
- * headed off with `missing` so the person gets a sentence rather than an error.
35
- */
36
- export declare function start(): Recording;
1
+ export { discard, missing, recorder, start } from '../machine/voice/record';
2
+ export type { Recorder, Recording } from '../machine/voice/record';
37
3
  /** Live dictation in progress, on the Mac's own recogniser. */
38
4
  export interface LiveDictation {
39
5
  /** The loudness now, 0 to 1. */
@@ -70,8 +36,6 @@ export declare function level(file: string): number;
70
36
  * actually there. Returns null until there is at least half a second to send.
71
37
  */
72
38
  export declare function soFar(file: string): string | null;
73
- /** Delete the recording and the folder holding it. Always, and immediately. */
74
- export declare function discard(file: string): void;
75
39
  /**
76
40
  * The words in a recording, according to our server.
77
41
  *
@@ -83,4 +47,3 @@ export declare function transcribe(baseUrl: string, credential: string, file: st
83
47
  text: string;
84
48
  provider: string;
85
49
  }>;
86
- export {};
package/out/lib/voice.js CHANGED
@@ -33,13 +33,10 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.recorder = recorder;
37
- exports.missing = missing;
38
- exports.start = start;
36
+ exports.start = exports.recorder = exports.missing = exports.discard = void 0;
39
37
  exports.listenLive = listenLive;
40
38
  exports.level = level;
41
39
  exports.soFar = soFar;
42
- exports.discard = discard;
43
40
  exports.transcribe = transcribe;
44
41
  /**
45
42
  * Saying a request instead of typing it.
@@ -73,144 +70,13 @@ exports.transcribe = transcribe;
73
70
  */
74
71
  const node_child_process_1 = require("node:child_process");
75
72
  const fs = __importStar(require("node:fs"));
76
- const os = __importStar(require("node:os"));
77
73
  const path = __importStar(require("node:path"));
78
74
  const gateway_1 = require("./gateway");
79
- /** Is this on the PATH at all. */
80
- function present(command) {
81
- try {
82
- return (0, node_child_process_1.spawnSync)('which', [command], { stdio: 'ignore' }).status === 0;
83
- }
84
- catch {
85
- return false;
86
- }
87
- }
88
- /**
89
- * Sixteen kilohertz, one channel, everywhere.
90
- *
91
- * What every speech model wants and nothing more. Recording in studio quality
92
- * would make a file several times the size, take longer to upload, and be
93
- * downsampled by the vendor on arrival anyway.
94
- */
95
- const RATE = '16000';
96
- const RECORDERS = [
97
- // sox first. It is the one built for this, it needs no device name, and it
98
- // stops cleanly.
99
- { name: 'sox', command: 'rec', args: (out) => ['-q', '-c', '1', '-r', RATE, out] },
100
- {
101
- name: 'ffmpeg',
102
- command: 'ffmpeg',
103
- args: (out) => [
104
- '-loglevel', 'quiet', '-nostdin',
105
- ...(process.platform === 'darwin'
106
- ? ['-f', 'avfoundation', '-i', ':default']
107
- : ['-f', 'alsa', '-i', 'default']),
108
- // Written as it is heard, not held until the end, so the meter and the
109
- // words so far have something to read while the person is speaking.
110
- '-ac', '1', '-ar', RATE, '-flush_packets', '1', '-y', out,
111
- ],
112
- },
113
- // Linux's own, present on most desktop installs.
114
- { name: 'arecord', command: 'arecord', args: (out) => ['-q', '-f', 'S16_LE', '-c', '1', '-r', RATE, out] },
115
- ];
116
- /** The first recording tool this machine has, or null. */
117
- function recorder() {
118
- return RECORDERS.find((r) => present(r.command)) ?? null;
119
- }
120
- /**
121
- * What to tell somebody who has no recording tool, or null when they do.
122
- *
123
- * One sentence and one command. Not a list of three options with trade-offs:
124
- * somebody who just wanted to say a sentence out loud is not shopping for audio
125
- * software.
126
- */
127
- function missing() {
128
- if (recorder())
129
- return null;
130
- return process.platform === 'darwin'
131
- ? 'Speaking needs a recording tool, and macOS does not come with one. '
132
- + 'Install it once with: brew install sox'
133
- : 'Speaking needs a recording tool. Install it once with: '
134
- + 'sudo apt install sox (or your system\'s package manager)';
135
- }
136
- /**
137
- * Start listening.
138
- *
139
- * Throws when there is nothing to listen with, which the caller should have
140
- * headed off with `missing` so the person gets a sentence rather than an error.
141
- */
142
- function start() {
143
- const tool = recorder();
144
- if (!tool)
145
- throw new Error(missing() ?? 'No recording tool.');
146
- const file = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'comprism-voice-')), 'said.wav');
147
- const child = (0, node_child_process_1.spawn)(tool.command, tool.args(file), {
148
- stdio: ['ignore', 'ignore', 'ignore'],
149
- });
150
- let stopped = null;
151
- // The recorder never outlives the session. Closing the window or ending the
152
- // process while it listens would otherwise leave a microphone recording to a
153
- // file nobody knows about, the worst thing this tool could do.
154
- const abandon = () => {
155
- try {
156
- child.kill('SIGKILL');
157
- }
158
- catch { /* already gone */ }
159
- discard(file);
160
- };
161
- const onSignal = (signal) => {
162
- abandon();
163
- release();
164
- process.kill(process.pid, signal);
165
- };
166
- const release = () => {
167
- process.off('exit', abandon);
168
- process.off('SIGHUP', onSignal);
169
- process.off('SIGTERM', onSignal);
170
- };
171
- process.once('exit', abandon);
172
- process.once('SIGHUP', onSignal);
173
- process.once('SIGTERM', onSignal);
174
- child.once('close', release);
175
- const stop = () => {
176
- if (stopped)
177
- return stopped;
178
- stopped = new Promise((resolve) => {
179
- const done = () => {
180
- let bytes = 0;
181
- try {
182
- bytes = fs.statSync(file).size;
183
- }
184
- catch {
185
- bytes = 0;
186
- }
187
- resolve({ file, bytes });
188
- };
189
- // Interrupt, not kill. Every one of these tools finishes writing the WAV
190
- // header on an interrupt and none of them does on a kill, and a header
191
- // that was never finished reads as silence to whoever transcribes it.
192
- child.once('close', done);
193
- try {
194
- child.kill('SIGINT');
195
- }
196
- catch {
197
- done();
198
- }
199
- // A tool that has not closed in two seconds is not going to. Taking the
200
- // file as it stands beats leaving somebody at a prompt that has stopped
201
- // answering them.
202
- setTimeout(() => {
203
- try {
204
- child.kill('SIGKILL');
205
- }
206
- catch { /* already gone */ }
207
- done();
208
- }, 2000).unref();
209
- });
210
- return stopped;
211
- };
212
- return { file, by: tool.name, stop };
213
- }
75
+ var record_1 = require("../machine/voice/record");
76
+ Object.defineProperty(exports, "discard", { enumerable: true, get: function () { return record_1.discard; } });
77
+ Object.defineProperty(exports, "missing", { enumerable: true, get: function () { return record_1.missing; } });
78
+ Object.defineProperty(exports, "recorder", { enumerable: true, get: function () { return record_1.recorder; } });
79
+ Object.defineProperty(exports, "start", { enumerable: true, get: function () { return record_1.start; } });
214
80
  /** The helper built beside this file on a Mac, when there is one. */
215
81
  function helper() {
216
82
  if (process.platform !== 'darwin')
@@ -341,13 +207,6 @@ function soFar(file) {
341
207
  return null;
342
208
  }
343
209
  }
344
- /** Delete the recording and the folder holding it. Always, and immediately. */
345
- function discard(file) {
346
- try {
347
- fs.rmSync(path.dirname(file), { recursive: true, force: true });
348
- }
349
- catch { /* a temp file we could not remove is not worth a word to anybody */ }
350
- }
351
210
  /**
352
211
  * The words in a recording, according to our server.
353
212
  *
@@ -364,7 +223,7 @@ async function transcribe(baseUrl, credential, file) {
364
223
  const granted = await (0, gateway_1.ask)({ intent: 'transcribe', prompt: '' });
365
224
  const pass = granted.ok && granted.served !== false ? granted.upload : null;
366
225
  if (granted.ok && granted.served !== false && !pass) {
367
- throw new Error(granted.message || 'your workspace would not allow that recording');
226
+ throw new Error(granted.message || 'your organization would not allow that recording');
368
227
  }
369
228
  const where = pass?.upload_path || '/api/v1/agent/transcribe';
370
229
  const res = await fetch(`${baseUrl.replace(/\/+$/, '')}${where}${pass?.ticket ? `?ticket=${encodeURIComponent(pass.ticket)}` : ''}`, { method: 'POST', headers: { Authorization: `Bearer ${credential}` }, body: form });
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The few words the published command line tool says on its own, in one place.
3
+ *
4
+ * Everything about the work arrives from the service already worded (rule 17).
5
+ * What is left here is what the tool must say when the service, a file or the
6
+ * operating system gives it nothing to say: a refused address, a file it cannot
7
+ * read, a call that failed without a reason. Same shape as the editor's
8
+ * `i18n.ts` and the desktop app's `labels.js`.
9
+ */
10
+ declare const EN: {
11
+ readonly connectorNotStarted: "the connector could not be started";
12
+ readonly addressRefused: "the organization address was refused";
13
+ readonly fileNotFound: "not found: {{file}}";
14
+ readonly fileNoPermission: "cannot read {{file}}: permission denied";
15
+ readonly providerCallFailed: "provider call failed";
16
+ readonly somethingWrong: "something went wrong";
17
+ };
18
+ export type WordKey = keyof typeof EN;
19
+ export declare function word(key: WordKey, vars?: Record<string, string | number>): string;
20
+ export {};