@illuminis/comprism 0.1.3 → 0.1.5

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 (142) hide show
  1. package/README.md +61 -2
  2. package/out/agent/command.d.ts +75 -4
  3. package/out/agent/command.js +221 -26
  4. package/out/agent/render.d.ts +56 -8
  5. package/out/agent/render.js +441 -39
  6. package/out/agent/session.d.ts +156 -2
  7. package/out/agent/session.js +795 -154
  8. package/out/commands/agents.d.ts +2 -0
  9. package/out/commands/agents.js +79 -0
  10. package/out/commands/ask.d.ts +1 -1
  11. package/out/commands/ask.js +88 -12
  12. package/out/commands/commands-thin.js +79 -17
  13. package/out/commands/config.d.ts +1 -0
  14. package/out/commands/config.js +138 -0
  15. package/out/commands/cost.d.ts +1 -0
  16. package/out/commands/cost.js +167 -0
  17. package/out/commands/hooks.d.ts +1 -0
  18. package/out/commands/hooks.js +83 -0
  19. package/out/commands/install.d.ts +44 -1
  20. package/out/commands/install.js +198 -4
  21. package/out/commands/instructions.d.ts +1 -0
  22. package/out/commands/instructions.js +113 -0
  23. package/out/commands/integrations.d.ts +3 -0
  24. package/out/commands/integrations.js +215 -0
  25. package/out/commands/jobs.d.ts +5 -0
  26. package/out/commands/jobs.js +157 -0
  27. package/out/commands/login.js +249 -43
  28. package/out/commands/memory.d.ts +3 -0
  29. package/out/commands/memory.js +113 -0
  30. package/out/commands/permissions.d.ts +1 -0
  31. package/out/commands/permissions.js +94 -0
  32. package/out/commands/plugins.d.ts +4 -0
  33. package/out/commands/plugins.js +192 -0
  34. package/out/commands/privacy.d.ts +1 -0
  35. package/out/commands/privacy.js +57 -0
  36. package/out/commands/providerKey.d.ts +32 -0
  37. package/out/commands/providerKey.js +108 -0
  38. package/out/commands/repl.d.ts +12 -1
  39. package/out/commands/repl.js +1649 -122
  40. package/out/commands/report.d.ts +39 -0
  41. package/out/commands/report.js +115 -0
  42. package/out/commands/review.d.ts +5 -0
  43. package/out/commands/review.js +223 -0
  44. package/out/commands/sessions.d.ts +23 -0
  45. package/out/commands/sessions.js +115 -0
  46. package/out/commands/settings.d.ts +3 -1
  47. package/out/commands/settings.js +18 -16
  48. package/out/commands/skills.d.ts +21 -0
  49. package/out/commands/skills.js +207 -0
  50. package/out/commands/unattended.d.ts +7 -0
  51. package/out/commands/unattended.js +351 -0
  52. package/out/commands/update.d.ts +1 -0
  53. package/out/commands/update.js +123 -0
  54. package/out/commands/welcome.d.ts +25 -1
  55. package/out/commands/welcome.js +71 -35
  56. package/out/commands/worktrees.d.ts +5 -0
  57. package/out/commands/worktrees.js +186 -0
  58. package/out/executor/browser.d.ts +14 -0
  59. package/out/executor/browser.js +270 -0
  60. package/out/executor/diagnostics.d.ts +2 -0
  61. package/out/executor/diagnostics.js +181 -0
  62. package/out/executor/files.js +270 -40
  63. package/out/executor/git.js +42 -29
  64. package/out/executor/hooks.d.ts +42 -58
  65. package/out/executor/hooks.js +89 -182
  66. package/out/executor/index.d.ts +21 -5
  67. package/out/executor/index.js +160 -14
  68. package/out/executor/paths.d.ts +6 -1
  69. package/out/executor/paths.js +34 -6
  70. package/out/executor/sandbox.d.ts +40 -0
  71. package/out/executor/sandbox.js +299 -0
  72. package/out/executor/shell.d.ts +49 -4
  73. package/out/executor/shell.js +302 -56
  74. package/out/executor/toolservers.d.ts +20 -0
  75. package/out/executor/toolservers.js +189 -0
  76. package/out/executor/worktree.d.ts +9 -0
  77. package/out/executor/worktree.js +119 -0
  78. package/out/graph/read-python.js +2 -1
  79. package/out/lib/attach.d.ts +56 -12
  80. package/out/lib/attach.js +230 -63
  81. package/out/lib/clipboard.d.ts +23 -0
  82. package/out/lib/clipboard.js +182 -0
  83. package/out/lib/commandlist.d.ts +20 -0
  84. package/out/lib/commandlist.js +58 -0
  85. package/out/lib/decision.d.ts +22 -0
  86. package/out/lib/decision.js +50 -0
  87. package/out/lib/fingerprint.d.ts +25 -0
  88. package/out/lib/fingerprint.js +58 -0
  89. package/out/lib/gateway.d.ts +190 -2
  90. package/out/lib/gateway.js +59 -4
  91. package/out/lib/history.d.ts +24 -0
  92. package/out/lib/history.js +137 -0
  93. package/out/lib/ide.d.ts +19 -0
  94. package/out/lib/ide.js +131 -0
  95. package/out/lib/keyboard.d.ts +98 -0
  96. package/out/lib/keyboard.js +390 -0
  97. package/out/lib/lastlogin.d.ts +14 -0
  98. package/out/lib/lastlogin.js +105 -0
  99. package/out/lib/machine.d.ts +21 -0
  100. package/out/lib/machine.js +91 -0
  101. package/out/lib/notify.d.ts +4 -0
  102. package/out/lib/notify.js +52 -0
  103. package/out/lib/output.d.ts +48 -0
  104. package/out/lib/output.js +108 -0
  105. package/out/lib/picker.d.ts +1 -0
  106. package/out/lib/picker.js +158 -0
  107. package/out/lib/project-ops.d.ts +19 -0
  108. package/out/lib/project-ops.js +146 -0
  109. package/out/lib/project.d.ts +28 -0
  110. package/out/lib/project.js +114 -0
  111. package/out/lib/prompt.d.ts +3 -0
  112. package/out/lib/prompt.js +36 -2
  113. package/out/lib/queue.d.ts +13 -0
  114. package/out/lib/queue.js +116 -0
  115. package/out/lib/readiness.d.ts +19 -0
  116. package/out/lib/readiness.js +170 -1
  117. package/out/lib/self.d.ts +23 -0
  118. package/out/lib/self.js +124 -0
  119. package/out/lib/sessions.d.ts +19 -0
  120. package/out/lib/sessions.js +221 -0
  121. package/out/lib/startcache.d.ts +13 -0
  122. package/out/lib/startcache.js +78 -0
  123. package/out/lib/stdin.d.ts +32 -0
  124. package/out/lib/stdin.js +117 -0
  125. package/out/lib/store.d.ts +40 -0
  126. package/out/lib/store.js +138 -0
  127. package/out/lib/sync.d.ts +18 -0
  128. package/out/lib/sync.js +81 -0
  129. package/out/lib/ui.d.ts +10 -4
  130. package/out/lib/ui.js +65 -25
  131. package/out/lib/voice.d.ts +36 -0
  132. package/out/lib/voice.js +160 -1
  133. package/out/native/dictate +0 -0
  134. package/out/postinstall.js +42 -17
  135. package/out/providers/anthropic.d.ts +22 -0
  136. package/out/providers/anthropic.js +80 -0
  137. package/out/providers/index.d.ts +8 -0
  138. package/out/providers/index.js +83 -0
  139. package/out/providers/openai.d.ts +11 -0
  140. package/out/providers/openai.js +57 -0
  141. package/out/thin.js +595 -33
  142. package/package.json +11 -50
