@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
package/out/commands/login.js
CHANGED
|
@@ -127,15 +127,147 @@ async function post(url, path, body, tenant) {
|
|
|
127
127
|
catch { /* non-JSON body */ }
|
|
128
128
|
return { status: res.status, json };
|
|
129
129
|
}
|
|
130
|
+
const DEVICE_GRANT = 'urn:ietf:params:oauth:grant-type:device_code';
|
|
131
|
+
/** The built in public client the portal accepts for this product's CLI. */
|
|
132
|
+
const CLIENT_ID = 'completionprism-cli';
|
|
133
|
+
function headersFor(tenant) {
|
|
134
|
+
return tenant ? { 'X-Tenant-ID': tenant } : {};
|
|
135
|
+
}
|
|
136
|
+
/** Whether this workspace's company signs in through its own identity provider. */
|
|
137
|
+
async function usesCompanyLogin(url, tenant) {
|
|
138
|
+
try {
|
|
139
|
+
const res = await fetch(`${url}/api/v1/auth/sso/providers`, { headers: headersFor(tenant) });
|
|
140
|
+
if (!res.ok)
|
|
141
|
+
return false;
|
|
142
|
+
const body = await res.json();
|
|
143
|
+
return (body.providers?.length ?? 0) > 0 && body.password_enabled === false;
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function openInBrowser(address) {
|
|
150
|
+
const { spawn } = require('node:child_process');
|
|
151
|
+
const [cmd, argv] = process.platform === 'darwin' ? ['open', [address]]
|
|
152
|
+
: process.platform === 'win32' ? ['cmd', ['/c', 'start', '', address]]
|
|
153
|
+
: ['xdg-open', [address]];
|
|
154
|
+
try {
|
|
155
|
+
spawn(cmd, argv, { stdio: 'ignore', detached: true }).on('error', () => { }).unref();
|
|
156
|
+
}
|
|
157
|
+
catch { /* the address is printed as well */ }
|
|
158
|
+
}
|
|
159
|
+
/** The portal's own device sign in: approve in a browser, get a token back. */
|
|
160
|
+
async function approveInBrowser(url, tenant, noBrowser) {
|
|
161
|
+
const form = (fields) => ({
|
|
162
|
+
method: 'POST',
|
|
163
|
+
headers: { ...headersFor(tenant), 'content-type': 'application/x-www-form-urlencoded' },
|
|
164
|
+
body: new URLSearchParams(fields).toString(),
|
|
165
|
+
});
|
|
166
|
+
let started;
|
|
167
|
+
try {
|
|
168
|
+
const res = await fetch(`${url}/oauth/device_authorization`, form({ client_id: CLIENT_ID, scope: 'api:read api:write' }));
|
|
169
|
+
if (!res.ok) {
|
|
170
|
+
out(ui.fail(res.status === 404 || res.status === 405
|
|
171
|
+
? 'That workspace is running a build without browser sign in.'
|
|
172
|
+
: 'The workspace would not start a browser sign in.'));
|
|
173
|
+
return '';
|
|
174
|
+
}
|
|
175
|
+
started = await res.json();
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
out(ui.fail(`Could not reach ${url}. Check your connection and try again.`));
|
|
179
|
+
return '';
|
|
180
|
+
}
|
|
181
|
+
out();
|
|
182
|
+
if (noBrowser) {
|
|
183
|
+
out(` On any device, open ${ui.c.bold(started.verification_uri)}`);
|
|
184
|
+
out(` and enter the code ${ui.c.bold(started.user_code)}`);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
out(ui.info('Your company signs in through its own login page. Opening your browser.'));
|
|
188
|
+
out(` If it does not open, go to ${started.verification_uri_complete}`);
|
|
189
|
+
out(` and confirm the code ${ui.c.bold(started.user_code)}`);
|
|
190
|
+
openInBrowser(started.verification_uri_complete);
|
|
191
|
+
}
|
|
192
|
+
out(ui.c.dim(' waiting for you to approve it there...'));
|
|
193
|
+
const deadline = Date.now() + Math.max(30, started.expires_in || 600) * 1000;
|
|
194
|
+
let wait = Math.max(1, started.interval || 5);
|
|
195
|
+
while (Date.now() < deadline) {
|
|
196
|
+
await new Promise((r) => setTimeout(r, wait * 1000));
|
|
197
|
+
let res;
|
|
198
|
+
try {
|
|
199
|
+
res = await fetch(`${url}/oauth/token`, form({ grant_type: DEVICE_GRANT, device_code: started.device_code, client_id: CLIENT_ID }));
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const body = await res.json().catch(() => ({}));
|
|
205
|
+
if (res.ok && body.access_token)
|
|
206
|
+
return body.access_token;
|
|
207
|
+
if (body.error === 'authorization_pending')
|
|
208
|
+
continue;
|
|
209
|
+
if (body.error === 'slow_down') {
|
|
210
|
+
wait += 5;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
out(ui.fail(body.error === 'access_denied' ? 'The sign in was declined in the browser.'
|
|
214
|
+
: 'That sign in was not approved.'));
|
|
215
|
+
return '';
|
|
216
|
+
}
|
|
217
|
+
out(ui.fail('That code expired before it was approved. Run comprism login again.'));
|
|
218
|
+
return '';
|
|
219
|
+
}
|
|
130
220
|
async function runLogin(args) {
|
|
131
|
-
|
|
221
|
+
// SIGN IN WITH AN INSTALL KEY (web manual 12.1). The key made on the
|
|
222
|
+
// Install and setup page is this machine's credential, so setup is the one
|
|
223
|
+
// command the page shows and nothing is typed. It needs no terminal, because
|
|
224
|
+
// the install script runs it on a pipe.
|
|
225
|
+
const keyAt = args.indexOf('--key');
|
|
226
|
+
if (keyAt >= 0)
|
|
227
|
+
return loginWithKey(args[keyAt + 1], args);
|
|
228
|
+
// Password and second factor entry require a person at a terminal. Do not
|
|
229
|
+
// open readline on a pipe: it can wait forever, or exit successfully at EOF
|
|
230
|
+
// without signing in. Check before workspace lookup or reading credentials.
|
|
231
|
+
if (process.stdin.isTTY !== true) {
|
|
232
|
+
out(ui.fail('Signing in requires an interactive terminal.'));
|
|
233
|
+
out(' Run comprism login in a terminal, then run your command again.');
|
|
234
|
+
process.exitCode = 1;
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
// Under the session's own mark when the session is about to open, so the
|
|
238
|
+
// name is not drawn twice in a row.
|
|
239
|
+
out(args.includes('--into-session') ? ` ${ui.c.bold('Sign in')}` : ui.banner('Sign in'));
|
|
132
240
|
out();
|
|
133
241
|
// Flags still work, for a script or a second machine. A person gets asked.
|
|
134
242
|
const flag = (name) => {
|
|
135
243
|
const i = args.indexOf(name);
|
|
136
244
|
return i >= 0 ? args[i + 1] : undefined;
|
|
137
245
|
};
|
|
138
|
-
|
|
246
|
+
// THE LAST PERSON, OFFERED. Signing in again is Enter: the email and the
|
|
247
|
+
// workspace they used last time, exactly as they were, so a local workspace
|
|
248
|
+
// stays local instead of being looked up again and sent to production.
|
|
249
|
+
const { readLast, saveLast, forgetLast, savedPassword, savePassword, forgetPassword, keychainAvailable } = await Promise.resolve().then(() => __importStar(require('../lib/lastlogin')));
|
|
250
|
+
if (args.includes('--forget')) {
|
|
251
|
+
forgetLast();
|
|
252
|
+
out(ui.ok('The last sign in and any saved password on this Mac are forgotten.'));
|
|
253
|
+
out();
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const last = flag('--username') || flag('--email') || flag('--workspace') ? null : readLast();
|
|
257
|
+
if (last) {
|
|
258
|
+
out(` ${ui.c.dim('last time')} ${ui.c.bold(last.email)} ${ui.c.dim('at')} ${ui.c.bold(last.workspace)}`);
|
|
259
|
+
out(` ${ui.c.dim('press Enter to sign in as them, or type another email')}`);
|
|
260
|
+
}
|
|
261
|
+
const typedEmail = flag('--username') || flag('--email') || await (0, prompt_1.askOrCancel)(' email: ');
|
|
262
|
+
// Ctrl+C is "never mind", not "yes, the last person".
|
|
263
|
+
if (typedEmail === null) {
|
|
264
|
+
out();
|
|
265
|
+
out(ui.c.dim(' sign in canceled'));
|
|
266
|
+
out();
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const username = typedEmail || last?.email || '';
|
|
270
|
+
const again = Boolean(last && username === last.email);
|
|
139
271
|
if (!username) {
|
|
140
272
|
out(ui.fail('An email address is needed.'));
|
|
141
273
|
out();
|
|
@@ -143,16 +275,21 @@ async function runLogin(args) {
|
|
|
143
275
|
return;
|
|
144
276
|
}
|
|
145
277
|
// Where this person's workspace is. Asked of the product, not of them.
|
|
146
|
-
let typedWorkspace = flag('--workspace');
|
|
278
|
+
let typedWorkspace = flag('--workspace') || (again ? last?.workspace : undefined);
|
|
147
279
|
// Captured BEFORE the email lookup can fill it in. A workspace the product
|
|
148
280
|
// worked out is not a workspace the person typed, and only the second may be
|
|
149
|
-
// a local address. See the fence in `writeConnection`.
|
|
150
|
-
|
|
281
|
+
// a local address. See the fence in `writeConnection`. The last sign in
|
|
282
|
+
// counts as typed only if it was typed then.
|
|
283
|
+
const typedByHand = Boolean(flag('--workspace')) || Boolean(again && last?.typed);
|
|
151
284
|
if (!typedWorkspace) {
|
|
152
285
|
const live = new ui.LiveIndicator();
|
|
153
286
|
live.start('finding your workspace');
|
|
154
|
-
|
|
155
|
-
|
|
287
|
+
try {
|
|
288
|
+
typedWorkspace = (await resolveWorkspace(username)) || undefined;
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
live.stop();
|
|
292
|
+
}
|
|
156
293
|
// The indicator clears its own line but leaves the cursor mid-row, so the
|
|
157
294
|
// next prompt landed after a run of blanks.
|
|
158
295
|
process.stdout.write('\r');
|
|
@@ -167,8 +304,27 @@ async function runLogin(args) {
|
|
|
167
304
|
}
|
|
168
305
|
const url = workspaceUrl(typedWorkspace);
|
|
169
306
|
const tenant = tenantFor(url, typedWorkspace, flag('--tenant'));
|
|
170
|
-
|
|
171
|
-
|
|
307
|
+
// Said before the password is asked for, so a person can see which company
|
|
308
|
+
// they are about to sign in to (manual 1.3).
|
|
309
|
+
out(` workspace: ${url}`);
|
|
310
|
+
// COMPANY LOGIN (manual 1.10). A workspace whose company signs in through
|
|
311
|
+
// its own identity provider is signed in to in a browser; this tool never
|
|
312
|
+
// sees that password. `--device` is the same thing for a machine with no
|
|
313
|
+
// browser: a short code to approve from any other device.
|
|
314
|
+
const byCompany = args.includes('--device') || args.includes('--browser')
|
|
315
|
+
|| await usesCompanyLogin(url, tenant);
|
|
316
|
+
const deviceToken = byCompany ? await approveInBrowser(url, tenant, args.includes('--device')) : '';
|
|
317
|
+
if (byCompany && !deviceToken) {
|
|
318
|
+
out();
|
|
319
|
+
process.exitCode = 1;
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
// The keychain first, when the person chose to keep it there last time.
|
|
323
|
+
const kept = byCompany ? null : savedPassword(username, typedWorkspace);
|
|
324
|
+
if (kept)
|
|
325
|
+
out(` ${ui.c.dim('password: from your Mac keychain')}`);
|
|
326
|
+
const password = byCompany ? '' : kept || await (0, prompt_1.askHidden)(' password: ');
|
|
327
|
+
if (!byCompany && !password) {
|
|
172
328
|
out(ui.fail('A password is needed.'));
|
|
173
329
|
out();
|
|
174
330
|
process.exitCode = 1;
|
|
@@ -185,7 +341,7 @@ async function runLogin(args) {
|
|
|
185
341
|
let signInMessage = '';
|
|
186
342
|
{
|
|
187
343
|
let reply = await (0, gateway_1.askAt)(url, {
|
|
188
|
-
intent: 'sign_in', prompt: username, password,
|
|
344
|
+
intent: 'sign_in', prompt: username, password, deviceToken,
|
|
189
345
|
machineLabel: `comprism on ${node_os_1.default.hostname()}`,
|
|
190
346
|
}, tenant);
|
|
191
347
|
if (reply.served === false) {
|
|
@@ -198,8 +354,9 @@ async function runLogin(args) {
|
|
|
198
354
|
}
|
|
199
355
|
if (!reply.ok) {
|
|
200
356
|
out();
|
|
201
|
-
out(ui.fail(
|
|
202
|
-
|
|
357
|
+
out(ui.fail(reply.httpStatus
|
|
358
|
+
? (reply.message || 'The service refused the sign in request.')
|
|
359
|
+
: `Could not reach ${url}. Check your connection and try again.`));
|
|
203
360
|
out();
|
|
204
361
|
process.exitCode = 1;
|
|
205
362
|
return;
|
|
@@ -210,7 +367,7 @@ async function runLogin(args) {
|
|
|
210
367
|
if (reply.sign_in?.needs_second_factor && reply.sign_in.challenge) {
|
|
211
368
|
out();
|
|
212
369
|
out(ui.info('This account uses a second step. Check your authenticator app or your email.'));
|
|
213
|
-
const code = await (0, prompt_1.
|
|
370
|
+
const code = await (0, prompt_1.askHidden)(' code from your authenticator app: ');
|
|
214
371
|
if (!code) {
|
|
215
372
|
out(ui.fail('A code is needed to finish signing in.'));
|
|
216
373
|
out();
|
|
@@ -231,6 +388,10 @@ async function runLogin(args) {
|
|
|
231
388
|
}
|
|
232
389
|
}
|
|
233
390
|
if (!reply.sign_in?.token) {
|
|
391
|
+
// A kept password that no longer works is removed, so the next sign in
|
|
392
|
+
// asks for the new one instead of failing the same way again.
|
|
393
|
+
if (kept)
|
|
394
|
+
forgetPassword(username, typedWorkspace);
|
|
234
395
|
out();
|
|
235
396
|
out(ui.fail(reply.message || 'That username and password were not accepted.'));
|
|
236
397
|
out(` ${ui.c.dim('check the workspace name too: a right password in the wrong workspace looks the same')}`);
|
|
@@ -254,31 +415,10 @@ async function runLogin(args) {
|
|
|
254
415
|
process.exitCode = 1;
|
|
255
416
|
return;
|
|
256
417
|
}
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
// The machine credential came back WITH the sign in, from the one service,
|
|
260
|
-
// which is why there is no second call here any more. An existing one is
|
|
261
|
-
// kept: signing in twice must not leave two entries in the workspace's own
|
|
262
|
-
// list of machines.
|
|
418
|
+
// A previous credential may belong to another account or workspace, or
|
|
419
|
+
// have been revoked during this sign in. Only retain the newly issued key.
|
|
263
420
|
const existing = (0, connection_1.readConnection)();
|
|
264
|
-
|
|
265
|
-
//
|
|
266
|
-
// This used to prefer whatever was already on the machine, so that signing
|
|
267
|
-
// in twice did not leave two entries in the workspace's own list of
|
|
268
|
-
// machines. The rule was right for the same person signing in again and
|
|
269
|
-
// wrong for every other case: sign in as a different account, or into a
|
|
270
|
-
// different workspace, and the machine carried on holding the OLD key. The
|
|
271
|
-
// sign in reported success, the header then said the workspace could not be
|
|
272
|
-
// reached, and nothing anywhere said that the credential belonged to
|
|
273
|
-
// somebody else. A revoked key behaved the same way.
|
|
274
|
-
//
|
|
275
|
-
// The server already ends the previous credential for this machine when it
|
|
276
|
-
// issues one, so taking the new key does not leave two live. The old one is
|
|
277
|
-
// kept only when the server issued nothing at all, which is the case where
|
|
278
|
-
// there is nothing better to hold.
|
|
279
|
-
const workspaceKey = signedIn?.machine_credential
|
|
280
|
-
|| existing?.workspaceKey
|
|
281
|
-
|| undefined;
|
|
421
|
+
const workspaceKey = signedIn?.machine_credential || undefined;
|
|
282
422
|
// `typedByHand` is the second half of the fence in `writeConnection`. It is
|
|
283
423
|
// true only when the person put the address on this command line, in this
|
|
284
424
|
// run. A workspace resolved from their email domain, or an address a server
|
|
@@ -312,16 +452,39 @@ async function runLogin(args) {
|
|
|
312
452
|
process.exitCode = 1;
|
|
313
453
|
return;
|
|
314
454
|
}
|
|
455
|
+
// So a new terminal presents the key just issued, not the last one or none.
|
|
456
|
+
for (const profile of (await Promise.resolve().then(() => __importStar(require('./install')))).finishSetup())
|
|
457
|
+
out(` Finishing setup: added one line to ${profile}`);
|
|
458
|
+
// Remembered for next time: who, and where, exactly as used now.
|
|
459
|
+
saveLast({ email: username, workspace: typedWorkspace, typed: typedByHand });
|
|
460
|
+
if (!byCompany && !kept && password && keychainAvailable()) {
|
|
461
|
+
const keep = (await (0, prompt_1.ask)(' keep your password in your Mac keychain, so next time is just Enter? (y/n) '))
|
|
462
|
+
.trim().toLowerCase();
|
|
463
|
+
if (keep === 'y' || keep === 'yes') {
|
|
464
|
+
out(savePassword(username, typedWorkspace, password)
|
|
465
|
+
? ` ${ui.c.dim('kept in your keychain; remove it with')} ${ui.c.bold('comprism login --forget')}`
|
|
466
|
+
: ` ${ui.c.dim('the keychain would not take it; you will be asked next time')}`);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
315
469
|
out();
|
|
316
|
-
|
|
470
|
+
// Where this machine will actually talk to: the address typed, when one was.
|
|
471
|
+
const where = (typedByHand && url) || signedIn?.workspace_url || url;
|
|
472
|
+
const onLaptop = /localhost|127\.0\.0\.1/.test(where);
|
|
473
|
+
out(ui.ok(`Signed in as ${ui.c.bold(username)} ${onLaptop
|
|
474
|
+
? `on this laptop (${ui.c.bold(where.replace(/^https?:\/\//, ''))})`
|
|
475
|
+
: `at ${ui.c.bold(where)}`}.`));
|
|
317
476
|
if (workspaceKey) {
|
|
318
|
-
out(` ${ui.c.dim('
|
|
477
|
+
out(` ${ui.c.dim('This machine is enrolled and can be revoked from Administration.')}`);
|
|
319
478
|
// Straight into the welcome screen, so the first thing after signing in is
|
|
320
479
|
// where they are rather than another instruction.
|
|
321
|
-
|
|
322
|
-
|
|
480
|
+
// Unless the session is about to open, which draws it itself.
|
|
481
|
+
if (!args.includes('--into-session')) {
|
|
482
|
+
const { showWelcome } = await Promise.resolve().then(() => __importStar(require('./welcome')));
|
|
483
|
+
await showWelcome();
|
|
484
|
+
}
|
|
323
485
|
}
|
|
324
486
|
else {
|
|
487
|
+
process.exitCode = 1;
|
|
325
488
|
out(ui.warn('Signed in, but this machine could not be enrolled.'));
|
|
326
489
|
// The workspace's OWN reason, carried back with the sign in. It used to
|
|
327
490
|
// come from a local variable set by a separate call, which meant a blank
|
|
@@ -331,6 +494,48 @@ async function runLogin(args) {
|
|
|
331
494
|
}
|
|
332
495
|
out();
|
|
333
496
|
}
|
|
497
|
+
/** Enrol this machine with an install key: checked with the service, then kept. */
|
|
498
|
+
async function loginWithKey(key, args) {
|
|
499
|
+
const flag = (name) => {
|
|
500
|
+
const i = args.indexOf(name);
|
|
501
|
+
return i >= 0 ? args[i + 1] : undefined;
|
|
502
|
+
};
|
|
503
|
+
const typed = flag('--workspace');
|
|
504
|
+
if (!key || !typed) {
|
|
505
|
+
out(ui.fail('Signing in with a key needs the key and the workspace: comprism login --key <key> --workspace <address>'));
|
|
506
|
+
process.exitCode = 1;
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
const url = workspaceUrl(typed);
|
|
510
|
+
const tenant = tenantFor(url, typed, flag('--tenant'));
|
|
511
|
+
const { ask: askWith } = await Promise.resolve().then(() => __importStar(require('../lib/gateway')));
|
|
512
|
+
const existing = (0, connection_1.readConnection)();
|
|
513
|
+
try {
|
|
514
|
+
(0, connection_1.writeConnection)({
|
|
515
|
+
url, token: '', workspaceKey: key, ...(tenant ? { tenant } : {}),
|
|
516
|
+
everyMinutes: existing?.everyMinutes ?? 5,
|
|
517
|
+
connectedAt: new Date().toISOString(),
|
|
518
|
+
}, true);
|
|
519
|
+
}
|
|
520
|
+
catch (err) {
|
|
521
|
+
out(ui.fail(err instanceof Error ? err.message : 'the workspace address was refused'));
|
|
522
|
+
process.exitCode = 1;
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
const reply = await askWith({ intent: 'catalog', prompt: '' });
|
|
526
|
+
if (!reply.ok) {
|
|
527
|
+
if (existing)
|
|
528
|
+
(0, connection_1.writeConnection)(existing, true);
|
|
529
|
+
out(ui.fail(reply.message || `That key was not accepted by ${url}.`));
|
|
530
|
+
out(` ${ui.c.dim('make a new one on the Install and setup page')}`);
|
|
531
|
+
process.exitCode = 1;
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
for (const profile of (await Promise.resolve().then(() => __importStar(require('./install')))).finishSetup())
|
|
535
|
+
out(` Finishing setup: added one line to ${profile}`);
|
|
536
|
+
out(ui.ok(`This machine is enrolled at ${ui.c.bold(url)}.`));
|
|
537
|
+
out(` ${ui.c.dim('It can be revoked from Developers, Install and setup.')}`);
|
|
538
|
+
}
|
|
334
539
|
/**
|
|
335
540
|
* Which workspace an email belongs to.
|
|
336
541
|
*
|
|
@@ -359,8 +564,9 @@ async function resolveWorkspace(email) {
|
|
|
359
564
|
// because a person looking for their workspace does not have one yet.
|
|
360
565
|
const root = `https://${WORKSPACE_DOMAIN}`;
|
|
361
566
|
const reply = await (0, gateway_1.askAt)(root, { intent: 'resolve_workspace', prompt: email });
|
|
362
|
-
if (!reply.ok || reply.served === false)
|
|
363
|
-
|
|
567
|
+
if (!reply.ok || reply.served === false || reply.complete === false) {
|
|
568
|
+
throw new Error(reply.message || 'Your workspace could not be looked up. Try again later.');
|
|
569
|
+
}
|
|
364
570
|
const url = reply.session?.workspace_url;
|
|
365
571
|
if (!url)
|
|
366
572
|
return null;
|
|
@@ -0,0 +1,113 @@
|
|
|
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.addNote = addNote;
|
|
37
|
+
exports.cmdMemory = cmdMemory;
|
|
38
|
+
/**
|
|
39
|
+
* `comprism memory` (manual 2.9): this project's notes. They are files under
|
|
40
|
+
* `.completionprism/memory/`; this command lists them, and adds, removes and
|
|
41
|
+
* opens them as the service says. Which notes are used, numbering and every
|
|
42
|
+
* word shown are the service's.
|
|
43
|
+
*/
|
|
44
|
+
const fs = __importStar(require("node:fs"));
|
|
45
|
+
const path = __importStar(require("node:path"));
|
|
46
|
+
const project_ops_1 = require("../lib/project-ops");
|
|
47
|
+
const ui = __importStar(require("../lib/ui"));
|
|
48
|
+
const out = (s = "") => process.stdout.write(s + "\n");
|
|
49
|
+
const FOLDER = ".completionprism/memory";
|
|
50
|
+
const SETTINGS_FILES = [".completionprism/settings.local.json", ".completionprism/settings.json"];
|
|
51
|
+
function notes(root) {
|
|
52
|
+
let names = [];
|
|
53
|
+
try {
|
|
54
|
+
names = fs.readdirSync(path.join(root, FOLDER)).filter((n) => n.endsWith(".md")).sort();
|
|
55
|
+
}
|
|
56
|
+
catch { /* none yet */ }
|
|
57
|
+
return names
|
|
58
|
+
.map((n) => ({ file: `${FOLDER}/${n}`, text: (0, project_ops_1.readInside)(root, `${FOLDER}/${n}`) ?? "" }))
|
|
59
|
+
.filter((n) => n.text.trim());
|
|
60
|
+
}
|
|
61
|
+
/** Carry out what the service said to write or delete, inside the project. */
|
|
62
|
+
function apply(facts, data) {
|
|
63
|
+
const write = data?.write;
|
|
64
|
+
if (write?.key)
|
|
65
|
+
(0, project_ops_1.writeSetting)(facts.root, write.file, write.key, write.value);
|
|
66
|
+
else if (write?.file && typeof write.content === "string") {
|
|
67
|
+
const full = path.join(facts.root, write.file);
|
|
68
|
+
fs.mkdirSync(path.dirname(full), { recursive: true });
|
|
69
|
+
fs.writeFileSync(full, write.content);
|
|
70
|
+
}
|
|
71
|
+
const gone = data?.delete;
|
|
72
|
+
if (typeof gone === "string" && gone.startsWith(`${FOLDER}/`))
|
|
73
|
+
fs.rmSync(path.join(facts.root, gone), { force: true });
|
|
74
|
+
}
|
|
75
|
+
/** `comprism memory add` and the session word `remember`. */
|
|
76
|
+
async function addNote(text) {
|
|
77
|
+
const facts = await (0, project_ops_1.currentProject)();
|
|
78
|
+
const reply = await (0, project_ops_1.call)("memory", "add", { text }, facts);
|
|
79
|
+
if (!reply)
|
|
80
|
+
return 1;
|
|
81
|
+
apply(facts, reply.data);
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
async function cmdMemory(args) {
|
|
85
|
+
const [verb, ...rest] = args;
|
|
86
|
+
const facts = await (0, project_ops_1.currentProject)();
|
|
87
|
+
const base = { notes: notes(facts.root), files: (0, project_ops_1.readSome)(facts.root, SETTINGS_FILES) };
|
|
88
|
+
if (verb === "add")
|
|
89
|
+
return addNote(rest.join(" "));
|
|
90
|
+
if (verb === "edit") {
|
|
91
|
+
const files = base.notes.map((n) => path.join(facts.root, n.file));
|
|
92
|
+
if (!files.length) {
|
|
93
|
+
out(ui.info('No notes yet. Add one with: comprism memory add "<text>"'));
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
return (await (0, project_ops_1.openEditor)(files)) ? 0 : 1;
|
|
97
|
+
}
|
|
98
|
+
if (verb === "forget" || verb === "on" || verb === "off") {
|
|
99
|
+
const reply = await (0, project_ops_1.call)("memory", verb, { ...base, number: rest[0] }, facts);
|
|
100
|
+
if (!reply)
|
|
101
|
+
return 1;
|
|
102
|
+
apply(facts, reply.data);
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
if (verb) {
|
|
106
|
+
out(ui.fail(`\`memory ${verb}\` is not a command. Use add, edit, forget N, on or off.`));
|
|
107
|
+
return 2;
|
|
108
|
+
}
|
|
109
|
+
out();
|
|
110
|
+
const reply = await (0, project_ops_1.call)("memory", "list", base, facts);
|
|
111
|
+
out();
|
|
112
|
+
return reply ? 0 : 1;
|
|
113
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cmdPermissions(args: string[]): Promise<number>;
|
|
@@ -0,0 +1,94 @@
|
|
|
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.cmdPermissions = cmdPermissions;
|
|
37
|
+
/**
|
|
38
|
+
* `comprism permissions` and `comprism permissions why <action> "<target>"`
|
|
39
|
+
* (manual 4.14).
|
|
40
|
+
*
|
|
41
|
+
* This reads the project's settings files and sends their text; the service
|
|
42
|
+
* decides and writes every line printed, with the same decision the agent's
|
|
43
|
+
* jobs use, so `why` and a real job can never disagree.
|
|
44
|
+
*/
|
|
45
|
+
const fs = __importStar(require("node:fs"));
|
|
46
|
+
const path = __importStar(require("node:path"));
|
|
47
|
+
const gateway_1 = require("../lib/gateway");
|
|
48
|
+
const ui = __importStar(require("../lib/ui"));
|
|
49
|
+
const sessions_1 = require("../lib/sessions");
|
|
50
|
+
/** The settings files that can hold rules, relative to the project root. The
|
|
51
|
+
* service decides which of them count and how. */
|
|
52
|
+
const RULE_FILES = [
|
|
53
|
+
'.completionprism/settings.json', '.completionprism/settings.local.json',
|
|
54
|
+
'.comprism/settings.json', '.comprism/settings.local.json',
|
|
55
|
+
];
|
|
56
|
+
const USAGE = 'Usage: comprism permissions, or comprism permissions why <action> "<target>"';
|
|
57
|
+
async function cmdPermissions(args) {
|
|
58
|
+
const [op, action, ...rest] = args;
|
|
59
|
+
if (op && op !== 'why') {
|
|
60
|
+
process.stderr.write(` ${USAGE}\n`);
|
|
61
|
+
return 2;
|
|
62
|
+
}
|
|
63
|
+
if (op === 'why' && (!action || rest.length === 0)) {
|
|
64
|
+
process.stderr.write(` ${USAGE}\n`);
|
|
65
|
+
return 2;
|
|
66
|
+
}
|
|
67
|
+
const { resolveProject, asWire } = await Promise.resolve().then(() => __importStar(require('../lib/project')));
|
|
68
|
+
const { currentWorkspace } = await Promise.resolve().then(() => __importStar(require('./settings')));
|
|
69
|
+
const facts = resolveProject(currentWorkspace().path, []);
|
|
70
|
+
const files = {};
|
|
71
|
+
for (const rel of RULE_FILES) {
|
|
72
|
+
try {
|
|
73
|
+
files[rel] = fs.readFileSync(path.join(facts.root, rel), 'utf8');
|
|
74
|
+
}
|
|
75
|
+
catch { /* none */ }
|
|
76
|
+
}
|
|
77
|
+
const reply = await (0, gateway_1.ask)({
|
|
78
|
+
intent: 'permissions',
|
|
79
|
+
prompt: '',
|
|
80
|
+
project: asWire(facts),
|
|
81
|
+
permissionsPayload: {
|
|
82
|
+
op: op === 'why' ? 'why' : 'list', action, target: rest.join(' '), files,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
const lines = reply.permissions?.lines;
|
|
86
|
+
if (!reply.ok || !Array.isArray(lines)) {
|
|
87
|
+
process.stderr.write(ui.fail(reply.served === false
|
|
88
|
+
? 'Your workspace is running a build without permission answers yet.'
|
|
89
|
+
: (0, sessions_1.failure)(reply)) + '\n');
|
|
90
|
+
return (0, sessions_1.exitFor)(reply);
|
|
91
|
+
}
|
|
92
|
+
process.stdout.write(`${lines.join('\n')}\n`);
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Every text file in the folder, by path inside it. Never runs anything. */
|
|
2
|
+
export declare function readFolder(dir: string): Record<string, string>;
|
|
3
|
+
export declare const PLUGINS_HELP = " comprism plugins installed plugins and their state\n comprism plugins install <name|folder> install from your company's catalog or a folder\n --scope project|personal skip the question\n comprism plugins enable <name> comprism plugins disable <name>\n comprism plugins update <name> shows what changes, then asks\n comprism plugins remove <name>";
|
|
4
|
+
export declare function cmdPlugins(args: string[]): Promise<number>;
|