lynxprompt 2.1.36 → 2.1.37
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/LICENSE +661 -0
- package/dist/index.js +185 -44
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,15 +7,22 @@ import chalk19 from "chalk";
|
|
|
7
7
|
// src/commands/login.ts
|
|
8
8
|
import chalk from "chalk";
|
|
9
9
|
import ora from "ora";
|
|
10
|
+
import { createHash, randomBytes } from "crypto";
|
|
10
11
|
|
|
11
12
|
// src/config.ts
|
|
12
13
|
import Conf from "conf";
|
|
14
|
+
import { chmodSync } from "fs";
|
|
13
15
|
var config = new Conf({
|
|
14
16
|
projectName: "lynxprompt",
|
|
17
|
+
// The file holds the API token: readable by its owner only.
|
|
18
|
+
configFileMode: 384,
|
|
15
19
|
schema: {
|
|
16
20
|
token: {
|
|
17
21
|
type: "string"
|
|
18
22
|
},
|
|
23
|
+
tokenApiUrl: {
|
|
24
|
+
type: "string"
|
|
25
|
+
},
|
|
19
26
|
apiUrl: {
|
|
20
27
|
type: "string",
|
|
21
28
|
default: "https://lynxprompt.com"
|
|
@@ -34,18 +41,44 @@ var config = new Conf({
|
|
|
34
41
|
apiUrl: "https://lynxprompt.com"
|
|
35
42
|
}
|
|
36
43
|
});
|
|
44
|
+
try {
|
|
45
|
+
chmodSync(config.path, 384);
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
function normalizeUrl(url) {
|
|
49
|
+
return url.replace(/\/+$/, "");
|
|
50
|
+
}
|
|
51
|
+
if (config.get("token") && !config.get("tokenApiUrl")) {
|
|
52
|
+
config.set("tokenApiUrl", normalizeUrl(config.get("apiUrl")));
|
|
53
|
+
}
|
|
54
|
+
function storedTokenMatchesApiUrl() {
|
|
55
|
+
return config.get("tokenApiUrl") === normalizeUrl(getApiUrl());
|
|
56
|
+
}
|
|
37
57
|
function getToken() {
|
|
38
58
|
const envToken = process.env.LYNXPROMPT_TOKEN;
|
|
39
59
|
if (envToken) {
|
|
40
60
|
return envToken;
|
|
41
61
|
}
|
|
62
|
+
if (!storedTokenMatchesApiUrl()) {
|
|
63
|
+
return void 0;
|
|
64
|
+
}
|
|
42
65
|
return config.get("token");
|
|
43
66
|
}
|
|
44
67
|
function setToken(token) {
|
|
45
68
|
config.set("token", token);
|
|
69
|
+
config.set("tokenApiUrl", normalizeUrl(getApiUrl()));
|
|
70
|
+
}
|
|
71
|
+
function getStoredCredentials() {
|
|
72
|
+
const token = config.get("token");
|
|
73
|
+
const apiUrl = config.get("tokenApiUrl");
|
|
74
|
+
if (!token || !apiUrl) {
|
|
75
|
+
return void 0;
|
|
76
|
+
}
|
|
77
|
+
return { token, apiUrl, email: config.get("user")?.email };
|
|
46
78
|
}
|
|
47
79
|
function clearToken() {
|
|
48
80
|
config.delete("token");
|
|
81
|
+
config.delete("tokenApiUrl");
|
|
49
82
|
config.delete("user");
|
|
50
83
|
}
|
|
51
84
|
function getApiUrl() {
|
|
@@ -59,6 +92,9 @@ function setApiUrl(url) {
|
|
|
59
92
|
config.set("apiUrl", url);
|
|
60
93
|
}
|
|
61
94
|
function getUser() {
|
|
95
|
+
if (!process.env.LYNXPROMPT_TOKEN && !storedTokenMatchesApiUrl()) {
|
|
96
|
+
return void 0;
|
|
97
|
+
}
|
|
62
98
|
return config.get("user");
|
|
63
99
|
}
|
|
64
100
|
function setUser(user) {
|
|
@@ -105,16 +141,37 @@ var ApiClient = class {
|
|
|
105
141
|
return data;
|
|
106
142
|
}
|
|
107
143
|
// Auth endpoints
|
|
108
|
-
|
|
144
|
+
// codeChallenge is the SHA-256 of a verifier that never leaves this process;
|
|
145
|
+
// polling sends the verifier, so the session ID in the sign-in URL is not
|
|
146
|
+
// enough to collect the token.
|
|
147
|
+
async initCliSession(codeChallenge) {
|
|
109
148
|
return this.request("/api/cli-auth/init", {
|
|
110
|
-
method: "POST"
|
|
149
|
+
method: "POST",
|
|
150
|
+
body: JSON.stringify({ code_challenge: codeChallenge })
|
|
111
151
|
});
|
|
112
152
|
}
|
|
113
|
-
async pollCliSession(sessionId) {
|
|
153
|
+
async pollCliSession(sessionId, verifier) {
|
|
114
154
|
return this.request(
|
|
115
|
-
`/api/cli-auth/poll?session=${sessionId}
|
|
155
|
+
`/api/cli-auth/poll?session=${encodeURIComponent(sessionId)}`,
|
|
156
|
+
{ headers: { "X-CLI-Verifier": verifier } }
|
|
116
157
|
);
|
|
117
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Revoke a stored token on the server that issued it. Best effort: logout
|
|
161
|
+
* clears the local copy whatever this returns.
|
|
162
|
+
*/
|
|
163
|
+
async revokeToken(apiUrl, token) {
|
|
164
|
+
try {
|
|
165
|
+
const response = await fetch(`${apiUrl}/api/cli-auth/revoke`, {
|
|
166
|
+
method: "POST",
|
|
167
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
168
|
+
signal: AbortSignal.timeout(1e4)
|
|
169
|
+
});
|
|
170
|
+
return response.ok;
|
|
171
|
+
} catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
118
175
|
// User endpoints
|
|
119
176
|
async getUser() {
|
|
120
177
|
return this.request("/api/v1/user");
|
|
@@ -281,7 +338,9 @@ async function loginCommand() {
|
|
|
281
338
|
}
|
|
282
339
|
const spinner = ora("Initializing authentication...").start();
|
|
283
340
|
try {
|
|
284
|
-
const
|
|
341
|
+
const verifier = randomBytes(32).toString("base64url");
|
|
342
|
+
const codeChallenge = createHash("sha256").update(verifier).digest("base64url");
|
|
343
|
+
const session = await api.initCliSession(codeChallenge);
|
|
285
344
|
spinner.stop();
|
|
286
345
|
console.log();
|
|
287
346
|
console.log(chalk.cyan("\u{1F510} Opening browser to authenticate..."));
|
|
@@ -300,7 +359,7 @@ async function loginCommand() {
|
|
|
300
359
|
while (Date.now() - startTime < maxWaitTime) {
|
|
301
360
|
await sleep(pollInterval);
|
|
302
361
|
try {
|
|
303
|
-
const result = await api.pollCliSession(session.session_id);
|
|
362
|
+
const result = await api.pollCliSession(session.session_id, verifier);
|
|
304
363
|
if (result.status === "completed" && result.token && result.user) {
|
|
305
364
|
pollSpinner.succeed("Authentication successful!");
|
|
306
365
|
setToken(result.token);
|
|
@@ -391,15 +450,23 @@ function displayWelcome(user) {
|
|
|
391
450
|
// src/commands/logout.ts
|
|
392
451
|
import chalk2 from "chalk";
|
|
393
452
|
async function logoutCommand() {
|
|
394
|
-
|
|
453
|
+
const stored = getStoredCredentials();
|
|
454
|
+
if (!stored) {
|
|
395
455
|
console.log(chalk2.yellow("You are not currently logged in."));
|
|
396
456
|
return;
|
|
397
457
|
}
|
|
398
|
-
const
|
|
399
|
-
const email = user?.email || "unknown";
|
|
458
|
+
const revoked = await api.revokeToken(stored.apiUrl, stored.token);
|
|
400
459
|
clearToken();
|
|
401
|
-
|
|
460
|
+
const who = stored.email ? `${stored.email} at ${stored.apiUrl}` : stored.apiUrl;
|
|
461
|
+
console.log(chalk2.green(`\u2713 Logged out from ${chalk2.bold(who)}`));
|
|
402
462
|
console.log(chalk2.gray(" Removed stored credentials"));
|
|
463
|
+
if (!revoked) {
|
|
464
|
+
console.log(
|
|
465
|
+
chalk2.yellow(
|
|
466
|
+
` Could not revoke the token on the server. Revoke it in Settings > API Tokens at ${stored.apiUrl}.`
|
|
467
|
+
)
|
|
468
|
+
);
|
|
469
|
+
}
|
|
403
470
|
}
|
|
404
471
|
|
|
405
472
|
// src/commands/whoami.ts
|
|
@@ -542,11 +609,11 @@ import { existsSync } from "fs";
|
|
|
542
609
|
// src/utils/blueprint-tracker.ts
|
|
543
610
|
import { readFile, writeFile, mkdir, access } from "fs/promises";
|
|
544
611
|
import { join, dirname } from "path";
|
|
545
|
-
import { createHash } from "crypto";
|
|
612
|
+
import { createHash as createHash2 } from "crypto";
|
|
546
613
|
import * as yaml from "yaml";
|
|
547
614
|
var BLUEPRINTS_FILE = ".lynxprompt/blueprints.yml";
|
|
548
615
|
function calculateChecksum(content) {
|
|
549
|
-
return
|
|
616
|
+
return createHash2("sha256").update(content).digest("hex").substring(0, 16);
|
|
550
617
|
}
|
|
551
618
|
async function loadBlueprints(cwd) {
|
|
552
619
|
const filePath = join(cwd, BLUEPRINTS_FILE);
|
|
@@ -1055,8 +1122,28 @@ import chalk6 from "chalk";
|
|
|
1055
1122
|
import ora5 from "ora";
|
|
1056
1123
|
import fs from "fs";
|
|
1057
1124
|
import path from "path";
|
|
1058
|
-
import { createHash as
|
|
1125
|
+
import { createHash as createHash3 } from "crypto";
|
|
1059
1126
|
import prompts2 from "prompts";
|
|
1127
|
+
|
|
1128
|
+
// src/utils/sensitive-ack.ts
|
|
1129
|
+
function sensitiveMatchesFrom(error) {
|
|
1130
|
+
if (!(error instanceof Error)) return null;
|
|
1131
|
+
const { statusCode, response } = error;
|
|
1132
|
+
if (statusCode !== 422 || response?.requires_acknowledgment !== true) return null;
|
|
1133
|
+
return Array.isArray(response.sensitive_data) ? response.sensitive_data : [];
|
|
1134
|
+
}
|
|
1135
|
+
async function withSensitiveDataAck(send, confirm) {
|
|
1136
|
+
try {
|
|
1137
|
+
return await send(false);
|
|
1138
|
+
} catch (error) {
|
|
1139
|
+
const matches = sensitiveMatchesFrom(error);
|
|
1140
|
+
if (!matches) throw error;
|
|
1141
|
+
if (!await confirm(matches)) throw error;
|
|
1142
|
+
return send(true);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
// src/commands/push.ts
|
|
1060
1147
|
var EXCLUDED_DIRS = /* @__PURE__ */ new Set([
|
|
1061
1148
|
"node_modules",
|
|
1062
1149
|
".git",
|
|
@@ -1090,7 +1177,7 @@ var CONFIG_PATTERNS = [
|
|
|
1090
1177
|
// Directory-based rules
|
|
1091
1178
|
{ match: (p) => normPath(p).includes(".cursor/rules/") && p.endsWith(".mdc"), type: "CURSOR_RULES", label: "Cursor Rules" },
|
|
1092
1179
|
{ match: (p) => normPath(p).includes(".trae/rules/") && p.endsWith(".mdc"), type: "TRAE_RULES", label: "Trae Rules" },
|
|
1093
|
-
{ match: (p) => normPath(p).includes(".idx/") && p.endsWith(".mdc"), type: "
|
|
1180
|
+
{ match: (p) => normPath(p).includes(".idx/") && p.endsWith(".mdc"), type: "IDX_CONFIG", label: "Firebase Rules" },
|
|
1094
1181
|
{ match: (p) => normPath(p).includes(".roo/rules/") && p.endsWith(".mdc"), type: "ROO_RULES", label: "Roo Rules" },
|
|
1095
1182
|
{ match: (p) => normPath(p).includes(".amazonq/rules/") && p.endsWith(".mdc"), type: "AMAZONQ_RULES", label: "Amazon Q Rules" },
|
|
1096
1183
|
{ match: (p) => normPath(p).includes(".augment/rules/") && p.endsWith(".mdc"), type: "AUGMENT_RULES", label: "Augment Rules" },
|
|
@@ -1099,26 +1186,26 @@ var CONFIG_PATTERNS = [
|
|
|
1099
1186
|
// Path-based configs
|
|
1100
1187
|
{ match: (p) => normPath(p).includes(".github/copilot-instructions.md"), type: "COPILOT_INSTRUCTIONS", label: "Copilot Instructions" },
|
|
1101
1188
|
{ match: (p) => normPath(p).includes(".zed/instructions.md"), type: "ZED_INSTRUCTIONS", label: "Zed Instructions" },
|
|
1102
|
-
{ match: (p) => normPath(p).includes(".openhands/microagents/repo.md"), type: "
|
|
1189
|
+
{ match: (p) => normPath(p).includes(".openhands/microagents/repo.md"), type: "OPENHANDS_MD", label: "OpenHands Config" },
|
|
1103
1190
|
{ match: (p) => normPath(p).includes(".junie/guidelines.md"), type: "JUNIE_GUIDELINES", label: "Junie Guidelines" },
|
|
1104
1191
|
{ match: (p) => normPath(p).includes(".void/config.json"), type: "VOID_CONFIG", label: "Void Config" },
|
|
1105
1192
|
{ match: (p) => normPath(p).includes(".continue/config.json"), type: "CONTINUE_CONFIG", label: "Continue Config" },
|
|
1106
1193
|
{ match: (p) => normPath(p).includes(".cody/config.json"), type: "CODY_CONFIG", label: "Cody Config" },
|
|
1107
|
-
{ match: (p) => normPath(p).includes(".supermaven/config.json"), type: "
|
|
1108
|
-
{ match: (p) => normPath(p).includes(".codegpt/config.json"), type: "
|
|
1194
|
+
{ match: (p) => normPath(p).includes(".supermaven/config.json"), type: "CUSTOM", label: "Supermaven Config" },
|
|
1195
|
+
{ match: (p) => normPath(p).includes(".codegpt/config.json"), type: "CUSTOM", label: "CodeGPT Config" },
|
|
1109
1196
|
// Basename rules
|
|
1110
1197
|
{ match: (p) => path.basename(p) === "AGENTS.md", type: "AGENTS_MD", label: "AGENTS.md" },
|
|
1111
1198
|
{ match: (p) => path.basename(p) === "CLAUDE.md", type: "CLAUDE_MD", label: "CLAUDE.md" },
|
|
1112
1199
|
{ match: (p) => path.basename(p) === "AIDER.md", type: "AIDER_MD", label: "AIDER.md" },
|
|
1113
1200
|
{ match: (p) => path.basename(p) === "GEMINI.md", type: "GEMINI_MD", label: "GEMINI.md" },
|
|
1114
1201
|
{ match: (p) => path.basename(p) === "WARP.md", type: "WARP_MD", label: "WARP.md" },
|
|
1115
|
-
{ match: (p) => path.basename(p) === "CRUSH.md", type: "
|
|
1202
|
+
{ match: (p) => path.basename(p) === "CRUSH.md", type: "CUSTOM", label: "CRUSH.md" },
|
|
1116
1203
|
{ match: (p) => path.basename(p) === ".windsurfrules", type: "WINDSURF_RULES", label: "Windsurf Rules" },
|
|
1117
1204
|
{ match: (p) => path.basename(p) === ".clinerules", type: "CLINE_RULES", label: "Cline Rules" },
|
|
1118
|
-
{ match: (p) => path.basename(p) === ".goosehints", type: "
|
|
1205
|
+
{ match: (p) => path.basename(p) === ".goosehints", type: "GOOSEHINTS", label: "Goose Hints" },
|
|
1119
1206
|
{ match: (p) => path.basename(p) === ".tabnine.yaml", type: "TABNINE_CONFIG", label: "Tabnine Config" },
|
|
1120
|
-
{ match: (p) => path.basename(p) === "opencode.json", type: "
|
|
1121
|
-
{ match: (p) => path.basename(p) === "firebender.json", type: "
|
|
1207
|
+
{ match: (p) => path.basename(p) === "opencode.json", type: "OPENCODE_JSON", label: "OpenCode Config" },
|
|
1208
|
+
{ match: (p) => path.basename(p) === "firebender.json", type: "CUSTOM", label: "Firebender Config" }
|
|
1122
1209
|
];
|
|
1123
1210
|
function matchConfigFile(relativePath) {
|
|
1124
1211
|
for (const pattern of CONFIG_PATTERNS) {
|
|
@@ -1277,12 +1364,12 @@ function createRepositoryRoot(rootPath) {
|
|
|
1277
1364
|
const gitConfig = fs.readFileSync(gitConfigPath, "utf-8");
|
|
1278
1365
|
const urlMatch = gitConfig.match(/url = (.+)/);
|
|
1279
1366
|
if (urlMatch) {
|
|
1280
|
-
return
|
|
1367
|
+
return createHash3("sha256").update(urlMatch[1].trim()).digest("hex").substring(0, 16);
|
|
1281
1368
|
}
|
|
1282
1369
|
}
|
|
1283
1370
|
} catch {
|
|
1284
1371
|
}
|
|
1285
|
-
return
|
|
1372
|
+
return createHash3("sha256").update(path.resolve(rootPath)).digest("hex").substring(0, 16);
|
|
1286
1373
|
}
|
|
1287
1374
|
async function pushCommand(fileArg, options) {
|
|
1288
1375
|
const cwd = process.cwd();
|
|
@@ -1361,7 +1448,13 @@ async function updateBlueprint(cwd, file, blueprintId, content, options, expecte
|
|
|
1361
1448
|
if (expectedChecksum && !options.force) {
|
|
1362
1449
|
updateData.expected_checksum = expectedChecksum;
|
|
1363
1450
|
}
|
|
1364
|
-
const result = await
|
|
1451
|
+
const result = await withSensitiveDataAck(
|
|
1452
|
+
(acknowledged) => api.updateBlueprint(
|
|
1453
|
+
blueprintId,
|
|
1454
|
+
acknowledged ? { ...updateData, sensitive_data_acknowledged: true } : updateData
|
|
1455
|
+
),
|
|
1456
|
+
confirmSensitive(options, spinner)
|
|
1457
|
+
);
|
|
1365
1458
|
spinner.succeed("Blueprint updated!");
|
|
1366
1459
|
await updateChecksum(cwd, file, content);
|
|
1367
1460
|
console.log();
|
|
@@ -1519,7 +1612,7 @@ async function createOrLinkBlueprint(cwd, file, filename, content, options) {
|
|
|
1519
1612
|
}
|
|
1520
1613
|
const spinner = ora5("Creating blueprint...").start();
|
|
1521
1614
|
try {
|
|
1522
|
-
const
|
|
1615
|
+
const createData = {
|
|
1523
1616
|
name,
|
|
1524
1617
|
description: description || "",
|
|
1525
1618
|
content,
|
|
@@ -1531,7 +1624,11 @@ async function createOrLinkBlueprint(cwd, file, filename, content, options) {
|
|
|
1531
1624
|
hierarchy_id: hierarchyId,
|
|
1532
1625
|
parent_id: hierarchyInfo.parentId,
|
|
1533
1626
|
repository_path: hierarchyInfo.repositoryPath
|
|
1534
|
-
}
|
|
1627
|
+
};
|
|
1628
|
+
const result = await withSensitiveDataAck(
|
|
1629
|
+
(acknowledged) => api.createBlueprint(acknowledged ? { ...createData, sensitive_data_acknowledged: true } : createData),
|
|
1630
|
+
confirmSensitive(options, spinner)
|
|
1631
|
+
);
|
|
1535
1632
|
spinner.succeed("Blueprint created!");
|
|
1536
1633
|
await trackBlueprint(cwd, {
|
|
1537
1634
|
id: result.blueprint.id,
|
|
@@ -1629,7 +1726,13 @@ async function pushHierarchy(cwd, files, options) {
|
|
|
1629
1726
|
if (tracked.checksum && !options.force) {
|
|
1630
1727
|
updateData.expected_checksum = tracked.checksum;
|
|
1631
1728
|
}
|
|
1632
|
-
await
|
|
1729
|
+
await withSensitiveDataAck(
|
|
1730
|
+
(acknowledged) => api.updateBlueprint(
|
|
1731
|
+
tracked.id,
|
|
1732
|
+
acknowledged ? { ...updateData, sensitive_data_acknowledged: true } : updateData
|
|
1733
|
+
),
|
|
1734
|
+
confirmSensitive(options, spinner)
|
|
1735
|
+
);
|
|
1633
1736
|
await updateChecksum(cwd, file.path, content);
|
|
1634
1737
|
if (file.isRoot) rootBlueprintId = tracked.id;
|
|
1635
1738
|
spinner.succeed(`${file.path} \u2192 updated (${tracked.id})`);
|
|
@@ -1638,7 +1741,13 @@ async function pushHierarchy(cwd, files, options) {
|
|
|
1638
1741
|
}
|
|
1639
1742
|
const existing = await findExistingBlueprintOnServer(file.path, hierarchyId);
|
|
1640
1743
|
if (existing) {
|
|
1641
|
-
await
|
|
1744
|
+
await withSensitiveDataAck(
|
|
1745
|
+
(acknowledged) => api.updateBlueprint(
|
|
1746
|
+
existing.id,
|
|
1747
|
+
acknowledged ? { content, sensitive_data_acknowledged: true } : { content }
|
|
1748
|
+
),
|
|
1749
|
+
confirmSensitive(options, spinner)
|
|
1750
|
+
);
|
|
1642
1751
|
await trackBlueprint(cwd, {
|
|
1643
1752
|
id: existing.id,
|
|
1644
1753
|
name: existing.name,
|
|
@@ -1656,7 +1765,7 @@ async function pushHierarchy(cwd, files, options) {
|
|
|
1656
1765
|
}
|
|
1657
1766
|
const blueprintName = file.isRoot ? hierarchyName : path.basename(file.path) === "AGENTS.md" ? path.dirname(file.path).replace(/[/\\]/g, " / ") : file.path.replace(/\\/g, "/");
|
|
1658
1767
|
const parentId = !file.isRoot && file.type === "AGENTS_MD" && rootBlueprintId ? rootBlueprintId : null;
|
|
1659
|
-
const
|
|
1768
|
+
const createData = {
|
|
1660
1769
|
name: blueprintName,
|
|
1661
1770
|
description: "",
|
|
1662
1771
|
content,
|
|
@@ -1666,7 +1775,11 @@ async function pushHierarchy(cwd, files, options) {
|
|
|
1666
1775
|
hierarchy_id: hierarchyId,
|
|
1667
1776
|
parent_id: parentId,
|
|
1668
1777
|
repository_path: file.path
|
|
1669
|
-
}
|
|
1778
|
+
};
|
|
1779
|
+
const result = await withSensitiveDataAck(
|
|
1780
|
+
(acknowledged) => api.createBlueprint(acknowledged ? { ...createData, sensitive_data_acknowledged: true } : createData),
|
|
1781
|
+
confirmSensitive(options, spinner)
|
|
1782
|
+
);
|
|
1670
1783
|
if (file.isRoot) rootBlueprintId = result.blueprint.id;
|
|
1671
1784
|
await trackBlueprint(cwd, {
|
|
1672
1785
|
id: result.blueprint.id,
|
|
@@ -1727,6 +1840,36 @@ function inferBlueprintType(filePath) {
|
|
|
1727
1840
|
if (filePath.endsWith(".md") || filePath.endsWith(".mdc")) return "AGENTS_MD";
|
|
1728
1841
|
return "CUSTOM";
|
|
1729
1842
|
}
|
|
1843
|
+
function confirmSensitive(options, spinner) {
|
|
1844
|
+
return async (matches) => {
|
|
1845
|
+
spinner?.stop();
|
|
1846
|
+
console.log(chalk6.yellow("\n\u26A0 This blueprint is public and may contain secrets:"));
|
|
1847
|
+
for (const m of matches) {
|
|
1848
|
+
console.log(chalk6.gray(` line ${m.line}: ${m.type}${m.snippet ? ` (${m.snippet})` : ""}`));
|
|
1849
|
+
}
|
|
1850
|
+
let publish = false;
|
|
1851
|
+
if (options.allowSensitive) {
|
|
1852
|
+
console.log(chalk6.gray(" Publishing anyway (--allow-sensitive)."));
|
|
1853
|
+
publish = true;
|
|
1854
|
+
} else if (!options.yes && process.stdin.isTTY) {
|
|
1855
|
+
const answer = await prompts2({
|
|
1856
|
+
type: "confirm",
|
|
1857
|
+
name: "publish",
|
|
1858
|
+
message: "Publish it anyway?",
|
|
1859
|
+
initial: false
|
|
1860
|
+
});
|
|
1861
|
+
publish = answer.publish === true;
|
|
1862
|
+
}
|
|
1863
|
+
if (publish) {
|
|
1864
|
+
spinner?.start();
|
|
1865
|
+
} else {
|
|
1866
|
+
console.log(
|
|
1867
|
+
chalk6.gray(" Remove them, keep the blueprint private, or run again with --allow-sensitive to publish anyway.")
|
|
1868
|
+
);
|
|
1869
|
+
}
|
|
1870
|
+
return publish;
|
|
1871
|
+
};
|
|
1872
|
+
}
|
|
1730
1873
|
function handleError(error) {
|
|
1731
1874
|
if (error instanceof ApiRequestError) {
|
|
1732
1875
|
console.error(chalk6.red(`Error: ${error.message}`));
|
|
@@ -3427,9 +3570,9 @@ function generateFileContent(options, platform2) {
|
|
|
3427
3570
|
if (preferCli) {
|
|
3428
3571
|
sections.push("");
|
|
3429
3572
|
sections.push("# Using LynxPrompt CLI (recommended):");
|
|
3430
|
-
sections.push(
|
|
3431
|
-
sections.push(
|
|
3432
|
-
sections.push(
|
|
3573
|
+
sections.push(`lynxp push # Upload local changes to cloud`);
|
|
3574
|
+
sections.push(`lynxp pull ${bpId} # Download cloud changes to local`);
|
|
3575
|
+
sections.push(`lynxp diff # Compare local vs cloud versions`);
|
|
3433
3576
|
sections.push("");
|
|
3434
3577
|
sections.push("# Install CLI: npm install -g lynxprompt");
|
|
3435
3578
|
sections.push("# Login: lynxp login");
|
|
@@ -3438,21 +3581,19 @@ function generateFileContent(options, platform2) {
|
|
|
3438
3581
|
sections.push("");
|
|
3439
3582
|
sections.push(`# Using curl with environment variable (token stored in $${tokenEnvVar}):`);
|
|
3440
3583
|
sections.push("");
|
|
3584
|
+
const apiUrl = getApiUrl().replace(/\/+$/, "");
|
|
3585
|
+
const url = `${apiUrl}/api/v1/blueprints/${bpId}`;
|
|
3441
3586
|
sections.push("# Push local changes to cloud:");
|
|
3442
|
-
sections.push(`curl -X PUT "
|
|
3443
|
-
sections.push(` -H "Authorization: Bearer $${tokenEnvVar}" \\`);
|
|
3444
|
-
sections.push(' -H "Content-Type: application/json" \\');
|
|
3445
|
-
sections.push(` -d "{\\"content\\": \\"$(cat ${fileName} | jq -Rs .)\\"}"`);
|
|
3587
|
+
sections.push(`jq -Rs '{content: .}' ${fileName} | curl -sf -X PUT "${url}" -H "Authorization: Bearer $${tokenEnvVar}" -H "Content-Type: application/json" --data-binary @-`);
|
|
3446
3588
|
sections.push("");
|
|
3447
3589
|
sections.push("# Pull cloud changes to local:");
|
|
3448
|
-
sections.push(`curl -
|
|
3449
|
-
sections.push(` -H "Authorization: Bearer $${tokenEnvVar}" | jq -r '.content' > ${fileName}`);
|
|
3590
|
+
sections.push(`curl -sf "${url}" -H "Authorization: Bearer $${tokenEnvVar}" | jq -er '.blueprint.content' > ${fileName}.new && mv ${fileName}.new ${fileName}`);
|
|
3450
3591
|
sections.push("");
|
|
3451
3592
|
sections.push(`# Set your token: export ${tokenEnvVar}="your_token_here"`);
|
|
3452
|
-
sections.push(
|
|
3593
|
+
sections.push(`# Generate token at: ${apiUrl}/settings`);
|
|
3453
3594
|
}
|
|
3454
3595
|
sections.push("");
|
|
3455
|
-
sections.push(
|
|
3596
|
+
sections.push(`Docs: ${getApiUrl().replace(/\/+$/, "")}/docs/api`);
|
|
3456
3597
|
sections.push("-->");
|
|
3457
3598
|
sections.push("");
|
|
3458
3599
|
}
|
|
@@ -5726,7 +5867,7 @@ var TEST_FRAMEWORKS2 = TEST_FRAMEWORKS;
|
|
|
5726
5867
|
|
|
5727
5868
|
// src/commands/wizard.ts
|
|
5728
5869
|
var DRAFTS_DIR = ".lynxprompt/drafts";
|
|
5729
|
-
var CLI_VERSION = "2.1.
|
|
5870
|
+
var CLI_VERSION = "2.1.37";
|
|
5730
5871
|
async function saveDraftLocally(name, config2, stepReached) {
|
|
5731
5872
|
const draftsPath = join3(process.cwd(), DRAFTS_DIR);
|
|
5732
5873
|
await mkdir3(draftsPath, { recursive: true });
|
|
@@ -11378,7 +11519,7 @@ async function configCommand(action, valueArg) {
|
|
|
11378
11519
|
}
|
|
11379
11520
|
|
|
11380
11521
|
// src/index.ts
|
|
11381
|
-
var CLI_VERSION2 = "2.1.
|
|
11522
|
+
var CLI_VERSION2 = "2.1.37";
|
|
11382
11523
|
var program = new Command();
|
|
11383
11524
|
program.name("lynxprompt").description("CLI for LynxPrompt - Generate AI IDE configuration files").version(CLI_VERSION2);
|
|
11384
11525
|
program.command("wizard").description("Generate AI IDE configuration (recommended for most users)").option("-n, --name <name>", "Project name").option("-d, --description <description>", "Project description").option("-s, --stack <stack>", "Tech stack (comma-separated)").option("-f, --format <format>", "Output format: agents, cursor, or comma-separated for multiple").option("-p, --platforms <platforms>", "Alias for --format (deprecated)").option("--persona <persona>", "AI persona (fullstack, backend, frontend, devops, data, security)").option("--boundaries <level>", "Boundary preset (conservative, standard, permissive)").option("-y, --yes", "Skip prompts, use defaults (generates AGENTS.md)").option("-o, --output <dir>", "Output directory (default: current directory)").option("--repo-url <url>", "Analyze remote repository URL (GitHub/GitLab supported)").option("--blueprint", "Generate with [[VARIABLE|default]] placeholders for templates").option("--license <type>", "License type (mit, apache-2.0, gpl-3.0, etc.)").option("--ci-cd <platform>", "CI/CD platform (github_actions, gitlab_ci, jenkins, etc.)").option("--project-type <type>", "Project type (work, leisure, opensource, learning)").option("--detect-only", "Only detect project info, don't generate files").option("--load-draft <name>", "Load a saved wizard draft").option("--save-draft <name>", "Save wizard state as a draft (auto-saves at end)").option("--vars <values>", "Fill variables: VAR1=value1,VAR2=value2").action(wizardCommand);
|
|
@@ -11391,7 +11532,7 @@ program.command("status").description("Show current AI configuration and tracked
|
|
|
11391
11532
|
program.command("pull <id>").description("Download a blueprint (bp_xxx) or entire hierarchy (ha_xxx)").option("-o, --output <path>", "Output directory", ".").option("-y, --yes", "Overwrite existing files without prompting").option("--preview", "Preview content without downloading").option("--no-track", "Don't track the blueprint for future syncs").action(pullCommand);
|
|
11392
11533
|
program.command("search <query>").description("Search public blueprints in the marketplace").option("-l, --limit <number>", "Number of results", "20").action(searchCommand);
|
|
11393
11534
|
program.command("list").description("List your blueprints").option("-l, --limit <number>", "Number of results", "20").option("-v, --visibility <visibility>", "Filter: PRIVATE, TEAM, PUBLIC, or all").action(listCommand);
|
|
11394
|
-
program.command("push [file]").description("Push local file to LynxPrompt cloud as a blueprint").option("-a, --all", "Scan recursively and push ALL AI config files (40+ types)").option("-n, --name <name>", "Blueprint name").option("-d, --description <desc>", "Blueprint description").option("-v, --visibility <vis>", "Visibility: PRIVATE, TEAM, or PUBLIC", "PRIVATE").option("-t, --tags <tags>", "Tags (comma-separated)").option("-y, --yes", "Skip prompts").option("-f, --force", "Force push (overwrite remote changes)").action(pushCommand);
|
|
11535
|
+
program.command("push [file]").description("Push local file to LynxPrompt cloud as a blueprint").option("-a, --all", "Scan recursively and push ALL AI config files (40+ types)").option("-n, --name <name>", "Blueprint name").option("-d, --description <desc>", "Blueprint description").option("-v, --visibility <vis>", "Visibility: PRIVATE, TEAM, or PUBLIC", "PRIVATE").option("-t, --tags <tags>", "Tags (comma-separated)").option("-y, --yes", "Skip prompts").option("-f, --force", "Force push (overwrite remote changes)").option("--allow-sensitive", "Publish public content even if it looks like it holds secrets").action(pushCommand);
|
|
11395
11536
|
program.command("hierarchies").description("List your blueprint hierarchies (monorepo groupings)").option("-l, --limit <number>", "Number of results", "50").option("-j, --json", "Output as JSON").action(hierarchiesCommand);
|
|
11396
11537
|
program.command("link [file] [blueprint-id]").description("Link a local file to a cloud blueprint for tracking").option("--list", "List all tracked blueprints").action(linkCommand);
|
|
11397
11538
|
program.command("unlink [file]").description("Disconnect a local file from its cloud blueprint").action(unlinkCommand);
|