ninepm 0.59.0 → 0.60.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 CHANGED
@@ -23,6 +23,8 @@ Authenticate in your own browser. The CLI stores the token in native protected s
23
23
 
24
24
  For CI, set `NINEPM_API_KEY` yourself in the CI secret store. `--token` and `NINEPM_API_KEY` still override the stored login token.
25
25
 
26
+ A key for CI or a server is minted from a signed-in machine with `9pm api-key create --name <name>`; the token is printed once. `9pm api-key ls` lists your active keys and `9pm api-key revoke <key-id>` retires one. Each account holds at most 10 such keys at a time (login tokens do not count). Store the key under any name but `NINEPM_*` when it lives in a 9pm app's own environment, and pass it with `--token`.
27
+
26
28
  ## Inventory
27
29
 
28
30
  List the apps you have on 9pm.ai before deploying or modifying:
package/dist/index.d.ts CHANGED
@@ -116,7 +116,7 @@ export interface CreatedServiceToken {
116
116
  clientSecret: string;
117
117
  }
118
118
  export type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
119
- export declare const CLI_VERSION = "0.59.0";
119
+ export declare const CLI_VERSION = "0.60.0";
120
120
  type CredentialBackend = "macos-keychain" | "windows-dpapi" | "linux-secret-service" | "file-plaintext" | "none";
