@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.
- package/README.md +61 -2
- package/out/agent/command.d.ts +75 -4
- package/out/agent/command.js +221 -26
- package/out/agent/render.d.ts +56 -8
- package/out/agent/render.js +441 -39
- package/out/agent/session.d.ts +156 -2
- package/out/agent/session.js +795 -154
- 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 +88 -12
- 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 +249 -43
- 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 +12 -1
- package/out/commands/repl.js +1649 -122
- 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/welcome.d.ts +25 -1
- package/out/commands/welcome.js +71 -35
- 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 +190 -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 +98 -0
- package/out/lib/keyboard.js +390 -0
- package/out/lib/lastlogin.d.ts +14 -0
- package/out/lib/lastlogin.js +105 -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/picker.d.ts +1 -0
- package/out/lib/picker.js +158 -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.d.ts +3 -0
- package/out/lib/prompt.js +36 -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/startcache.d.ts +13 -0
- package/out/lib/startcache.js +78 -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 +10 -4
- package/out/lib/ui.js +65 -25
- package/out/lib/voice.d.ts +36 -0
- package/out/lib/voice.js +160 -1
- package/out/native/dictate +0 -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 +595 -33
- package/package.json +11 -50
|
@@ -0,0 +1,167 @@
|
|
|
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.cmdCost = cmdCost;
|
|
37
|
+
/**
|
|
38
|
+
* `comprism cost` — what a session cost, and what drove it.
|
|
39
|
+
*
|
|
40
|
+
* ── This file draws. It decides nothing ───────────────────────────────────
|
|
41
|
+
*
|
|
42
|
+
* Every word and every number below is written by `spend_report` on our
|
|
43
|
+
* server: which figures belong on the screen, what each one is called, what
|
|
44
|
+
* order they read in, and how each is formatted. This file receives that and
|
|
45
|
+
* puts it on a terminal.
|
|
46
|
+
*
|
|
47
|
+
* It was not always so. The first cut of this command held the list of cost
|
|
48
|
+
* factors, their labels and their formatting, which meant adding a factor or
|
|
49
|
+
* renaming one obliged every customer to install a new version of the tool to
|
|
50
|
+
* see a figure we had already begun measuring. A published package is the one
|
|
51
|
+
* thing in this product we cannot change, so as little as possible is decided
|
|
52
|
+
* inside it.
|
|
53
|
+
*
|
|
54
|
+
* What genuinely has to stay here is drawing: the server does not know the
|
|
55
|
+
* width of somebody's window or whether their terminal renders color, and
|
|
56
|
+
* finished text sent from a server breaks on a narrow window.
|
|
57
|
+
*
|
|
58
|
+
* A dash is how "nobody measured this" reaches the screen. The server sends
|
|
59
|
+
* null for it, never zero, because a zero in a cost column reads as a
|
|
60
|
+
* measurement.
|
|
61
|
+
*/
|
|
62
|
+
const gateway_1 = require("../lib/gateway");
|
|
63
|
+
const connection_1 = require("../lib/connection");
|
|
64
|
+
const ui = __importStar(require("../lib/ui"));
|
|
65
|
+
const out = (s = '') => process.stdout.write(`${s}\n`);
|
|
66
|
+
/** A cell. Null and undefined are a figure nobody measured, so they print as a dash. */
|
|
67
|
+
const cell = (v) => v === null || v === undefined || v === '' ? ui.c.dim('—') : String(v);
|
|
68
|
+
async function cmdCost(args) {
|
|
69
|
+
const conn = (0, connection_1.readConnection)();
|
|
70
|
+
if (!conn?.url || !conn.workspaceKey) {
|
|
71
|
+
out();
|
|
72
|
+
out(ui.warn('This machine is not signed in, so there is no work to read.'));
|
|
73
|
+
out(` ${ui.c.dim('$')} comprism login`);
|
|
74
|
+
out();
|
|
75
|
+
return 1;
|
|
76
|
+
}
|
|
77
|
+
// The first bare word is a session, when one is given. No flag, because
|
|
78
|
+
// "which session" is the only thing this command takes.
|
|
79
|
+
const wanted = args.find((a) => a && !a.startsWith('-'));
|
|
80
|
+
const reply = await (0, gateway_1.ask)({
|
|
81
|
+
intent: 'spend',
|
|
82
|
+
prompt: '',
|
|
83
|
+
...(wanted ? { spendSession: wanted } : {}),
|
|
84
|
+
});
|
|
85
|
+
// A workspace whose build does not carry this question says so, and that is a
|
|
86
|
+
// complete answer rather than an absence. Reading a refusal as "no spend"
|
|
87
|
+
// would tell somebody their work cost nothing.
|
|
88
|
+
if (reply.served === false) {
|
|
89
|
+
out();
|
|
90
|
+
out(ui.warn('This workspace is running a build that cannot answer this yet.'));
|
|
91
|
+
out(` ${ui.c.dim(reply.message || '')}`);
|
|
92
|
+
out();
|
|
93
|
+
return 1;
|
|
94
|
+
}
|
|
95
|
+
const report = reply.spend;
|
|
96
|
+
if (!report || !report.session) {
|
|
97
|
+
out();
|
|
98
|
+
out(ui.info(reply.message || 'No work has been recorded in this workspace yet.'));
|
|
99
|
+
out();
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
const s = report.session;
|
|
103
|
+
// An older workspace answers without a presentation block. Saying so beats
|
|
104
|
+
// drawing an empty screen, and beats this file inventing the layout again.
|
|
105
|
+
const p = report.presentation;
|
|
106
|
+
if (!p) {
|
|
107
|
+
out();
|
|
108
|
+
out(ui.warn('This workspace answered without the breakdown this version draws.'));
|
|
109
|
+
out(` ${ui.c.dim('Deploy the workspace, or run `comprism status` to see which one you are on.')}`);
|
|
110
|
+
out();
|
|
111
|
+
return 1;
|
|
112
|
+
}
|
|
113
|
+
out();
|
|
114
|
+
out(ui.banner(String(p.heading || 'What this session cost')));
|
|
115
|
+
out();
|
|
116
|
+
out(` ${ui.c.bold(String(s.title || s.session_id))}`);
|
|
117
|
+
out(ui.c.dim(` ${String(s.started_at || '').slice(0, 16).replace('T', ' ')}`
|
|
118
|
+
+ ` · ${String(s.turns ?? 0)} turn(s)`
|
|
119
|
+
+ ` · ${String(s.surface || 'unknown surface')}`));
|
|
120
|
+
out();
|
|
121
|
+
const headline = (p.headline || []);
|
|
122
|
+
// One line per figure, label, value and the service's note in full, so a
|
|
123
|
+
// word like "estimated" is never cut to fit a box (manual 10.11). A figure
|
|
124
|
+
// nobody measured is blank.
|
|
125
|
+
if (headline.length > 0) {
|
|
126
|
+
for (const h of headline) {
|
|
127
|
+
const value = h.value === null || h.value === undefined ? 'blank' : String(h.value);
|
|
128
|
+
out(` ${String(h.label || '').padEnd(18)} ${ui.c.bold(value.padEnd(9))} ${ui.c.dim(h.note ? String(h.note) : '')}`);
|
|
129
|
+
}
|
|
130
|
+
out();
|
|
131
|
+
}
|
|
132
|
+
const factorRows = (p.factor_rows || []);
|
|
133
|
+
out(ui.c.muted(` ${String(p.factor_heading || '')}`));
|
|
134
|
+
out();
|
|
135
|
+
if (factorRows.length > 0) {
|
|
136
|
+
out(ui.table(p.factor_columns || ['factor', 'cost'], factorRows.map((r) => [String(r.label ?? ''), cell(r.value)])));
|
|
137
|
+
out();
|
|
138
|
+
if (p.driver) {
|
|
139
|
+
out(ui.c.dim(` ${String(p.driver)}`));
|
|
140
|
+
out();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else if (p.factor_empty) {
|
|
144
|
+
out(ui.c.dim(` ${String(p.factor_empty)}`));
|
|
145
|
+
out();
|
|
146
|
+
}
|
|
147
|
+
const callRows = (p.call_rows || []);
|
|
148
|
+
const callColumns = (p.call_columns || []);
|
|
149
|
+
if (callRows.length > 0 && callColumns.length > 0) {
|
|
150
|
+
out(ui.c.muted(` ${String(p.call_heading || '')}`));
|
|
151
|
+
out();
|
|
152
|
+
out(ui.table(callColumns,
|
|
153
|
+
// Read by the column names the server sent, so a column added there
|
|
154
|
+
// appears here without this file being told about it.
|
|
155
|
+
callRows.map((r) => callColumns.map((k) => cell(r[k])))));
|
|
156
|
+
out();
|
|
157
|
+
}
|
|
158
|
+
if (p.footnote) {
|
|
159
|
+
out(ui.c.dim(` ${String(p.footnote)}`));
|
|
160
|
+
out();
|
|
161
|
+
}
|
|
162
|
+
if (reply.details_url) {
|
|
163
|
+
out(ui.c.dim(` Every figure here traces to its records: ${reply.details_url}`));
|
|
164
|
+
out();
|
|
165
|
+
}
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cmdHooks(args: string[]): Promise<number>;
|
|
@@ -0,0 +1,83 @@
|
|
|
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.cmdHooks = cmdHooks;
|
|
37
|
+
/**
|
|
38
|
+
* `comprism hooks` and `comprism hooks test` (manual 10.10).
|
|
39
|
+
*
|
|
40
|
+
* This reads the project's settings files and sends them; the service says
|
|
41
|
+
* which hooks are in force here, where each came from, and for a test which
|
|
42
|
+
* ones run, in order. This then runs exactly those and shows what they did.
|
|
43
|
+
*/
|
|
44
|
+
const ui = __importStar(require("../lib/ui"));
|
|
45
|
+
const project_ops_1 = require("../lib/project-ops");
|
|
46
|
+
const hooks_1 = require("../executor/hooks");
|
|
47
|
+
const out = (s = "") => process.stdout.write(`${s}\n`);
|
|
48
|
+
async function cmdHooks(args) {
|
|
49
|
+
const facts = await (0, project_ops_1.currentProject)();
|
|
50
|
+
const files = (0, hooks_1.hookFiles)(facts.root);
|
|
51
|
+
if (args[0] === "test") {
|
|
52
|
+
const [, when, action, target] = args;
|
|
53
|
+
if (!when) {
|
|
54
|
+
out(ui.fail("Say which moment: comprism hooks test <when> [action] [file], "
|
|
55
|
+
+ "for example comprism hooks test after edit_file calc.py"));
|
|
56
|
+
return 2;
|
|
57
|
+
}
|
|
58
|
+
if ((0, hooks_1.insideHook)()) {
|
|
59
|
+
out(ui.fail("Hooks do not run from inside a hook, so nothing was run."));
|
|
60
|
+
return 1;
|
|
61
|
+
}
|
|
62
|
+
const reply = await (0, project_ops_1.call)("hooks", "test", { files, when, action: action ?? "" }, facts);
|
|
63
|
+
if (!reply)
|
|
64
|
+
return 1;
|
|
65
|
+
const chosen = (reply.data?.hooks) ?? [];
|
|
66
|
+
const done = await (0, hooks_1.runHooks)(facts.root, chosen, {
|
|
67
|
+
when, action, args: target ? { path: target } : {},
|
|
68
|
+
});
|
|
69
|
+
for (const line of (0, hooks_1.reportLines)(done))
|
|
70
|
+
out(` ${line}`);
|
|
71
|
+
if (done.refusedBy)
|
|
72
|
+
out(` ${done.detail}`);
|
|
73
|
+
out();
|
|
74
|
+
return done.refusedBy || done.failed.length ? 1 : 0;
|
|
75
|
+
}
|
|
76
|
+
if (args[0] && args[0] !== "list") {
|
|
77
|
+
out(ui.fail(`\`hooks ${args[0]}\` is not a command. Use comprism hooks, or comprism hooks test.`));
|
|
78
|
+
return 2;
|
|
79
|
+
}
|
|
80
|
+
const reply = await (0, project_ops_1.call)("hooks", "list", { files }, facts);
|
|
81
|
+
out();
|
|
82
|
+
return reply ? 0 : 1;
|
|
83
|
+
}
|
|
@@ -1,6 +1,17 @@
|
|
|
1
|
+
import { type InstallMethod } from '../lib/self';
|
|
1
2
|
/** The header the tenant reads to decide whether this machine may be served. */
|
|
2
3
|
export declare const WORKSPACE_KEY_HEADER = "x-comprism-key";
|
|
3
4
|
export declare function envScriptPath(): string;
|
|
5
|
+
/** The PowerShell form of the setup script, for Windows (manual 1.2). */
|
|
6
|
+
export declare function envScriptPathPs(): string;
|
|
7
|
+
/**
|
|
8
|
+
* The PowerShell profiles to set up on Windows: Windows PowerShell's and,
|
|
9
|
+
* where it exists, PowerShell 7's. Windows PowerShell's is created when there
|
|
10
|
+
* is none, because it is the shell every Windows machine has.
|
|
11
|
+
*/
|
|
12
|
+
export declare function powershellProfiles(): string[];
|
|
13
|
+
/** The PowerShell script: the same rule as the POSIX one, in PowerShell. */
|
|
14
|
+
export declare function envScriptPs(baseUrl: string | null, workspaceKey?: string | null): string;
|
|
4
15
|
export declare function pidFilePath(): string;
|
|
5
16
|
export declare function proxyLogPath(): string;
|
|
6
17
|
/** Every profile we should add the line to, that actually exists. */
|
|
@@ -118,9 +129,41 @@ export declare function install(options?: {
|
|
|
118
129
|
binary?: LaunchCommand;
|
|
119
130
|
workspaceKey?: string | null;
|
|
120
131
|
}): InstallReport;
|
|
121
|
-
/**
|
|
132
|
+
/**
|
|
133
|
+
* Rewrite the generated script from the connection as it now stands.
|
|
134
|
+
*
|
|
135
|
+
* Sign in and sign out call this. Before, only the install wrote it, and the
|
|
136
|
+
* install runs before anybody has signed in, so the script said "not enrolled"
|
|
137
|
+
* forever and signing in never switched measurement on. After sign out it kept
|
|
138
|
+
* presenting a revoked key. Touches no shell profile: a machine that never had
|
|
139
|
+
* the line added is left exactly as it was.
|
|
140
|
+
*/
|
|
141
|
+
export declare function refreshEnvScript(): void;
|
|
142
|
+
/**
|
|
143
|
+
* What sign in calls: refresh the setup script, and on a Python install that
|
|
144
|
+
* was never set up, do the setup now (manual 1.1, Option 3).
|
|
145
|
+
*
|
|
146
|
+
* npm runs the setup as it installs and the install script runs it itself, but
|
|
147
|
+
* pip has no step of its own, so the first sign in is the first moment this
|
|
148
|
+
* tool runs on a machine that asked for it. Returns the profiles it changed,
|
|
149
|
+
* for the caller to say so. Every other install is left to `refreshEnvScript`.
|
|
150
|
+
*/
|
|
151
|
+
export declare function finishSetup(): string[];
|
|
152
|
+
/** Does this profile carry our fenced line. */
|
|
153
|
+
export declare function profileHasSetup(profile: string): boolean;
|
|
154
|
+
/**
|
|
155
|
+
* Take the line back out. An install you cannot cleanly undo is a liability.
|
|
156
|
+
*
|
|
157
|
+
* Removes only what the install wrote: the fenced profile block, the generated
|
|
158
|
+
* `env.sh` and our own Claude Desktop entry. The sign in and settings stay, and
|
|
159
|
+
* the caller says so, because deleting a live credential file is not revoking it.
|
|
160
|
+
*/
|
|
122
161
|
export declare function uninstall(): {
|
|
123
162
|
profilesCleaned: string[];
|
|
163
|
+
scriptRemoved: boolean;
|
|
164
|
+
programRemoved: boolean;
|
|
165
|
+
method: InstallMethod;
|
|
166
|
+
kept: string;
|
|
124
167
|
};
|
|
125
168
|
/**
|
|
126
169
|
* The command that starts this package's CLI, resolved from the package itself.
|
package/out/commands/install.js
CHANGED
|
@@ -5,6 +5,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.WORKSPACE_KEY_HEADER = void 0;
|
|
7
7
|
exports.envScriptPath = envScriptPath;
|
|
8
|
+
exports.envScriptPathPs = envScriptPathPs;
|
|
9
|
+
exports.powershellProfiles = powershellProfiles;
|
|
10
|
+
exports.envScriptPs = envScriptPs;
|
|
8
11
|
exports.pidFilePath = pidFilePath;
|
|
9
12
|
exports.proxyLogPath = proxyLogPath;
|
|
10
13
|
exports.shellProfiles = shellProfiles;
|
|
@@ -15,6 +18,9 @@ exports.installDesktopConnector = installDesktopConnector;
|
|
|
15
18
|
exports.probeDesktopConnector = probeDesktopConnector;
|
|
16
19
|
exports.uninstallDesktopConnector = uninstallDesktopConnector;
|
|
17
20
|
exports.install = install;
|
|
21
|
+
exports.refreshEnvScript = refreshEnvScript;
|
|
22
|
+
exports.finishSetup = finishSetup;
|
|
23
|
+
exports.profileHasSetup = profileHasSetup;
|
|
18
24
|
exports.uninstall = uninstall;
|
|
19
25
|
exports.launchShellString = launchShellString;
|
|
20
26
|
exports.proxyHealthy = proxyHealthy;
|
|
@@ -72,6 +78,7 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
72
78
|
const node_child_process_1 = require("node:child_process");
|
|
73
79
|
const config_1 = require("../lib/config");
|
|
74
80
|
const connection_1 = require("../lib/connection");
|
|
81
|
+
const self_1 = require("../lib/self");
|
|
75
82
|
/** The header the tenant reads to decide whether this machine may be served. */
|
|
76
83
|
exports.WORKSPACE_KEY_HEADER = 'x-comprism-key';
|
|
77
84
|
const MARKER_START = '# >>> comprism >>>';
|
|
@@ -79,6 +86,53 @@ const MARKER_END = '# <<< comprism <<<';
|
|
|
79
86
|
function envScriptPath() {
|
|
80
87
|
return node_path_1.default.join((0, config_1.homeDir)(), 'env.sh');
|
|
81
88
|
}
|
|
89
|
+
/** The PowerShell form of the setup script, for Windows (manual 1.2). */
|
|
90
|
+
function envScriptPathPs() {
|
|
91
|
+
return node_path_1.default.join((0, config_1.homeDir)(), 'env.ps1');
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The PowerShell profiles to set up on Windows: Windows PowerShell's and,
|
|
95
|
+
* where it exists, PowerShell 7's. Windows PowerShell's is created when there
|
|
96
|
+
* is none, because it is the shell every Windows machine has.
|
|
97
|
+
*/
|
|
98
|
+
function powershellProfiles() {
|
|
99
|
+
if (process.platform !== 'win32' && !process.env.COMPRISM_TEST_POWERSHELL)
|
|
100
|
+
return [];
|
|
101
|
+
const docs = node_path_1.default.join(process.env.USERPROFILE || node_os_1.default.homedir(), 'Documents');
|
|
102
|
+
const classic = node_path_1.default.join(docs, 'WindowsPowerShell', 'Microsoft.PowerShell_profile.ps1');
|
|
103
|
+
const modern = node_path_1.default.join(docs, 'PowerShell', 'Microsoft.PowerShell_profile.ps1');
|
|
104
|
+
const out = [classic];
|
|
105
|
+
if (node_fs_1.default.existsSync(node_path_1.default.dirname(modern)))
|
|
106
|
+
out.push(modern);
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
/** The PowerShell script: the same rule as the POSIX one, in PowerShell. */
|
|
110
|
+
function envScriptPs(baseUrl, workspaceKey) {
|
|
111
|
+
const bin = (0, self_1.standaloneBinDir)();
|
|
112
|
+
const pathLine = node_fs_1.default.existsSync(node_path_1.default.join(bin, 'comprism.exe'))
|
|
113
|
+
? `if ($env:Path -notlike '*${bin.replace(/'/g, "''")}*') { $env:Path = '${bin.replace(/'/g, "''")};' + $env:Path }\n`
|
|
114
|
+
: '';
|
|
115
|
+
const head = '# Managed by comprism. Edit nothing here; run `comprism install` to regenerate.\n' + pathLine;
|
|
116
|
+
if (!baseUrl || !workspaceKey) {
|
|
117
|
+
return `${head}#\n# This machine is not signed in, so this sets nothing and your tools are untouched.\n`;
|
|
118
|
+
}
|
|
119
|
+
const q = (v) => v.replace(/'/g, "''");
|
|
120
|
+
return `${head}#
|
|
121
|
+
# Points AI clients at your CompletionPrism tenant so your work is measured.
|
|
122
|
+
# If the tenant is not reachable, this sets NOTHING and your tools talk
|
|
123
|
+
# straight to the provider exactly as they would without us.
|
|
124
|
+
try {
|
|
125
|
+
Invoke-WebRequest -Uri '${q(baseUrl)}/health' -UseBasicParsing -TimeoutSec 2 | Out-Null
|
|
126
|
+
$env:ANTHROPIC_BASE_URL = '${q(baseUrl)}'
|
|
127
|
+
$__comprism = '${exports.WORKSPACE_KEY_HEADER}: ${q(workspaceKey)}'
|
|
128
|
+
if (-not $env:ANTHROPIC_CUSTOM_HEADERS) { $env:ANTHROPIC_CUSTOM_HEADERS = $__comprism }
|
|
129
|
+
elseif ($env:ANTHROPIC_CUSTOM_HEADERS -notlike '*${exports.WORKSPACE_KEY_HEADER}*') {
|
|
130
|
+
$env:ANTHROPIC_CUSTOM_HEADERS = "$($env:ANTHROPIC_CUSTOM_HEADERS)\`n$__comprism"
|
|
131
|
+
}
|
|
132
|
+
Remove-Variable __comprism -ErrorAction SilentlyContinue
|
|
133
|
+
} catch { }
|
|
134
|
+
`;
|
|
135
|
+
}
|
|
82
136
|
function pidFilePath() {
|
|
83
137
|
return node_path_1.default.join((0, config_1.homeDir)(), 'proxy.pid');
|
|
84
138
|
}
|
|
@@ -92,6 +146,11 @@ function shellProfiles() {
|
|
|
92
146
|
const found = candidates
|
|
93
147
|
.map((f) => node_path_1.default.join(home, f))
|
|
94
148
|
.filter((p) => node_fs_1.default.existsSync(p));
|
|
149
|
+
// Windows shells read none of these, and the generated script is POSIX. An
|
|
150
|
+
// existing Git Bash profile is still set up; nothing is created, because a
|
|
151
|
+
// new .zshrc on Windows is litter that measures nothing.
|
|
152
|
+
if (process.platform === 'win32')
|
|
153
|
+
return found;
|
|
95
154
|
// A machine with none of them: create the one matching the login shell rather
|
|
96
155
|
// than silently doing nothing.
|
|
97
156
|
if (found.length === 0) {
|
|
@@ -135,6 +194,21 @@ function tenantBaseUrl(explicit) {
|
|
|
135
194
|
* the day there is an OpenAI-shaped endpoint to point at.
|
|
136
195
|
*/
|
|
137
196
|
function envScript(baseUrl, workspaceKey) {
|
|
197
|
+
return envScriptBody(baseUrl, workspaceKey).replace(/^#!\/bin\/sh\n/, `#!/bin/sh\n${pathBlock()}`);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* The install script puts the program in `~/.comprism/bin` (manual 1.1,
|
|
201
|
+
* Option 1), so the setup script puts that folder on the PATH, once. It is
|
|
202
|
+
* written only when the program is there, so an npm or Python install is
|
|
203
|
+
* unchanged.
|
|
204
|
+
*/
|
|
205
|
+
function pathBlock() {
|
|
206
|
+
const bin = (0, self_1.standaloneBinDir)();
|
|
207
|
+
if (!node_fs_1.default.existsSync(node_path_1.default.join(bin, 'comprism')))
|
|
208
|
+
return '';
|
|
209
|
+
return `case ":$PATH:" in *":${bin}:"*) : ;; *) PATH="${bin}:$PATH"; export PATH ;; esac\n`;
|
|
210
|
+
}
|
|
211
|
+
function envScriptBody(baseUrl, workspaceKey) {
|
|
138
212
|
if (!baseUrl) {
|
|
139
213
|
return `#!/bin/sh
|
|
140
214
|
# Managed by comprism. Edit nothing here; run \`comprism install\` to regenerate.
|
|
@@ -388,6 +462,27 @@ function install(options = {}) {
|
|
|
388
462
|
const line = `${MARKER_START}\n[ -f "${scriptPath}" ] && . "${scriptPath}"\n${MARKER_END}`;
|
|
389
463
|
const touched = [];
|
|
390
464
|
const already = [];
|
|
465
|
+
// Windows: the same fenced line, in PowerShell's own profile (manual 1.2).
|
|
466
|
+
const psProfiles = powershellProfiles();
|
|
467
|
+
if (psProfiles.length) {
|
|
468
|
+
const ps = envScriptPathPs();
|
|
469
|
+
node_fs_1.default.writeFileSync(ps, envScriptPs(baseUrl, workspaceKey));
|
|
470
|
+
const psLine = `${MARKER_START}\nif (Test-Path '${ps.replace(/'/g, "''")}') { . '${ps.replace(/'/g, "''")}' }\n${MARKER_END}`;
|
|
471
|
+
for (const profile of psProfiles) {
|
|
472
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(profile), { recursive: true });
|
|
473
|
+
const contents = node_fs_1.default.existsSync(profile) ? node_fs_1.default.readFileSync(profile, 'utf8') : '';
|
|
474
|
+
if (contents.includes(MARKER_START)) {
|
|
475
|
+
const next = contents.replace(new RegExp(`${MARKER_START}[\\s\\S]*?${MARKER_END}`), psLine);
|
|
476
|
+
if (next !== contents)
|
|
477
|
+
node_fs_1.default.writeFileSync(profile, next);
|
|
478
|
+
already.push(profile);
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
node_fs_1.default.appendFileSync(profile, `\n${psLine}\n`);
|
|
482
|
+
touched.push(profile);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
391
486
|
for (const profile of shellProfiles()) {
|
|
392
487
|
let contents = '';
|
|
393
488
|
try {
|
|
@@ -419,10 +514,60 @@ function install(options = {}) {
|
|
|
419
514
|
desktop: installDesktopConnector(resolveDesktopBinary()),
|
|
420
515
|
};
|
|
421
516
|
}
|
|
422
|
-
/**
|
|
517
|
+
/**
|
|
518
|
+
* Rewrite the generated script from the connection as it now stands.
|
|
519
|
+
*
|
|
520
|
+
* Sign in and sign out call this. Before, only the install wrote it, and the
|
|
521
|
+
* install runs before anybody has signed in, so the script said "not enrolled"
|
|
522
|
+
* forever and signing in never switched measurement on. After sign out it kept
|
|
523
|
+
* presenting a revoked key. Touches no shell profile: a machine that never had
|
|
524
|
+
* the line added is left exactly as it was.
|
|
525
|
+
*/
|
|
526
|
+
function refreshEnvScript() {
|
|
527
|
+
const script = envScriptPath();
|
|
528
|
+
if (!node_fs_1.default.existsSync(script))
|
|
529
|
+
return;
|
|
530
|
+
node_fs_1.default.writeFileSync(script, envScript(tenantBaseUrl(), (0, connection_1.readConnection)()?.workspaceKey ?? null), { mode: 0o700 });
|
|
531
|
+
const ps = envScriptPathPs();
|
|
532
|
+
if (node_fs_1.default.existsSync(ps))
|
|
533
|
+
node_fs_1.default.writeFileSync(ps, envScriptPs(tenantBaseUrl(), (0, connection_1.readConnection)()?.workspaceKey ?? null));
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* What sign in calls: refresh the setup script, and on a Python install that
|
|
537
|
+
* was never set up, do the setup now (manual 1.1, Option 3).
|
|
538
|
+
*
|
|
539
|
+
* npm runs the setup as it installs and the install script runs it itself, but
|
|
540
|
+
* pip has no step of its own, so the first sign in is the first moment this
|
|
541
|
+
* tool runs on a machine that asked for it. Returns the profiles it changed,
|
|
542
|
+
* for the caller to say so. Every other install is left to `refreshEnvScript`.
|
|
543
|
+
*/
|
|
544
|
+
function finishSetup() {
|
|
545
|
+
if ((0, self_1.installMethod)() === 'python' && !node_fs_1.default.existsSync(envScriptPath()))
|
|
546
|
+
return install().profilesTouched;
|
|
547
|
+
refreshEnvScript();
|
|
548
|
+
return [];
|
|
549
|
+
}
|
|
550
|
+
/** Does this profile carry our fenced line. */
|
|
551
|
+
function profileHasSetup(profile) {
|
|
552
|
+
try {
|
|
553
|
+
return node_fs_1.default.readFileSync(profile, 'utf8').includes(MARKER_START);
|
|
554
|
+
}
|
|
555
|
+
catch {
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Take the line back out. An install you cannot cleanly undo is a liability.
|
|
561
|
+
*
|
|
562
|
+
* Removes only what the install wrote: the fenced profile block, the generated
|
|
563
|
+
* `env.sh` and our own Claude Desktop entry. The sign in and settings stay, and
|
|
564
|
+
* the caller says so, because deleting a live credential file is not revoking it.
|
|
565
|
+
*/
|
|
423
566
|
function uninstall() {
|
|
567
|
+
// Read first: once the program's folder is gone it can no longer be told.
|
|
568
|
+
const method = (0, self_1.installMethod)();
|
|
424
569
|
const cleaned = [];
|
|
425
|
-
for (const profile of shellProfiles()) {
|
|
570
|
+
for (const profile of [...powershellProfiles(), ...shellProfiles()]) {
|
|
426
571
|
let contents = '';
|
|
427
572
|
try {
|
|
428
573
|
contents = node_fs_1.default.readFileSync(profile, 'utf8');
|
|
@@ -439,7 +584,54 @@ function uninstall() {
|
|
|
439
584
|
cleaned.push(profile);
|
|
440
585
|
}
|
|
441
586
|
uninstallDesktopConnector();
|
|
442
|
-
|
|
587
|
+
const script = envScriptPath();
|
|
588
|
+
const scriptRemoved = node_fs_1.default.existsSync(script) || node_fs_1.default.existsSync(envScriptPathPs());
|
|
589
|
+
node_fs_1.default.rmSync(script, { force: true });
|
|
590
|
+
node_fs_1.default.rmSync(envScriptPathPs(), { force: true });
|
|
591
|
+
const programRemoved = method === 'script' ? removeStandaloneProgram() : false;
|
|
592
|
+
return { profilesCleaned: cleaned, scriptRemoved, programRemoved, method, kept: (0, config_1.homeDir)() };
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* The install script's program and its folder, and on Windows the PATH entry
|
|
596
|
+
* the installer added (manual 1.8). Only `~/.comprism/bin`: the sign in and
|
|
597
|
+
* settings beside it stay.
|
|
598
|
+
*
|
|
599
|
+
* A running program can be deleted on macOS and Linux. Windows refuses to
|
|
600
|
+
* delete it but allows it to be moved, so there the program is moved to the
|
|
601
|
+
* temporary folder and the folder removed at once; the moved copy is deleted a
|
|
602
|
+
* moment after this process exits. Everything is done before returning,
|
|
603
|
+
* rather than left to a background process: a background process can be
|
|
604
|
+
* stopped with its parent, and the first version, which worked that way, left
|
|
605
|
+
* the folder and the PATH entry behind on Windows Server.
|
|
606
|
+
*/
|
|
607
|
+
function removeStandaloneProgram() {
|
|
608
|
+
const bin = (0, self_1.standaloneBinDir)();
|
|
609
|
+
if (process.platform !== 'win32') {
|
|
610
|
+
node_fs_1.default.rmSync(bin, { recursive: true, force: true });
|
|
611
|
+
return true;
|
|
612
|
+
}
|
|
613
|
+
const q = (v) => v.replace(/'/g, "''");
|
|
614
|
+
(0, node_child_process_1.spawnSync)('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command',
|
|
615
|
+
`$b='${q(bin)}'; $p=[Environment]::GetEnvironmentVariable('Path','User'); `
|
|
616
|
+
+ `if ($p) { [Environment]::SetEnvironmentVariable('Path', (($p -split ';') | Where-Object { $_ -and $_ -ne $b }) -join ';', 'User') }`,
|
|
617
|
+
], { stdio: 'ignore', windowsHide: true });
|
|
618
|
+
const parked = node_path_1.default.join(node_os_1.default.tmpdir(), `comprism-removed-${process.pid}.exe`);
|
|
619
|
+
try {
|
|
620
|
+
node_fs_1.default.renameSync(node_path_1.default.join(bin, 'comprism.exe'), parked);
|
|
621
|
+
}
|
|
622
|
+
catch {
|
|
623
|
+
// Not there, or on another drive: the folder removal below says which.
|
|
624
|
+
}
|
|
625
|
+
try {
|
|
626
|
+
node_fs_1.default.rmSync(bin, { recursive: true, force: true });
|
|
627
|
+
}
|
|
628
|
+
catch {
|
|
629
|
+
return false;
|
|
630
|
+
}
|
|
631
|
+
(0, node_child_process_1.spawn)('cmd.exe', ['/d', '/c', `ping -n 3 127.0.0.1 >nul & del /f /q "${parked}"`], {
|
|
632
|
+
detached: true, stdio: 'ignore', windowsHide: true,
|
|
633
|
+
}).unref();
|
|
634
|
+
return true;
|
|
443
635
|
}
|
|
444
636
|
/** The shell form, quoted, for the profile script that must be a shell string. */
|
|
445
637
|
function launchShellString(launch) {
|
|
@@ -486,6 +678,8 @@ function resolveBinary() {
|
|
|
486
678
|
* genuinely better because it survives the package moving.
|
|
487
679
|
*/
|
|
488
680
|
function resolveDesktopBinary() {
|
|
681
|
+
if ((0, self_1.isStandalone)())
|
|
682
|
+
return { command: process.execPath, args: [] };
|
|
489
683
|
const compiled = node_path_1.default.join(__dirname, 'cli.js');
|
|
490
684
|
if (!node_fs_1.default.existsSync(compiled))
|
|
491
685
|
return resolveBinary();
|
|
@@ -514,7 +708,7 @@ async function startDaemon(port) {
|
|
|
514
708
|
return true;
|
|
515
709
|
(0, config_1.ensureHome)();
|
|
516
710
|
const out = node_fs_1.default.openSync(proxyLogPath(), 'a');
|
|
517
|
-
const child = (0, node_child_process_1.spawn)(
|
|
711
|
+
const child = (0, node_child_process_1.spawn)(...(0, self_1.selfLaunch)(['proxy', '--port', String(port)]), {
|
|
518
712
|
detached: true,
|
|
519
713
|
stdio: ['ignore', out, out],
|
|
520
714
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cmdInstructions(args: string[]): Promise<number>;
|