pinsay-cli 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +12 -2
  2. package/dist/cli.js +188 -246
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -19,7 +19,7 @@ npx pinsay-cli init
19
19
  |---|---|
20
20
  | `npx pinsay-cli init` | Set up this repo (sign in, project, skills; Chrome extension by default) |
21
21
  | `npx pinsay-cli embed` | Put the widget in your app's code |
22
- | `npx pinsay-cli login` / `whoami` / `logout` | Sign in; the key is saved in this repo (`--global`: this machine); check / clear credentials |
22
+ | `npx pinsay-cli login` / `whoami` / `logout` | Sign in; the key is saved in this repo; check (account, workspace) / clear credentials |
23
23
  | `npx pinsay-cli remove` | Remove everything PinSay added to this repo |
24
24
  | `npx pinsay-cli status` | Who you are, this folder's project, comments waiting |
25
25
  | `npx pinsay-cli open` | Open this project in the dashboard |
@@ -42,7 +42,7 @@ npx pinsay-cli init
42
42
 
43
43
  ## API keys
44
44
 
45
- Keys look like `pnsy_` + 40 hex characters. They resolve in this order: `PINSAY_API_KEY` env var → `.pinsay/credentials.env` → the per-machine store (`~/.config/pinsay/credentials.json`, mode 0600, never committed). `init` and `login` save the key to `.pinsay/credentials.env` (hidden from git) by default; `--global` saves to the machine store instead; `login --global` moves a repo key there.
45
+ Keys look like `pnsy_` + 40 hex characters. They resolve in this order: `PINSAY_API_KEY` env var → `.pinsay/credentials.env` (mode 0600, hidden from git). `init` and `login` save the key in the repo; there is no machine-wide key since 0.10.0, and `npx pinsay-cli update` deletes one an older version saved (`~/.config/pinsay/credentials.json`).
46
46
 
47
47
  ## MCP (Claude Code / Cursor)
48
48
 
@@ -62,6 +62,16 @@ Full guides: https://pinsay.dev/docs/ · Issues: https://github.com/moamen-ui/pi
62
62
 
63
63
  ## Changelog
64
64
 
65
+ ### 0.10.0
66
+
67
+ - The API key lives only in the repo: `--global` (login, init, logout, remove) is gone, and a key saved on the machine is no longer read.
68
+ - `update` deletes the machine-wide key file an older version saved, and says which file it removed.
69
+ - `whoami` shows the workspace; `status` / `apply` say "Project … not found in workspace …" instead of a generic error.
70
+
71
+ ### 0.9.1
72
+
73
+ - `remove` works when the repo sits behind a linked or shortened path (Windows junctions / short paths).
74
+
65
75
  ### 0.9.0
66
76
 
67
77
  - No more `.pinsay/pinsay.sh` (old copies keep working; `remove` deletes them).
package/dist/cli.js CHANGED
@@ -392,7 +392,7 @@ var init_build_constants = __esm({
392
392
  "src/build-constants.ts"() {
393
393
  "use strict";
394
394
  BUILD_DEFAULT_SERVER = true ? "https://app.pinsay.dev" : "https://app.pinsay.dev";
395
- BUILD_CLI_VERSION = true ? "0.9.0" : "0.0.0-dev";
395
+ BUILD_CLI_VERSION = true ? "0.10.0" : "0.0.0-dev";
396
396
  }
397
397
  });
398
398
 
@@ -1424,6 +1424,10 @@ var init_api = __esm({
1424
1424
  });
1425
1425
 
1426
1426
  // src/errors.ts
1427
+ function projectNotFoundMessage(project, workspace) {
1428
+ const where = workspace ? `workspace ${workspace}` : "this workspace";
1429
+ return `Project "${project}" not found in ${where}. Sign in with an account in the project's workspace (npx pinsay-cli login), or fix the key in .pinsay/config.json.`;
1430
+ }
1427
1431
  function isOutage(err) {
1428
1432
  return err instanceof NetworkError || err instanceof ApiError && err.code >= 500;
1429
1433
  }
@@ -1435,6 +1439,8 @@ function hostOf(server) {
1435
1439
  }
1436
1440
  }
