@fixprompt/cli 0.2.0 → 0.3.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/dist/cli.js +105 -55
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -91,7 +91,14 @@ import { readFileSync, readdirSync, statSync } from "fs";
|
|
|
91
91
|
import { join } from "path";
|
|
92
92
|
import { spawnSync } from "child_process";
|
|
93
93
|
var DEFAULT_ENDPOINT = "https://geosloghub-production.up.railway.app";
|
|
94
|
-
var
|
|
94
|
+
var ENV_VAR_BY_SCOPE = {
|
|
95
|
+
deploy: "FIXPROMPT_DEPLOY_TOKEN",
|
|
96
|
+
read: "FIXPROMPT_READ_TOKEN"
|
|
97
|
+
};
|
|
98
|
+
var ENDPOINT_BY_SCOPE = {
|
|
99
|
+
deploy: "deploy-tokens",
|
|
100
|
+
read: "read-tokens"
|
|
101
|
+
};
|
|
95
102
|
function fileExists(p) {
|
|
96
103
|
try {
|
|
97
104
|
return statSync(p).isFile();
|
|
@@ -123,8 +130,9 @@ function detectProvider(cwd) {
|
|
|
123
130
|
}
|
|
124
131
|
return null;
|
|
125
132
|
}
|
|
126
|
-
async function
|
|
127
|
-
const
|
|
133
|
+
async function mintToken(opts) {
|
|
134
|
+
const path = ENDPOINT_BY_SCOPE[opts.scope];
|
|
135
|
+
const url = `${opts.endpoint.replace(/\/$/, "")}/admin/projects/${opts.projectId}/${path}`;
|
|
128
136
|
const res = await fetch(url, {
|
|
129
137
|
method: "POST",
|
|
130
138
|
headers: {
|
|
@@ -135,28 +143,30 @@ async function mintDeployToken(opts) {
|
|
|
135
143
|
});
|
|
136
144
|
const text = await res.text();
|
|
137
145
|
if (!res.ok) {
|
|
138
|
-
throw new Error(
|
|
146
|
+
throw new Error(
|
|
147
|
+
`broker /admin/projects/${opts.projectId}/${path} responded ${res.status}: ${text.slice(0, 200)}`
|
|
148
|
+
);
|
|
139
149
|
}
|
|
140
150
|
return JSON.parse(text);
|
|
141
151
|
}
|
|
142
|
-
function writeEas(token, env) {
|
|
143
|
-
console.log(`
|
|
144
|
-
\u2022 Writing ${TOKEN_ENV_VAR_NAME} to EAS env '${env}'\u2026`);
|
|
152
|
+
function writeEas(envVar, token, env, force) {
|
|
153
|
+
console.log(` \xB7 writing ${envVar} to EAS env '${env}'\u2026`);
|
|
145
154
|
const useShell = process.platform === "win32";
|
|
146
155
|
const args = [
|
|
147
156
|
"eas-cli",
|
|
148
157
|
"env:create",
|
|
149
158
|
env,
|
|
150
159
|
"--name",
|
|
151
|
-
|
|
160
|
+
envVar,
|
|
152
161
|
"--value",
|
|
153
162
|
token,
|
|
154
163
|
"--type",
|
|
155
|
-
"
|
|
164
|
+
"string",
|
|
156
165
|
"--visibility",
|
|
157
166
|
"plaintext",
|
|
158
167
|
"--non-interactive"
|
|
159
168
|
];
|
|
169
|
+
if (force) args.push("--force");
|
|
160
170
|
const r = spawnSync("npx", useShell ? args.map((a) => `"${a.replace(/"/g, '\\"')}"`) : args, {
|
|
161
171
|
stdio: "inherit",
|
|
162
172
|
shell: useShell
|
|
@@ -166,15 +176,14 @@ function writeEas(token, env) {
|
|
|
166
176
|
`eas env:create exited ${r.status}. Common causes:
|
|
167
177
|
\u2022 Not logged in to EAS \u2014 run 'eas login'
|
|
168
178
|
\u2022 Wrong env name \u2014 pass --eas-env <production|preview|development>
|
|
169
|
-
\u2022 Variable already exists
|
|
179
|
+
\u2022 Variable already exists \u2014 re-run with --force to overwrite.`
|
|
170
180
|
);
|
|
171
181
|
}
|
|
172
182
|
}
|
|
173
|
-
function writeGitHubActions(token, cwd) {
|
|
174
|
-
console.log(`
|
|
175
|
-
\u2022 Writing ${TOKEN_ENV_VAR_NAME} to GitHub Actions repo secrets\u2026`);
|
|
183
|
+
function writeGitHubActions(envVar, token, cwd) {
|
|
184
|
+
console.log(` \xB7 writing ${envVar} to GitHub Actions repo secrets\u2026`);
|
|
176
185
|
const useShell = process.platform === "win32";
|
|
177
|
-
const args = ["secret", "set",
|
|
186
|
+
const args = ["secret", "set", envVar, "--body", "-"];
|
|
178
187
|
const r = spawnSync("gh", useShell ? args.map((a) => `"${a.replace(/"/g, '\\"')}"`) : args, {
|
|
179
188
|
input: token,
|
|
180
189
|
cwd,
|
|
@@ -189,7 +198,7 @@ function writeGitHubActions(token, cwd) {
|
|
|
189
198
|
);
|
|
190
199
|
}
|
|
191
200
|
}
|
|
192
|
-
async function writeVercel(token, target) {
|
|
201
|
+
async function writeVercel(envVar, token, target) {
|
|
193
202
|
const vt = process.env.VERCEL_TOKEN;
|
|
194
203
|
if (!vt) {
|
|
195
204
|
throw new Error(
|
|
@@ -215,8 +224,7 @@ directory or pass --vercel-project-id <prj_\u2026>.`
|
|
|
215
224
|
}
|
|
216
225
|
}
|
|
217
226
|
const targets = target.vercelTargets ?? ["production", "preview"];
|
|
218
|
-
console.log(`
|
|
219
|
-
\u2022 Writing ${TOKEN_ENV_VAR_NAME} to Vercel project ${projectId} (${targets.join(", ")})\u2026`);
|
|
227
|
+
console.log(` \xB7 writing ${envVar} to Vercel project ${projectId} (${targets.join(", ")})\u2026`);
|
|
220
228
|
const res = await fetch(`https://api.vercel.com/v10/projects/${projectId}/env`, {
|
|
221
229
|
method: "POST",
|
|
222
230
|
headers: {
|
|
@@ -224,7 +232,7 @@ directory or pass --vercel-project-id <prj_\u2026>.`
|
|
|
224
232
|
"Content-Type": "application/json"
|
|
225
233
|
},
|
|
226
234
|
body: JSON.stringify({
|
|
227
|
-
key:
|
|
235
|
+
key: envVar,
|
|
228
236
|
value: token,
|
|
229
237
|
type: "plain",
|
|
230
238
|
target: targets
|
|
@@ -235,15 +243,26 @@ directory or pass --vercel-project-id <prj_\u2026>.`
|
|
|
235
243
|
throw new Error(`Vercel /env responded ${res.status}: ${text.slice(0, 200)}`);
|
|
236
244
|
}
|
|
237
245
|
}
|
|
246
|
+
function writeToProvider(envVar, token, provider, target, force, cwd) {
|
|
247
|
+
switch (provider) {
|
|
248
|
+
case "eas":
|
|
249
|
+
writeEas(envVar, token, target.easEnv, force);
|
|
250
|
+
return;
|
|
251
|
+
case "github-actions":
|
|
252
|
+
writeGitHubActions(envVar, token, cwd);
|
|
253
|
+
return;
|
|
254
|
+
case "vercel":
|
|
255
|
+
return writeVercel(envVar, token, target);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
238
258
|
async function connect(args) {
|
|
239
259
|
const cwd = process.cwd();
|
|
240
|
-
const
|
|
241
|
-
if (
|
|
242
|
-
console.error(
|
|
243
|
-
"Missing --project-id (or $FIXPROMPT_PROJECT_ID).\n Find it in the FixPrompt dashboard URL after picking your project."
|
|
244
|
-
);
|
|
260
|
+
const scopeRaw = (args.flags.scope ?? "deploy").toLowerCase();
|
|
261
|
+
if (scopeRaw !== "deploy" && scopeRaw !== "read" && scopeRaw !== "both") {
|
|
262
|
+
console.error(`Invalid --scope '${scopeRaw}'. Allowed: deploy | read | both.`);
|
|
245
263
|
process.exit(1);
|
|
246
264
|
}
|
|
265
|
+
const scope = scopeRaw;
|
|
247
266
|
const adminToken = args.flags["admin-token"] ?? process.env.FIXPROMPT_ADMIN_TOKEN ?? process.env.INTERNAL_ADMIN_TOKEN;
|
|
248
267
|
if (!adminToken) {
|
|
249
268
|
console.error(
|
|
@@ -252,6 +271,27 @@ async function connect(args) {
|
|
|
252
271
|
process.exit(1);
|
|
253
272
|
}
|
|
254
273
|
const endpoint = (args.flags.endpoint ?? process.env.FIXPROMPT_ENDPOINT ?? DEFAULT_ENDPOINT).replace(/\/$/, "");
|
|
274
|
+
let projectId = args.flags["project-id"] ?? process.env.FIXPROMPT_PROJECT_ID ?? null;
|
|
275
|
+
const projectSlug = args.flags["project-slug"] ?? null;
|
|
276
|
+
if (!projectId && projectSlug) {
|
|
277
|
+
const res = await fetch(`${endpoint}/admin/projects/by-slug/${encodeURIComponent(projectSlug)}`, {
|
|
278
|
+
headers: { "x-internal-admin-token": adminToken }
|
|
279
|
+
});
|
|
280
|
+
const text = await res.text();
|
|
281
|
+
if (!res.ok) {
|
|
282
|
+
console.error(`Project lookup failed (${res.status}): ${text.slice(0, 200)}`);
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
const parsed = JSON.parse(text);
|
|
286
|
+
projectId = parsed.id;
|
|
287
|
+
console.log(` resolved --project-slug=${projectSlug} \u2192 ${parsed.id} (${parsed.name})`);
|
|
288
|
+
}
|
|
289
|
+
if (!projectId) {
|
|
290
|
+
console.error(
|
|
291
|
+
"Need --project-id or --project-slug (or $FIXPROMPT_PROJECT_ID).\n --project-slug is easiest: it matches the value you see in the dashboard."
|
|
292
|
+
);
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
255
295
|
const explicit = args.flags.provider;
|
|
256
296
|
const provider = explicit ?? detectProvider(cwd);
|
|
257
297
|
if (!provider) {
|
|
@@ -268,50 +308,52 @@ async function connect(args) {
|
|
|
268
308
|
vercelProjectId: args.flags["vercel-project-id"] ?? void 0,
|
|
269
309
|
vercelTargets: typeof args.flags["vercel-targets"] === "string" ? args.flags["vercel-targets"].split(",").map((t) => t.trim()) : void 0
|
|
270
310
|
};
|
|
271
|
-
const
|
|
311
|
+
const labelBase = args.flags.label ?? `${provider}-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19)}`;
|
|
272
312
|
console.log(`fixprompt connect`);
|
|
313
|
+
console.log(` scope : ${scope}`);
|
|
273
314
|
console.log(` provider : ${provider}`);
|
|
274
315
|
console.log(` project_id : ${projectId}`);
|
|
275
316
|
console.log(` endpoint : ${endpoint}`);
|
|
276
|
-
console.log(` label : ${
|
|
317
|
+
console.log(` label : ${labelBase}`);
|
|
277
318
|
if (provider === "eas") console.log(` eas-env : ${target.easEnv}`);
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
break;
|
|
291
|
-
case "github-actions":
|
|
292
|
-
writeGitHubActions(minted.token, cwd);
|
|
293
|
-
break;
|
|
294
|
-
case "vercel":
|
|
295
|
-
await writeVercel(minted.token, target);
|
|
296
|
-
break;
|
|
319
|
+
const force = args.flags.force === true;
|
|
320
|
+
const scopes = scope === "both" ? ["deploy", "read"] : [scope];
|
|
321
|
+
const mintedTokens = [];
|
|
322
|
+
for (const s of scopes) {
|
|
323
|
+
const envVar = ENV_VAR_BY_SCOPE[s];
|
|
324
|
+
const label = scope === "both" ? `${labelBase}-${s}` : labelBase;
|
|
325
|
+
console.log(`
|
|
326
|
+
\u2022 Minting ${s} token via broker\u2026`);
|
|
327
|
+
const minted = await mintToken({ endpoint, adminToken, projectId, label, scope: s });
|
|
328
|
+
console.log(` \u2713 token_id ${minted.token_id}`);
|
|
329
|
+
await writeToProvider(envVar, minted.token, provider, target, force, cwd);
|
|
330
|
+
mintedTokens.push({ scope: s, token_id: minted.token_id, envVar });
|
|
297
331
|
}
|
|
298
332
|
console.log(`
|
|
299
|
-
\u2713 ${
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
333
|
+
\u2713 ${mintedTokens.map((m) => m.envVar).join(" + ")} now set on ${provider}.`);
|
|
334
|
+
if (mintedTokens.some((m) => m.scope === "deploy")) {
|
|
335
|
+
if (provider === "eas") {
|
|
336
|
+
console.log(` Next OTA: npm run release:ota --branch=production --message="..."`);
|
|
337
|
+
} else if (provider === "github-actions") {
|
|
338
|
+
console.log(` Reference \${{ secrets.FIXPROMPT_DEPLOY_TOKEN }} in your workflow.`);
|
|
339
|
+
} else {
|
|
340
|
+
console.log(` The deploy token is in your Vercel project env.`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (mintedTokens.some((m) => m.scope === "read")) {
|
|
344
|
+
console.log(` Customer-side agents can now fetch logs via FIXPROMPT_READ_TOKEN \u2014`);
|
|
345
|
+
console.log(` see the fixprompt-debug skill at fixloop-dashboard.vercel.app/claude-skill.md.`);
|
|
307
346
|
}
|
|
308
347
|
console.log(`
|
|
309
|
-
|
|
348
|
+
Revoke any of these from the dashboard \u2192 Integrations.`);
|
|
349
|
+
for (const m of mintedTokens) {
|
|
350
|
+
console.log(` ${m.scope}: ${m.token_id}`);
|
|
351
|
+
}
|
|
310
352
|
}
|
|
311
353
|
|
|
312
354
|
// src/version.ts
|
|
313
355
|
var CLI_NAME = "@fixprompt/cli";
|
|
314
|
-
var CLI_VERSION = "0.
|
|
356
|
+
var CLI_VERSION = "0.3.0";
|
|
315
357
|
|
|
316
358
|
// src/cli.ts
|
|
317
359
|
var DEFAULT_ENDPOINT2 = "https://geosloghub-production.up.railway.app";
|
|
@@ -350,13 +392,21 @@ Usage:
|
|
|
350
392
|
fixprompt help
|
|
351
393
|
|
|
352
394
|
connect options:
|
|
353
|
-
--
|
|
395
|
+
--scope <s> deploy | read | both (default: deploy)
|
|
396
|
+
deploy \u2192 mints fpd_\u2026, writes FIXPROMPT_DEPLOY_TOKEN
|
|
397
|
+
read \u2192 mints fpr_\u2026, writes FIXPROMPT_READ_TOKEN
|
|
398
|
+
(lets a customer's coding agent fetch
|
|
399
|
+
production logs from FixLoop directly)
|
|
400
|
+
both \u2192 does both in one pass
|
|
401
|
+
--project-id <uuid> FixLoop project id (or $FIXPROMPT_PROJECT_ID)
|
|
402
|
+
--project-slug <slug> Look up project_id by slug (no UUID needed)
|
|
354
403
|
--admin-token <fpa_\u2026> Broker admin token (or $FIXPROMPT_ADMIN_TOKEN)
|
|
355
404
|
--provider <name> eas | vercel | github-actions (auto-detected from cwd)
|
|
356
405
|
--eas-env <env> EAS env to write to (default: production)
|
|
357
406
|
--vercel-project-id <id> Override Vercel project id (default: .vercel/project.json)
|
|
358
407
|
--vercel-targets <list> Comma-separated (default: production,preview)
|
|
359
408
|
--label <text> Token label (default: <provider>-<timestamp>)
|
|
409
|
+
--force Overwrite an existing env var value
|
|
360
410
|
|
|
361
411
|
deploy-start auth (pick one):
|
|
362
412
|
--deploy-token <fpd_...> Deploy-only token from dashboard (recommended for CI)
|
package/package.json
CHANGED