@fixprompt/cli 0.2.1 → 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.
Files changed (2) hide show
  1. package/dist/cli.js +82 -50
  2. 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 TOKEN_ENV_VAR_NAME = "FIXPROMPT_DEPLOY_TOKEN";
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 mintDeployToken(opts) {
127
- const url = `${opts.endpoint.replace(/\/$/, "")}/admin/projects/${opts.projectId}/deploy-tokens`;
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,20 +143,21 @@ async function mintDeployToken(opts) {
135
143
  });
136
144
  const text = await res.text();
137
145
  if (!res.ok) {
138
- throw new Error(`broker /admin/projects/${opts.projectId}/deploy-tokens responded ${res.status}: ${text.slice(0, 200)}`);
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, force) {
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
- TOKEN_ENV_VAR_NAME,
160
+ envVar,
152
161
  "--value",
153
162
  token,
154
163
  "--type",
@@ -171,11 +180,10 @@ function writeEas(token, env, force) {
171
180
  );
172
181
  }
173
182
  }
174
- function writeGitHubActions(token, cwd) {
175
- console.log(`
176
- \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`);
177
185
  const useShell = process.platform === "win32";
178
- const args = ["secret", "set", TOKEN_ENV_VAR_NAME, "--body", "-"];
186
+ const args = ["secret", "set", envVar, "--body", "-"];
179
187
  const r = spawnSync("gh", useShell ? args.map((a) => `"${a.replace(/"/g, '\\"')}"`) : args, {
180
188
  input: token,
181
189
  cwd,
@@ -190,7 +198,7 @@ function writeGitHubActions(token, cwd) {
190
198
  );
191
199
  }
192
200
  }
193
- async function writeVercel(token, target) {
201
+ async function writeVercel(envVar, token, target) {
194
202
  const vt = process.env.VERCEL_TOKEN;
195
203
  if (!vt) {
196
204
  throw new Error(
@@ -216,8 +224,7 @@ directory or pass --vercel-project-id <prj_\u2026>.`
216
224
  }
217
225
  }
218
226
  const targets = target.vercelTargets ?? ["production", "preview"];
219
- console.log(`
220
- \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`);
221
228
  const res = await fetch(`https://api.vercel.com/v10/projects/${projectId}/env`, {
222
229
  method: "POST",
223
230
  headers: {
@@ -225,7 +232,7 @@ directory or pass --vercel-project-id <prj_\u2026>.`
225
232
  "Content-Type": "application/json"
226
233
  },
227
234
  body: JSON.stringify({
228
- key: TOKEN_ENV_VAR_NAME,
235
+ key: envVar,
229
236
  value: token,
230
237
  type: "plain",
231
238
  target: targets
@@ -236,8 +243,26 @@ directory or pass --vercel-project-id <prj_\u2026>.`
236
243
  throw new Error(`Vercel /env responded ${res.status}: ${text.slice(0, 200)}`);
237
244
  }
238
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
+ }
239
258
  async function connect(args) {
240
259
  const cwd = process.cwd();
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.`);
263
+ process.exit(1);
264
+ }
265
+ const scope = scopeRaw;
241
266
  const adminToken = args.flags["admin-token"] ?? process.env.FIXPROMPT_ADMIN_TOKEN ?? process.env.INTERNAL_ADMIN_TOKEN;
242
267
  if (!adminToken) {
243
268
  console.error(
@@ -283,51 +308,52 @@ async function connect(args) {
283
308
  vercelProjectId: args.flags["vercel-project-id"] ?? void 0,
284
309
  vercelTargets: typeof args.flags["vercel-targets"] === "string" ? args.flags["vercel-targets"].split(",").map((t) => t.trim()) : void 0
285
310
  };
286
- const label = args.flags.label ?? `${provider}-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19)}`;
311
+ const labelBase = args.flags.label ?? `${provider}-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19)}`;
287
312
  console.log(`fixprompt connect`);
313
+ console.log(` scope : ${scope}`);
288
314
  console.log(` provider : ${provider}`);
289
315
  console.log(` project_id : ${projectId}`);
290
316
  console.log(` endpoint : ${endpoint}`);
291
- console.log(` label : ${label}`);
317
+ console.log(` label : ${labelBase}`);
292
318
  if (provider === "eas") console.log(` eas-env : ${target.easEnv}`);
293
- console.log(`
294
- \u2022 Minting deploy token via broker\u2026`);
295
- const minted = await mintDeployToken({
296
- endpoint,
297
- adminToken,
298
- projectId,
299
- label
300
- });
301
- console.log(` \u2713 token_id ${minted.token_id} (value hidden \u2014 written to CI store only)`);
302
319
  const force = args.flags.force === true;
303
- switch (provider) {
304
- case "eas":
305
- writeEas(minted.token, target.easEnv, force);
306
- break;
307
- case "github-actions":
308
- writeGitHubActions(minted.token, cwd);
309
- break;
310
- case "vercel":
311
- await writeVercel(minted.token, target);
312
- break;
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 });
313
331
  }
314
332
  console.log(`
315
- \u2713 ${TOKEN_ENV_VAR_NAME} is now set on ${provider}.`);
316
- console.log(` Next deploy can run with no shell paste:`);
317
- if (provider === "eas") {
318
- console.log(` npm run release:ota --branch=production --message="..."`);
319
- } else if (provider === "github-actions") {
320
- console.log(` Reference \${{ secrets.${TOKEN_ENV_VAR_NAME} }} in your workflow.`);
321
- } else {
322
- console.log(` The token is in your Vercel project env for the next deploy.`);
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.`);
323
346
  }
324
347
  console.log(`
325
- To revoke later: dashboard \u2192 Integrations \u2192 Deploy tokens \u2192 ${minted.token_id}.`);
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
+ }
326
352
  }
327
353
 
328
354
  // src/version.ts
329
355
  var CLI_NAME = "@fixprompt/cli";
330
- var CLI_VERSION = "0.2.1";
356
+ var CLI_VERSION = "0.3.0";
331
357
 
332
358
  // src/cli.ts
333
359
  var DEFAULT_ENDPOINT2 = "https://geosloghub-production.up.railway.app";
@@ -366,15 +392,21 @@ Usage:
366
392
  fixprompt help
367
393
 
368
394
  connect options:
369
- --project-id <uuid> FixPrompt project id (or $FIXPROMPT_PROJECT_ID)
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)
370
403
  --admin-token <fpa_\u2026> Broker admin token (or $FIXPROMPT_ADMIN_TOKEN)
371
404
  --provider <name> eas | vercel | github-actions (auto-detected from cwd)
372
405
  --eas-env <env> EAS env to write to (default: production)
373
406
  --vercel-project-id <id> Override Vercel project id (default: .vercel/project.json)
374
407
  --vercel-targets <list> Comma-separated (default: production,preview)
375
408
  --label <text> Token label (default: <provider>-<timestamp>)
376
- --force Overwrite an existing FIXPROMPT_DEPLOY_TOKEN value
377
- --project-slug <slug> Look up project_id by slug (no UUID needed)
409
+ --force Overwrite an existing env var value
378
410
 
379
411
  deploy-start auth (pick one):
380
412
  --deploy-token <fpd_...> Deploy-only token from dashboard (recommended for CI)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fixprompt/cli",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "FixPrompt CLI — annotate deployments and ship them to the broker so the dashboard knows what changed.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {