pinsay-cli 0.9.1 → 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 +8 -2
  2. package/dist/cli.js +184 -244
  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,12 @@ 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
+
65
71
  ### 0.9.1
66
72
 
67
73
  - `remove` works when the repo sits behind a linked or shortened path (Windows junctions / short paths).
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.1" : "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}`);
@@ -3497,7 +3501,7 @@ async function writableInsideRoot(root, abs) {
3497
3501
  return false;
3498
3502
  }
3499
3503
  }
3500
- async function collectRemovals(root, opts) {
3504
+ async function collectRemovals(root) {
3501
3505
  const config = await readConfig(root);
3502
3506
  const removals = [];
3503
3507
  if (await pathExists(join28(root, ".pinsay"))) {
@@ -3544,9 +3548,6 @@ async function collectRemovals(root, opts) {
3544
3548
  }
3545
3549
  }
3546
3550
  }
3547
- if (opts.global) {
3548
- removals.push({ kind: "global-key", target: normalizeServerOrigin(opts.server) });
3549
- }
3550
3551
  const byHand = [];
3551
3552
  for (const appDir of appDirs) {
3552
3553
  for (const name of VITE_CONFIGS2) {
@@ -3585,13 +3586,11 @@ async function removeEmptyParents(root, abs) {
3585
3586
  }
3586
3587
  async function removeCommand(cwd2, options) {
3587
3588
  const json = options["json"] === true;
3589
+ if (options["global"] === true)
3590
+ exitWithError(2, GLOBAL_KEY_REMOVED, json);
3588
3591
  const interactive = isInteractive(options);
3589
- const server = resolveServer();
3590
3592
  const root = await findRepoRoot(cwd2);
3591
- const { removals, byHand, tracked } = await collectRemovals(root, {
3592
- global: options["global"] === true,
3593
- server
3594
- });
3593
+ const { removals, byHand, tracked } = await collectRemovals(root);
3595
3594
  if (removals.length === 0) {
3596
3595
  if (json)
3597
3596
  console.log(JSON.stringify({ ok: true, removed: [] }));
@@ -3602,7 +3601,7 @@ async function removeCommand(cwd2, options) {
3602
3601
  if (!json) {
3603
3602
  console.log("Will remove:");
3604
3603
  for (const r of removals) {
3605
- 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)" : "";
3606
3605
  console.log(` ${r.target}${reason ? dim(reason) : ""}`);
3607
3606
  }
3608
3607
  if (byHand.length > 0) {
@@ -3680,8 +3679,6 @@ async function removeCommand(cwd2, options) {
3680
3679
  if (!abs)
3681
3680
  continue;
3682
3681
  await fs25.rm(abs, { recursive: true, force: true });
3683
- } else if (r.kind === "global-key") {
3684
- await removeGlobalCredential(server);
3685
3682
  }
3686
3683
  }
3687
3684
  if (json)
@@ -3697,8 +3694,7 @@ var init_remove = __esm({
3697
3694
  "use strict";
3698
3695
  init_git_exclude();
3699
3696
  init_config();
3700
- init_credentials();
3701
- init_server();
3697
+ init_key_scope();
3702
3698
  init_errors();
3703
3699
  init_interactive();
3704
3700
  init_prompt();
@@ -4262,7 +4258,7 @@ async function runInitChecks(cwd2, overrides = {}, cliVersion = "0.0.0") {
4262
4258
  checks.push({
4263
4259
  id: "key",
4264
4260
  status: "error",
4265
- message: "No API key found (env, repo, or global store)",
4261
+ message: "No API key found (env var or this repo)",
4266
4262
  hint: "Run `npx pinsay-cli login`"
4267
4263
  });
4268
4264
  } else if (!serverReachable) {
@@ -5118,25 +5114,7 @@ async function detectDesignTokens(cwd2, options) {
5118
5114
 
5119
5115
  // src/commands/init.ts
5120
5116
  init_credentials();
5121
-
5122
- // src/key-scope.ts
5123
- function scopeFromFlags(options) {
5124
- const raw = options["scope"];
5125
- if (typeof raw === "string") {
5126
- const value = raw.toLowerCase();
5127
- if (value !== "global" && value !== "repo") {
5128
- return { error: `Invalid --scope "${raw}". Valid values: global, repo.` };
5129
- }
5130
- return { scope: value };
5131
- }
5132
- if (options["global"] === true)
5133
- return { scope: "global" };
5134
- if (options["local-credentials"] === true)
5135
- return { scope: "repo" };
5136
- return {};
5137
- }
5138
-
5139
- // src/commands/init.ts
5117
+ init_key_scope();
5140
5118
  init_server();
5141
5119
  init_errors();
5142
5120
  init_style();
@@ -5468,18 +5446,13 @@ function accountText(plan) {
5468
5446
  if (account.keySaved === "repo") {
5469
5447
  return `${account.displayName} \xB7 key saved in this repo (.pinsay/credentials.env, hidden from git)`;
5470
5448
  }
5471
- if (account.keySaved === "global") {
5472
- return `${account.displayName} \xB7 key saved on this machine (${account.globalPath})`;
5473
- }
5474
5449
  if (account.existingSource === "env")
5475
5450
  return `${account.displayName} \xB7 key from PINSAY_API_KEY`;
5476
- if (account.existingSource === "global")
5477
- return `${account.displayName} \xB7 key already saved on this machine`;
5478
5451
  return `${account.displayName} \xB7 key already saved in this repo`;
5479
5452
  }
5480
5453
  if (account.kind === "pending")
5481
5454
  return "you'll sign in in your browser";
5482
- 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";
5483
5456
  return `key found (${where}), not checked in a dry run`;
5484
5457
  }
5485
5458
  function sharedText(plan) {
@@ -5715,21 +5688,14 @@ async function decideWidget(cwd2, options, config, ctx) {
5715
5688
  const w = await findExistingWidget(cwd2, config.htmlPath);
5716
5689
  return w ? { widget: { kind: "already", file: w.file } } : { widget: { kind: "extension" } };
5717
5690
  }
5718
- function keyPlacement(session, saveGlobal) {
5719
- if (session.origin === "env" || session.origin === "repo" || session.origin === "global")
5691
+ function keyPlacement(session) {
5692
+ if (session.origin === "env" || session.origin === "repo")
5720
5693
  return "existing";
5721
- return saveGlobal ? "global" : "repo";
5694
+ return "repo";
5722
5695
  }
5723
- async function saveKey(cwd2, server, session, keySaved, project) {
5724
- if (keySaved === "repo") {
5696
+ async function saveKey(cwd2, session, keySaved, project) {
5697
+ if (keySaved === "repo")
5725
5698
  await writeCredentials(cwd2, session.key, project ? { project } : {});
5726
- } else if (keySaved === "global") {
5727
- await saveGlobalCredential(server, {
5728
- apiKey: session.key,
5729
- email: session.me.email,
5730
- displayName: session.me.displayName
5731
- });
5732
- }
5733
5699
  }
5734
5700
  async function readStackNames(cwd2) {
5735
5701
  const pkgStr = await fs17.readFile(join17(cwd2, "package.json"), "utf8").catch(() => "{}");
@@ -5766,7 +5732,6 @@ async function initCommand(cwd2, options = {}) {
5766
5732
  const scopeResult = scopeFromFlags(options);
5767
5733
  if (scopeResult.error)
5768
5734
  exitWithError(2, scopeResult.error, json);
5769
- const saveGlobal = scopeResult.scope === "global";
5770
5735
  const shareResult = shareFromFlags(options);
5771
5736
  if (shareResult.error)
5772
5737
  exitWithError(2, shareResult.error, json);
@@ -5828,7 +5793,6 @@ async function initCommand(cwd2, options = {}) {
5828
5793
  product,
5829
5794
  pathFlag,
5830
5795
  configIsMulti,
5831
- saveGlobal,
5832
5796
  share: shareResult.share,
5833
5797
  wantEmbed,
5834
5798
  notes
@@ -5852,7 +5816,7 @@ async function initCommand(cwd2, options = {}) {
5852
5816
  const files2 = [
5853
5817
  ".pinsay/config.json",
5854
5818
  ".pinsay/stack.json",
5855
- ...account.kind === "pending" && !saveGlobal ? [".pinsay/credentials.env"] : [],
5819
+ ...account.kind === "pending" ? [".pinsay/credentials.env"] : [],
5856
5820
  ...widget2.kind === "embed" ? widget2.files : [],
5857
5821
  ".git/info/exclude (PinSay block)"
5858
5822
  ];
@@ -5889,7 +5853,7 @@ async function initCommand(cwd2, options = {}) {
5889
5853
  });
5890
5854
  if (!json)
5891
5855
  console.log(`${green(sym.check)} Signed in as ${session.me.displayName}`);
5892
- const keySaved = keyPlacement(session, saveGlobal);
5856
+ const keySaved = keyPlacement(session);
5893
5857
  if (isJoin && configIsMulti) {
5894
5858
  await handleMultiJoin({
5895
5859
  cwd: cwd2,
@@ -5963,8 +5927,7 @@ async function initCommand(cwd2, options = {}) {
5963
5927
  kind: "signed-in",
5964
5928
  displayName: session.me.displayName,
5965
5929
  keySaved,
5966
- existingSource: session.origin === "env" || session.origin === "repo" || session.origin === "global" ? session.origin : void 0,
5967
- globalPath: globalCredentialsPath()
5930
+ existingSource: session.origin === "env" || session.origin === "repo" ? session.origin : void 0
5968
5931
  },
5969
5932
  widget,
5970
5933
  skills,
@@ -6031,7 +5994,7 @@ async function initCommand(cwd2, options = {}) {
6031
5994
  let htmlPath;
6032
5995
  if (keySaved !== "existing") {
6033
5996
  progress.step("Saving your key");
6034
- await saveKey(cwd2, server, session, keySaved, project.key);
5997
+ await saveKey(cwd2, session, keySaved, project.key);
6035
5998
  if (keySaved === "repo")
6036
5999
  filesMod.push(".pinsay/credentials.env");
6037
6000
  }
@@ -6257,12 +6220,11 @@ function planAccount(session, keySaved) {
6257
6220
  kind: "signed-in",
6258
6221
  displayName: session.me.displayName,
6259
6222
  keySaved,
6260
- existingSource: session.origin === "env" || session.origin === "repo" || session.origin === "global" ? session.origin : void 0,
6261
- globalPath: globalCredentialsPath()
6223
+ existingSource: session.origin === "env" || session.origin === "repo" ? session.origin : void 0
6262
6224
  };
6263
6225
  }
6264
6226
  async function multiDryRun(args) {
6265
- 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;
6266
6228
  const flagTool = typeof options["tool"] === "string" ? options["tool"] : void 0;
6267
6229
  const resolved = await resolveApiKey(cwd2, server);
6268
6230
  const account = resolved.key && resolved.source ? { kind: "found", source: resolved.source } : { kind: "pending" };
@@ -6270,7 +6232,7 @@ async function multiDryRun(args) {
6270
6232
  const tool = tools[0];
6271
6233
  const names = pathFlag ? await readAppStackNames(cwd2, [pathFlag]) : await readAppStackNames(cwd2, listProjects(config).map((p) => p.path));
6272
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 };
6273
- const keyFile = account.kind === "pending" && !saveGlobal ? [".pinsay/credentials.env"] : [];
6235
+ const keyFile = account.kind === "pending" ? [".pinsay/credentials.env"] : [];
6274
6236
  let plan;
6275
6237
  let projectLine;
6276
6238
  if (pathFlag) {
@@ -6485,7 +6447,7 @@ async function handleMultiJoin(args) {
6485
6447
  const progress = createProgress(labels.length, progressMode(isJson));
6486
6448
  if (keySaved !== "existing") {
6487
6449
  progress.step("Saving your key");
6488
- await saveKey(cwd2, server, session, keySaved);
6450
+ await saveKey(cwd2, session, keySaved);
6489
6451
  }
6490
6452
  const skillWarnings = [];
6491
6453
  const hide = [];
@@ -6655,7 +6617,7 @@ async function handleMultiProjectSetup(args) {
6655
6617
  const progress = createProgress(labels.length, progressMode(isJson));
6656
6618
  if (keySaved !== "existing") {
6657
6619
  progress.step("Saving your key");
6658
- await saveKey(cwd2, server, session, keySaved);
6620
+ await saveKey(cwd2, session, keySaved);
6659
6621
  }
6660
6622
  const skillWarnings = [];
6661
6623
  const hide = [];
@@ -6957,6 +6919,7 @@ init_repo_paths();
6957
6919
  init_server();
6958
6920
  init_git_exclude();
6959
6921
  init_style();
6922
+ init_credentials();
6960
6923
  function sourceFor(path) {
6961
6924
  if (path.includes("pinsay-init"))
6962
6925
  return "/pinsay-init.md";
@@ -6980,6 +6943,17 @@ async function updateCommand(cwd2, options) {
6980
6943
  const note = pinsayShNote(cwd2);
6981
6944
  if (note)
6982
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
+ }
6983
6957
  const config = await readConfig(cwd2);
6984
6958
  const server = (options.server || resolveServer()).replace(/\/$/, "");
6985
6959
  if (!config.project && !isMultiProject(config)) {
@@ -8018,7 +7992,7 @@ init_projection();
8018
7992
  init_server();
8019
7993
  async function exitIfWorkspaceFrozen(server, token) {
8020
7994
  if (!token)
8021
- return;
7995
+ return void 0;
8022
7996
  try {
8023
7997
  const me = await api(server, "/api/auth/me", { token });
8024
7998
  if (me?.workspaceDeletionScheduledFor) {
@@ -8032,7 +8006,9 @@ async function exitIfWorkspaceFrozen(server, token) {
8032
8006
  );
8033
8007
  process.exit(2);
8034
8008
  }
8009
+ return me?.tenantName || void 0;
8035
8010
  } catch {
8011
+ return void 0;
8036
8012
  }
8037
8013
  }
8038
8014
  async function checkMetaAndNotify(server, config, root, parsed) {
@@ -8114,13 +8090,14 @@ async function applyCommand(cwd2, parsed, positionals = []) {
8114
8090
  );
8115
8091
  process.exit(3);
8116
8092
  }
8117
- await exitIfWorkspaceFrozen(server, token);
8093
+ const workspaceName = await exitIfWorkspaceFrozen(server, token);
8118
8094
  const clientCtx = {
8119
8095
  server,
8120
8096
  project,
8121
8097
  token,
8122
8098
  apiKey,
8123
- cwd: root
8099
+ cwd: root,
8100
+ workspaceName
8124
8101
  };
8125
8102
  if (parsed["mark"] !== void 0) {
8126
8103
  const markVal2 = parsed["mark"];
@@ -8246,7 +8223,7 @@ async function applyAllProjects(root, cwd2, config, server, parsed) {
8246
8223
  );
8247
8224
  process.exit(3);
8248
8225
  }
8249
- await exitIfWorkspaceFrozen(server, token);
8226
+ const workspaceName = await exitIfWorkspaceFrozen(server, token);
8250
8227
  const projects = listProjects(config);
8251
8228
  const plan = parsed["plan"] === true;
8252
8229
  const status = typeof parsed["status"] === "string" ? parsed["status"] : void 0;
@@ -8254,7 +8231,7 @@ async function applyAllProjects(root, cwd2, config, server, parsed) {
8254
8231
  if (parsed["json"] === true && !plan) {
8255
8232
  const all = [];
8256
8233
  for (const p of projects) {
8257
- const clientCtx = { server, project: p.key, token, apiKey, cwd: root };
8234
+ const clientCtx = { server, project: p.key, token, apiKey, cwd: root, workspaceName };
8258
8235
  const items = await fetchQueue(clientCtx, { status, environment });
8259
8236
  all.push({ project: p.key, path: p.path, items: items.map((item) => toAiCommentView(item)) });
8260
8237
  }
@@ -8263,7 +8240,7 @@ async function applyAllProjects(root, cwd2, config, server, parsed) {
8263
8240
  }
8264
8241
  const sections = [];
8265
8242
  for (const p of projects) {
8266
- const clientCtx = { server, project: p.key, token, apiKey, cwd: root };
8243
+ const clientCtx = { server, project: p.key, token, apiKey, cwd: root, workspaceName };
8267
8244
  const result = await runApply({ plan, status, environment }, clientCtx);
8268
8245
  sections.push(`# Project: ${p.key} (${p.path})