121
121
  export interface StoredCredentialEntry {
122
122
  token: string;
@@ -453,6 +453,21 @@ type CliCommand = {
453
453
  site?: string;
454
454
  apiUrl: string;
455
455
  token: string;
456
+ } | {
457
+ kind: "api-key";
458
+ action: {
459
+ kind: "create";
460
+ name?: string;
461
+ json: boolean;
462
+ } | {
463
+ kind: "ls";
464
+ json: boolean;
465
+ } | {
466
+ kind: "revoke";
467
+ keyId: string;
468
+ };
469
+ apiUrl: string;
470
+ token: string;
456
471
  } | {
457
472
  kind: "files";
458
473
  action: {
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import { managedHostnamesIn, siblingCallWarning, siblingsOf } from "./sibling-ho
15
15
  import { MAX_STATIC_ASSET_FILE_BYTES, MAX_WORKER_ENTRY_COMPRESSED_BYTES, MAX_WORKER_SCRIPT_COMPRESSED_BYTES, WORKER_ENTRY_COMPRESSED_WARN_BYTES, assetFileTooLargeMessage, formatMebibytes, formatMegabytes, oversizedAssets, workerBundleNearLimitMessage, workerBundleTooLargeMessage, workerEntryNearLimitMessage, workerEntryTooLargeMessage, } from "./deploy-limits.js";
16
16
  import { buildFileManifest, readManifestFile } from "./manifest.js";
17
17
  import { API_KEY_HELP, API_URL_HELP, CLI_CLIENT_ID, DEFAULT_API_URL, reportCliError } from "./cli-config.js";
18
- export const CLI_VERSION = "0.59.0";
18
+ export const CLI_VERSION = "0.60.0";
19
19
  const PUBLIC_DASHBOARD_URL = "https://9pm.ai/dashboard";
20
20
  const TOKEN_SERVICE = "9pm";
21
21
  const TOKEN_ACCOUNT = "default";
@@ -2291,6 +2291,9 @@ export function parseArgs(argv) {
2291
2291
  if (command === "support") {
2292
2292
  return parseSupportArgs(argv.slice(1));
2293
2293
  }
2294
+ if (command === "api-key" || command === "api-keys") {
2295
+ return parseApiKeyArgs(argv.slice(1));
2296
+ }
2294
2297
  if (command !== "deploy") {
2295
2298
  throw new Error(`Unknown command: ${command}`);
2296
2299
  }
@@ -2975,6 +2978,30 @@ function readBaseApiOptions(flags) {
2975
2978
  function readApiToken(flags) {
2976
2979
  return readStringFlag(flags, "token") ?? process.env.NINEPM_API_KEY ?? readStoredCredential();
2977
2980
  }
2981
+ function parseApiKeyArgs(argv) {
2982
+ const subcommand = argv[0];
2983
+ if (!subcommand || subcommand === "help" || subcommand === "--help") {
2984
+ throw new UsageError();
2985
+ }
2986
+ const flags = parseFlags(argv);
2987
+ const base = readBaseApiOptions(flags);
2988
+ const json = flags.has("json");
2989
+ if (subcommand === "create") {
2990
+ const name = readStringFlag(flags, "name");
2991
+ return { kind: "api-key", ...base, action: { kind: "create", ...(name ? { name } : {}), json } };
2992
+ }
2993
+ if (subcommand === "ls" || subcommand === "list") {
2994
+ return { kind: "api-key", ...base, action: { kind: "ls", json } };
2995
+ }
2996
+ if (subcommand === "revoke") {
2997
+ const keyId = argv[1];
2998
+ if (!keyId || keyId.startsWith("--")) {
2999
+ throw new Error("Usage: 9pm api-key revoke <key-id>");
3000
+ }
3001
+ return { kind: "api-key", ...base, action: { kind: "revoke", keyId } };
3002
+ }
3003
+ throw new Error(`Unknown api-key subcommand: ${subcommand}`);
3004
+ }
2978
3005
  function parseSupportArgs(argv) {
2979
3006
  // Normalize the -m short flag so parseFlags (which only handles --flags) sees it.
2980
3007
  const normalized = argv.map((arg) => (arg === "-m" ? "--message" : arg));
@@ -4234,6 +4261,10 @@ async function main() {
4234
4261
  await runSupportCommand(command);
4235
4262
  return;
4236
4263
  }
4264
+ if (command.kind === "api-key") {
4265
+ await runApiKeyCommand(command);
4266
+ return;
4267
+ }
4237
4268
  await runAccessCommand(command);
4238
4269
  }
4239
4270
  catch (error) {
@@ -4246,6 +4277,42 @@ async function main() {
4246
4277
  process.exitCode = 1;
4247
4278
  }
4248
4279
  }
4280
+ async function runApiKeyCommand(command) {
4281
+ const base = { apiUrl: command.apiUrl, token: command.token };
4282
+ const action = command.action;
4283
+ if (action.kind === "create") {
4284
+ const result = await apiRequest(base, "/v1/me/api-keys", {
4285
+ method: "POST",
4286
+ body: JSON.stringify(action.name ? { name: action.name } : {}),
4287
+ });
4288
+ if (action.json) {
4289
+ console.log(JSON.stringify({ apiKey: result.apiKey, token: result.token }));
4290
+ return;
4291
+ }
4292
+ console.log(`Created API key ${result.apiKey.id} (${result.apiKey.name}). The token is shown once:`);
4293
+ console.log(result.token);
4294
+ console.log("Store it in your CI or server secret store under any name but NINEPM_*, and pass it as --token or NINEPM_API_KEY.");
4295
+ return;
4296
+ }
4297
+ if (action.kind === "ls") {
4298
+ const result = await apiRequest(base, "/v1/me/api-keys", { method: "GET" });
4299
+ const live = result.apiKeys.filter((key) => !key.revokedAt);
4300
+ if (action.json) {
4301
+ console.log(JSON.stringify({ apiKeys: live }));
4302
+ return;
4303
+ }
4304
+ if (live.length === 0) {
4305
+ console.log("No active API keys.");
4306
+ return;
4307
+ }
4308
+ for (const key of live) {
4309
+ console.log(`${key.id}\t${key.name}\t${key.prefix}...\tcreated ${key.createdAt.slice(0, 10)}\tlast used ${key.lastUsedAt ? key.lastUsedAt.slice(0, 10) : "never"}`);
4310
+ }
4311
+ return;
4312
+ }
4313
+ const revoked = await apiRequest(base, `/v1/me/api-keys/${encodeURIComponent(action.keyId)}/revoke`, { method: "POST" });
4314
+ console.log(`Revoked API key ${revoked.apiKey.id} (${revoked.apiKey.name}).`);
4315
+ }
4249
4316
  async function runSupportCommand(command) {
4250
4317
  const subject = command.subject ?? (command.message.split("\n")[0] ?? command.message).slice(0, 200);
4251
4318
  const body = {
@@ -4487,6 +4554,9 @@ function printUsage() {
4487
4554
  9pm access service revoke <site> <service-token-id>
4488
4555
  9pm access token create <site> [--name <name>]
4489
4556
  9pm access token revoke <site> <token-id>
4557
+ 9pm api-key create [--name <name>] [--json] mint a platform API key for CI or a server; the token is shown once
4558
+ 9pm api-key ls [--json]
4559
+ 9pm api-key revoke <key-id>
4490
4560
 
4491
4561
  Environment:
4492
4562
  NINEPM_API_URL ${API_URL_HELP}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ninepm",
3
- "version": "0.59.0",
3
+ "version": "0.60.0",
4
4
  "description": "CLI for deploying built apps to 9pm.ai.",
5
5
  "homepage": "https://9pm.ai",
6
6
  "keywords": [