@@ -0,0 +1,390 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Keyboard = exports.LINE_BREAK = void 0;
4
+ exports.asOneLine = asOneLine;
5
+ /**
6
+ * The keystrokes a session's reader never sees on their own (manual 6.2, 6.3,
7
+ * 6.5).
8
+ *
9
+ * Node's line reader edits one line well and knows nothing about a paste: every
10
+ * line break inside a pasted stack trace reached it as Enter, so a 240 line
11
+ * paste was sent as 240 requests, each billed. It also has no Alt+Enter and no
12
+ * search of earlier requests.
13
+ *
14
+ * So the session's reader reads from this filter instead of from the keyboard.
15
+ * The filter passes ordinary typing straight through and handles four things
16
+ * itself:
17
+ *
18
+ * * A paste, bracketed by the terminal, becomes one piece of the request. The
19
+ * reader is given a short label, `[pasted 240 lines]`, that can be edited
20
+ * around like any word, and the label is swapped back for the exact text,
21
+ * tabs and trailing spaces included, when the request is sent.
22
+ * * A terminal that does not bracket pastes still delivers a paste in one read,
23
+ * so several lines arriving in one read are treated the same way. A person
24
+ * typing never produces a line break followed by more text in one read.
25
+ * * Alt+Enter continues the request on a new line, exactly as `\` then Enter.
26
+ * * Ctrl+R searches earlier requests; Enter puts the match at the prompt to be
27
+ * edited or sent, and Esc or Ctrl+C puts back what was there.
28
+ *
29
+ * Nothing here is written anywhere. Pieces live in memory for the session.
30
+ */
31
+ const node_stream_1 = require("node:stream");
32
+ const node_string_decoder_1 = require("node:string_decoder");
33
+ const PASTE_ON = '\x1b[?2004h';
34
+ const PASTE_OFF = '\x1b[?2004l';
35
+ const PASTE_START = '\x1b[200~';
36
+ const PASTE_END = '\x1b[201~';
37
+ /** Stands for a line break inside a request shown on one line, such as a
38
+ * recalled request. Turned back into a line break when it is sent. */
39
+ exports.LINE_BREAK = '␤';
40
+ /** A request as it is shown on one editable line. */
41
+ function asOneLine(text) {
42
+ return text.replace(/\r\n?|\n/g, exports.LINE_BREAK);
43
+ }
44
+ class Keyboard {
45
+ history;
46
+ /** What the session's reader reads. */
47
+ input = new node_stream_1.PassThrough();
48
+ decoder = new node_string_decoder_1.StringDecoder('utf8');
49
+ pieces = [];
50
+ pasting = null;
51
+ carry = '';
52
+ continued = false;
53
+ search = null;
54
+ reader = null;
55
+ listening = false;
56
+ onData = (chunk) => this.take(typeof chunk === 'string' ? chunk : this.decoder.write(chunk));
57
+ /** Esc on its own (manual 6.7). Returns true when it was used, so the key
58
+ * goes no further; false passes it to the reader as before. */
59
+ onEscape = null;
60
+ /** The command list under the prompt (manual 6.4): Up, Down, Enter and Tab
61
+ * go to it while it is showing. Returns true when it used the key. */
62
+ onMenuKey = null;
63
+ /** Ctrl+O, the short or full view (manual 6.9). */
64
+ onFullView = null;
65
+ /** Ctrl+G, the request in your own editor (manual 6.6). */
66
+ onEditor = null;
67
+ /** Ctrl+V, the clipboard's picture, where the terminal passes it (3.4). */
68
+ onPasteImage = null;
69
+ /** `keys vi` (manual 6.10): Esc at the prompt enters normal mode. */
70
+ vi = false;
71
+ normal = false;
72
+ pendingD = false;
73
+ /** `history` is the session's earlier requests, newest first, as shown. */
74
+ constructor(history) {
75
+ this.history = history;
76
+ }
77
+ /** The reader whose line a search edits. */
78
+ bind(reader) {
79
+ this.reader = reader;
80
+ }
81
+ /**
82
+ * Start reading the keyboard. Raw, because the reader no longer holds the
83
+ * keyboard itself and so cannot switch it; bracketed, so the terminal marks
84
+ * where a paste begins and ends.
85
+ */
86
+ attach() {
87
+ if (this.listening || !process.stdin.isTTY)
88
+ return;
89
+ this.listening = true;
90
+ process.stdin.setRawMode(true);
91
+ process.stdin.on('data', this.onData);
92
+ process.stdin.resume();
93
+ process.stdout.write(PASTE_ON);
94
+ }
95
+ /**
96
+ * Stop reading, and give the keyboard back exactly as it was found, for a
97
+ * command that asks its own question and for the shell afterwards. A shell
98
+ * left with bracketed paste on prints markers around everything pasted.
99
+ */
100
+ detach() {
101
+ if (!this.listening)
102
+ return;
103
+ this.listening = false;
104
+ process.stdin.off('data', this.onData);
105
+ process.stdout.write(PASTE_OFF);
106
+ if (process.stdin.isTTY)
107
+ process.stdin.setRawMode(false);
108
+ process.stdin.pause();
109
+ this.search = null;
110
+ }
111
+ /** True once after Alt+Enter, so the line just ended continues the request. */
112
+ takeContinued() {
113
+ const was = this.continued;
114
+ this.continued = false;
115
+ return was;
116
+ }
117
+ /** The request as it will be sent: every paste label swapped for its text,
118
+ * and every shown line break made a real one again. */
119
+ expand(text) {
120
+ let out = text;
121
+ for (const p of this.pieces)
122
+ out = out.split(p.label).join(p.text);
123
+ return out.split(exports.LINE_BREAK).join('\n');
124
+ }
125
+ /** Feed raw keyboard text. Public so a test can type without a terminal. */
126
+ take(s) {
127
+ s = this.carry + s;
128
+ this.carry = '';
129
+ while (s) {
130
+ if (this.pasting !== null) {
131
+ const end = s.indexOf(PASTE_END);
132
+ if (end === -1) {
133
+ const keep = partialTail(s, PASTE_END);
134
+ this.pasting += s.slice(0, s.length - keep);
135
+ this.carry = s.slice(s.length - keep);
136
+ return;
137
+ }
138
+ const pasted = this.pasting + s.slice(0, end);
139
+ this.pasting = null;
140
+ s = s.slice(end + PASTE_END.length);
141
+ this.paste(pasted);
142
+ continue;
143
+ }
144
+ const start = s.indexOf(PASTE_START);
145
+ if (start === -1) {
146
+ // Only a tail that is clearly the start of a marker is held back. A
147
+ // lone Esc is a key of its own and must not wait for the next read.
148
+ const keep = partialTail(s, PASTE_START);
149
+ const held = keep >= 3 ? keep : 0;
150
+ this.keys(s.slice(0, s.length - held));
151
+ this.carry = s.slice(s.length - held);
152
+ return;
153
+ }
154
+ this.keys(s.slice(0, start));
155
+ s = s.slice(start + PASTE_START.length);
156
+ this.pasting = '';
157
+ }
158
+ }
159
+ forward(s) {
160
+ if (s)
161
+ this.input.write(s);
162
+ }
163
+ /** Typing, outside a bracketed paste. */
164
+ keys(s) {
165
+ if (!s)
166
+ return;
167
+ if (this.search)
168
+ return this.searchKeys(s);
169
+ // A lone Esc is a key of its own; a longer read starting with it is an
170
+ // arrow or another sequence and goes to the reader.
171
+ if (s === '\x1b' && this.onEscape?.())
172
+ return;
173
+ const menu = { '\x1b[A': 'up', '\x1bOA': 'up', '\x1b[B': 'down', '\x1bOB': 'down',
174
+ '\r': 'enter', '\t': 'tab' }[s];
175
+ if (menu && this.onMenuKey?.(menu))
176
+ return;
177
+ if (this.vi) {
178
+ if (s === '\x1b') {
179
+ this.normal = true;
180
+ return;
181
+ }
182
+ if (this.normal)
183
+ return this.viKeys(s);
184
+ }
185
+ // Several lines in one read is a paste from a terminal that does not mark
186
+ // them. An escape sequence is a key, never a paste.
187
+ if (s.length > 1 && !s.startsWith('\x1b') && /[\r\n]/.test(s.slice(0, -1))) {
188
+ return this.paste(s);
189
+ }
190
+ for (let i = 0; i < s.length; i += 1) {
191
+ const ch = s[i];
192
+ if (ch === '\x1b' && (s[i + 1] === '\r' || s[i + 1] === '\n')) {
193
+ this.continued = true;
194
+ this.forward('\r');
195
+ i += 1;
196
+ continue;
197
+ }
198
+ if (ch === '\x12') {
199
+ this.startSearch();
200
+ return this.keys(s.slice(i + 1));
201
+ }
202
+ if (ch === '\x0f' && this.onFullView) {
203
+ this.onFullView();
204
+ continue;
205
+ }
206
+ if (ch === '\x07' && this.onEditor) {
207
+ this.onEditor();
208
+ continue;
209
+ }
210
+ if (ch === '\x16' && this.onPasteImage) {
211
+ this.onPasteImage();
212
+ continue;
213
+ }
214
+ // One character at a time only up to the next special key; the rest is
215
+ // passed on whole so escape sequences stay together.
216
+ const next = nextSpecial(s, i);
217
+ this.forward(s.slice(i, next));
218
+ i = next - 1;
219
+ }
220
+ }
221
+ paste(raw) {
222
+ const text = raw.replace(/\r\n?/g, '\n');
223
+ // A paste of plain words is typing that arrived quickly. A tab would
224
+ // reach the reader as Tab and ask for completion, and a line break as
225
+ // Enter and send, so either makes it a piece.
226
+ if (!/[\n\t]/.test(text))
227
+ return this.keys(text);
228
+ const lines = text.replace(/\n$/, '').split('\n').length;
229
+ let label = `[pasted ${lines} line${lines === 1 ? '' : 's'}]`;
230
+ const same = this.pieces.filter((p) => p.label.startsWith(label.slice(0, -1))).length;
231
+ if (same > 0)
232
+ label = `${label.slice(0, -1)} #${same + 1}]`;
233
+ this.pieces.push({ label, text });
234
+ this.forward(label);
235
+ }
236
+ // ── keys vi ─────────────────────────────────────────────────────────────
237
+ // Normal mode, the handful of keys a line needs. Each is turned into the
238
+ // key the line editor already understands, so there is one editor, not two.
239
+ viKeys(s) {
240
+ const KEY = {
241
+ h: '\x1b[D', l: '\x1b[C', '0': '\x01', '$': '\x05', w: '\x1bf', b: '\x1bb',
242
+ x: '\x1b[3~', k: '\x1b[A', j: '\x1b[B', D: '\x0b',
243
+ };
244
+ for (let at = 0; at < s.length; at += 1) {
245
+ const ch = s[at];
246
+ // Back to typing: whatever follows in this read is typed as it is.
247
+ if (!this.normal)
248
+ return this.keys(s.slice(at));
249
+ if (this.pendingD) {
250
+ this.pendingD = false;
251
+ if (ch === 'd')
252
+ this.forward('\x01\x0b');
253
+ continue;
254
+ }
255
+ if (ch === 'd') {
256
+ this.pendingD = true;
257
+ continue;
258
+ }
259
+ if (ch === 'i') {
260
+ this.normal = false;
261
+ continue;
262
+ }
263
+ if (ch === 'a') {
264
+ this.forward('\x1b[C');
265
+ this.normal = false;
266
+ continue;
267
+ }
268
+ if (ch === 'A') {
269
+ this.forward('\x05');
270
+ this.normal = false;
271
+ continue;
272
+ }
273
+ if (ch === 'I') {
274
+ this.forward('\x01');
275
+ this.normal = false;
276
+ continue;
277
+ }
278
+ if (ch === '\r' || ch === '\n') {
279
+ this.normal = false;
280
+ this.forward('\r');
281
+ continue;
282
+ }
283
+ if (ch === '\x03') {
284
+ this.normal = false;
285
+ this.forward(ch);
286
+ continue;
287
+ }
288
+ const key = KEY[ch];
289
+ if (key)
290
+ this.forward(key);
291
+ }
292
+ }
293
+ // ── Ctrl+R ────────────────────────────────────────────────────────────────
294
+ editable() {
295
+ return this.reader;
296
+ }
297
+ startSearch() {
298
+ const r = this.editable();
299
+ if (!r)
300
+ return;
301
+ this.search = { query: '', skip: 0, before: r.line, prompt: r.getPrompt() };
302
+ this.drawSearch();
303
+ }
304
+ match() {
305
+ const s = this.search;
306
+ if (!s)
307
+ return null;
308
+ const q = s.query.toLowerCase();
309
+ const hits = this.history().filter((h) => h.toLowerCase().includes(q));
310
+ return hits[Math.min(s.skip, hits.length - 1)] ?? null;
311
+ }
312
+ drawSearch() {
313
+ const r = this.editable();
314
+ const s = this.search;
315
+ if (!r || !s)
316
+ return;
317
+ const hit = this.match();
318
+ r.setPrompt(` search earlier requests: ${s.query}${hit === null && s.query ? ' (no match)' : ''} > `);
319
+ this.setLine(hit ?? '');
320
+ }
321
+ setLine(text) {
322
+ const r = this.editable();
323
+ if (!r)
324
+ return;
325
+ r.line = text;
326
+ r.cursor = text.length;
327
+ r._refreshLine();
328
+ }
329
+ endSearch(keep) {
330
+ const r = this.editable();
331
+ const s = this.search;
332
+ const hit = keep ? this.match() : null;
333
+ this.search = null;
334
+ if (!r || !s)
335
+ return;
336
+ r.setPrompt(s.prompt);
337
+ this.setLine(keep ? (hit ?? s.before) : s.before);
338
+ }
339
+ searchKeys(s) {
340
+ const state = this.search;
341
+ for (let i = 0; i < s.length; i += 1) {
342
+ const ch = s[i];
343
+ if (ch === '\x1b') {
344
+ // A lone Esc cancels. An arrow or any other key sequence accepts the
345
+ // match and is then handled as that key.
346
+ if (i === s.length - 1)
347
+ return this.endSearch(false);
348
+ this.endSearch(true);
349
+ return this.keys(s.slice(i));
350
+ }
351
+ if (ch === '\r' || ch === '\n') {
352
+ this.endSearch(true);
353
+ return this.keys(s.slice(i + 1));
354
+ }
355
+ if (ch === '\x03' || ch === '\x07')
356
+ return this.endSearch(false);
357
+ if (ch === '\x12')
358
+ state.skip += 1;
359
+ else if (ch === '\x7f' || ch === '\b') {
360
+ state.query = state.query.slice(0, -1);
361
+ state.skip = 0;
362
+ }
363
+ else if (ch >= ' ') {
364
+ state.query += ch;
365
+ state.skip = 0;
366
+ }
367
+ }
368
+ this.drawSearch();
369
+ }
370
+ }
371
+ exports.Keyboard = Keyboard;
372
+ /** How many characters at the end of `s` could be the start of `marker`. */
373
+ function partialTail(s, marker) {
374
+ for (let n = Math.min(marker.length - 1, s.length); n > 0; n -= 1) {
375
+ if (marker.startsWith(s.slice(s.length - n)))
376
+ return n;
377
+ }
378
+ return 0;
379
+ }
380
+ /** Where the next key this filter handles itself begins, from `i + 1`. */
381
+ function nextSpecial(s, i) {
382
+ for (let j = i + 1; j < s.length; j += 1) {
383
+ const ch = s[j];
384
+ if (ch === '\x12' || ch === '\x0f' || ch === '\x07' || ch === '\x16')
385
+ return j;
386
+ if (ch === '\x1b' && (s[j + 1] === '\r' || s[j + 1] === '\n'))
387
+ return j;
388
+ }
389
+ return s.length;
390
+ }
@@ -0,0 +1,14 @@
1
+ export interface LastLogin {
2
+ email: string;
3
+ /** Exactly what was typed or found as the workspace, so a local one stays local. */
4
+ workspace: string;
5
+ /** Whether that workspace was typed by the person (only then may it be local). */
6
+ typed: boolean;
7
+ }
8
+ export declare function readLast(): LastLogin | null;
9
+ export declare function saveLast(last: LastLogin): void;
10
+ export declare function forgetLast(): void;
11
+ export declare const keychainAvailable: () => boolean;
12
+ export declare function savedPassword(email: string, workspace: string): string | null;
13
+ export declare function savePassword(email: string, workspace: string, password: string): boolean;
14
+ export declare function forgetPassword(email: string, workspace: string): void;
@@ -0,0 +1,105 @@
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.keychainAvailable = void 0;
37
+ exports.readLast = readLast;
38
+ exports.saveLast = saveLast;
39
+ exports.forgetLast = forgetLast;
40
+ exports.savedPassword = savedPassword;
41
+ exports.savePassword = savePassword;
42
+ exports.forgetPassword = forgetPassword;
43
+ /**
44
+ * Who signed in last on this machine, so signing in again is one key.
45
+ *
46
+ * The email and the workspace address are kept in a small file. The password
47
+ * is never written to that file or anywhere else this tool controls: on a Mac,
48
+ * and only when the person says yes, it goes into their own login keychain,
49
+ * the same place Safari and git keep theirs, and it is read back from there.
50
+ * Signing out leaves both, so the next `login` is Enter; `login --forget`
51
+ * removes both.
52
+ */
53
+ const node_child_process_1 = require("node:child_process");
54
+ const fs = __importStar(require("node:fs"));
55
+ const path = __importStar(require("node:path"));
56
+ const config_1 = require("./config");
57
+ function file() {
58
+ return path.join((0, config_1.ensureHome)(), 'last-login.json');
59
+ }
60
+ function readLast() {
61
+ try {
62
+ const got = JSON.parse(fs.readFileSync(file(), 'utf8'));
63
+ return got.email && got.workspace ? got : null;
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ }
69
+ function saveLast(last) {
70
+ try {
71
+ fs.writeFileSync(file(), JSON.stringify(last), { mode: 0o600 });
72
+ }
73
+ catch { /* next login asks */ }
74
+ }
75
+ function forgetLast() {
76
+ const last = readLast();
77
+ if (last)
78
+ forgetPassword(last.email, last.workspace);
79
+ try {
80
+ fs.rmSync(file(), { force: true });
81
+ }
82
+ catch { /* nothing to remove */ }
83
+ }
84
+ /** The keychain entry's name: one per workspace, so two workspaces never mix. */
85
+ const service = (workspace) => `CompletionPrism ${workspace}`;
86
+ const keychainAvailable = () => process.platform === 'darwin';
87
+ exports.keychainAvailable = keychainAvailable;
88
+ function savedPassword(email, workspace) {
89
+ if (!(0, exports.keychainAvailable)() || !email || !workspace)
90
+ return null;
91
+ const r = (0, node_child_process_1.spawnSync)('security', ['find-generic-password', '-a', email, '-s', service(workspace), '-w'], { encoding: 'utf8' });
92
+ return r.status === 0 ? r.stdout.replace(/\n$/, '') : null;
93
+ }
94
+ function savePassword(email, workspace, password) {
95
+ if (!(0, exports.keychainAvailable)())
96
+ return false;
97
+ const r = (0, node_child_process_1.spawnSync)('security', ['add-generic-password', '-U', '-a', email, '-s', service(workspace),
98
+ '-l', service(workspace), '-w', password], { stdio: 'ignore' });
99
+ return r.status === 0;
100
+ }
101
+ function forgetPassword(email, workspace) {
102
+ if (!(0, exports.keychainAvailable)())
103
+ return;
104
+ (0, node_child_process_1.spawnSync)('security', ['delete-generic-password', '-a', email, '-s', service(workspace)], { stdio: 'ignore' });
105
+ }
@@ -0,0 +1,21 @@
1
+ export interface MachineKey {
2
+ values: string[] | null;
3
+ fallback: string;
4
+ what: string;
5
+ }
6
+ /** Every machine setting, its allowed values, and its default. `editor` takes
7
+ * any command. */
8
+ export declare const MACHINE_KEYS: Record<string, MachineKey>;
9
+ export declare function machineSettings(): Record<string, string>;
10
+ /** Whether a value was stored for this key, rather than the default. */
11
+ export declare function isSet(key: string): boolean;
12
+ /** Store one value. Returns why not, or null when it was stored. */
13
+ export declare function setMachine(key: string, value: string): string | null;
14
+ export declare function unsetMachine(key: string): string | null;
15
+ /**
16
+ * Apply the look before anything is drawn (manual 6.10, 6.12). `NO_COLOR`,
17
+ * `--plain`, `plain true` and `theme plain` all mean the same thing: no color
18
+ * and every update on a new line.
19
+ */
20
+ export declare function applyLook(plainFlag: boolean): void;
21
+ export declare function isPlain(): boolean;
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MACHINE_KEYS = void 0;
4
+ exports.machineSettings = machineSettings;
5
+ exports.isSet = isSet;
6
+ exports.setMachine = setMachine;
7
+ exports.unsetMachine = unsetMachine;
8
+ exports.applyLook = applyLook;
9
+ exports.isPlain = isPlain;
10
+ /**
11
+ * Settings that describe this machine and nothing else (manual 2.10, 6.5,
12
+ * 6.10 to 6.12): how it looks, which keys edit a line, how it tells you a job
13
+ * finished, and whether requests are remembered.
14
+ *
15
+ * Kept in the tool's own `config.json`, never on the service, because none of
16
+ * them changes what the agent does: two people sharing a project can each have
17
+ * their own theme. Settings that do change what the agent does are the
18
+ * service's and the project's (manual 2.10), and are not written here.
19
+ */
20
+ const config_1 = require("./config");
21
+ /** Every machine setting, its allowed values, and its default. `editor` takes
22
+ * any command. */
23
+ exports.MACHINE_KEYS = {
24
+ theme: { values: ['dark', 'light', 'plain'], fallback: 'dark', what: 'colors for a dark or light terminal, or none' },
25
+ keys: { values: ['emacs', 'vi'], fallback: 'emacs', what: 'line editing keys' },
26
+ notify: { values: ['desktop', 'bell', 'off'], fallback: 'bell', what: 'how you are told a long job finished' },
27
+ plain: { values: ['true', 'false'], fallback: 'false', what: 'no color and no redrawn lines' },
28
+ history: { values: ['on', 'off'], fallback: 'on', what: 'remember earlier requests' },
29
+ editor: { values: null, fallback: '', what: 'editor for Ctrl+G when VISUAL and EDITOR are not set' },
30
+ };
31
+ function machineSettings() {
32
+ const stored = ((0, config_1.loadConfig)().machine ?? {});
33
+ const out = {};
34
+ for (const [k, spec] of Object.entries(exports.MACHINE_KEYS)) {
35
+ const v = stored[k];
36
+ out[k] = typeof v === 'string' && (spec.values === null || spec.values.includes(v)) ? v : spec.fallback;
37
+ }
38
+ return out;
39
+ }
40
+ /** Whether a value was stored for this key, rather than the default. */
41
+ function isSet(key) {
42
+ const stored = ((0, config_1.loadConfig)().machine ?? {});
43
+ return typeof stored[key] === 'string';
44
+ }
45
+ /** Store one value. Returns why not, or null when it was stored. */
46
+ function setMachine(key, value) {
47
+ const spec = exports.MACHINE_KEYS[key];
48
+ if (!spec)
49
+ return `${key} is not a setting for this machine.`;
50
+ if (spec.values !== null && !spec.values.includes(value)) {
51
+ return `${key} is one of ${spec.values.join(', ')}.`;
52
+ }
53
+ const cfg = (0, config_1.loadConfig)();
54
+ cfg.machine = { ...(cfg.machine ?? {}), [key]: value };
55
+ (0, config_1.saveConfig)(cfg);
56
+ return null;
57
+ }
58
+ function unsetMachine(key) {
59
+ if (!exports.MACHINE_KEYS[key])
60
+ return `${key} is not a setting for this machine.`;
61
+ const cfg = (0, config_1.loadConfig)();
62
+ const next = { ...(cfg.machine ?? {}) };
63
+ delete next[key];
64
+ cfg.machine = next;
65
+ (0, config_1.saveConfig)(cfg);
66
+ return null;
67
+ }
68
+ /**
69
+ * Apply the look before anything is drawn (manual 6.10, 6.12). `NO_COLOR`,
70
+ * `--plain`, `plain true` and `theme plain` all mean the same thing: no color
71
+ * and every update on a new line.
72
+ */
73
+ function applyLook(plainFlag) {
74
+ let m;
75
+ try {
76
+ m = machineSettings();
77
+ }
78
+ catch {
79
+ m = { theme: 'dark', plain: 'false' };
80
+ }
81
+ const plain = plainFlag || m.plain === 'true' || m.theme === 'plain'
82
+ || (process.env.NO_COLOR !== undefined && process.env.NO_COLOR !== '');
83
+ if (plain) {
84
+ process.env.NO_COLOR = process.env.NO_COLOR || '1';
85
+ process.env.COMPRISM_PLAIN = '1';
86
+ }
87
+ process.env.COMPRISM_THEME = m.theme === 'light' ? 'light' : 'dark';
88
+ }
89
+ function isPlain() {
90
+ return process.env.COMPRISM_PLAIN === '1';
91
+ }
@@ -0,0 +1,4 @@
1
+ /** The way this machine will notify, after falling back. */
2
+ export declare function howNotified(): 'desktop' | 'bell' | 'off';
3
+ /** Tell the person. `say` prints a line in the session. */
4
+ export declare function notify(message: string, say: (line: string) => void): void;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.howNotified = howNotified;
4
+ exports.notify = notify;
5
+ /**
6
+ * Being told when it finishes (manual 6.11).
7
+ *
8
+ * `notify desktop` asks the operating system to show a notification: the
9
+ * built in `osascript` on macOS, `notify-send` on Linux. Where neither is
10
+ * there, the terminal bell rings instead and the session says so once. `bell`
11
+ * rings the bell, `off` does nothing. What to say is decided by the caller;
12
+ * this only reaches the operating system.
13
+ */
14
+ const node_child_process_1 = require("node:child_process");
15
+ const machine_1 = require("./machine");
16
+ let saidFallback = false;
17
+ function hasProgram(name) {
18
+ const r = (0, node_child_process_1.spawnSync)(process.platform === 'win32' ? 'where' : 'which', [name], { stdio: 'ignore' });
19
+ return r.status === 0;
20
+ }
21
+ /** The way this machine will notify, after falling back. */
22
+ function howNotified() {
23
+ const wanted = (0, machine_1.machineSettings)().notify;
24
+ if (wanted === 'off')
25
+ return 'off';
26
+ if (wanted === 'desktop') {
27
+ if (process.platform === 'darwin' && hasProgram('osascript'))
28
+ return 'desktop';
29
+ if (process.platform === 'linux' && hasProgram('notify-send'))
30
+ return 'desktop';
31
+ }
32
+ return 'bell';
33
+ }
34
+ /** Tell the person. `say` prints a line in the session. */
35
+ function notify(message, say) {
36
+ const how = howNotified();
37
+ if (how === 'off')
38
+ return;
39
+ if (how === 'desktop') {
40
+ const args = process.platform === 'darwin'
41
+ ? ['-e', `display notification ${JSON.stringify(message)} with title "comprism"`]
42
+ : ['comprism', message];
43
+ // nosemgrep: javascript.lang.security.detect-child-process.detect-child-process
44
+ (0, node_child_process_1.spawn)(process.platform === 'darwin' ? 'osascript' : 'notify-send', args, { stdio: 'ignore', detached: true }).unref();
45
+ return;
46
+ }
47
+ if ((0, machine_1.machineSettings)().notify === 'desktop' && !saidFallback) {
48
+ saidFallback = true;
49
+ say(' desktop notifications are not available here, so the terminal bell is used instead');
50
+ }
51
+ process.stdout.write('\x07');
52
+ }