8269
8246
 
@@ -14690,8 +14667,8 @@ init_git_exclude();
14690
14667
  import { existsSync as existsSync7 } from "node:fs";
14691
14668
  import { join as join26 } from "node:path";
14692
14669
  import { execFileSync as execFileSync2 } from "node:child_process";
14693
- init_credentials();
14694
14670
  init_prompt();
14671
+ init_key_scope();
14695
14672
  init_server();
14696
14673
  init_device_login();
14697
14674
  init_errors();
@@ -14710,11 +14687,13 @@ async function loginCommand(cwd2, options = {}) {
14710
14687
  if (flags.error)
14711
14688
  exitWithError(2, flags.error, json);
14712
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
+ }
14713
14694
  const server = resolveServer();
14714
14695
  const branding = await getBranding(server);
14715
14696
  const product = branding.productName;
14716
- const inRepo = existsSync7(join26(root, ".pinsay")) || inGitRepo(root);
14717
- const scope = flags.scope ?? (inRepo ? "repo" : "global");
14718
14697
  let flagKey = typeof options["key"] === "string" ? options["key"].trim() : "";
14719
14698
  if (options["key"] === true) {
14720
14699
  if (!process.stdin.isTTY)
@@ -14723,24 +14702,6 @@ async function loginCommand(cwd2, options = {}) {
14723
14702
  if (!flagKey)
14724
14703
  exitWithError(2, noKeyMessage(product), json);
14725
14704
  }
14726
- if (scope === "global" && !flagKey) {
14727
- const repoKey = await readRepoApiKey(root);
14728
- if (repoKey) {
14729
- try {
14730
- const { me } = await exchangeKey(server, repoKey);
14731
- await saveGlobalCredential(server, { apiKey: repoKey, email: me.email, displayName: me.displayName });
14732
- await removeRepoCredentials(root);
14733
- done(
14734
- json,
14735
- { server, scope: "global", moved: true, displayName: me.displayName, email: me.email },
14736
- `${green(sym.check)} Moved your key to this machine's store (${globalCredentialsPath()}). Every repo on this machine can use it now.`
14737
- );
14738
- } catch (err) {
14739
- if (!(err instanceof InvalidKeyError))
14740
- throw err;
14741
- }
14742
- }
14743
- }
14744
14705
  let key;
14745
14706
  let displayName;
14746
14707
  let email;
@@ -14768,23 +14729,15 @@ async function loginCommand(cwd2, options = {}) {
14768
14729
  }
14769
14730
  closePrompts();
14770
14731
  const who = displayName ? `${displayName}${email ? ` (${email})` : ""}` : email ?? "you";
14771
- if (scope === "repo") {
14772
- await writeCredentials(root, key);
14773
- const hideWarnings = formatHideWarnings(await hidePinsayFiles(root));
14774
- if (!json)
14775
- for (const line of hideWarnings)
14776
- console.error(line);
14777
- done(
14778
- json,
14779
- { server, scope: "repo", moved: false, displayName, email },
14780
- `${green(sym.check)} Signed in as ${who}. Key saved in this repo (.pinsay/credentials.env, hidden from git).`
14781
- );
14782
- }
14783
- 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);
14784
14737
  done(
14785
14738
  json,
14786
- { server, scope: "global", moved: false, displayName, email },
14787
- 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).`
14788
14741
  );
14789
14742
  }
14790
14743
  function done(json, payload, message) {
@@ -14798,35 +14751,33 @@ function done(json, payload, message) {
14798
14751
  // src/commands/logout.ts
14799
14752
  init_config();
14800
14753
  init_credentials();
14754
+ init_key_scope();
14801
14755
  init_server();
14756
+ init_errors();
14802
14757
  init_style();
14803
14758
  var ENV_KEY_MESSAGE = "Your key comes from the PINSAY_API_KEY variable. Unset it to sign out.";
14804
14759
  async function logoutCommand(cwd2, options = {}) {
14805
14760
  const json = options["json"] === true;
14761
+ const flags = scopeFromFlags(options);
14762
+ if (flags.error)
14763
+ exitWithError(2, flags.error, json);
14806
14764
  const server = resolveServer();
14807
14765
  const origin = normalizeServerOrigin(server);
14808
14766
  const root = await findRepoRoot(cwd2);
14809
- const finish = (source, removed2, message) => {
14767
+ const finish = (source, removed, message) => {
14810
14768
  if (json)
14811
- console.log(JSON.stringify({ ok: true, server: origin, source, removed: removed2 }));
14769
+ console.log(JSON.stringify({ ok: true, server: origin, source, removed }));
14812
14770
  else
14813
14771
  console.log(message);
14814
14772
  process.exit(0);
14815
14773
  };
14816
- const isGlobal = options["global"] === true || String(options["scope"] ?? "").toLowerCase() === "global";
14817
- if (!isGlobal && process.env.PINSAY_API_KEY?.trim()) {
14774
+ if (process.env.PINSAY_API_KEY?.trim())
14818
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).`);
14819
14779
  }