1437
1441
  function describeError(err, server) {
1442
+ if (err instanceof ProjectNotFoundError)
1443
+ return { code: 3, message: err.message };
1438
1444
  if (err instanceof NetworkError) {
1439
1445
  return { code: 4, message: `Can't reach ${err.host}. Check your internet connection and try again.` };
1440
1446
  }
@@ -1463,12 +1469,21 @@ function reportFatal(err, opts) {
1463
1469
  const { code, message } = describeError(err, opts.server);
1464
1470
  exitWithError(code, message, opts.json);
1465
1471
  }
1466
- var ISSUES_URL;
1472
+ var ISSUES_URL, ProjectNotFoundError;
1467
1473
  var init_errors = __esm({
1468
1474
  "src/errors.ts"() {
1469
1475
  "use strict";
1470
1476
  init_api();
1471
1477
  ISSUES_URL = "https://github.com/moamen-ui/pinsay-cli/issues";
1478
+ ProjectNotFoundError = class extends Error {
1479
+ constructor(project, workspace) {
1480
+ super(projectNotFoundMessage(project, workspace));
1481
+ this.project = project;
1482
+ this.workspace = workspace;
1483
+ this.name = "ProjectNotFoundError";
1484
+ }
1485
+ code = "not_found";
1486
+ };
1472
1487
  }
1473
1488
  });
1474
1489
 
@@ -1476,14 +1491,12 @@ var init_errors = __esm({
1476
1491
  import { promises as fs10 } from "node:fs";
1477
1492
  import { createHash } from "node:crypto";
1478
1493
  import { homedir } from "node:os";
1479
- import { join as join9, dirname as dirname5 } from "node:path";
1494
+ import { dirname as dirname5, join as join9 } from "node:path";
1480
1495
  function sourceLabel(source) {
1481
1496
  if (source === "env")
1482
1497
  return "env var";
1483
1498
  if (source === "repo")
1484
1499
  return "repo credentials.env";
1485
- if (source === "global")
1486
- return "global store";
1487
1500
  return "none";
1488
1501
  }
1489
1502
  function normalizeServerOrigin(server) {
@@ -1512,6 +1525,38 @@ function legacyGlobalCredentialsPath() {
1512
1525
  return void 0;
1513
1526
  return join9(configBase(), LEGACY_DIR_NAME, "credentials.json");
1514
1527
  }
1528
+ async function findMachineKeyFiles() {
1529
+ const found = [];
1530
+ const current = globalCredentialsPath();
1531
+ try {
1532
+ await fs10.access(current);
1533
+ found.push(current);
1534
+ } catch {
1535
+ }
1536
+ const legacy = legacyGlobalCredentialsPath();
1537
+ if (legacy) {
1538
+ try {
1539
+ if (isCredentialStore(JSON.parse(await fs10.readFile(legacy, "utf8"))))
1540
+ found.push(legacy);
1541
+ } catch {
1542
+ }
1543
+ }
1544
+ return found;
1545
+ }
1546
+ async function removeMachineKeyFiles() {
1547
+ const removed = [];
1548
+ for (const file of await findMachineKeyFiles()) {
1549
+ try {
1550
+ await fs10.rm(file);
1551
+ } catch {
1552
+ continue;
1553
+ }
1554
+ removed.push(file);
1555
+ await fs10.rmdir(dirname5(file)).catch(() => {
1556
+ });
1557
+ }
1558
+ return removed;
1559
+ }
1515
1560
  function globalCacheDir() {
1516
1561
  if (process.env.PINSAY_CONFIG_DIR)
1517
1562
  return join9(process.env.PINSAY_CONFIG_DIR, "cache");
@@ -1531,76 +1576,6 @@ function isCredentialStore(value) {
1531
1576
  (entry) => !!entry && typeof entry === "object" && typeof entry.apiKey === "string"
1532
1577
  );
1533
1578
  }
1534
- async function readGlobalStore() {
1535
- let raw;
1536
- try {
1537
- raw = await fs10.readFile(globalCredentialsPath(), "utf8");
1538
- } catch (err) {
1539
- if (err?.code === "ENOENT")
1540
- return migrateLegacyStore();
1541
- return {};
1542
- }
1543
- try {
1544
- const parsed = JSON.parse(raw);
1545
- return parsed && typeof parsed === "object" ? parsed : {};
1546
- } catch {
1547
- return {};
1548
- }
1549
- }
1550
- async function migrateLegacyStore() {
1551
- const legacyFile = legacyGlobalCredentialsPath();
1552
- if (!legacyFile)
1553
- return {};
1554
- let legacy;
1555
- try {
1556
- legacy = JSON.parse(await fs10.readFile(legacyFile, "utf8"));
1557
- } catch {
1558
- return {};
1559
- }
1560
- if (!isCredentialStore(legacy))
1561
- return {};
1562
- try {
1563
- await writeGlobalStore(legacy);
1564
- } catch {
1565
- return legacy;
1566
- }
1567
- await fs10.rm(legacyFile, { force: true }).catch(() => {
1568
- });
1569
- await fs10.rmdir(dirname5(legacyFile)).catch(() => {
1570
- });
1571
- return legacy;
1572
- }
1573
- async function writeGlobalStore(store) {
1574
- const file = globalCredentialsPath();
1575
- await fs10.mkdir(dirname5(file), { recursive: true });
1576
- await fs10.writeFile(file, JSON.stringify(store, null, 2) + "\n", { encoding: "utf8", mode: 384 });
1577
- await fs10.chmod(file, 384).catch(() => {
1578
- });
1579
- }
1580
- async function getGlobalCredential(server) {
1581
- const store = await readGlobalStore();
1582
- const origin = normalizeServerOrigin(server);
1583
- if (!store[origin] && origin === "https://app.pinsay.dev")
1584
- return store["https://api.pinsay.dev"];
1585
- return store[origin];
1586
- }
1587
- async function saveGlobalCredential(server, entry) {
1588
- const store = await readGlobalStore();
1589
- store[normalizeServerOrigin(server)] = { ...entry, savedAt: (/* @__PURE__ */ new Date()).toISOString() };
1590
- await writeGlobalStore(store);
1591
- }
1592
- async function removeGlobalCredential(server) {
1593
- const store = await readGlobalStore();
1594
- const origin = normalizeServerOrigin(server);
1595
- const origins = origin === "https://app.pinsay.dev" ? [origin, "https://api.pinsay.dev"] : [origin];
1596
- const present = origins.filter((o) => o in store);
1597
- if (present.length === 0)
1598
- return false;
1599
- for (const o of present)
1600
- delete store[o];
1601
- await writeGlobalStore(store);
1602
- return true;
1603
- }
1604
1579
  async function readRepoApiKey(root) {
1605
1580
  try {
1606
1581
  const raw = await fs10.readFile(join9(root, ".pinsay", "credentials.env"), "utf8");
@@ -1622,18 +1597,13 @@ async function removeRepoCredentials(root) {
1622
1597
  await fs10.rm(file, { force: true });
1623
1598
  return true;
1624
1599
  }
1625
- async function resolveApiKey(root, server) {
1600
+ async function resolveApiKey(root, _server) {
1626
1601
  const envKey = process.env.PINSAY_API_KEY?.trim();
1627
1602
  if (envKey)
1628
1603
  return { key: envKey, source: "env" };
1629
1604
  const repoKey = await readRepoApiKey(root);
1630
1605
  if (repoKey)
1631
1606
  return { key: repoKey, source: "repo" };
1632
- if (server) {
1633
- const globalEntry = await getGlobalCredential(server);
1634
- if (globalEntry?.apiKey)
1635
- return { key: globalEntry.apiKey, source: "global" };
1636
- }
1637
1607
  return { key: void 0, source: null };
1638
1608
  }
1639
1609
  async function removeStaleRepoTokenCache(root) {
@@ -1676,6 +1646,31 @@ var init_server = __esm({
1676
1646
  }
1677
1647
  });
1678
1648
 
1649
+ // src/key-scope.ts
1650
+ function scopeFromFlags(options) {
1651
+ const raw = options["scope"];
1652
+ if (typeof raw === "string") {
1653
+ const value = raw.toLowerCase();
1654
+ if (value === "global")
1655
+ return { error: GLOBAL_KEY_REMOVED };
1656
+ if (value !== "repo")
1657
+ return { error: `Invalid --scope "${raw}". Valid value: repo.` };
1658
+ return { explicitRepo: true };
1659
+ }
1660
+ if (options["global"] === true)
1661
+ return { error: GLOBAL_KEY_REMOVED };
1662
+ if (options["local-credentials"] === true)
1663
+ return { explicitRepo: true };
1664
+ return {};
1665
+ }
1666
+ var GLOBAL_KEY_REMOVED;
1667
+ var init_key_scope = __esm({
1668
+ "src/key-scope.ts"() {
1669
+ "use strict";
1670
+ GLOBAL_KEY_REMOVED = "--global is no longer supported: the API key is saved in this repo only (.pinsay/credentials.env). Run npx pinsay-cli update to delete a machine-wide key an older version saved.";
1671
+ }
1672
+ });
1673
+
1679
1674
  // src/ui/interactive.ts
1680
1675
  function isInteractive(options = {}) {
1681
1676
  if (!process.stdin.isTTY || !process.stdout.isTTY)
@@ -2658,17 +2653,27 @@ async function fetchQueue(ctx, filter) {
2658
2653
  };
2659
2654
  });
2660
2655
  } catch (err) {
2656
+ if (err instanceof ApiError && err.code === 404)
2657
+ throw new ProjectNotFoundError(ctx.project, ctx.workspaceName);
2661
2658
  if (err instanceof ApiError && err.code === 403) {
2662
2659
  if (!warnedNonAdminFallback) {
2663
2660
  console.error("Note: predefined-action prompts need an admin key");
2664
2661
  warnedNonAdminFallback = true;
2665
2662
  }
2666
2663
  const summaryQuery = `view=summary${qs ? `&${qs.slice(1)}` : ""}`;
2667
- const res = await api(
2668
- ctx.server,
2669
- `/api/projects/${encodeURIComponent(ctx.project)}/comments?${summaryQuery}`,
2670
- { token: ctx.token }
2671
- );
2664
+ let res;
2665
+ try {
2666
+ res = await api(
2667
+ ctx.server,
2668
+ `/api/projects/${encodeURIComponent(ctx.project)}/comments?${summaryQuery}`,
2669
+ { token: ctx.token }
2670
+ );
2671
+ } catch (fallbackErr) {
2672
+ if (fallbackErr instanceof ApiError && fallbackErr.code === 404) {
2673
+ throw new ProjectNotFoundError(ctx.project, ctx.workspaceName);
2674
+ }
2675
+ throw fallbackErr;
2676
+ }
2672
2677
  const items = res?.items ?? [];
2673
2678
  return items.map((item) => ({
2674
2679
  id: item.id,
@@ -2705,6 +2710,7 @@ var init_queue = __esm({
2705
2710
  "src/apply/queue.ts"() {
2706
2711
  "use strict";
2707
2712
  init_api();
2713
+ init_errors();
2708
2714
  warnedNonAdminFallback = false;
2709
2715
  }
2710
2716
  });
@@ -3081,7 +3087,7 @@ async function overviewCommand(cwd2, options) {
3081
3087
  for (const p of projects) {
3082
3088
  const name = projectNames[p.key] || p.key;
3083
3089
  const items = await fetchQueue(
3084
- { server, project: p.key, token, apiKey: key, cwd: root },
3090
+ { server, project: p.key, token, apiKey: key, cwd: root, workspaceName: me?.tenantName || void 0 },
3085
3091
  {}
3086
3092
  );
3087
3093
  projectsWithPending.push({
@@ -3109,8 +3115,6 @@ async function overviewCommand(cwd2, options) {
3109
3115
  let sourceDesc = "key from this repo";
3110
3116
  if (source === "env") {
3111
3117
  sourceDesc = "key from PINSAY_API_KEY";
3112
- } else if (source === "global") {
3113
- sourceDesc = "key from this machine";
3114
3118
  }
3115
3119
  const who = email ? `${displayName} (${email})` : displayName;
3116
3120
  console.log(` Signed in ${who} ${sym.dot} ${sourceDesc}`);
@@ -3461,7 +3465,8 @@ var init_open = __esm({
3461
3465
  var remove_exports = {};
3462
3466
  __export(remove_exports, {
3463
3467
  collectRemovals: () => collectRemovals,
3464
- removeCommand: () => removeCommand
3468
+ removeCommand: () => removeCommand,
3469
+ writableInsideRoot: () => writableInsideRoot
3465
3470
  });
3466
3471
  import { promises as fs25 } from "node:fs";
3467
3472
  import { dirname as dirname10, isAbsolute as isAbsolute5, join as join28, relative as relative10, resolve as resolve10 } from "node:path";
@@ -3489,13 +3494,14 @@ async function readIfExists(p) {
3489
3494
  }
3490
3495
  async function writableInsideRoot(root, abs) {
3491
3496
  try {
3492
- const back = relative10(resolve10(root), await fs25.realpath(abs));
3497
+ const realRoot = await fs25.realpath(resolve10(root));
3498
+ const back = relative10(realRoot, await fs25.realpath(abs));
3493
3499
  return back !== "" && !back.startsWith("..") && !isAbsolute5(back);
3494
3500
  } catch {
3495
3501
  return false;
3496
3502
  }
3497
3503
  }
3498
- async function collectRemovals(root, opts) {
3504
+ async function collectRemovals(root) {
3499
3505
  const config = await readConfig(root);
3500
3506
  const removals = [];
3501
3507
  if (await pathExists(join28(root, ".pinsay"))) {
@@ -3542,9 +3548,6 @@ async function collectRemovals(root, opts) {
3542
3548
  }
3543
3549
  }
3544
3550
  }
3545
- if (opts.global) {
3546
- removals.push({ kind: "global-key", target: normalizeServerOrigin(opts.server) });
3547
- }
3548
3551
  const byHand = [];
3549
3552
  for (const appDir of appDirs) {
3550
3553
  for (const name of VITE_CONFIGS2) {
@@ -3583,13 +3586,11 @@ async function removeEmptyParents(root, abs) {
3583
3586
  }
3584
3587
  async function removeCommand(cwd2, options) {
3585
3588
  const json = options["json"] === true;
3589
+ if (options["global"] === true)
3590
+ exitWithError(2, GLOBAL_KEY_REMOVED, json);
3586
3591
  const interactive = isInteractive(options);
3587
- const server = resolveServer();
3588
3592
  const root = await findRepoRoot(cwd2);
3589
- const { removals, byHand, tracked } = await collectRemovals(root, {
3590
- global: options["global"] === true,
3591
- server
3592
- });
3593
+ const { removals, byHand, tracked } = await collectRemovals(root);
3593
3594
  if (removals.length === 0) {
3594
3595
  if (json)
3595
3596
  console.log(JSON.stringify({ ok: true, removed: [] }));
@@ -3600,7 +3601,7 @@ async function removeCommand(cwd2, options) {
3600
3601
  if (!json) {
3601
3602
  console.log("Will remove:");
3602
3603
  for (const r of removals) {
3603
- const reason = r.kind === "exclude-block" || r.kind === "html-block" ? " (PinSay block)" : r.kind === "env-lines" ? " (VITE_PINSAY_* lines)" : r.kind === "global-key" ? ` (this machine's key for ${r.target})` : "";
3604
+ const reason = r.kind === "exclude-block" || r.kind === "html-block" ? " (PinSay block)" : r.kind === "env-lines" ? " (VITE_PINSAY_* lines)" : "";
3604
3605
  console.log(` ${r.target}${reason ? dim(reason) : ""}`);
3605
3606
  }
3606
3607
  if (byHand.length > 0) {
@@ -3678,8 +3679,6 @@ async function removeCommand(cwd2, options) {
3678
3679
  if (!abs)
3679
3680
  continue;
3680
3681
  await fs25.rm(abs, { recursive: true, force: true });
3681
- } else if (r.kind === "global-key") {
3682
- await removeGlobalCredential(server);
3683
3682
  }
3684
3683
  }
3685
3684
  if (json)
@@ -3695,8 +3694,7 @@ var init_remove = __esm({
3695
3694
  "use strict";
3696
3695
  init_git_exclude();
3697
3696
  init_config();
3698
- init_credentials();
3699
- init_server();
3697
+ init_key_scope();
3700
3698
  init_errors();
3701
3699
  init_interactive();
3702
3700
  init_prompt();
@@ -4260,7 +4258,7 @@ async function runInitChecks(cwd2, overrides = {}, cliVersion = "0.0.0") {
4260
4258
  checks.push({
4261
4259
  id: "key",
4262
4260
  status: "error",
4263
- message: "No API key found (env, repo, or global store)",
4261
+ message: "No API key found (env var or this repo)",
4264
4262
  hint: "Run `npx pinsay-cli login`"
4265
4263
  });
4266
4264
  } else if (!serverReachable) {
@@ -5116,25 +5114,7 @@ async function detectDesignTokens(cwd2, options) {
5116
5114
 
5117
5115
  // src/commands/init.ts
5118
5116
  init_credentials();
5119
-
5120
- // src/key-scope.ts
5121
- function scopeFromFlags(options) {
5122
- const raw = options["scope"];
5123
- if (typeof raw === "string") {
5124
- const value = raw.toLowerCase();
5125
- if (value !== "global" && value !== "repo") {
5126
- return { error: `Invalid --scope "${raw}". Valid values: global, repo.` };
5127
- }
5128
- return { scope: value };
5129
- }
5130
- if (options["global"] === true)
5131
- return { scope: "global" };
5132
- if (options["local-credentials"] === true)
5133
- return { scope: "repo" };
5134
- return {};
5135
- }
5136
-
5137
- // src/commands/init.ts
5117
+ init_key_scope();
5138
5118
  init_server();
5139
5119
  init_errors();
5140
5120
  init_style();
@@ -5466,18 +5446,13 @@ function accountText(plan) {
5466
5446
  if (account.keySaved === "repo") {
5467
5447
  return `${account.displayName} \xB7 key saved in this repo (.pinsay/credentials.env, hidden from git)`;
5468
5448
  }
5469
- if (account.keySaved === "global") {
5470
- return `${account.displayName} \xB7 key saved on this machine (${account.globalPath})`;
5471
- }
5472
5449
  if (account.existingSource === "env")
5473
5450
  return `${account.displayName} \xB7 key from PINSAY_API_KEY`;
5474
- if (account.existingSource === "global")
5475
- return `${account.displayName} \xB7 key already saved on this machine`;
5476
5451
  return `${account.displayName} \xB7 key already saved in this repo`;
5477
5452
  }
5478
5453
  if (account.kind === "pending")
5479
5454
  return "you'll sign in in your browser";
5480
- const where = account.source === "env" ? "PINSAY_API_KEY" : account.source === "repo" ? "this repo" : "this machine";
5455
+ const where = account.source === "env" ? "PINSAY_API_KEY" : "this repo";
5481
5456
  return `key found (${where}), not checked in a dry run`;
5482
5457
  }
5483
5458
  function sharedText(plan) {
@@ -5713,21 +5688,14 @@ async function decideWidget(cwd2, options, config, ctx) {
5713
5688
  const w = await findExistingWidget(cwd2, config.htmlPath);
5714
5689
  return w ? { widget: { kind: "already", file: w.file } } : { widget: { kind: "extension" } };
5715
5690
  }
5716
- function keyPlacement(session, saveGlobal) {
5717
- if (session.origin === "env" || session.origin === "repo" || session.origin === "global")
5691
+ function keyPlacement(session) {
5692
+ if (session.origin === "env" || session.origin === "repo")
5718
5693
  return "existing";
5719
- return saveGlobal ? "global" : "repo";
5694
+ return "repo";
5720
5695
  }
5721
- async function saveKey(cwd2, server, session, keySaved, project) {
5722
- if (keySaved === "repo") {
5696
+ async function saveKey(cwd2, session, keySaved, project) {
5697
+ if (keySaved === "repo")
5723
5698
  await writeCredentials(cwd2, session.key, project ? { project } : {});
5724
- } else if (keySaved === "global") {
5725
- await saveGlobalCredential(server, {
5726
- apiKey: session.key,
5727
- email: session.me.email,
5728
- displayName: session.me.displayName
5729
- });
5730
- }
5731
5699
  }
5732
5700
  async function readStackNames(cwd2) {
5733
5701
  const pkgStr = await fs17.readFile(join17(cwd2, "package.json"), "utf8").catch(() => "{}");
@@ -5764,7 +5732,6 @@ async function initCommand(cwd2, options = {}) {
5764
5732
  const scopeResult = scopeFromFlags(options);
5765
5733
  if (scopeResult.error)
5766
5734
  exitWithError(2, scopeResult.error, json);
5767
- const saveGlobal = scopeResult.scope === "global";
5768
5735
  const shareResult = shareFromFlags(options);
5769
5736
  if (shareResult.error)
5770
5737
  exitWithError(2, shareResult.error, json);
@@ -5826,7 +5793,6 @@ async function initCommand(cwd2, options = {}) {
5826
5793
  product,
5827
5794
  pathFlag,
5828
5795
  configIsMulti,
5829
- saveGlobal,
5830
5796
  share: shareResult.share,
5831
5797
  wantEmbed,
5832
5798
  notes
@@ -5850,7 +5816,7 @@ async function initCommand(cwd2, options = {}) {
5850
5816
  const files2 = [
5851
5817
  ".pinsay/config.json",
5852
5818
  ".pinsay/stack.json",
5853
- ...account.kind === "pending" && !saveGlobal ? [".pinsay/credentials.env"] : [],
5819
+ ...account.kind === "pending" ? [".pinsay/credentials.env"] : [],
5854
5820
  ...widget2.kind === "embed" ? widget2.files : [],
5855
5821
  ".git/info/exclude (PinSay block)"
5856
5822
  ];
@@ -5887,7 +5853,7 @@ async function initCommand(cwd2, options = {}) {
5887
5853
  });
5888
5854
  if (!json)
5889
5855
  console.log(`${green(sym.check)} Signed in as ${session.me.displayName}`);
5890
- const keySaved = keyPlacement(session, saveGlobal);
5856
+ const keySaved = keyPlacement(session);
5891
5857
  if (isJoin && configIsMulti) {
5892
5858
  await handleMultiJoin({
5893
5859
  cwd: cwd2,
@@ -5961,8 +5927,7 @@ async function initCommand(cwd2, options = {}) {
5961
5927
  kind: "signed-in",
5962
5928
  displayName: session.me.displayName,
5963
5929
  keySaved,
5964
- existingSource: session.origin === "env" || session.origin === "repo" || session.origin === "global" ? session.origin : void 0,
5965
- globalPath: globalCredentialsPath()
5930
+ existingSource: session.origin === "env" || session.origin === "repo" ? session.origin : void 0
5966
5931
  },
5967
5932
  widget,
5968
5933
  skills,
@@ -6029,7 +5994,7 @@ async function initCommand(cwd2, options = {}) {
6029
5994
  let htmlPath;
6030
5995
  if (keySaved !== "existing") {
6031
5996
  progress.step("Saving your key");
6032
- await saveKey(cwd2, server, session, keySaved, project.key);
5997
+ await saveKey(cwd2, session, keySaved, project.key);
6033
5998
  if (keySaved === "repo")
6034
5999
  filesMod.push(".pinsay/credentials.env");
6035
6000
  }
@@ -6255,12 +6220,11 @@ function planAccount(session, keySaved) {
6255
6220
  kind: "signed-in",
6256
6221
  displayName: session.me.displayName,
6257
6222
  keySaved,
6258
- existingSource: session.origin === "env" || session.origin === "repo" || session.origin === "global" ? session.origin : void 0,
6259
- globalPath: globalCredentialsPath()
6223
+ existingSource: session.origin === "env" || session.origin === "repo" ? session.origin : void 0
6260
6224
  };
6261
6225
  }
6262
6226
  async function multiDryRun(args) {
6263
- const { cwd: cwd2, config, options, server, json, interactive, product, pathFlag, configIsMulti, saveGlobal, wantEmbed } = args;
6227
+ const { cwd: cwd2, config, options, server, json, interactive, product, pathFlag, configIsMulti, wantEmbed } = args;
6264
6228
  const flagTool = typeof options["tool"] === "string" ? options["tool"] : void 0;
6265
6229
  const resolved = await resolveApiKey(cwd2, server);
6266
6230
  const account = resolved.key && resolved.source ? { kind: "found", source: resolved.source } : { kind: "pending" };
@@ -6268,7 +6232,7 @@ async function multiDryRun(args) {
6268
6232
  const tool = tools[0];
6269
6233
  const names = pathFlag ? await readAppStackNames(cwd2, [pathFlag]) : await readAppStackNames(cwd2, listProjects(config).map((p) => p.path));
6270
6234
  const shared2 = args.share !== void 0 ? { decided: true, share: args.share, saved: false, ...names, aiTools: tools } : typeof config.shareStack === "boolean" ? { decided: true, share: config.shareStack, saved: true, ...names, aiTools: tools } : interactive ? { decided: false, share: true, saved: false, ...names, aiTools: tools } : { decided: true, share: true, saved: false, ...names, aiTools: tools };
6271
- const keyFile = account.kind === "pending" && !saveGlobal ? [".pinsay/credentials.env"] : [];
6235
+ const keyFile = account.kind === "pending" ? [".pinsay/credentials.env"] : [];
6272
6236
  let plan;
6273
6237
  let projectLine;
6274
6238
  if (pathFlag) {
@@ -6483,7 +6447,7 @@ async function handleMultiJoin(args) {
6483
6447
  const progress = createProgress(labels.length, progressMode(isJson));
6484
6448
  if (keySaved !== "existing") {
6485
6449
  progress.step("Saving your key");
6486
- await saveKey(cwd2, server, session, keySaved);
6450
+ await saveKey(cwd2, session, keySaved);
6487
6451
  }
6488
6452
  const skillWarnings = [];
6489
6453
  const hide = [];
@@ -6653,7 +6617,7 @@ async function handleMultiProjectSetup(args) {
6653
6617
  const progress = createProgress(labels.length, progressMode(isJson));
6654
6618
  if (keySaved !== "existing") {
6655
6619
  progress.step("Saving your key");
6656
- await saveKey(cwd2, server, session, keySaved);
6620
+ await saveKey(cwd2, session, keySaved);
6657
6621
  }
6658
6622
  const skillWarnings = [];
6659
6623
  const hide = [];
@@ -6955,6 +6919,7 @@ init_repo_paths();
6955
6919
  init_server();
6956
6920
  init_git_exclude();
6957
6921
  init_style();
6922
+ init_credentials();
6958
6923
  function sourceFor(path) {
6959
6924
  if (path.includes("pinsay-init"))
6960
6925
  return "/pinsay-init.md";
@@ -6978,6 +6943,17 @@ async function updateCommand(cwd2, options) {
6978
6943
  const note = pinsayShNote(cwd2);
6979
6944
  if (note)
6980
6945
  console.log(dim(note));
6946
+ if (options.check) {
6947
+ for (const f of await findMachineKeyFiles())
6948
+ console.log(`machine-wide key found: ${f} (update deletes it)`);
6949
+ } else {
6950
+ const removedKeys = await removeMachineKeyFiles();
6951
+ for (const f of removedKeys)
6952
+ console.log(`removed machine-wide key ${f}`);
6953
+ if (removedKeys.length > 0 && !(await resolveApiKey(cwd2)).key) {
6954
+ console.log("This repo has no key of its own yet. Sign in for it: npx pinsay-cli login");
6955
+ }
6956
+ }
6981
6957
  const config = await readConfig(cwd2);
6982
6958
  const server = (options.server || resolveServer()).replace(/\/$/, "");
6983
6959
  if (!config.project && !isMultiProject(config)) {
@@ -8016,7 +7992,7 @@ init_projection();
8016
7992
  init_server();
8017
7993
  async function exitIfWorkspaceFrozen(server, token) {
8018
7994
  if (!token)
8019
- return;
7995
+ return void 0;
8020
7996
  try {
8021
7997
  const me = await api(server, "/api/auth/me", { token });
8022
7998
  if (me?.workspaceDeletionScheduledFor) {
@@ -8030,7 +8006,9 @@ async function exitIfWorkspaceFrozen(server, token) {
8030
8006
  );
8031
8007
  process.exit(2);
8032
8008
  }
8009
+ return me?.tenantName || void 0;
8033
8010
  } catch {
8011
+ return void 0;
8034
8012
  }
8035
8013
  }
8036
8014
  async function checkMetaAndNotify(server, config, root, parsed) {
@@ -8112,13 +8090,14 @@ async function applyCommand(cwd2, parsed, positionals = []) {
8112
8090
  );
8113
8091
  process.exit(3);
8114
8092
  }
8115
- await exitIfWorkspaceFrozen(server, token);
8093
+ const workspaceName = await exitIfWorkspaceFrozen(server, token);
8116
8094
  const clientCtx = {
8117
8095
  server,
8118
8096
  project,
8119
8097
  token,
8120
8098
  apiKey,
8121
- cwd: root
8099
+ cwd: root,
8100
+ workspaceName
8122
8101
  };
8123
8102
  if (parsed["mark"] !== void 0) {
8124
8103
  const markVal2 = parsed["mark"];
@@ -8244,7 +8223,7 @@ async function applyAllProjects(root, cwd2, config, server, parsed) {
8244
8223
  );
8245
8224
  process.exit(3);
8246
8225
  }
8247
- await exitIfWorkspaceFrozen(server, token);
8226
+ const workspaceName = await exitIfWorkspaceFrozen(server, token);
8248
8227
  const projects = listProjects(config);
8249
8228
  const plan = parsed["plan"] === true;
8250
8229
  const status = typeof parsed["status"] === "string" ? parsed["status"] : void 0;
@@ -8252,7 +8231,7 @@ async function applyAllProjects(root, cwd2, config, server, parsed) {
8252
8231
  if (parsed["json"] === true && !plan) {
8253
8232
  const all = [];
8254
8233
  for (const p of projects) {
8255
- const clientCtx = { server, project: p.key, token, apiKey, cwd: root };
8234
+ const clientCtx = { server, project: p.key, token, apiKey, cwd: root, workspaceName };
8256
8235
  const items = await fetchQueue(clientCtx, { status, environment });
8257
8236
  all.push({ project: p.key, path: p.path, items: items.map((item) => toAiCommentView(item)) });
8258
8237
  }
@@ -8261,7 +8240,7 @@ async function applyAllProjects(root, cwd2, config, server, parsed) {
8261
8240
  }
8262
8241
  const sections = [];
8263
8242
  for (const p of projects) {
8264
- const clientCtx = { server, project: p.key, token, apiKey, cwd: root };
8243
+ const clientCtx = { server, project: p.key, token, apiKey, cwd: root, workspaceName };
8265
8244
  const result = await runApply({ plan, status, environment }, clientCtx);
8266
8245
  sections.push(`# Project: ${p.key} (${p.path})
8267
8246
 
@@ -14688,8 +14667,8 @@ init_git_exclude();
14688
14667
  import { existsSync as existsSync7 } from "node:fs";
14689
14668
  import { join as join26 } from "node:path";
14690
14669
  import { execFileSync as execFileSync2 } from "node:child_process";
14691
- init_credentials();
14692
14670
  init_prompt();
14671
+ init_key_scope();
14693
14672
  init_server();
14694
14673
  init_device_login();
14695
14674
  init_errors();
@@ -14708,11 +14687,13 @@ async function loginCommand(cwd2, options = {}) {
14708
14687
  if (flags.error)
14709
14688
  exitWithError(2, flags.error, json);
14710
14689
  const root = await findRepoRoot(cwd2);
14690
+ const inRepo = existsSync7(join26(root, ".pinsay")) || inGitRepo(root);
14691
+ if (!inRepo && !flags.explicitRepo) {
14692
+ exitWithError(2, "Run this inside your project's repo: the key is saved there (.pinsay/credentials.env).", json);
14693
+ }
14711
14694
  const server = resolveServer();
14712
14695
  const branding = await getBranding(server);
14713
14696
  const product = branding.productName;
14714
- const inRepo = existsSync7(join26(root, ".pinsay")) || inGitRepo(root);
14715
- const scope = flags.scope ?? (inRepo ? "repo" : "global");
14716
14697
  let flagKey = typeof options["key"] === "string" ? options["key"].trim() : "";
14717
14698
  if (options["key"] === true) {
14718
14699
  if (!process.stdin.isTTY)
@@ -14721,24 +14702,6 @@ async function loginCommand(cwd2, options = {}) {
14721
14702
  if (!flagKey)
14722
14703
  exitWithError(2, noKeyMessage(product), json);
14723
14704
  }
14724
- if (scope === "global" && !flagKey) {
14725
- const repoKey = await readRepoApiKey(root);
14726
- if (repoKey) {
14727
- try {
14728
- const { me } = await exchangeKey(server, repoKey);
14729
- await saveGlobalCredential(server, { apiKey: repoKey, email: me.email, displayName: me.displayName });
14730
- await removeRepoCredentials(root);
14731
- done(
14732
- json,
14733
- { server, scope: "global", moved: true, displayName: me.displayName, email: me.email },
14734
- `${green(sym.check)} Moved your key to this machine's store (${globalCredentialsPath()}). Every repo on this machine can use it now.`
14735
- );
14736
- } catch (err) {
14737
- if (!(err instanceof InvalidKeyError))
14738
- throw err;
14739
- }
14740
- }
14741
- }
14742
14705
  let key;
14743
14706
  let displayName;
14744
14707
  let email;
@@ -14766,23 +14729,15 @@ async function loginCommand(cwd2, options = {}) {
14766
14729
  }
14767
14730
  closePrompts();
14768
14731
  const who = displayName ? `${displayName}${email ? ` (${email})` : ""}` : email ?? "you";
14769
- if (scope === "repo") {
14770
- await writeCredentials(root, key);
14771
- const hideWarnings = formatHideWarnings(await hidePinsayFiles(root));
14772
- if (!json)
14773
- for (const line of hideWarnings)
14774
- console.error(line);
14775
- done(
14776
- json,
14777
- { server, scope: "repo", moved: false, displayName, email },
14778
- `${green(sym.check)} Signed in as ${who}. Key saved in this repo (.pinsay/credentials.env, hidden from git).`
14779
- );
14780
- }
14781
- await saveGlobalCredential(server, { apiKey: key, email, displayName });
14732
+ await writeCredentials(root, key);
14733
+ const hideWarnings = formatHideWarnings(await hidePinsayFiles(root));
14734
+ if (!json)
14735
+ for (const line of hideWarnings)
14736
+ console.error(line);
14782
14737
  done(
14783
14738
  json,
14784
- { server, scope: "global", moved: false, displayName, email },
14785
- inRepo || flags.scope === "global" ? `${green(sym.check)} Signed in as ${who}. Key saved on this machine (${globalCredentialsPath()}) for every repo.` : `${green(sym.check)} Signed in as ${who}. No repo here, so the key is saved on this machine (${globalCredentialsPath()}) for every repo.`
14739
+ { server, scope: "repo", moved: false, displayName, email },
14740
+ `${green(sym.check)} Signed in as ${who}. Key saved in this repo (.pinsay/credentials.env, hidden from git).`
14786
14741
  );
14787
14742
  }
14788
14743
  function done(json, payload, message) {
@@ -14796,35 +14751,33 @@ function done(json, payload, message) {
14796
14751
  // src/commands/logout.ts
14797
14752
  init_config();
14798
14753
  init_credentials();
14754
+ init_key_scope();
14799
14755
  init_server();
14756
+ init_errors();
14800
14757
  init_style();
14801
14758
  var ENV_KEY_MESSAGE = "Your key comes from the PINSAY_API_KEY variable. Unset it to sign out.";
14802
14759
  async function logoutCommand(cwd2, options = {}) {
14803
14760
  const json = options["json"] === true;
14761
+ const flags = scopeFromFlags(options);
14762
+ if (flags.error)
14763
+ exitWithError(2, flags.error, json);
14804
14764
  const server = resolveServer();
14805
14765
  const origin = normalizeServerOrigin(server);
14806
14766
  const root = await findRepoRoot(cwd2);
14807
- const finish = (source, removed2, message) => {
14767
+ const finish = (source, removed, message) => {
14808
14768
  if (json)
14809
- console.log(JSON.stringify({ ok: true, server: origin, source, removed: removed2 }));
14769
+ console.log(JSON.stringify({ ok: true, server: origin, source, removed }));
14810
14770
  else
14811
14771
  console.log(message);
14812
14772
  process.exit(0);
14813
14773
  };
14814
- const isGlobal = options["global"] === true || String(options["scope"] ?? "").toLowerCase() === "global";
14815
- if (!isGlobal && process.env.PINSAY_API_KEY?.trim()) {
14774
+ if (process.env.PINSAY_API_KEY?.trim())
14816
14775
  finish("env", false, ENV_KEY_MESSAGE);
14776
+ if (await readRepoApiKey(root)) {
14777
+ const removed = await removeRepoCredentials(root);
14778
+ finish("repo", removed, `${green(sym.check)} Signed out: removed this repo's key (.pinsay/credentials.env).`);
14817
14779
  }
14818
- if (!isGlobal && await readRepoApiKey(root)) {
14819
- const removed2 = await removeRepoCredentials(root);
14820
- finish("repo", removed2, `${green(sym.check)} Signed out: removed this repo's key (.pinsay/credentials.env).`);
14821
- }
14822
- const removed = await removeGlobalCredential(server);
14823
- finish(
14824
- "global",
14825
- removed,
14826
- removed ? `${green(sym.check)} Removed the saved key for ${origin} from this machine.` : `No saved key for ${origin} on this machine.`
14827
- );
14780
+ finish("repo", false, "No key saved in this repo.");
14828
14781
  }
14829
14782
 
14830
14783
  // src/commands/whoami.ts
@@ -14841,33 +14794,31 @@ async function whoamiCommand(cwd2, options = {}) {
14841
14794
  if (isJson) {
14842
14795
  console.log(JSON.stringify({ ok: false, server, source: null }));
14843
14796
  } else {
14844
- console.error(`No API key found for ${server} (checked env var, repo, and global store).`);
14797
+ console.error(`No API key found for ${server} (checked PINSAY_API_KEY and this repo's .pinsay/credentials.env).`);
14845
14798
  console.error("Run `npx pinsay-cli login` to sign in.");
14846
14799
  }
14847
14800
  process.exit(3);
14848
14801
  }
14849
14802
  let displayName;
14850
14803
  let email;
14804
+ let workspace;
14851
14805
  try {
14852
14806
  const login = await api(server, "/api/auth/login-with-key", { method: "POST", body: { apiKey: key } });
14853
14807
  if (login?.status === "ok" && login.token) {
14854
- const me = login.user ?? await api(server, "/api/auth/me", { token: login.token });
14855
- displayName = me?.displayName;
14856
- email = me?.email;
14808
+ const me = await api(server, "/api/auth/me", { token: login.token });
14809
+ displayName = me?.displayName || void 0;
14810
+ email = me?.email || void 0;
14811
+ workspace = me?.tenantName || void 0;
14857
14812
  }
14858
14813
  } catch {
14859
14814
  }
14860
- if (source === "global" && (!displayName || !email)) {
14861
- const cached = await getGlobalCredential(server);
14862
- displayName = displayName ?? cached?.displayName;
14863
- email = email ?? cached?.email;
14864
- }
14865
14815
  if (isJson) {
14866
- console.log(JSON.stringify({ ok: true, server, displayName, email, source }));
14816
+ console.log(JSON.stringify({ ok: true, server, displayName, email, workspace: workspace ?? null, source }));
14867
14817
  process.exit(0);
14868
14818
  }
14869
14819
  const who = displayName ? `${displayName}${email ? ` (${email})` : ""}` : email ?? "unknown user";
14870
- console.log(`${server} \u2014 ${who} \u2014 key source: ${sourceLabel(source)}`);
14820
+ const where = workspace ? ` \u2014 workspace: ${workspace}` : "";
14821
+ console.log(`${server} \u2014 ${who}${where} \u2014 key source: ${sourceLabel(source)}`);
14871
14822
  process.exit(0);
14872
14823
  }
14873
14824
 
@@ -14903,6 +14854,7 @@ function parseArgs(args) {
14903
14854
  // 0.9.0
14904
14855
  "embed",
14905
14856
  "global",
14857
+ // rejected since 0.10.0 (key-scope.ts); kept so it never takes the next argument
14906
14858
  "share-stack",
14907
14859
  "no-share-stack",
14908
14860
  "no-color",
@@ -14940,7 +14892,7 @@ var HELP = `Usage: npx pinsay-cli <command> [options]
14940
14892
  Set up
14941
14893
  init Set up this repo (sign in, project, skills)
14942
14894
  embed Put the widget in your app's code
14943
- login Sign in; the key is saved in this repo (--global: this machine)
14895
+ login Sign in; the key is saved in this repo
14944
14896
  logout Remove the key this repo uses
14945
14897
  remove Remove everything PinSay added to this repo
14946
14898
  Daily
@@ -14952,10 +14904,10 @@ Daily
14952
14904
  reply Reply to a comment
14953
14905
  Tools
14954
14906
  doctor Check this install and fix what it can
14955
- update Refresh the AI skills
14907
+ update Refresh the AI skills; delete an old machine-wide key
14956
14908
  map Rebuild the source map without a build
14957
14909
  mcp Start the MCP server for AI tools
14958
- whoami Show the signed-in account and where the key came from
14910
+ whoami Show the signed-in account, workspace and key source
14959
14911
 
14960
14912
  Run npx pinsay-cli <command> --help for examples.
14961
14913
  Flags: --no-color No colours or styling (also NO_COLOR=1)
@@ -14986,7 +14938,6 @@ var HELP_TEXTS = {
14986
14938
  ["--dry-run", "Show the plan; write and send nothing"],
14987
14939
  ["--path <app>", "The app folder, in a repo with several apps"],
14988
14940
  ["--key <key>", "Sign in with this key instead of the browser"],
14989
- ["--global", "Save the key on this machine (repo is the default)"],
14990
14941
  ["--no-browser", "Print the sign-in link; don't open a browser"],
14991
14942
  ["--tool <tool>", "AI tool for the skills (asked when omitted)"],
14992
14943
  ["--skills-dir <path>", "Where the skills are installed"],
@@ -14999,7 +14950,7 @@ var HELP_TEXTS = {
14999
14950
  ["--no-share-stack", "Share nothing about this app's stack"],
15000
14951
  ["--delivery <embed|extension>", "How reviewers open the widget", true],
15001
14952
  ["--no-inject", "Skip injection (use --embed or --html)", true],
15002
- ["--scope <global|repo>", "Where the key is saved (--global is the new spelling)", true],
14953
+ ["--scope repo", "Old spelling; the key is always saved in this repo", true],
15003
14954
  ["--local-credentials", "Alias of --scope repo", true],
15004
14955
  ["--no-color", "No colours or styling (also NO_COLOR=1)"]
15005
14956
  ],
@@ -15038,45 +14989,39 @@ var HELP_TEXTS = {
15038
14989
  ]
15039
14990
  },
15040
14991
  login: {
15041
- purpose: "Sign in; the key is saved in this repo (--global: this machine)",
14992
+ purpose: "Sign in; the key is saved in this repo",
15042
14993
  usage: "npx pinsay-cli login [options]",
15043
14994
  common: [
15044
14995
  ["--key <key>", "Validate a pasted key; skip the browser"],
15045
- ["--global", "Save the key on this machine, not this repo"],
15046
14996
  ["--no-browser", "Print the sign-in link; don't open a browser"],
15047
14997
  ["--json", "Machine-readable output"]
15048
14998
  ],
15049
14999
  all: [
15050
15000
  ["--key <key>", "Validate a pasted key; skip the browser"],
15051
- ["--global", "Save the key on this machine, not this repo"],
15052
15001
  ["--no-browser", "Print the sign-in link; don't open a browser"],
15053
15002
  ["--json", "Machine-readable output"],
15054
- ["--scope <global|repo>", "Where to save the key, without asking", true],
15003
+ ["--scope repo", "Old spelling; the key is always saved in this repo", true],
15055
15004
  ["--local-credentials", "Alias of --scope repo", true],
15056
15005
  ["--no-color", "No colours or styling (also NO_COLOR=1)"]
15057
15006
  ],
15058
15007
  examples: [
15059
15008
  ["npx pinsay-cli login", "Open the browser; save the key in this repo"],
15060
- ["npx pinsay-cli login --global", "Save the key on this machine"],
15061
15009
  ["npx pinsay-cli login --key <key>", "Save a pasted key; no browser"]
15062
15010
  ]
15063
15011
  },
15064
15012
  logout: {
15065
- purpose: "Remove the key this repo uses",
15013
+ purpose: "Remove this repo's key",
15066
15014
  usage: "npx pinsay-cli logout [options]",
15067
15015
  common: [
15068
- ["--global", "Remove this machine's saved key instead"],
15069
15016
  ["--json", "Emit { ok, server, source, removed } as JSON"]
15070
15017
  ],
15071
15018
  all: [
15072
- ["--global", "Remove this machine's saved key instead"],
15073
15019
  ["--json", "Emit { ok, server, source, removed } as JSON"],
15074
- ["--scope <global|repo>", "Old spelling; only global changes anything", true],
15075
15020
  ["--no-color", "No colours or styling (also NO_COLOR=1)"]
15076
15021
  ],
15077
15022
  examples: [
15078
15023
  ["npx pinsay-cli logout", "Remove this repo's key"],
15079
- ["npx pinsay-cli logout --global", "Also remove this machine's key"]
15024
+ ["npx pinsay-cli update", "Also delete an old machine-wide key"]
15080
15025
  ]
15081
15026
  },
15082
15027
  remove: {
@@ -15085,20 +15030,17 @@ var HELP_TEXTS = {
15085
15030
  common: [
15086
15031
  ["--dry-run", "Show what would be removed; delete nothing"],
15087
15032
  ["-y, --yes", "Remove without asking"],
15088
- ["--global", "Also remove this machine's saved key"],
15089
15033
  ["--json", "Machine-readable output"]
15090
15034
  ],
15091
15035
  all: [
15092
15036
  ["--dry-run", "Show what would be removed; delete nothing"],
15093
15037
  ["-y, --yes", "Remove without asking"],
15094
- ["--global", "Also remove this machine's saved key"],
15095
15038
  ["--json", "Machine-readable output"],
15096
15039
  ["--no-color", "No colours or styling (also NO_COLOR=1)"]
15097
15040
  ],
15098
15041
  examples: [
15099
15042
  ["npx pinsay-cli remove --dry-run", "Show what would be removed"],
15100
- ["npx pinsay-cli remove --yes", "Remove without asking"],
15101
- ["npx pinsay-cli remove --global", "Also remove this machine's key"]
15043
+ ["npx pinsay-cli remove --yes", "Remove without asking"]
15102
15044
  ]
15103
15045
  },
15104
15046
  status: {
@@ -15264,7 +15206,7 @@ var HELP_TEXTS = {
15264
15206
  ]
15265
15207
  },
15266
15208
  update: {
15267
- purpose: "Refresh the AI skills",
15209
+ purpose: "Refresh the AI skills; delete an old machine-wide key",
15268
15210
  usage: "npx pinsay-cli update [options]",
15269
15211
  common: [
15270
15212
  ["--check", "Report what is out of date; write nothing"]
@@ -15313,13 +15255,13 @@ var HELP_TEXTS = {
15313
15255
  ]
15314
15256
  },
15315
15257
  whoami: {
15316
- purpose: "Show the signed-in account and where the key came from",
15258
+ purpose: "Show the signed-in account, workspace and key source",
15317
15259
  usage: "npx pinsay-cli whoami [options]",
15318
15260
  common: [
15319
- ["--json", "Emit { ok, server, displayName, email, source } as JSON"]
15261
+ ["--json", "Emit { ok, server, displayName, email, workspace, source } as JSON"]
15320
15262
  ],
15321
15263
  all: [
15322
- ["--json", "Emit { ok, server, displayName, email, source } as JSON"],
15264
+ ["--json", "Emit { ok, server, displayName, email, workspace, source } as JSON"],
15323
15265
  ["--no-color", "No colours or styling (also NO_COLOR=1)"]
15324
15266
  ],
15325
15267
  examples: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinsay-cli",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Click-to-comment feedback for your web app: install the widget, then turn pending comments into AI apply prompts.",
5
5
  "type": "module",
6
6
  "license": "MIT",