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.
- package/README.md +12 -2
- package/dist/cli.js +188 -246
- 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 (
|
|
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`
|
|
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.
|
|
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 {
|
|
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,
|
|
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
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
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
|
|
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
|
|
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)" :
|
|
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
|
-
|
|
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
|
|
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" :
|
|
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
|
|
5717
|
-
if (session.origin === "env" || session.origin === "repo"
|
|
5691
|
+
function keyPlacement(session) {
|
|
5692
|
+
if (session.origin === "env" || session.origin === "repo")
|
|
5718
5693
|
return "existing";
|
|
5719
|
-
return
|
|
5694
|
+
return "repo";
|
|
5720
5695
|
}
|
|
5721
|
-
async function saveKey(cwd2,
|
|
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"
|
|
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
|
|
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"
|
|
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,
|
|
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"
|
|
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,
|
|
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"
|
|
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,
|
|
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,
|
|
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
|
-
|
|
14770
|
-
|
|
14771
|
-
|
|
14772
|
-
|
|
14773
|
-
|
|
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: "
|
|
14785
|
-
|
|
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,
|
|
14767
|
+
const finish = (source, removed, message) => {
|
|
14808
14768
|
if (json)
|
|
14809
|
-
console.log(JSON.stringify({ ok: true, server: origin, source, removed
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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