percher 0.5.15 → 0.5.17

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.
@@ -105969,6 +105969,9 @@ class AccountResource {
105969
105969
  listTokens() {
105970
105970
  return this.http.get("/account/tokens");
105971
105971
  }
105972
+ createToken(input) {
105973
+ return this.http.post("/account/tokens", input);
105974
+ }
105972
105975
  revokeToken(id) {
105973
105976
  return this.http.delete(`/account/tokens/${encodeURIComponent(id)}`);
105974
105977
  }
@@ -114802,6 +114805,12 @@ function classifyPublishApiError(err, input) {
114802
114805
  const auth = matchAuthError(err);
114803
114806
  if (auth)
114804
114807
  return buildResult(auth, input);
114808
+ const tooLarge = matchInlinePublishTooLarge(err);
114809
+ if (tooLarge)
114810
+ return buildResult(tooLarge, input);
114811
+ const repoMissingConfig = matchRepoMissingConfig(err);
114812
+ if (repoMissingConfig)
114813
+ return buildResult(repoMissingConfig, input);
114805
114814
  const retryLimit = matchRetryLimitReached(err, input);
114806
114815
  if (retryLimit)
114807
114816
  return buildResult(retryLimit, input);
@@ -115086,6 +115095,40 @@ function matchDailyQuotaExceeded(err, input) {
115086
115095
  summary: `Daily ${kind}-deploy quota reached (${used}/${limit}). Resets ${resetAt}.`
115087
115096
  };
115088
115097
  }
115098
+ function matchInlinePublishTooLarge(err) {
115099
+ if (err.code !== "INLINE_PUBLISH_TOO_LARGE")
115100
+ return null;
115101
+ return {
115102
+ title: "Inline source too large",
115103
+ explanation: err.message,
115104
+ suggestion: "Inline publishing is for small, text-generated apps. Publish a larger or on-disk app another way: on-disk project → the local `bunx percher publish` CLI (or `npx -y @percher/mcp`); public repo → percher_publish_from_git; private repo → connect it via the GitHub App, then percher_deploy.",
115105
+ recovery: recoveryAsk({
115106
+ prompt: `${err.message} Publish by source instead: on-disk → local \`bunx percher publish\` / \`npx -y @percher/mcp\`; public repo → percher_publish_from_git; private repo → connect via the GitHub App, then percher_deploy.`,
115107
+ options: [
115108
+ "publish on-disk (bunx percher publish / npx @percher/mcp)",
115109
+ "public repo (percher_publish_from_git)",
115110
+ "private repo (GitHub App + percher_deploy)"
115111
+ ],
115112
+ reasonCode: "unknown"
115113
+ }),
115114
+ summary: `Inline source too large: ${err.message}`
115115
+ };
115116
+ }
115117
+ function matchRepoMissingConfig(err) {
115118
+ if (err.code !== "REPO_MISSING_CONFIG")
115119
+ return null;
115120
+ return {
115121
+ title: "Repo has no percher.toml",
115122
+ explanation: err.message,
115123
+ suggestion: "Add a percher.toml (with [app] name + runtime) to the repo root and push, then retry — or publish the source inline with percher_publish instead.",
115124
+ recovery: recoveryAsk({
115125
+ prompt: `${err.message} Add a percher.toml to the repo root (then retry percher_publish_from_git), or publish the source inline with percher_publish.`,
115126
+ options: ["add percher.toml to the repo", "publish inline (percher_publish)"],
115127
+ reasonCode: "config_missing"
115128
+ }),
115129
+ summary: `Repo missing percher.toml: ${err.message}`
115130
+ };
115131
+ }
115089
115132
  function matchGenericClientError(err) {
115090
115133
  if (err.status < 400 || err.status >= 500)
115091
115134
  return null;
@@ -117039,6 +117082,34 @@ function summarize2(verdict, health) {
117039
117082
  }
117040
117083
  return "API responded but reported a non-ok status";
117041
117084
  }
117085
+ // ../core/dist/commands/token.js
117086
+ var tokenCreateInputSchema = exports_external.object({
117087
+ name: exports_external.string().trim().min(1).max(80).describe("Human label for the token (shown in the list)."),
117088
+ scope: exports_external.enum(["deploy", "env", "read", "full"]).default("full").describe("Least-privilege scope: deploy (publish/rollback/logs), env (set/read env), read (GETs only), or full (account-wide). Prefer the narrowest that works for CI/agents."),
117089
+ app: exports_external.string().trim().min(1).optional().describe("Bind the token to a single app by name. Omit for all of your apps."),
117090
+ ttlDays: exports_external.number().int().positive().max(365).optional().describe("Token lifetime in days (server default ≈ 90).")
117091
+ });
117092
+ var tokenRevokeInputSchema = exports_external.object({
117093
+ id: exports_external.string().min(1).describe("Token id from `percher token list`.")
117094
+ });
117095
+ async function tokenCreate(ctx, input) {
117096
+ ctx.status("Minting token…");
117097
+ return ctx.client.account.createToken({
117098
+ name: input.name,
117099
+ scope: input.scope,
117100
+ app: input.app,
117101
+ ttlDays: input.ttlDays
117102
+ });
117103
+ }
117104
+ async function tokenList(ctx) {
117105
+ ctx.status("Loading tokens…");
117106
+ const { tokens } = await ctx.client.account.listTokens();
117107
+ return tokens;
117108
+ }
117109
+ async function tokenRevoke(ctx, input) {
117110
+ ctx.status("Revoking token…");
117111
+ await ctx.client.account.revokeToken(input.id);
117112
+ }
117042
117113
  // ../core/dist/commands/transfers.js
117043
117114
  function resolveApp5(ctx, app) {
117044
117115
  const name = app ?? readPercherTomlAppName(ctx.cwd);
@@ -119168,18 +119239,91 @@ async function renderHumanStatus(ctx, result) {
119168
119239
  }
119169
119240
  }
119170
119241
 
119171
- // src/commands/transfers.ts
119242
+ // src/commands/token.ts
119172
119243
  var import_picocolors14 = __toESM(require_picocolors(), 1);
119244
+ var VALID_SCOPES = ["deploy", "env", "read", "full"];
119245
+ function scopeLabel(scopes, appName) {
119246
+ const s = scopes && scopes.length > 0 ? scopes.join("+") : "full";
119247
+ return appName ? `${s} · ${appName}` : `${s} · all apps`;
119248
+ }
119249
+ function registerTokenCommands(cli, ctx) {
119250
+ cli.command("token [action] [arg]", "Manage API tokens: create | list | revoke").option("--scope <scope>", "(create) deploy | env | read | full (default: full)").option("--app <name>", "(create) bind the token to a single app").option("--ttl-days <n>", "(create) token lifetime in days").action(async (action, arg, options) => {
119251
+ if (!ctx.client.token) {
119252
+ await writeLine(ctx, `${import_picocolors14.default.red("✗")} Not logged in. Run: percher login`);
119253
+ throw new SilentExitError;
119254
+ }
119255
+ if (action === "create") {
119256
+ if (!arg) {
119257
+ await writeLine(ctx, `${import_picocolors14.default.red("✗")} A name is required. Usage: percher token create <name> [--scope deploy] [--app <name>]`);
119258
+ throw new SilentExitError;
119259
+ }
119260
+ const scope = options.scope ?? "full";
119261
+ if (!VALID_SCOPES.includes(scope)) {
119262
+ await writeLine(ctx, `${import_picocolors14.default.red("✗")} Invalid --scope "${options.scope}". Use one of: ${VALID_SCOPES.join(", ")}.`);
119263
+ throw new SilentExitError;
119264
+ }
119265
+ const ttlDays = options.ttlDays !== undefined ? Number(options.ttlDays) : undefined;
119266
+ if (ttlDays !== undefined && (!Number.isInteger(ttlDays) || ttlDays <= 0)) {
119267
+ await writeLine(ctx, `${import_picocolors14.default.red("✗")} --ttl-days must be a positive whole number.`);
119268
+ throw new SilentExitError;
119269
+ }
119270
+ const created = await tokenCreate(ctx, {
119271
+ name: arg,
119272
+ scope,
119273
+ app: options.app,
119274
+ ttlDays
119275
+ });
119276
+ await writeLine(ctx, `${import_picocolors14.default.green("✓")} Token created: ${import_picocolors14.default.bold(created.name)} ${import_picocolors14.default.dim(`(${scopeLabel(created.scopes, created.appName)})`)}`);
119277
+ await writeLine(ctx, "");
119278
+ await writeLine(ctx, ` ${import_picocolors14.default.bold(created.token)}`);
119279
+ await writeLine(ctx, import_picocolors14.default.yellow(" ↑ Store this now — the secret is shown only once and can't be retrieved."));
119280
+ await writeLine(ctx, "");
119281
+ await writeLine(ctx, import_picocolors14.default.dim(" Use it in CI / an agent:"));
119282
+ await writeLine(ctx, import_picocolors14.default.dim(` PERCHER_TOKEN=${created.token} bunx percher publish`));
119283
+ return;
119284
+ }
119285
+ if (action === "list") {
119286
+ const tokens = await tokenList(ctx);
119287
+ if (tokens.length === 0) {
119288
+ await writeLine(ctx, import_picocolors14.default.dim("No API tokens."));
119289
+ return;
119290
+ }
119291
+ for (const t of tokens) {
119292
+ const expires = t.expiresAt ? `expires ${new Date(t.expiresAt).toISOString().slice(0, 10)}` : "no expiry";
119293
+ await writeLine(ctx, `${import_picocolors14.default.bold(t.id)} ${t.name.padEnd(20)} ${import_picocolors14.default.dim(scopeLabel(t.scopes, t.appName))} ${import_picocolors14.default.dim(expires)}`);
119294
+ }
119295
+ await writeLine(ctx, "");
119296
+ await writeLine(ctx, import_picocolors14.default.dim("Revoke one with: percher token revoke <id>"));
119297
+ return;
119298
+ }
119299
+ if (action === "revoke") {
119300
+ if (!arg) {
119301
+ await writeLine(ctx, `${import_picocolors14.default.red("✗")} A token id is required. Usage: percher token revoke <id> (list ids with \`percher token list\`).`);
119302
+ throw new SilentExitError;
119303
+ }
119304
+ await tokenRevoke(ctx, { id: arg });
119305
+ await writeLine(ctx, `${import_picocolors14.default.green("✓")} Token revoked.`);
119306
+ return;
119307
+ }
119308
+ throw new Error(`Unknown token action "${action ?? ""}". Use: create | list | revoke.`);
119309
+ });
119310
+ cli.command("token create <name>", "Mint a scoped API token (the secret is shown once)").option("--scope <scope>", "deploy | env | read | full (default: full)").option("--app <name>", "Bind the token to a single app").option("--ttl-days <n>", "Token lifetime in days").action(async () => {});
119311
+ cli.command("token list", "List your API tokens with scope + bound app").action(async () => {});
119312
+ cli.command("token revoke <id>", "Revoke an API token by id").action(async () => {});
119313
+ }
119314
+
119315
+ // src/commands/transfers.ts
119316
+ var import_picocolors15 = __toESM(require_picocolors(), 1);
119173
119317
  function registerTransfersCommands(cli, ctx) {
119174
119318
  cli.command("transfer [emailOrTransferId]", "Transfer ownership of an app: `transfer <email>` initiates; `transfer --cancel <id>` cancels; `transfer --list` shows history").option("--app <name>", "App name (overrides percher.toml)").option("--cancel", "Cancel a pending transfer by id (positional arg is the transferId)").option("--list", "List the transfer history for this app").action(async (emailOrTransferId, options) => {
119175
119319
  if (options.list) {
119176
119320
  const rows = await transferListForApp(ctx, { app: options.app });
119177
119321
  if (rows.length === 0) {
119178
- await writeLine(ctx, import_picocolors14.default.dim("No transfers for this app."));
119322
+ await writeLine(ctx, import_picocolors15.default.dim("No transfers for this app."));
119179
119323
  return;
119180
119324
  }
119181
119325
  for (const r of rows) {
119182
- const status2 = r.status === "pending" ? import_picocolors14.default.yellow("pending") : r.status === "accepted" ? import_picocolors14.default.green("accepted") : import_picocolors14.default.dim(r.status);
119326
+ const status2 = r.status === "pending" ? import_picocolors15.default.yellow("pending") : r.status === "accepted" ? import_picocolors15.default.green("accepted") : import_picocolors15.default.dim(r.status);
119183
119327
  await writeLine(ctx, `${r.id} ${status2.padEnd(20)} → ${r.toEmail} (${r.createdAt})`);
119184
119328
  }
119185
119329
  return;
@@ -119192,7 +119336,7 @@ function registerTransfersCommands(cli, ctx) {
119192
119336
  app: options.app,
119193
119337
  transferId: emailOrTransferId
119194
119338
  });
119195
- await writeLine(ctx, `${import_picocolors14.default.green("✓")} Cancelled transfer ${emailOrTransferId}`);
119339
+ await writeLine(ctx, `${import_picocolors15.default.green("✓")} Cancelled transfer ${emailOrTransferId}`);
119196
119340
  return;
119197
119341
  }
119198
119342
  if (!emailOrTransferId) {
@@ -119202,17 +119346,17 @@ function registerTransfersCommands(cli, ctx) {
119202
119346
  app: options.app,
119203
119347
  email: emailOrTransferId
119204
119348
  });
119205
- await writeLine(ctx, `${import_picocolors14.default.green("✓")} Transfer initiated to ${initiated.toEmail} (id ${initiated.id})`);
119206
- await writeLine(ctx, import_picocolors14.default.dim(`Expires ${new Date(initiated.expiresAt).toLocaleDateString()}. The recipient must accept via 'percher transfers accept ${initiated.id}'.`));
119349
+ await writeLine(ctx, `${import_picocolors15.default.green("✓")} Transfer initiated to ${initiated.toEmail} (id ${initiated.id})`);
119350
+ await writeLine(ctx, import_picocolors15.default.dim(`Expires ${new Date(initiated.expiresAt).toLocaleDateString()}. The recipient must accept via 'percher transfers accept ${initiated.id}'.`));
119207
119351
  if (initiated.notes) {
119208
- await writeLine(ctx, import_picocolors14.default.yellow(` ⚠ ${initiated.notes}`));
119352
+ await writeLine(ctx, import_picocolors15.default.yellow(` ⚠ ${initiated.notes}`));
119209
119353
  }
119210
119354
  });
119211
119355
  cli.command("transfers [action] [transferId]", "Incoming ownership transfers: list (default) | accept <transferId> | decline <transferId>").action(async (action, transferId) => {
119212
119356
  if (action === undefined || action === "list") {
119213
119357
  const rows = await transfersIncoming(ctx, {});
119214
119358
  if (rows.length === 0) {
119215
- await writeLine(ctx, import_picocolors14.default.dim("No pending incoming transfers."));
119359
+ await writeLine(ctx, import_picocolors15.default.dim("No pending incoming transfers."));
119216
119360
  return;
119217
119361
  }
119218
119362
  for (const r of rows) {
@@ -119225,7 +119369,7 @@ function registerTransfersCommands(cli, ctx) {
119225
119369
  throw new Error("Usage: percher transfers accept <transferId>");
119226
119370
  }
119227
119371
  const accepted = await transfersAccept(ctx, { transferId });
119228
- await writeLine(ctx, `${import_picocolors14.default.green("✓")} You now own ${import_picocolors14.default.cyan(accepted.appName)}. Run 'percher open --app ${accepted.appName}' to see it.`);
119372
+ await writeLine(ctx, `${import_picocolors15.default.green("✓")} You now own ${import_picocolors15.default.cyan(accepted.appName)}. Run 'percher open --app ${accepted.appName}' to see it.`);
119229
119373
  return;
119230
119374
  }
119231
119375
  if (action === "decline") {
@@ -119233,7 +119377,7 @@ function registerTransfersCommands(cli, ctx) {
119233
119377
  throw new Error("Usage: percher transfers decline <transferId>");
119234
119378
  }
119235
119379
  await transfersDecline(ctx, { transferId });
119236
- await writeLine(ctx, `${import_picocolors14.default.green("✓")} Declined transfer ${transferId}.`);
119380
+ await writeLine(ctx, `${import_picocolors15.default.green("✓")} Declined transfer ${transferId}.`);
119237
119381
  return;
119238
119382
  }
119239
119383
  throw new Error(`Unknown transfers action "${action}". Use list | accept <id> | decline <id>.`);
@@ -119302,52 +119446,52 @@ async function createCliContext() {
119302
119446
  }
119303
119447
 
119304
119448
  // src/output.ts
119305
- var import_picocolors15 = __toESM(require_picocolors(), 1);
119449
+ var import_picocolors16 = __toESM(require_picocolors(), 1);
119306
119450
  async function renderError(stderr, error48) {
119307
119451
  const writer = stderr.getWriter();
119308
119452
  const encoder = new TextEncoder;
119309
119453
  const lines = [];
119310
119454
  if (error48 instanceof PercherTomlError) {
119311
- lines.push(`${import_picocolors15.default.red(`error (${error48.code}):`)} ${error48.message}`);
119455
+ lines.push(`${import_picocolors16.default.red(`error (${error48.code}):`)} ${error48.message}`);
119312
119456
  if (error48.tomlLine !== undefined) {
119313
- lines.push(import_picocolors15.default.dim(` at percher.toml line ${error48.tomlLine}`));
119457
+ lines.push(import_picocolors16.default.dim(` at percher.toml line ${error48.tomlLine}`));
119314
119458
  }
119315
119459
  for (const issue2 of error48.issues ?? []) {
119316
- lines.push(`${import_picocolors15.default.dim(" · ") + import_picocolors15.default.yellow(issue2.path.join("."))}: ${issue2.message}`);
119460
+ lines.push(`${import_picocolors16.default.dim(" · ") + import_picocolors16.default.yellow(issue2.path.join("."))}: ${issue2.message}`);
119317
119461
  }
119318
119462
  } else if (error48 instanceof SunsetError) {
119319
119463
  if (error48.gateCode === "platform_delete_gated") {
119320
- lines.push(`${import_picocolors15.default.yellow("Percher is shutting down — deletes are paused.")}`);
119464
+ lines.push(`${import_picocolors16.default.yellow("Percher is shutting down — deletes are paused.")}`);
119321
119465
  lines.push(error48.message);
119322
- lines.push(import_picocolors15.default.dim(` To delete and permanently forfeit your export window, confirm with your password in the dashboard: ${error48.dashboardUrl}`));
119323
- lines.push(import_picocolors15.default.dim(" The CLI cannot perform this delete during sunset — it must go through the dashboard."));
119466
+ lines.push(import_picocolors16.default.dim(` To delete and permanently forfeit your export window, confirm with your password in the dashboard: ${error48.dashboardUrl}`));
119467
+ lines.push(import_picocolors16.default.dim(" The CLI cannot perform this delete during sunset — it must go through the dashboard."));
119324
119468
  } else {
119325
- lines.push(`${import_picocolors15.default.yellow("Percher is in read-only mode ahead of shutdown.")}`);
119469
+ lines.push(`${import_picocolors16.default.yellow("Percher is in read-only mode ahead of shutdown.")}`);
119326
119470
  lines.push(error48.message);
119327
- lines.push(import_picocolors15.default.dim(` Your apps keep running. Export them any time from the dashboard: ${error48.dashboardUrl}`));
119471
+ lines.push(import_picocolors16.default.dim(` Your apps keep running. Export them any time from the dashboard: ${error48.dashboardUrl}`));
119328
119472
  }
119329
- lines.push(import_picocolors15.default.dim(` Final data deletion: ${error48.finalDeletionDate}`));
119473
+ lines.push(import_picocolors16.default.dim(` Final data deletion: ${error48.finalDeletionDate}`));
119330
119474
  } else if (error48 instanceof PercherApiError) {
119331
119475
  if (error48.status === 502 || error48.status === 503) {
119332
- lines.push(`${import_picocolors15.default.red("error:")} Percher API is temporarily unavailable (HTTP ${error48.status}). Try again in a few seconds.`);
119476
+ lines.push(`${import_picocolors16.default.red("error:")} Percher API is temporarily unavailable (HTTP ${error48.status}). Try again in a few seconds.`);
119333
119477
  } else {
119334
- lines.push(`${import_picocolors15.default.red("error:")} ${error48.message}`);
119335
- lines.push(import_picocolors15.default.dim(` ${error48.code} (HTTP ${error48.status})`));
119478
+ lines.push(`${import_picocolors16.default.red("error:")} ${error48.message}`);
119479
+ lines.push(import_picocolors16.default.dim(` ${error48.code} (HTTP ${error48.status})`));
119336
119480
  }
119337
119481
  if (error48.requestId)
119338
- lines.push(import_picocolors15.default.dim(` request: ${error48.requestId}`));
119482
+ lines.push(import_picocolors16.default.dim(` request: ${error48.requestId}`));
119339
119483
  } else if (error48 instanceof PercherCoreError) {
119340
- lines.push(`${import_picocolors15.default.red(`error (${error48.code}):`)} ${error48.message}`);
119484
+ lines.push(`${import_picocolors16.default.red(`error (${error48.code}):`)} ${error48.message}`);
119341
119485
  if (error48.hint)
119342
- lines.push(import_picocolors15.default.dim(` ${error48.hint}`));
119486
+ lines.push(import_picocolors16.default.dim(` ${error48.hint}`));
119343
119487
  } else if (error48 instanceof Error) {
119344
- lines.push(`${import_picocolors15.default.red("unexpected error:")} ${error48.message}`);
119488
+ lines.push(`${import_picocolors16.default.red("unexpected error:")} ${error48.message}`);
119345
119489
  if (error48.stack)
119346
- lines.push(import_picocolors15.default.dim(error48.stack.split(`
119490
+ lines.push(import_picocolors16.default.dim(error48.stack.split(`
119347
119491
  `).slice(1, 4).join(`
119348
119492
  `)));
119349
119493
  } else {
119350
- lines.push(`${import_picocolors15.default.red("unexpected error:")} ${String(error48)}`);
119494
+ lines.push(`${import_picocolors16.default.red("unexpected error:")} ${String(error48)}`);
119351
119495
  }
119352
119496
  try {
119353
119497
  await writer.write(encoder.encode(`${lines.join(`
@@ -119360,7 +119504,7 @@ async function renderError(stderr, error48) {
119360
119504
  // package.json
119361
119505
  var package_default = {
119362
119506
  name: "percher",
119363
- version: "0.5.15",
119507
+ version: "0.5.17",
119364
119508
  description: "CLI for the Percher hosting platform — publish apps with one command",
119365
119509
  type: "module",
119366
119510
  bin: {
@@ -119457,7 +119601,7 @@ async function showTelemetryNotice() {
119457
119601
  }
119458
119602
 
119459
119603
  // src/version-check.ts
119460
- var import_picocolors16 = __toESM(require_picocolors(), 1);
119604
+ var import_picocolors17 = __toESM(require_picocolors(), 1);
119461
119605
  async function checkForUpdates() {
119462
119606
  try {
119463
119607
  const res = await fetch("https://registry.npmjs.org/percher/latest", {
@@ -119468,9 +119612,9 @@ async function checkForUpdates() {
119468
119612
  const data2 = await res.json();
119469
119613
  const latest = data2.version;
119470
119614
  if (latest && latest !== VERSION) {
119471
- console.error(import_picocolors16.default.yellow(`
119615
+ console.error(import_picocolors17.default.yellow(`
119472
119616
  Update available: ${VERSION} → ${latest}`));
119473
- console.error(import_picocolors16.default.dim(`Run: bunx percher@latest
119617
+ console.error(import_picocolors17.default.dim(`Run: bunx percher@latest
119474
119618
  `));
119475
119619
  }
119476
119620
  } catch {}
@@ -119491,6 +119635,7 @@ async function run(argv, ctxOverride) {
119491
119635
  registerMigrateCommands(cli, ctx);
119492
119636
  registerSharingCommands(cli, ctx);
119493
119637
  registerStatusCommands(cli, ctx);
119638
+ registerTokenCommands(cli, ctx);
119494
119639
  registerTransfersCommands(cli, ctx);
119495
119640
  registerAdminCommands(cli, ctx);
119496
119641
  cli.help();
package/dist/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "percher",
3
- "version": "0.5.15",
4
- "description": "CLI for the Percher hosting platform \u2014 publish apps with one command",
3
+ "version": "0.5.17",
4
+ "description": "CLI for the Percher hosting platform — publish apps with one command",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "percher": "./dist/bin/percher.js"
@@ -0,0 +1,4 @@
1
+ import type { Context } from "@percher/core";
2
+ import type { CAC } from "cac";
3
+ export declare function registerTokenCommands(cli: CAC, ctx: Context): void;
4
+ //# sourceMappingURL=token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../../src/commands/token.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAY/B,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAwHlE"}
@@ -0,0 +1,97 @@
1
+ import * as core from "@percher/core";
2
+ import pc from "picocolors";
3
+ import { SilentExitError, writeLine } from "../cli-utils";
4
+ const VALID_SCOPES = ["deploy", "env", "read", "full"];
5
+ function scopeLabel(scopes, appName) {
6
+ const s = scopes && scopes.length > 0 ? scopes.join("+") : "full";
7
+ return appName ? `${s} · ${appName}` : `${s} · all apps`;
8
+ }
9
+ export function registerTokenCommands(cli, ctx) {
10
+ // cac matches commands by single first-token equality, so `token create
11
+ // <name>` routes here with action="create". `[arg]` is the token label for
12
+ // create and the token id for revoke.
13
+ cli
14
+ .command("token [action] [arg]", "Manage API tokens: create | list | revoke")
15
+ .option("--scope <scope>", "(create) deploy | env | read | full (default: full)")
16
+ .option("--app <name>", "(create) bind the token to a single app")
17
+ .option("--ttl-days <n>", "(create) token lifetime in days")
18
+ .action(async (action, arg, options) => {
19
+ if (!ctx.client.token) {
20
+ await writeLine(ctx, `${pc.red("✗")} Not logged in. Run: percher login`);
21
+ throw new SilentExitError();
22
+ }
23
+ if (action === "create") {
24
+ if (!arg) {
25
+ await writeLine(ctx, `${pc.red("✗")} A name is required. Usage: percher token create <name> [--scope deploy] [--app <name>]`);
26
+ throw new SilentExitError();
27
+ }
28
+ const scope = (options.scope ?? "full");
29
+ if (!VALID_SCOPES.includes(scope)) {
30
+ await writeLine(ctx, `${pc.red("✗")} Invalid --scope "${options.scope}". Use one of: ${VALID_SCOPES.join(", ")}.`);
31
+ throw new SilentExitError();
32
+ }
33
+ const ttlDays = options.ttlDays !== undefined ? Number(options.ttlDays) : undefined;
34
+ if (ttlDays !== undefined && (!Number.isInteger(ttlDays) || ttlDays <= 0)) {
35
+ await writeLine(ctx, `${pc.red("✗")} --ttl-days must be a positive whole number.`);
36
+ throw new SilentExitError();
37
+ }
38
+ const created = await core.tokenCreate(ctx, {
39
+ name: arg,
40
+ scope,
41
+ app: options.app,
42
+ ttlDays,
43
+ });
44
+ await writeLine(ctx, `${pc.green("✓")} Token created: ${pc.bold(created.name)} ${pc.dim(`(${scopeLabel(created.scopes, created.appName)})`)}`);
45
+ await writeLine(ctx, "");
46
+ await writeLine(ctx, ` ${pc.bold(created.token)}`);
47
+ await writeLine(ctx, pc.yellow(" ↑ Store this now — the secret is shown only once and can't be retrieved."));
48
+ await writeLine(ctx, "");
49
+ await writeLine(ctx, pc.dim(" Use it in CI / an agent:"));
50
+ await writeLine(ctx, pc.dim(` PERCHER_TOKEN=${created.token} bunx percher publish`));
51
+ return;
52
+ }
53
+ if (action === "list") {
54
+ const tokens = await core.tokenList(ctx);
55
+ if (tokens.length === 0) {
56
+ await writeLine(ctx, pc.dim("No API tokens."));
57
+ return;
58
+ }
59
+ for (const t of tokens) {
60
+ const expires = t.expiresAt
61
+ ? `expires ${new Date(t.expiresAt).toISOString().slice(0, 10)}`
62
+ : "no expiry";
63
+ await writeLine(ctx, `${pc.bold(t.id)} ${t.name.padEnd(20)} ${pc.dim(scopeLabel(t.scopes, t.appName))} ${pc.dim(expires)}`);
64
+ }
65
+ await writeLine(ctx, "");
66
+ await writeLine(ctx, pc.dim("Revoke one with: percher token revoke <id>"));
67
+ return;
68
+ }
69
+ if (action === "revoke") {
70
+ if (!arg) {
71
+ await writeLine(ctx, `${pc.red("✗")} A token id is required. Usage: percher token revoke <id> (list ids with \`percher token list\`).`);
72
+ throw new SilentExitError();
73
+ }
74
+ await core.tokenRevoke(ctx, { id: arg });
75
+ await writeLine(ctx, `${pc.green("✓")} Token revoked.`);
76
+ return;
77
+ }
78
+ throw new Error(`Unknown token action "${action ?? ""}". Use: create | list | revoke.`);
79
+ });
80
+ // Help-only registrations (unreachable — cac falls through to the parent
81
+ // `token [action] ...`), so `--help` surfaces each subcommand.
82
+ cli
83
+ .command("token create <name>", "Mint a scoped API token (the secret is shown once)")
84
+ .option("--scope <scope>", "deploy | env | read | full (default: full)")
85
+ .option("--app <name>", "Bind the token to a single app")
86
+ .option("--ttl-days <n>", "Token lifetime in days")
87
+ .action(async () => {
88
+ /* Unreachable — handled by `token [action] ...`. */
89
+ });
90
+ cli.command("token list", "List your API tokens with scope + bound app").action(async () => {
91
+ /* Unreachable — handled by `token [action] ...`. */
92
+ });
93
+ cli.command("token revoke <id>", "Revoke an API token by id").action(async () => {
94
+ /* Unreachable — handled by `token [action] ...`. */
95
+ });
96
+ }
97
+ //# sourceMappingURL=token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.js","sourceRoot":"","sources":["../../../src/commands/token.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAqB7C,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAoEhF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAsB7C,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAqEhF"}
package/dist/src/index.js CHANGED
@@ -12,6 +12,7 @@ import { registerGithubCommands } from "./commands/github";
12
12
  import { registerMigrateCommands } from "./commands/migrate";
13
13
  import { registerSharingCommands } from "./commands/sharing";
14
14
  import { registerStatusCommands } from "./commands/status";
15
+ import { registerTokenCommands } from "./commands/token";
15
16
  import { registerTransfersCommands } from "./commands/transfers";
16
17
  import { createCliContext } from "./context";
17
18
  import { renderError } from "./output";
@@ -31,6 +32,7 @@ export async function run(argv, ctxOverride) {
31
32
  registerMigrateCommands(cli, ctx);
32
33
  registerSharingCommands(cli, ctx);
33
34
  registerStatusCommands(cli, ctx);
35
+ registerTokenCommands(cli, ctx);
34
36
  registerTransfersCommands(cli, ctx);
35
37
  registerAdminCommands(cli, ctx);
36
38
  cli.help();
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,IAAc,EAAE,WAAqB;IAC7D,MAAM,GAAG,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3B,MAAM,GAAG,GAAG,WAAW,IAAI,CAAC,MAAM,gBAAgB,EAAE,CAAC,CAAC;IAEtD,oBAAoB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC/B,uBAAuB,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IACxC,mBAAmB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC9B,sBAAsB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACjC,oBAAoB,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IACrC,oBAAoB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC/B,uBAAuB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAClC,sBAAsB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACjC,uBAAuB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAClC,uBAAuB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAClC,sBAAsB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACjC,qBAAqB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAChC,yBAAyB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACpC,qBAAqB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAEhC,GAAG,CAAC,IAAI,EAAE,CAAC;IACX,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAErB,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;IAElC,IAAI,CAAC;QACH,MAAM,mBAAmB,EAAE,CAAC;QAC5B,GAAG,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3D,mEAAmE;QACnE,4DAA4D;QAC5D,4DAA4D;QAC5D,+DAA+D;QAC/D,mEAAmE;QACnE,iEAAiE;QACjE,gEAAgE;QAChE,+DAA+D;QAC/D,gEAAgE;QAChE,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,GAAG,CAAC,cAAc,EAAE,CAAC;YACvB,MAAM,GAAG,CAAC,iBAAiB,EAAE,CAAC;QAChC,CAAC;aAAM,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;YAC3D,2DAA2D;YAC3D,GAAG,CAAC,UAAU,EAAE,CAAC;QACnB,CAAC;aAAM,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACrC,6DAA6D;YAC7D,wDAAwD;QAC1D,CAAC;aAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;YAChE,2DAA2D;YAC3D,iEAAiE;YACjE,+DAA+D;QACjE,CAAC;aAAM,CAAC;YACN,gEAAgE;YAChE,gEAAgE;YAChE,MAAM,mBAAmB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YAC1C,MAAM,IAAI,eAAe,CAAC,CAAC,CAAC,CAAC;QAC/B,CAAC;QACD,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAC/B,MAAM,eAAe,EAAE,CAAC;QACxB,OAAO,CAAC,CAAC;IACX,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,yDAAyD;QACzD,IAAI,GAAG,YAAY,eAAe,EAAE,CAAC;YACnC,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;YACtC,OAAO,GAAG,CAAC,QAAQ,CAAC;QACtB,CAAC;QACD,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QACxE,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACtC,MAAM,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACnC,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC"}
@@ -30,3 +30,62 @@ export async function complete(
30
30
  const data = (await res.json()) as { choices?: Array<{ message?: { content?: string } }> };
31
31
  return data.choices?.[0]?.message?.content ?? "";
32
32
  }
33
+
34
+ /**
35
+ * Streaming variant of {@link complete} — calls `onToken` with each text delta
36
+ * as it arrives and resolves with the full text. Same auth + billing as
37
+ * `complete` (keyless, billed to the app owner). Use it for a typewriter UI:
38
+ *
39
+ * const full = await streamComplete("Write a haiku", (t) => { output.value += t; });
40
+ */
41
+ export async function streamComplete(
42
+ prompt: string,
43
+ onToken: (delta: string) => void,
44
+ opts?: { model?: string; maxTokens?: number },
45
+ ): Promise<string> {
46
+ if (!AI_URL) throw new Error("Managed inference isn't configured for this app.");
47
+ if (!pb.authStore.isValid) throw new Error("Sign in to use AI.");
48
+ const res = await fetch(AI_URL, {
49
+ method: "POST",
50
+ headers: { "content-type": "application/json", authorization: pb.authStore.token },
51
+ body: JSON.stringify({
52
+ model: opts?.model,
53
+ max_tokens: opts?.maxTokens ?? 512,
54
+ messages: [{ role: "user", content: prompt }],
55
+ stream: true,
56
+ }),
57
+ });
58
+ if (!res.ok || !res.body) throw new Error(`AI request failed (${res.status})`);
59
+
60
+ const reader = res.body.getReader();
61
+ const decoder = new TextDecoder();
62
+ let buffer = "";
63
+ let full = "";
64
+ for (;;) {
65
+ const { done, value } = await reader.read();
66
+ if (done) break;
67
+ buffer += decoder.decode(value, { stream: true });
68
+ // SSE events are separated by a blank line; parse each complete one.
69
+ let sep = buffer.indexOf("\n\n");
70
+ while (sep !== -1) {
71
+ const event = buffer.slice(0, sep);
72
+ buffer = buffer.slice(sep + 2);
73
+ sep = buffer.indexOf("\n\n");
74
+ const line = event.startsWith("data:") ? event.slice(5).trim() : "";
75
+ if (!line || line === "[DONE]") continue;
76
+ try {
77
+ const chunk = JSON.parse(line) as {
78
+ choices?: Array<{ delta?: { content?: string } }>;
79
+ };
80
+ const delta = chunk.choices?.[0]?.delta?.content ?? "";
81
+ if (delta) {
82
+ full += delta;
83
+ onToken(delta);
84
+ }
85
+ } catch {
86
+ /* ignore keep-alive / non-JSON lines */
87
+ }
88
+ }
89
+ }
90
+ return full;
91
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "percher",
3
- "version": "0.5.15",
4
- "description": "CLI for the Percher hosting platform \u2014 publish apps with one command",
3
+ "version": "0.5.17",
4
+ "description": "CLI for the Percher hosting platform — publish apps with one command",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "percher": "./dist/bin/percher.js"