14820
- if (!isGlobal && await readRepoApiKey(root)) {
14821
- const removed2 = await removeRepoCredentials(root);
14822
- finish("repo", removed2, `${green(sym.check)} Signed out: removed this repo's key (.pinsay/credentials.env).`);
14823
- }
14824
- const removed = await removeGlobalCredential(server);
14825
- finish(
14826
- "global",
14827
- removed,
14828
- removed ? `${green(sym.check)} Removed the saved key for ${origin} from this machine.` : `No saved key for ${origin} on this machine.`
14829
- );
14780
+ finish("repo", false, "No key saved in this repo.");
14830
14781
  }
14831
14782
 
14832
14783
  // src/commands/whoami.ts
@@ -14843,33 +14794,31 @@ async function whoamiCommand(cwd2, options = {}) {
14843
14794
  if (isJson) {
14844
14795
  console.log(JSON.stringify({ ok: false, server, source: null }));
14845
14796
  } else {
14846
- 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).`);
14847
14798
  console.error("Run `npx pinsay-cli login` to sign in.");
14848
14799
  }
14849
14800
  process.exit(3);
14850
14801
  }
14851
14802
  let displayName;
14852
14803
  let email;
14804
+ let workspace;
14853
14805
  try {
14854
14806
  const login = await api(server, "/api/auth/login-with-key", { method: "POST", body: { apiKey: key } });
14855
14807
  if (login?.status === "ok" && login.token) {
14856
- const me = login.user ?? await api(server, "/api/auth/me", { token: login.token });
14857
- displayName = me?.displayName;
14858
- 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;
14859
14812
  }
14860
14813
  } catch {
14861
14814
  }
14862
- if (source === "global" && (!displayName || !email)) {
14863
- const cached = await getGlobalCredential(server);
14864
- displayName = displayName ?? cached?.displayName;
14865
- email = email ?? cached?.email;
14866
- }
14867
14815
  if (isJson) {
14868
- console.log(JSON.stringify({ ok: true, server, displayName, email, source }));
14816
+ console.log(JSON.stringify({ ok: true, server, displayName, email, workspace: workspace ?? null, source }));
14869
14817
  process.exit(0);
14870
14818
  }
14871
14819
  const who = displayName ? `${displayName}${email ? ` (${email})` : ""}` : email ?? "unknown user";
14872
- 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)}`);
14873
14822
  process.exit(0);
