neuvikon 0.2.2 → 0.2.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 +2 -1
- package/dist/index.mjs +70 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ as being worked on, by whom, with which tool.
|
|
|
38
38
|
| `neuvikon start --ticket EWWQ-1` | Begin tracking. `--tool claude-code\|codex\|cursor\|other` |
|
|
39
39
|
| `neuvikon stop` | Stop tracking |
|
|
40
40
|
| `neuvikon status` | Login, project setup, current ticket |
|
|
41
|
-
| `neuvikon logout` |
|
|
41
|
+
| `neuvikon logout` | Revoke this machine's token and forget it |
|
|
42
42
|
|
|
43
43
|
`start` also accepts the ticket on stdin, and pulls the reference out of it:
|
|
44
44
|
|
|
@@ -71,6 +71,7 @@ always exits 0 — a token count is never worth interrupting someone's work.
|
|
|
71
71
|
|---|---|
|
|
72
72
|
| `~/.neuvikon/config.json` | Token and deployment URL, `0600` |
|
|
73
73
|
| `.neuvikon-session` | Which ticket this directory is tracking. Git-ignored by `init` |
|
|
74
|
+
| `--dev` | Target the development deployment instead of production |
|
|
74
75
|
| `NEUVIKON_URL` | Point at a self-hosted deployment. `--url` does the same per command |
|
|
75
76
|
| `NEUVIKON_TOKEN` | Use this token instead of logging in — for CI and anywhere a browser cannot open |
|
|
76
77
|
|
package/dist/index.mjs
CHANGED
|
@@ -142,14 +142,35 @@ function isInstalled(root) {
|
|
|
142
142
|
//#endregion
|
|
143
143
|
//#region src/config.ts
|
|
144
144
|
/**
|
|
145
|
-
* The
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
* installed copy of this CLI hardcodes whatever ships here, and a dev
|
|
150
|
-
* deployment can be reset out from under them.
|
|
145
|
+
* The two deployments this CLI knows about. The browser origin is kept here
|
|
146
|
+
* rather than read back from the server, because the API answers on a Convex
|
|
147
|
+
* domain while approval happens on the web app's — one is not derivable from
|
|
148
|
+
* the other, and guessing wrong sends people to a page that does not exist.
|
|
151
149
|
*/
|
|
152
|
-
const
|
|
150
|
+
const DEPLOYMENTS = {
|
|
151
|
+
production: {
|
|
152
|
+
api: "https://flexible-bird-801.eu-west-1.convex.site",
|
|
153
|
+
web: "https://www.neuvikon.space"
|
|
154
|
+
},
|
|
155
|
+
development: {
|
|
156
|
+
api: "https://calm-sturgeon-831.eu-west-1.convex.site",
|
|
157
|
+
web: "http://localhost:3000"
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* `--dev` targets the development deployment; NEUVIKON_URL overrides the API
|
|
162
|
+
* for a self-hosted install, in which case the web origin is unknowable and
|
|
163
|
+
* falls back to the matching default.
|
|
164
|
+
*/
|
|
165
|
+
function resolveDeployment(flags) {
|
|
166
|
+
const base = flags.has("dev") ? DEPLOYMENTS.development : DEPLOYMENTS.production;
|
|
167
|
+
const override = flags.get("url") || process.env.NEUVIKON_URL;
|
|
168
|
+
return override ? {
|
|
169
|
+
api: override.trim().replace(/\/+$/, ""),
|
|
170
|
+
web: base.web
|
|
171
|
+
} : base;
|
|
172
|
+
}
|
|
173
|
+
const DEFAULT_SITE_URL = process.env.NEUVIKON_URL ?? DEPLOYMENTS.production.api;
|
|
153
174
|
const CONFIG_DIR = join(homedir(), ".neuvikon");
|
|
154
175
|
const CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
155
176
|
/**
|
|
@@ -317,7 +338,8 @@ function sleep(ms) {
|
|
|
317
338
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
318
339
|
}
|
|
319
340
|
async function login(flags) {
|
|
320
|
-
const
|
|
341
|
+
const deployment = resolveDeployment(flags);
|
|
342
|
+
const url = normalizeUrl(deployment.api);
|
|
321
343
|
let start;
|
|
322
344
|
try {
|
|
323
345
|
start = await post(url, "/api/cli/auth/start", {});
|
|
@@ -326,12 +348,13 @@ async function login(flags) {
|
|
|
326
348
|
if (error instanceof ApiError) fail(error.message);
|
|
327
349
|
throw error;
|
|
328
350
|
}
|
|
351
|
+
const verifyUrl = `${deployment.web}/cli?code=${encodeURIComponent(start.userCode)}`;
|
|
329
352
|
info("");
|
|
330
|
-
info(` Open ${cyan(
|
|
353
|
+
info(` Open ${cyan(verifyUrl)}`);
|
|
331
354
|
info(` and confirm this code: ${bold(start.userCode)}`);
|
|
332
355
|
info("");
|
|
333
356
|
info(dim(" Waiting for approval..."));
|
|
334
|
-
openBrowser(
|
|
357
|
+
openBrowser(verifyUrl);
|
|
335
358
|
const interval = Math.max(1e3, start.intervalMs);
|
|
336
359
|
for (;;) {
|
|
337
360
|
if (Date.now() > start.expiresAt) fail("That login request expired.", "Run `npx neuvikon login` to get a fresh code.");
|
|
@@ -366,15 +389,6 @@ async function login(flags) {
|
|
|
366
389
|
}
|
|
367
390
|
}
|
|
368
391
|
//#endregion
|
|
369
|
-
//#region src/commands/logout.ts
|
|
370
|
-
function logout() {
|
|
371
|
-
if (clearCredentials()) {
|
|
372
|
-
success("Logged out.");
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
info("You were not logged in.");
|
|
376
|
-
}
|
|
377
|
-
//#endregion
|
|
378
392
|
//#region src/session.ts
|
|
379
393
|
/**
|
|
380
394
|
* Scoped to the working directory rather than the machine: two checkouts driven
|
|
@@ -409,6 +423,42 @@ function clearSession() {
|
|
|
409
423
|
} catch {}
|
|
410
424
|
}
|
|
411
425
|
//#endregion
|
|
426
|
+
//#region src/commands/logout.ts
|
|
427
|
+
/**
|
|
428
|
+
* Deleting the local file is not enough: the token stays valid until the server
|
|
429
|
+
* is told otherwise, so a copy of it lifted from a backup or a shared machine
|
|
430
|
+
* would still be able to report. Revoking is the part that actually ends the
|
|
431
|
+
* session; forgetting it locally is bookkeeping.
|
|
432
|
+
*/
|
|
433
|
+
async function logout() {
|
|
434
|
+
const credentials = readCredentials();
|
|
435
|
+
if (!credentials) {
|
|
436
|
+
info("You were not logged in.");
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
const session = readSession();
|
|
440
|
+
if (session) {
|
|
441
|
+
await post(credentials.url, "/api/agent/events", {
|
|
442
|
+
token: credentials.token,
|
|
443
|
+
event: "stop",
|
|
444
|
+
sessionKey: session.sessionKey
|
|
445
|
+
}).catch(() => void 0);
|
|
446
|
+
clearSession();
|
|
447
|
+
}
|
|
448
|
+
let revoked = false;
|
|
449
|
+
try {
|
|
450
|
+
revoked = (await post(credentials.url, "/api/cli/auth/revoke", { token: credentials.token })).revoked;
|
|
451
|
+
} catch {}
|
|
452
|
+
if (process.env.NEUVIKON_TOKEN) warn("NEUVIKON_TOKEN is set in your environment — unset it to finish logging out.");
|
|
453
|
+
clearCredentials();
|
|
454
|
+
if (revoked) {
|
|
455
|
+
success("Logged out. That token has been revoked.");
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
success("Logged out on this machine.");
|
|
459
|
+
info(dim(" Could not reach Neuvikon to revoke the token — revoke it in settings if it leaked."));
|
|
460
|
+
}
|
|
461
|
+
//#endregion
|
|
412
462
|
//#region src/ticket.ts
|
|
413
463
|
/**
|
|
414
464
|
* Ticket refs look like `AWKN-14`. This pattern is deliberately looser than the
|
|
@@ -687,6 +737,7 @@ ${bold("Options")}
|
|
|
687
737
|
--tool <name> claude-code | codex | cursor | other
|
|
688
738
|
--transcript <path> Read token usage from an agent transcript
|
|
689
739
|
--tokens-in <n> --tokens-out <n>
|
|
740
|
+
--dev Use the development deployment
|
|
690
741
|
--url <site-url> Point at a self-hosted deployment
|
|
691
742
|
|
|
692
743
|
${dim("Paste a ticket into any coding agent after logging in — it takes it from there.")}`;
|