@illuminis/comprism 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -2
- package/out/agent/command.d.ts +75 -4
- package/out/agent/command.js +220 -25
- package/out/agent/render.d.ts +17 -2
- package/out/agent/render.js +157 -14
- package/out/agent/session.d.ts +141 -2
- package/out/agent/session.js +735 -149
- package/out/commands/agents.d.ts +2 -0
- package/out/commands/agents.js +79 -0
- package/out/commands/ask.d.ts +1 -1
- package/out/commands/ask.js +78 -11
- package/out/commands/commands-thin.js +79 -17
- package/out/commands/config.d.ts +1 -0
- package/out/commands/config.js +138 -0
- package/out/commands/cost.d.ts +1 -0
- package/out/commands/cost.js +167 -0
- package/out/commands/hooks.d.ts +1 -0
- package/out/commands/hooks.js +83 -0
- package/out/commands/install.d.ts +44 -1
- package/out/commands/install.js +198 -4
- package/out/commands/instructions.d.ts +1 -0
- package/out/commands/instructions.js +113 -0
- package/out/commands/integrations.d.ts +3 -0
- package/out/commands/integrations.js +215 -0
- package/out/commands/jobs.d.ts +5 -0
- package/out/commands/jobs.js +157 -0
- package/out/commands/login.js +188 -36
- package/out/commands/memory.d.ts +3 -0
- package/out/commands/memory.js +113 -0
- package/out/commands/permissions.d.ts +1 -0
- package/out/commands/permissions.js +94 -0
- package/out/commands/plugins.d.ts +4 -0
- package/out/commands/plugins.js +192 -0
- package/out/commands/privacy.d.ts +1 -0
- package/out/commands/privacy.js +57 -0
- package/out/commands/providerKey.d.ts +32 -0
- package/out/commands/providerKey.js +108 -0
- package/out/commands/repl.d.ts +8 -1
- package/out/commands/repl.js +1207 -118
- package/out/commands/report.d.ts +39 -0
- package/out/commands/report.js +115 -0
- package/out/commands/review.d.ts +5 -0
- package/out/commands/review.js +223 -0
- package/out/commands/sessions.d.ts +23 -0
- package/out/commands/sessions.js +115 -0
- package/out/commands/settings.d.ts +3 -1
- package/out/commands/settings.js +18 -16
- package/out/commands/skills.d.ts +21 -0
- package/out/commands/skills.js +207 -0
- package/out/commands/unattended.d.ts +7 -0
- package/out/commands/unattended.js +351 -0
- package/out/commands/update.d.ts +1 -0
- package/out/commands/update.js +123 -0
- package/out/commands/worktrees.d.ts +5 -0
- package/out/commands/worktrees.js +186 -0
- package/out/executor/browser.d.ts +14 -0
- package/out/executor/browser.js +270 -0
- package/out/executor/diagnostics.d.ts +2 -0
- package/out/executor/diagnostics.js +181 -0
- package/out/executor/files.js +270 -40
- package/out/executor/git.js +42 -29
- package/out/executor/hooks.d.ts +42 -58
- package/out/executor/hooks.js +89 -182
- package/out/executor/index.d.ts +21 -5
- package/out/executor/index.js +160 -14
- package/out/executor/paths.d.ts +6 -1
- package/out/executor/paths.js +34 -6
- package/out/executor/sandbox.d.ts +40 -0
- package/out/executor/sandbox.js +299 -0
- package/out/executor/shell.d.ts +49 -4
- package/out/executor/shell.js +302 -56
- package/out/executor/toolservers.d.ts +20 -0
- package/out/executor/toolservers.js +189 -0
- package/out/executor/worktree.d.ts +9 -0
- package/out/executor/worktree.js +119 -0
- package/out/graph/read-python.js +2 -1
- package/out/lib/attach.d.ts +56 -12
- package/out/lib/attach.js +230 -63
- package/out/lib/clipboard.d.ts +23 -0
- package/out/lib/clipboard.js +182 -0
- package/out/lib/commandlist.d.ts +20 -0
- package/out/lib/commandlist.js +58 -0
- package/out/lib/decision.d.ts +22 -0
- package/out/lib/decision.js +50 -0
- package/out/lib/fingerprint.d.ts +25 -0
- package/out/lib/fingerprint.js +58 -0
- package/out/lib/gateway.d.ts +186 -2
- package/out/lib/gateway.js +59 -4
- package/out/lib/history.d.ts +24 -0
- package/out/lib/history.js +137 -0
- package/out/lib/ide.d.ts +19 -0
- package/out/lib/ide.js +131 -0
- package/out/lib/keyboard.d.ts +95 -0
- package/out/lib/keyboard.js +383 -0
- package/out/lib/machine.d.ts +21 -0
- package/out/lib/machine.js +91 -0
- package/out/lib/notify.d.ts +4 -0
- package/out/lib/notify.js +52 -0
- package/out/lib/output.d.ts +48 -0
- package/out/lib/output.js +108 -0
- package/out/lib/project-ops.d.ts +19 -0
- package/out/lib/project-ops.js +146 -0
- package/out/lib/project.d.ts +28 -0
- package/out/lib/project.js +114 -0
- package/out/lib/prompt.js +15 -2
- package/out/lib/queue.d.ts +13 -0
- package/out/lib/queue.js +116 -0
- package/out/lib/readiness.d.ts +19 -0
- package/out/lib/readiness.js +170 -1
- package/out/lib/self.d.ts +23 -0
- package/out/lib/self.js +124 -0
- package/out/lib/sessions.d.ts +19 -0
- package/out/lib/sessions.js +221 -0
- package/out/lib/stdin.d.ts +32 -0
- package/out/lib/stdin.js +117 -0
- package/out/lib/store.d.ts +40 -0
- package/out/lib/store.js +138 -0
- package/out/lib/sync.d.ts +18 -0
- package/out/lib/sync.js +81 -0
- package/out/lib/ui.d.ts +2 -4
- package/out/lib/ui.js +31 -25
- package/out/lib/voice.js +24 -0
- package/out/postinstall.js +42 -17
- package/out/providers/anthropic.d.ts +22 -0
- package/out/providers/anthropic.js +80 -0
- package/out/providers/index.d.ts +8 -0
- package/out/providers/index.js +83 -0
- package/out/providers/openai.d.ts +11 -0
- package/out/providers/openai.js +57 -0
- package/out/thin.js +594 -32
- package/package.json +9 -49
package/out/lib/readiness.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
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
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -7,6 +40,8 @@ exports.REQUIREMENTS = void 0;
|
|
|
7
40
|
exports.present = present;
|
|
8
41
|
exports.howToInstall = howToInstall;
|
|
9
42
|
exports.check = check;
|
|
43
|
+
exports.systemName = systemName;
|
|
44
|
+
exports.diagnose = diagnose;
|
|
10
45
|
/**
|
|
11
46
|
* What this machine needs before the work can be done on it, and what to type
|
|
12
47
|
* when something is missing.
|
|
@@ -38,6 +73,10 @@ exports.check = check;
|
|
|
38
73
|
*/
|
|
39
74
|
const node_child_process_1 = require("node:child_process");
|
|
40
75
|
const node_os_1 = __importDefault(require("node:os"));
|
|
76
|
+
const install_1 = require("../commands/install");
|
|
77
|
+
const connection_1 = require("./connection");
|
|
78
|
+
const gateway_1 = require("./gateway");
|
|
79
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
41
80
|
exports.REQUIREMENTS = [
|
|
42
81
|
{
|
|
43
82
|
purpose: 'working with a repository',
|
|
@@ -57,7 +96,11 @@ exports.REQUIREMENTS = [
|
|
|
57
96
|
/** Is this program on the machine, by name, without running anything of theirs. */
|
|
58
97
|
function present(program) {
|
|
59
98
|
try {
|
|
60
|
-
|
|
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' });
|
|
61
104
|
return true;
|
|
62
105
|
}
|
|
63
106
|
catch {
|
|
@@ -105,3 +148,129 @@ function check() {
|
|
|
105
148
|
}
|
|
106
149
|
return { ready: missing.length === 0, lines, fix: howToInstall(missing) };
|
|
107
150
|
}
|
|
151
|
+
/** The system this is, in the words its owner would use. */
|
|
152
|
+
function systemName() {
|
|
153
|
+
try {
|
|
154
|
+
if (node_os_1.default.platform() === 'darwin') {
|
|
155
|
+
const v = (0, node_child_process_1.execFileSync)('sw_vers', ['-productVersion'], { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
|
|
156
|
+
return `macOS ${v} ${node_os_1.default.arch()}`;
|
|
157
|
+
}
|
|
158
|
+
if (node_os_1.default.platform() === 'linux') {
|
|
159
|
+
const release = node_fs_1.default.readFileSync('/etc/os-release', 'utf8').match(/^PRETTY_NAME="?([^"\n]+)"?/m);
|
|
160
|
+
return `${release ? release[1] : 'Linux'} ${node_os_1.default.arch()}`;
|
|
161
|
+
}
|
|
162
|
+
if (node_os_1.default.platform() === 'win32')
|
|
163
|
+
return `${node_os_1.default.version()} ${node_os_1.default.arch()}`;
|
|
164
|
+
}
|
|
165
|
+
catch { /* fall through to the plain name */ }
|
|
166
|
+
return `${node_os_1.default.type()} ${node_os_1.default.release()} ${node_os_1.default.arch()}`;
|
|
167
|
+
}
|
|
168
|
+
function version(program, args) {
|
|
169
|
+
try {
|
|
170
|
+
const text = (0, node_child_process_1.execFileSync)(program, args, { stdio: ['ignore', 'pipe', 'ignore'] }).toString();
|
|
171
|
+
return text.match(/\d+\.\d+(\.\d+)?/)?.[0] ?? '';
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Everything `comprism check` reports (manual 1.6).
|
|
179
|
+
*
|
|
180
|
+
* Required: Node 22 or newer (built in for the install script and Python
|
|
181
|
+
* installs), a sign in, and a service that answers. Anything
|
|
182
|
+
* else is optional and never makes the check fail. Whether the service still
|
|
183
|
+
* accepts this machine is asked of the service itself, on the same session
|
|
184
|
+
* operation `status` uses, so the two can never disagree.
|
|
185
|
+
*/
|
|
186
|
+
async function diagnose() {
|
|
187
|
+
const found = [];
|
|
188
|
+
found.push({ ok: true, required: false, line: systemName() });
|
|
189
|
+
// Which proxy and certificate file are in use (manual 1.11).
|
|
190
|
+
const proxy = process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy;
|
|
191
|
+
if (proxy) {
|
|
192
|
+
const bypass = process.env.NO_PROXY || process.env.no_proxy;
|
|
193
|
+
found.push({ ok: true, required: false, line: `proxy ${proxy.replace(/\/\/[^@/]*@/, '//***@')}`
|
|
194
|
+
+ (bypass ? `, not used for ${bypass}` : '') });
|
|
195
|
+
}
|
|
196
|
+
const caFile = process.env.NODE_EXTRA_CA_CERTS;
|
|
197
|
+
if (caFile) {
|
|
198
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs')));
|
|
199
|
+
found.push(fs.existsSync(caFile)
|
|
200
|
+
? { ok: true, required: false, line: `extra certificate authorities from ${caFile}` }
|
|
201
|
+
: { ok: false, required: true, line: `NODE_EXTRA_CA_CERTS names ${caFile}, which does not exist`,
|
|
202
|
+
fix: 'point NODE_EXTRA_CA_CERTS at your company certificate authority file' });
|
|
203
|
+
}
|
|
204
|
+
const node = process.versions.node;
|
|
205
|
+
const major = Number(node.split('.')[0]);
|
|
206
|
+
// The install script and Python installs carry their own Node (manual 1.1),
|
|
207
|
+
// so the line says so rather than asking for a Node nobody installed.
|
|
208
|
+
const { installMethod, isStandalone } = await Promise.resolve().then(() => __importStar(require('./self')));
|
|
209
|
+
if (isStandalone()) {
|
|
210
|
+
const how = installMethod() === 'script' ? 'the install script' : 'Python (pip)';
|
|
211
|
+
found.push({ ok: true, required: true, line: `installed with ${how}; Node ${node} is built in, nothing else needed` });
|
|
212
|
+
}
|
|
213
|
+
else
|
|
214
|
+
found.push(major >= 22
|
|
215
|
+
? { ok: true, required: true, line: `Node ${node} (22 or newer needed)` }
|
|
216
|
+
: { ok: false, required: true, line: `Node ${node} is too old: 22 or newer needed`,
|
|
217
|
+
fix: 'install Node 22 or newer from https://nodejs.org' });
|
|
218
|
+
const connection = (0, connection_1.readConnection)();
|
|
219
|
+
if (!connection?.url || !connection.workspaceKey) {
|
|
220
|
+
found.push({ ok: false, required: true, line: 'not signed in', fix: 'comprism login' });
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
const started = Date.now();
|
|
224
|
+
const reply = await (0, gateway_1.ask)({ intent: 'session', prompt: '' });
|
|
225
|
+
const ms = Date.now() - started;
|
|
226
|
+
if (!reply.ok && !reply.httpStatus && /certificate problem/.test(reply.message || '')) {
|
|
227
|
+
found.push({ ok: false, required: true, line: `service at ${connection.url}: ${reply.message}`,
|
|
228
|
+
fix: 'set NODE_EXTRA_CA_CERTS to your company certificate authority file' });
|
|
229
|
+
}
|
|
230
|
+
else if (!reply.ok && !reply.httpStatus) {
|
|
231
|
+
found.push({ ok: false, required: true, line: `service at ${connection.url} did not answer`,
|
|
232
|
+
fix: 'check your network, then comprism status' });
|
|
233
|
+
}
|
|
234
|
+
else if (reply.httpStatus === 401 || (reply.ok && reply.session && !reply.session.signed_in)) {
|
|
235
|
+
found.push({ ok: false, required: true, line: "signed out: this machine's key was revoked or has expired",
|
|
236
|
+
fix: 'comprism login' });
|
|
237
|
+
found.push({ ok: true, required: true, line: `service reachable in ${ms} ms` });
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
found.push({ ok: true, required: true,
|
|
241
|
+
line: `signed in to ${reply.session?.company || connection.url}` });
|
|
242
|
+
found.push({ ok: true, required: true, line: `service reachable in ${ms} ms` });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const { powershellProfiles } = await Promise.resolve().then(() => __importStar(require('../commands/install')));
|
|
246
|
+
const profile = [...powershellProfiles(), ...(0, install_1.shellProfiles)()].find(install_1.profileHasSetup);
|
|
247
|
+
const home = node_os_1.default.homedir();
|
|
248
|
+
if (profile) {
|
|
249
|
+
found.push({ ok: true, required: false,
|
|
250
|
+
line: `shell profile line present in ${profile.startsWith(home) ? `~${profile.slice(home.length)}` : profile}` });
|
|
251
|
+
}
|
|
252
|
+
else if (node_os_1.default.platform() === 'win32') {
|
|
253
|
+
found.push({ ok: false, required: false,
|
|
254
|
+
line: 'no PowerShell profile line, so other AI tools here are not measured', fix: 'comprism install' });
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
found.push({ ok: false, required: false, line: 'no shell profile line, so other AI tools here are not measured',
|
|
258
|
+
fix: 'comprism install' });
|
|
259
|
+
}
|
|
260
|
+
const git = version('git', ['--version']);
|
|
261
|
+
found.push(git !== null
|
|
262
|
+
? { 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']) });
|
|
265
|
+
const rg = version('rg', ['--version']);
|
|
266
|
+
found.push(rg !== null
|
|
267
|
+
? { ok: true, required: false, line: `ripgrep ${rg}` }
|
|
268
|
+
: { 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)
|
|
272
|
+
? { ok: true, required: false, line: 'a PDF reader' }
|
|
273
|
+
: { ok: false, required: false, line: `no PDF reader: ${pdf.without} will not work`,
|
|
274
|
+
fix: howToInstall(['pdftotext']) });
|
|
275
|
+
return found;
|
|
276
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** True when running as the ready to run program rather than under Node. */
|
|
2
|
+
export declare function isStandalone(): boolean;
|
|
3
|
+
/** Where the install script keeps the program: `~/.comprism/bin`. */
|
|
4
|
+
export declare function standaloneBinDir(): string;
|
|
5
|
+
export type InstallMethod = "script" | "npm" | "python";
|
|
6
|
+
/**
|
|
7
|
+
* Which of the three install options put this copy here.
|
|
8
|
+
*
|
|
9
|
+
* The program is only ever distributed two ways, so where it sits decides it:
|
|
10
|
+
* in `~/.comprism/bin` it came from the install script, anywhere else pip put
|
|
11
|
+
* it. Under Node it came from npm.
|
|
12
|
+
*/
|
|
13
|
+
export declare function installMethod(): InstallMethod;
|
|
14
|
+
/** The command and arguments that start another copy of this tool. */
|
|
15
|
+
export declare function selfLaunch(args: string[], nodeFlags?: string[]): [string, string[]];
|
|
16
|
+
/**
|
|
17
|
+
* A file shipped beside the code, as a path on disk.
|
|
18
|
+
*
|
|
19
|
+
* Under npm it sits in the package. Inside the program it is carried as an
|
|
20
|
+
* asset and written once to `~/.comprism/assets`, because the programs it is
|
|
21
|
+
* handed to (Python, for the reader script) need a real file.
|
|
22
|
+
*/
|
|
23
|
+
export declare function shippedFile(name: string, packagePath: string): string;
|
package/out/lib/self.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
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.isStandalone = isStandalone;
|
|
37
|
+
exports.standaloneBinDir = standaloneBinDir;
|
|
38
|
+
exports.installMethod = installMethod;
|
|
39
|
+
exports.selfLaunch = selfLaunch;
|
|
40
|
+
exports.shippedFile = shippedFile;
|
|
41
|
+
/**
|
|
42
|
+
* How this copy of the tool was installed, and how it starts another copy of
|
|
43
|
+
* itself (manual 1.1).
|
|
44
|
+
*
|
|
45
|
+
* The same code ships two ways. Through npm it is a script that Node runs, so
|
|
46
|
+
* `process.argv[1]` is the script and a second copy is `node <script> <args>`.
|
|
47
|
+
* Through the install script and the Python package it is one ready to run
|
|
48
|
+
* program with Node built in, where `process.argv[1]` is the program again and
|
|
49
|
+
* a second copy is just `<program> <args>`. Passing the program's own path as
|
|
50
|
+
* the first argument there would read as a command called "/Users/me/...", so
|
|
51
|
+
* every place that starts a copy of the tool asks here instead of guessing.
|
|
52
|
+
*/
|
|
53
|
+
const fs = __importStar(require("fs"));
|
|
54
|
+
const path = __importStar(require("path"));
|
|
55
|
+
const config_1 = require("./config");
|
|
56
|
+
let sea;
|
|
57
|
+
/** True when running as the ready to run program rather than under Node. */
|
|
58
|
+
function isStandalone() {
|
|
59
|
+
if (sea === undefined) {
|
|
60
|
+
try {
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
62
|
+
sea = Boolean(require("node:sea").isSea());
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
sea = false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return sea;
|
|
69
|
+
}
|
|
70
|
+
/** Where the install script keeps the program: `~/.comprism/bin`. */
|
|
71
|
+
function standaloneBinDir() {
|
|
72
|
+
return path.join((0, config_1.homeDir)(), "bin");
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Which of the three install options put this copy here.
|
|
76
|
+
*
|
|
77
|
+
* The program is only ever distributed two ways, so where it sits decides it:
|
|
78
|
+
* in `~/.comprism/bin` it came from the install script, anywhere else pip put
|
|
79
|
+
* it. Under Node it came from npm.
|
|
80
|
+
*/
|
|
81
|
+
function installMethod() {
|
|
82
|
+
if (!isStandalone())
|
|
83
|
+
return "npm";
|
|
84
|
+
try {
|
|
85
|
+
const here = fs.realpathSync(process.execPath);
|
|
86
|
+
const bin = fs.realpathSync(standaloneBinDir());
|
|
87
|
+
if (path.dirname(here) === bin)
|
|
88
|
+
return "script";
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// No `~/.comprism/bin`: not an install script install.
|
|
92
|
+
}
|
|
93
|
+
return "python";
|
|
94
|
+
}
|
|
95
|
+
/** The command and arguments that start another copy of this tool. */
|
|
96
|
+
function selfLaunch(args, nodeFlags = []) {
|
|
97
|
+
if (isStandalone())
|
|
98
|
+
return [process.execPath, args];
|
|
99
|
+
return [process.execPath, [...nodeFlags, process.argv[1] ?? "", ...args]];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* A file shipped beside the code, as a path on disk.
|
|
103
|
+
*
|
|
104
|
+
* Under npm it sits in the package. Inside the program it is carried as an
|
|
105
|
+
* asset and written once to `~/.comprism/assets`, because the programs it is
|
|
106
|
+
* handed to (Python, for the reader script) need a real file.
|
|
107
|
+
*/
|
|
108
|
+
function shippedFile(name, packagePath) {
|
|
109
|
+
if (!isStandalone())
|
|
110
|
+
return packagePath;
|
|
111
|
+
const target = path.join((0, config_1.homeDir)(), "assets", name);
|
|
112
|
+
try {
|
|
113
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
114
|
+
const text = require("node:sea").getAsset(name, "utf8");
|
|
115
|
+
if (!fs.existsSync(target) || fs.readFileSync(target, "utf8") !== text) {
|
|
116
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
117
|
+
fs.writeFileSync(target, text);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// An asset that cannot be written leaves the caller's own fallback in charge.
|
|
122
|
+
}
|
|
123
|
+
return target;
|
|
124
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { GatewayReply } from './gateway';
|
|
2
|
+
/** Ask the service one sessions question, with where this machine is. */
|
|
3
|
+
export declare function sessions(op: 'list' | 'resume' | 'latest' | 'opened' | 'rename' | 'branch' | 'export' | 'by_pr' | 'remote_on' | 'remote_poll' | 'remote_off' | 'lease' | 'read' | 'steering' | 'steer', extra?: Record<string, unknown>, folder?: string): Promise<GatewayReply>;
|
|
4
|
+
/** Why a sessions question got no answer, in the service's words when it has them. */
|
|
5
|
+
export declare function failure(reply: GatewayReply): string;
|
|
6
|
+
/** The exit number for a sessions question that got no answer (Appendix B). */
|
|
7
|
+
export declare function exitFor(reply: GatewayReply): number;
|
|
8
|
+
/**
|
|
9
|
+
* Choose one line with the arrow keys; Enter picks, Esc or Ctrl+C leaves.
|
|
10
|
+
* Resolves with the index chosen, or null. Without a terminal it asks for a
|
|
11
|
+
* number instead, so a script is never left waiting on keys it cannot send.
|
|
12
|
+
*/
|
|
13
|
+
export declare function pick(lines: string[]): Promise<number | null>;
|
|
14
|
+
/**
|
|
15
|
+
* `comprism sessions` and its words (manual 8.5 to 8.7, 8.10). Every answer,
|
|
16
|
+
* every line and every refusal is the service's; the export is written to
|
|
17
|
+
* standard output exactly as it came, so `> session.md` holds the file.
|
|
18
|
+
*/
|
|
19
|
+
export declare function cmdSessions(args: string[]): Promise<number>;
|
|
@@ -0,0 +1,221 @@
|
|
|
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.sessions = sessions;
|
|
37
|
+
exports.failure = failure;
|
|
38
|
+
exports.exitFor = exitFor;
|
|
39
|
+
exports.pick = pick;
|
|
40
|
+
exports.cmdSessions = cmdSessions;
|
|
41
|
+
/**
|
|
42
|
+
* A person's sessions, as the service keeps them (manual chapter 8).
|
|
43
|
+
*
|
|
44
|
+
* Nothing about a session is decided or stored here. The list, the numbers,
|
|
45
|
+
* the names, the costs, whether the conversation was kept and the words of
|
|
46
|
+
* every line come from the service's `sessions` intent; this file sends the
|
|
47
|
+
* question, draws the answer, and runs the arrow key list for `--resume`.
|
|
48
|
+
*/
|
|
49
|
+
const ui = __importStar(require("./ui"));
|
|
50
|
+
const gateway_1 = require("./gateway");
|
|
51
|
+
const out = (s = '') => process.stdout.write(s + '\n');
|
|
52
|
+
/** Ask the service one sessions question, with where this machine is. */
|
|
53
|
+
async function sessions(op, extra = {}, folder) {
|
|
54
|
+
const { resolveProject, asWire } = await Promise.resolve().then(() => __importStar(require('./project')));
|
|
55
|
+
const { currentWorkspace } = await Promise.resolve().then(() => __importStar(require('../commands/settings')));
|
|
56
|
+
const facts = resolveProject(folder ?? currentWorkspace().path, []);
|
|
57
|
+
return (0, gateway_1.ask)({
|
|
58
|
+
intent: 'sessions',
|
|
59
|
+
prompt: '',
|
|
60
|
+
sessionsPayload: {
|
|
61
|
+
op,
|
|
62
|
+
project: asWire(facts),
|
|
63
|
+
// The service writes the times, in this machine's own clock.
|
|
64
|
+
tz_minutes: -new Date().getTimezoneOffset(),
|
|
65
|
+
...extra,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/** Why a sessions question got no answer, in the service's words when it has them. */
|
|
70
|
+
function failure(reply) {
|
|
71
|
+
if (reply.served === false) {
|
|
72
|
+
return 'Your workspace is running a build without sessions yet.';
|
|
73
|
+
}
|
|
74
|
+
return reply.message || 'Your workspace could not be reached.';
|
|
75
|
+
}
|
|
76
|
+
/** The exit number for a sessions question that got no answer (Appendix B). */
|
|
77
|
+
function exitFor(reply) {
|
|
78
|
+
if (reply.message === 'this machine is not signed in')
|
|
79
|
+
return 3;
|
|
80
|
+
if (!reply.ok && reply.message === 'the service could not be reached')
|
|
81
|
+
return 6;
|
|
82
|
+
return 1;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Choose one line with the arrow keys; Enter picks, Esc or Ctrl+C leaves.
|
|
86
|
+
* Resolves with the index chosen, or null. Without a terminal it asks for a
|
|
87
|
+
* number instead, so a script is never left waiting on keys it cannot send.
|
|
88
|
+
*/
|
|
89
|
+
async function pick(lines) {
|
|
90
|
+
if (lines.length === 0)
|
|
91
|
+
return null;
|
|
92
|
+
if (!process.stdin.isTTY) {
|
|
93
|
+
lines.forEach((l, i) => out(`${String(i + 1).padStart(3)}. ${l.trimStart()}`));
|
|
94
|
+
const { ask: askLine, closePrompt } = await Promise.resolve().then(() => __importStar(require('./prompt')));
|
|
95
|
+
const said = (await askLine(' which one? ')).trim();
|
|
96
|
+
closePrompt();
|
|
97
|
+
const n = Number(said);
|
|
98
|
+
return Number.isInteger(n) && n >= 1 && n <= lines.length ? n - 1 : null;
|
|
99
|
+
}
|
|
100
|
+
const stdin = process.stdin;
|
|
101
|
+
let at = 0;
|
|
102
|
+
const draw = (first) => {
|
|
103
|
+
if (!first)
|
|
104
|
+
process.stdout.write(`\x1b[${lines.length}A`);
|
|
105
|
+
lines.forEach((l, i) => {
|
|
106
|
+
const mark = i === at ? ui.c.bold('> ') : ' ';
|
|
107
|
+
process.stdout.write(`\x1b[2K${mark}${i === at ? ui.c.bold(l.trimStart()) : l.trimStart()}\n`);
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
out(ui.c.dim(' Up and Down to choose, Enter to resume, Esc to leave.'));
|
|
111
|
+
draw(true);
|
|
112
|
+
const wasRaw = stdin.isRaw;
|
|
113
|
+
stdin.setRawMode(true);
|
|
114
|
+
stdin.resume();
|
|
115
|
+
return new Promise((resolve) => {
|
|
116
|
+
const done = (chosen) => {
|
|
117
|
+
stdin.off('data', onKey);
|
|
118
|
+
stdin.setRawMode(Boolean(wasRaw));
|
|
119
|
+
stdin.pause();
|
|
120
|
+
resolve(chosen);
|
|
121
|
+
};
|
|
122
|
+
const onKey = (buf) => {
|
|
123
|
+
const key = buf.toString();
|
|
124
|
+
if (key === '\x1b[A' || key === 'k') {
|
|
125
|
+
at = (at + lines.length - 1) % lines.length;
|
|
126
|
+
draw(false);
|
|
127
|
+
}
|
|
128
|
+
else if (key === '\x1b[B' || key === 'j') {
|
|
129
|
+
at = (at + 1) % lines.length;
|
|
130
|
+
draw(false);
|
|
131
|
+
}
|
|
132
|
+
else if (key === '\r' || key === '\n')
|
|
133
|
+
done(at);
|
|
134
|
+
else if (key === '\x1b' || key === '\x03' || key === 'q')
|
|
135
|
+
done(null);
|
|
136
|
+
};
|
|
137
|
+
stdin.on('data', onKey);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
/** Print what the service sent, or why it sent nothing. */
|
|
141
|
+
function answered(reply) {
|
|
142
|
+
out();
|
|
143
|
+
if (!reply.ok || !reply.sessions) {
|
|
144
|
+
out(ui.fail(failure(reply)));
|
|
145
|
+
out();
|
|
146
|
+
return exitFor(reply);
|
|
147
|
+
}
|
|
148
|
+
for (const line of reply.sessions.lines)
|
|
149
|
+
out(line);
|
|
150
|
+
out();
|
|
151
|
+
return 0;
|
|
152
|
+
}
|
|
153
|
+
const USAGE = [
|
|
154
|
+
'comprism sessions [--search <words>] [--pr <number>]',
|
|
155
|
+
'comprism sessions rename <number> "<name>"',
|
|
156
|
+
'comprism sessions branch <number>',
|
|
157
|
+
'comprism sessions export <number> [--format json]',
|
|
158
|
+
];
|
|
159
|
+
/**
|
|
160
|
+
* `comprism sessions` and its words (manual 8.5 to 8.7, 8.10). Every answer,
|
|
161
|
+
* every line and every refusal is the service's; the export is written to
|
|
162
|
+
* standard output exactly as it came, so `> session.md` holds the file.
|
|
163
|
+
*/
|
|
164
|
+
async function cmdSessions(args) {
|
|
165
|
+
const [word, key, ...rest] = args;
|
|
166
|
+
if (word === 'rename') {
|
|
167
|
+
const name = rest.join(' ').trim();
|
|
168
|
+
if (!key || !name) {
|
|
169
|
+
out(ui.fail(`Say which session and the new name: ${USAGE[1]}`));
|
|
170
|
+
return 2;
|
|
171
|
+
}
|
|
172
|
+
return answered(await sessions('rename', { key, name }));
|
|
173
|
+
}
|
|
174
|
+
if (word === 'branch') {
|
|
175
|
+
if (!key) {
|
|
176
|
+
out(ui.fail(`Say which session to branch: ${USAGE[2]}`));
|
|
177
|
+
return 2;
|
|
178
|
+
}
|
|
179
|
+
return answered(await sessions('branch', { key }));
|
|
180
|
+
}
|
|
181
|
+
if (word === 'export') {
|
|
182
|
+
const at = rest.indexOf('--format');
|
|
183
|
+
const format = at === -1 ? 'markdown' : rest[at + 1];
|
|
184
|
+
if (!key || (format !== 'markdown' && format !== 'json' && format !== 'md')) {
|
|
185
|
+
process.stderr.write(ui.fail(`Say which session, and json or markdown: ${USAGE[3]}`) + '\n');
|
|
186
|
+
return 2;
|
|
187
|
+
}
|
|
188
|
+
const reply = await sessions('export', { key, format: format === 'json' ? 'json' : 'markdown' });
|
|
189
|
+
const got = reply.sessions;
|
|
190
|
+
if (!reply.ok || !got) {
|
|
191
|
+
process.stderr.write(ui.fail(failure(reply)) + '\n');
|
|
192
|
+
return exitFor(reply);
|
|
193
|
+
}
|
|
194
|
+
process.stdout.write(got.format === 'json'
|
|
195
|
+
? `${JSON.stringify(got.document, null, 2)}\n`
|
|
196
|
+
: `${String(got.text ?? '')}\n`);
|
|
197
|
+
return 0;
|
|
198
|
+
}
|
|
199
|
+
const prAt = args.indexOf('--pr');
|
|
200
|
+
if (prAt !== -1) {
|
|
201
|
+
const pr = args[prAt + 1];
|
|
202
|
+
if (!pr) {
|
|
203
|
+
out(ui.fail('Say which pull request: comprism sessions --pr 42'));
|
|
204
|
+
return 2;
|
|
205
|
+
}
|
|
206
|
+
return answered(await sessions('by_pr', { pr }));
|
|
207
|
+
}
|
|
208
|
+
const at = args.indexOf('--search');
|
|
209
|
+
const search = at === -1 ? '' : args.slice(at + 1).join(' ').trim();
|
|
210
|
+
if (at !== -1 && !search) {
|
|
211
|
+
out(ui.fail('Say what to search for: comprism sessions --search parser'));
|
|
212
|
+
return 2;
|
|
213
|
+
}
|
|
214
|
+
if (word && !word.startsWith('-')) {
|
|
215
|
+
out(ui.fail(`There is no sessions command called "${word}".`));
|
|
216
|
+
for (const u of USAGE)
|
|
217
|
+
out(` ${ui.c.dim(u)}`);
|
|
218
|
+
return 2;
|
|
219
|
+
}
|
|
220
|
+
return answered(await sessions('list', { search, limit: 20 }));
|
|
221
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as tty from 'node:tty';
|
|
2
|
+
import * as attach from './attach';
|
|
3
|
+
/** The name piped text is attached under. */
|
|
4
|
+
export declare const STDIN_NAME = "standard input";
|
|
5
|
+
/**
|
|
6
|
+
* Everything piped in, read to the end. Null when standard input is a
|
|
7
|
+
* terminal, and empty when the pipe closed with nothing on it.
|
|
8
|
+
*
|
|
9
|
+
* A pipe that stays open with nothing coming (a command still running, or one
|
|
10
|
+
* that never closes) is announced after two seconds on standard error, so the
|
|
11
|
+
* person is not left looking at a still screen. Ctrl+C ends it as usual.
|
|
12
|
+
*/
|
|
13
|
+
export declare function readPiped(input?: NodeJS.ReadableStream & {
|
|
14
|
+
isTTY?: boolean;
|
|
15
|
+
}, say?: (line: string) => void): Promise<string | null>;
|
|
16
|
+
/**
|
|
17
|
+
* The piped text, uploaded as `standard input`. Written to a temporary file so
|
|
18
|
+
* it travels exactly like any attachment, and the file removed straight after.
|
|
19
|
+
*/
|
|
20
|
+
export declare function attachPiped(text: string, baseUrl: string, credential: string): Promise<{
|
|
21
|
+
attached: attach.Attached[];
|
|
22
|
+
refused: {
|
|
23
|
+
file: string;
|
|
24
|
+
why: string;
|
|
25
|
+
}[];
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* The terminal the person is sitting at, when standard input is a pipe.
|
|
29
|
+
* Null when there is none (a script, a scheduler, a container), which makes
|
|
30
|
+
* the job unattended.
|
|
31
|
+
*/
|
|
32
|
+
export declare function openTerminal(platform?: string): tty.ReadStream | null;
|