14874
14823
  }
14875
14824
 
@@ -14905,6 +14854,7 @@ function parseArgs(args) {
14905
14854
  // 0.9.0
14906
14855
  "embed",
14907
14856
  "global",
14857
+ // rejected since 0.10.0 (key-scope.ts); kept so it never takes the next argument
14908
14858
  "share-stack",
14909
14859
  "no-share-stack",
14910
14860
  "no-color",
@@ -14942,7 +14892,7 @@ var HELP = `Usage: npx pinsay-cli <command> [options]
14942
14892
  Set up
14943
14893
  init Set up this repo (sign in, project, skills)
14944
14894
  embed Put the widget in your app's code
14945
- login Sign in; the key is saved in this repo (--global: this machine)
14895
+ login Sign in; the key is saved in this repo
14946
14896
  logout Remove the key this repo uses
14947
14897
  remove Remove everything PinSay added to this repo
14948
14898
  Daily
@@ -14954,10 +14904,10 @@ Daily
14954
14904
  reply Reply to a comment
14955
14905
  Tools
14956
14906
  doctor Check this install and fix what it can
14957
- update Refresh the AI skills
14907
+ update Refresh the AI skills; delete an old machine-wide key
14958
14908
  map Rebuild the source map without a build
14959
14909
  mcp Start the MCP server for AI tools
14960
- whoami Show the signed-in account and where the key came from
14910
+ whoami Show the signed-in account, workspace and key source
14961
14911
 
14962
14912
  Run npx pinsay-cli <command> --help for examples.
14963
14913
  Flags: --no-color No colours or styling (also NO_COLOR=1)
@@ -14988,7 +14938,6 @@ var HELP_TEXTS = {
14988
14938
  ["--dry-run", "Show the plan; write and send nothing"],
14989
14939
  ["--path <app>", "The app folder, in a repo with several apps"],
14990
14940
  ["--key <key>", "Sign in with this key instead of the browser"],
14991
- ["--global", "Save the key on this machine (repo is the default)"],
14992
14941
  ["--no-browser", "Print the sign-in link; don't open a browser"],
14993
14942
  ["--tool <tool>", "AI tool for the skills (asked when omitted)"],
14994
14943
  ["--skills-dir <path>", "Where the skills are installed"],
@@ -15001,7 +14950,7 @@ var HELP_TEXTS = {
15001
14950
  ["--no-share-stack", "Share nothing about this app's stack"],
15002
14951
  ["--delivery <embed|extension>", "How reviewers open the widget", true],
15003
14952
  ["--no-inject", "Skip injection (use --embed or --html)", true],
15004
- ["--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],
15005
14954
  ["--local-credentials", "Alias of --scope repo", true],
15006
14955
  ["--no-color", "No colours or styling (also NO_COLOR=1)"]
15007
14956
  ],
@@ -15040,45 +14989,39 @@ var HELP_TEXTS = {
15040
14989
  ]
15041
14990
  },
15042
14991
  login: {
15043
- purpose: "Sign in; the key is saved in this repo (--global: this machine)",
14992
+ purpose: "Sign in; the key is saved in this repo",
15044
14993
  usage: "npx pinsay-cli login [options]",
15045
14994
  common: [
15046
14995
  ["--key <key>", "Validate a pasted key; skip the browser"],
15047
- ["--global", "Save the key on this machine, not this repo"],
15048
14996
  ["--no-browser", "Print the sign-in link; don't open a browser"],
15049
14997
  ["--json", "Machine-readable output"]
15050
14998
  ],
15051
14999
  all: [
15052
15000
  ["--key <key>", "Validate a pasted key; skip the browser"],
15053
- ["--global", "Save the key on this machine, not this repo"],
15054
15001
  ["--no-browser", "Print the sign-in link; don't open a browser"],
15055
15002
  ["--json", "Machine-readable output"],
15056
- ["--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],
15057
15004
  ["--local-credentials", "Alias of --scope repo", true],
15058
15005
  ["--no-color", "No colours or styling (also NO_COLOR=1)"]
15059
15006
  ],
15060
15007
  examples: [
15061
15008
  ["npx pinsay-cli login", "Open the browser; save the key in this repo"],
15062
- ["npx pinsay-cli login --global", "Save the key on this machine"],
15063
15009
  ["npx pinsay-cli login --key <key>", "Save a pasted key; no browser"]
15064
15010
  ]
15065
15011
  },
15066
15012
  logout: {
15067
- purpose: "Remove the key this repo uses",
15013
+ purpose: "Remove this repo's key",
15068
15014
  usage: "npx pinsay-cli logout [options]",
15069
15015
  common: [
15070
- ["--global", "Remove this machine's saved key instead"],
15071
15016
  ["--json", "Emit { ok, server, source, removed } as JSON"]
15072
15017
  ],
15073
15018
  all: [
15074
- ["--global", "Remove this machine's saved key instead"],
15075
15019
  ["--json", "Emit { ok, server, source, removed } as JSON"],
15076
- ["--scope <global|repo>", "Old spelling; only global changes anything", true],
15077
15020
  ["--no-color", "No colours or styling (also NO_COLOR=1)"]
15078
15021
  ],
15079
15022
  examples: [
15080
15023
  ["npx pinsay-cli logout", "Remove this repo's key"],
15081
- ["npx pinsay-cli logout --global", "Also remove this machine's key"]
15024
+ ["npx pinsay-cli update", "Also delete an old machine-wide key"]
15082
15025
  ]
15083
15026
  },
15084
15027
  remove: {
@@ -15087,20 +15030,17 @@ var HELP_TEXTS = {
15087
15030
  common: [
15088
15031
  ["--dry-run", "Show what would be removed; delete nothing"],
15089
15032
  ["-y, --yes", "Remove without asking"],
15090
- ["--global", "Also remove this machine's saved key"],
15091
15033
  ["--json", "Machine-readable output"]
15092
15034
  ],
15093
15035
  all: [
15094
15036
  ["--dry-run", "Show what would be removed; delete nothing"],
15095
15037
  ["-y, --yes", "Remove without asking"],
15096
- ["--global", "Also remove this machine's saved key"],
15097
15038
  ["--json", "Machine-readable output"],
15098
15039
  ["--no-color", "No colours or styling (also NO_COLOR=1)"]
15099
15040
  ],
15100
15041
  examples: [
15101
15042
  ["npx pinsay-cli remove --dry-run", "Show what would be removed"],
15102
- ["npx pinsay-cli remove --yes", "Remove without asking"],
15103
- ["npx pinsay-cli remove --global", "Also remove this machine's key"]
15043
+ ["npx pinsay-cli remove --yes", "Remove without asking"]
15104
15044
  ]
15105
15045
  },
15106
15046
  status: {
@@ -15266,7 +15206,7 @@ var HELP_TEXTS = {
15266
15206
  ]
15267
15207
  },
15268
15208
  update: {
15269
- purpose: "Refresh the AI skills",
15209
+ purpose: "Refresh the AI skills; delete an old machine-wide key",
15270
15210
  usage: "npx pinsay-cli update [options]",
15271
15211
  common: [
15272
15212
  ["--check", "Report what is out of date; write nothing"]
@@ -15315,13 +15255,13 @@ var HELP_TEXTS = {
15315
15255
  ]
15316
15256
  },
15317
15257
  whoami: {
15318
- purpose: "Show the signed-in account and where the key came from",
15258
+ purpose: "Show the signed-in account, workspace and key source",
15319
15259
  usage: "npx pinsay-cli whoami [options]",
15320
15260
  common: [
15321
- ["--json", "Emit { ok, server, displayName, email, source } as JSON"]
15261
+ ["--json", "Emit { ok, server, displayName, email, workspace, source } as JSON"]
15322
15262
  ],
15323
15263
  all: [
15324
- ["--json", "Emit { ok, server, displayName, email, source } as JSON"],
15264
+ ["--json", "Emit { ok, server, displayName, email, workspace, source } as JSON"],
15325
15265
  ["--no-color", "No colours or styling (also NO_COLOR=1)"]
15326
15266
  ],
15327
15267
  examples: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinsay-cli",
3
- "version": "0.9.1",
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",