pylot-cli 0.1.4 → 0.1.5

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/index.mjs +61 -43
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -14362,37 +14362,42 @@ async function resolveConfig(opts) {
14362
14362
  pickToken(env2.PYLOT_API_TOKEN, "PYLOT_API_TOKEN");
14363
14363
  pickToken(env2.PYLOT_SESSION_JWT, "PYLOT_SESSION_JWT");
14364
14364
  pickToken(env2.PYLOT_BROKER_TOKEN, "PYLOT_BROKER_TOKEN");
14365
- pickToken(env2.PYLOT_DISPATCH_TOKEN, "PYLOT_DISPATCH_TOKEN");
14366
14365
  pickUrl(env2.PYLOT_DISPATCH_URL, "PYLOT_DISPATCH_URL");
14367
14366
  pickUrl(env2.PYLOT_API_URL, "PYLOT_API_URL");
14368
14367
  pickUrl(env2.PYLOT_GATEWAY_URL, "PYLOT_GATEWAY_URL");
14369
14368
  pickUrl(env2.PYLOT_API, "PYLOT_API");
14370
- pickToken(await confGet("token"), "config file");
14371
14369
  pickUrl(await confGet("url"), "config file");
14372
- if (token === null) {
14373
- const org = opts.org ?? await confGet("org");
14374
- if (org) {
14375
- try {
14376
- const creds = readCredentials();
14377
- let entry = creds.orgs[org];
14378
- if (entry?.token && entry.refresh_token && baseUrl && isNearExpiry(entry.expires_at)) {
14379
- const rotated = await refreshOrgCredential(baseUrl, org, entry).catch(() => null);
14380
- if (rotated) {
14381
- creds.orgs[org] = rotated;
14382
- try {
14383
- writeCredentials(creds);
14384
- } catch {
14385
- }
14386
- entry = rotated;
14387
- } else if (new Date(entry.expires_at).getTime() <= Date.now()) {
14388
- throw new UsageError(`stored token for ${org} expired and refresh failed \u2014 run: pylot auth login`);
14370
+ const selectedOrg = opts.org ?? await confGet("org");
14371
+ if (selectedOrg && token === null) {
14372
+ let entry;
14373
+ try {
14374
+ const creds = readCredentials();
14375
+ entry = creds.orgs[selectedOrg];
14376
+ if (entry?.token && entry.refresh_token && baseUrl && isNearExpiry(entry.expires_at)) {
14377
+ const rotated = await refreshOrgCredential(baseUrl, selectedOrg, entry).catch(() => null);
14378
+ if (rotated) {
14379
+ creds.orgs[selectedOrg] = rotated;
14380
+ try {
14381
+ writeCredentials(creds);
14382
+ } catch {
14389
14383
  }
14384
+ entry = rotated;
14385
+ } else if (new Date(entry.expires_at).getTime() <= Date.now()) {
14386
+ throw new UsageError(`stored token for ${selectedOrg} expired and refresh failed \u2014 run: pylot auth login`);
14390
14387
  }
14391
- if (entry?.token) pickToken(entry.token, "credentials file");
14392
- } catch (err) {
14393
- if (err instanceof UsageError) throw err;
14394
14388
  }
14389
+ } catch (err) {
14390
+ if (err instanceof UsageError) throw err;
14395
14391
  }
14392
+ if (!entry?.token) {
14393
+ throw new UsageError(
14394
+ `no credentials found for org '${selectedOrg}' \u2014 run: pylot auth login --org ${selectedOrg}`
14395
+ );
14396
+ }
14397
+ pickToken(entry.token, "credentials file");
14398
+ } else {
14399
+ pickToken(env2.PYLOT_DISPATCH_TOKEN, "PYLOT_DISPATCH_TOKEN");
14400
+ pickToken(await confGet("token"), "config file");
14396
14401
  }
14397
14402
  if (!baseUrl) {
14398
14403
  throw new UsageError(
@@ -14918,9 +14923,11 @@ function registerAuth(program3) {
14918
14923
  );
14919
14924
  });
14920
14925
  const tokens = auth.command("tokens").description("scoped API tokens (plt_*) \u2014 token plane #1884");
14921
- tokens.command("create").description("mint a scoped API token (POST /auth/tokens \u2014 requires admin)").requiredOption("--org <org>", "org the token is scoped to").option("--label <label>", "human-readable label").option("--scopes <a,b,c>", "comma-separated scopes (e.g. dispatch,missions:read)").option("--expires-at <iso>", "expiry timestamp (ISO 8601)").action(async (flags, cmd) => {
14926
+ tokens.command("create").description("mint a scoped API token (POST /auth/tokens \u2014 requires admin)").option("--org <org>", "org the token is scoped to (or use global --org before the subcommand)").option("--label <label>", "human-readable label").option("--scopes <a,b,c>", "comma-separated scopes (e.g. dispatch,missions:read)").option("--expires-at <iso>", "expiry timestamp (ISO 8601)").action(async (flags, cmd) => {
14922
14927
  const { opts, cfg } = await ctx(cmd);
14923
- const body = { org: flags.org, label: flags.label, expires_at: flags.expiresAt };
14928
+ const org = cmd.optsWithGlobals().org;
14929
+ if (!org) throw new UsageError("--org <org> is required");
14930
+ const body = { org, label: flags.label, expires_at: flags.expiresAt };
14924
14931
  if (flags.scopes) body.scopes = String(flags.scopes).split(",").map((s) => s.trim()).filter(Boolean);
14925
14932
  for (const key of Object.keys(body)) if (body[key] === void 0) delete body[key];
14926
14933
  emit(opts, await request(cfg, "/auth/tokens", { method: "POST", body }));
@@ -15015,7 +15022,7 @@ Waiting for authorization\u2026
15015
15022
  process.stdout.write(`Credentials written to ~/.pylot/credentials (mode 0600)
15016
15023
  `);
15017
15024
  if (flags.org) {
15018
- process.stdout.write(`Active org: ${flags.org} \u2014 use PYLOT_API_TOKEN or pylot config set org ${flags.org}
15025
+ process.stdout.write(`Active org: ${flags.org} \u2014 use --org ${flags.org} or: pylot config set org ${flags.org}
15019
15026
  `);
15020
15027
  }
15021
15028
  return;
@@ -15069,7 +15076,7 @@ Waiting for authorization\u2026
15069
15076
  process.stdout.write(`Credentials written to ~/.pylot/credentials (mode 0600)
15070
15077
  `);
15071
15078
  if (flags.org) {
15072
- process.stdout.write(`Active org: ${flags.org} \u2014 use PYLOT_API_TOKEN or pylot config set org ${flags.org}
15079
+ process.stdout.write(`Active org: ${flags.org} \u2014 use --org ${flags.org} or: pylot config set org ${flags.org}
15073
15080
  `);
15074
15081
  }
15075
15082
  }
@@ -15078,7 +15085,7 @@ Waiting for authorization\u2026
15078
15085
  }
15079
15086
 
15080
15087
  // src/commands/config-cmd.mts
15081
- var KEYS = ["url", "token", "staging-url", "staging-token"];
15088
+ var KEYS = ["url", "token", "org", "staging-url", "staging-token"];
15082
15089
  function redact(key, value) {
15083
15090
  if (!key.includes("token") || typeof value !== "string" || value.length === 0) return value;
15084
15091
  return value.length > 8 ? `${value.slice(0, 4)}\u2026${value.slice(-4)}` : "****";
@@ -15332,14 +15339,16 @@ function convPath(id, sub = "") {
15332
15339
  }
15333
15340
  function registerConversations(program3) {
15334
15341
  const conversations = program3.command("conversations").alias("convo").description("chat conversations: lifecycle, messages, wakes, resources");
15335
- conversations.command("create").description("create a conversation (POST /conversations)").requiredOption("--org <org>", "owning org (required)").option("--title <title>", "conversation title").option("--team <team>", "team name (must belong to --org)").option("--repos <a,b>", "comma-separated repo list").option("--branch <branch>", "working branch").option("--user-handle <handle>", "user handle").action(async (flags, cmd) => {
15342
+ conversations.command("create").description("create a conversation (POST /conversations)").option("--org <org>", "owning org (required; or use global --org before the subcommand)").option("--title <title>", "conversation title").option("--team <team>", "team name (must belong to --org)").option("--repos <a,b>", "comma-separated repo list").option("--branch <branch>", "working branch").option("--user-handle <handle>", "user handle").action(async (flags, cmd) => {
15336
15343
  const { opts, cfg } = await ctx(cmd);
15344
+ const org = cmd.optsWithGlobals().org;
15345
+ if (!org) throw new UsageError("--org <org> is required");
15337
15346
  emit(
15338
15347
  opts,
15339
15348
  await request(cfg, "/conversations", {
15340
15349
  method: "POST",
15341
15350
  body: {
15342
- org: flags.org,
15351
+ org,
15343
15352
  title: flags.title,
15344
15353
  team: flags.team,
15345
15354
  repos: flags.repos ? String(flags.repos).split(",").map((r) => r.trim()).filter(Boolean) : void 0,
@@ -15700,7 +15709,7 @@ function withParentBuildOpts(flags, cmd) {
15700
15709
  function registerDeploy(program3) {
15701
15710
  const deploy = program3.command("deploy").description(
15702
15711
  "trigger a cdk deploy via CodeBuild (POST /admin/deploy) \u2014 a 409 means a deploy is already running (deploy lock held: {stage, held_by, since, build_id}); wait for it or poll `pylot deploy status`"
15703
- ).option("--source-version <ref>", "git ref to deploy (default: gateway's stage branch)").option("--wait", "poll the build to a terminal state (exit 0 only on SUCCEEDED)").option("--timeout <seconds>", "max seconds to wait (default 1500 = 25min)", (v) => parseInt(v, 10), 1500).action(async (flags, cmd) => {
15712
+ ).allowExcessArguments(false).showHelpAfterError("(run 'pylot deploy --help' to list valid subcommands)").option("--source-version <ref>", "git ref to deploy (default: gateway's stage branch)").option("--wait", "poll the build to a terminal state (exit 0 only on SUCCEEDED)").option("--timeout <seconds>", "max seconds to wait (default 1500 = 25min)", (v) => parseInt(v, 10), 1500).action(async (flags, cmd) => {
15704
15713
  const { opts, cfg } = await ctx(cmd);
15705
15714
  const body = {};
15706
15715
  if (flags.sourceVersion) body.source_version = flags.sourceVersion;
@@ -15898,21 +15907,24 @@ function registerSkills(program3) {
15898
15907
  const { opts, cfg } = await ctx(cmd);
15899
15908
  emit(opts, await request(cfg, "/admin/skills/lock"));
15900
15909
  });
15901
- skills.command("staleness").description("SHA-mismatch report vs origin HEAD (GET /admin/skills/staleness?org=<org>)").requiredOption("--org <org>", "GitHub org to check (staleness is per-org, #1919)").action(async (flags, cmd) => {
15910
+ skills.command("staleness").description("SHA-mismatch report vs origin HEAD (GET /admin/skills/staleness?org=<org>)").option("--org <org>", "GitHub org to check (staleness is per-org, #1919; or use global --org before the subcommand)").action(async (flags, cmd) => {
15902
15911
  const { opts, cfg } = await ctx(cmd);
15903
- emit(opts, await request(cfg, "/admin/skills/staleness", { query: { org: flags.org } }));
15912
+ const org = cmd.optsWithGlobals().org;
15913
+ if (!org) throw new UsageError("--org <org> is required");
15914
+ emit(opts, await request(cfg, "/admin/skills/staleness", { query: { org } }));
15904
15915
  });
15905
15916
  skills.command("sync").description(
15906
15917
  "trigger the CodeBuild skills-sync job for one org (POST /admin/skills/sync, body {org}) or every installed org (--all \u2192 POST /admin/skills/sync-all)"
15907
15918
  ).option("--all", "fan out one sync per installed org").option("--org <org>", "GitHub org to sync (required unless --all)").action(async (flags, cmd) => {
15908
15919
  const { opts, cfg } = await ctx(cmd);
15920
+ const org = cmd.optsWithGlobals().org;
15909
15921
  if (flags.all) {
15910
- if (flags.org) throw new UsageError("pass either --all or --org <org>, not both");
15922
+ if (org) throw new UsageError("pass either --all or --org <org>, not both");
15911
15923
  emit(opts, await request(cfg, "/admin/skills/sync-all", { method: "POST", body: {} }));
15912
15924
  return;
15913
15925
  }
15914
- if (!flags.org) throw new UsageError("--org <org> is required (or --all to sync every installed org)");
15915
- emit(opts, await request(cfg, "/admin/skills/sync", { method: "POST", body: { org: flags.org } }));
15926
+ if (!org) throw new UsageError("--org <org> is required (or --all to sync every installed org)");
15927
+ emit(opts, await request(cfg, "/admin/skills/sync", { method: "POST", body: { org } }));
15916
15928
  });
15917
15929
  skills.command("sync-status").description("skills-sync build status + logs (GET /admin/skills/sync/:id)").argument("[build_id]", "CodeBuild build id (from `skills sync`)").action(async (buildId, _flags, cmd) => {
15918
15930
  const { opts, cfg } = await ctx(cmd);
@@ -15924,26 +15936,30 @@ function registerSkills(program3) {
15924
15936
  const body = await bodyFromArgs(fields, flags.file);
15925
15937
  emit(opts, await request(cfg, "/admin/skills/reconcile-org", { method: "POST", body }));
15926
15938
  });
15927
- skills.command("update").description("update catalog metadata (PATCH /admin/skills/:name?org=<org> \u2014 tags, origin_repo, version, commit_sha, needs_worker)").argument("<name>").argument("[fields...]", "key=value pairs").requiredOption("--org <org>", "owning org (catalog rows are keyed (org, name))").option("--file <path>", "JSON body from file ('-' for stdin)").action(async (name, fields, flags, cmd) => {
15939
+ skills.command("update").description("update catalog metadata (PATCH /admin/skills/:name?org=<org> \u2014 tags, origin_repo, version, commit_sha, needs_worker)").argument("<name>").argument("[fields...]", "key=value pairs").option("--org <org>", "owning org (catalog rows are keyed (org, name)); or use global --org before the subcommand)").option("--file <path>", "JSON body from file ('-' for stdin)").action(async (name, fields, flags, cmd) => {
15928
15940
  const { opts, cfg } = await ctx(cmd);
15941
+ const org = cmd.optsWithGlobals().org;
15942
+ if (!org) throw new UsageError("--org <org> is required");
15929
15943
  const body = await bodyFromArgs(fields, flags.file);
15930
15944
  emit(
15931
15945
  opts,
15932
15946
  await request(cfg, `/admin/skills/${encodeURIComponent(name)}`, {
15933
15947
  method: "PATCH",
15934
- query: { org: flags.org },
15948
+ query: { org },
15935
15949
  body
15936
15950
  })
15937
15951
  );
15938
15952
  });
15939
- skills.command("delete").description("remove a skill from the catalog (DELETE /admin/skills/:name?org=<org>)").argument("<name>").requiredOption("--org <org>", "owning org (catalog rows are keyed (org, name))").option("--force", "required to confirm deletion").action(async (name, flags, cmd) => {
15953
+ skills.command("delete").description("remove a skill from the catalog (DELETE /admin/skills/:name?org=<org>)").argument("<name>").option("--org <org>", "owning org (catalog rows are keyed (org, name)); or use global --org before the subcommand)").option("--force", "required to confirm deletion").action(async (name, flags, cmd) => {
15940
15954
  const { opts, cfg } = await ctx(cmd);
15941
15955
  if (!flags.force) throw new UsageError("deleting a catalog skill is destructive \u2014 re-run with --force");
15956
+ const org = cmd.optsWithGlobals().org;
15957
+ if (!org) throw new UsageError("--org <org> is required");
15942
15958
  emit(
15943
15959
  opts,
15944
15960
  await request(cfg, `/admin/skills/${encodeURIComponent(name)}`, {
15945
15961
  method: "DELETE",
15946
- query: { org: flags.org }
15962
+ query: { org }
15947
15963
  })
15948
15964
  );
15949
15965
  });
@@ -16004,9 +16020,11 @@ function registerAutomations(program3) {
16004
16020
  init_http();
16005
16021
  function registerProviders(program3) {
16006
16022
  const providers = program3.command("providers").description("LLM provider instances and org purpose-chains");
16007
- providers.command("list").description("provider instances for an org (GET /admin/providers?org=)").requiredOption("--org <org>", "org to list (required by the gateway)").action(async (flags, cmd) => {
16023
+ providers.command("list").description("provider instances for an org (GET /admin/providers?org=)").option("--org <org>", "org to list (required by the gateway; or use global --org before the subcommand)").action(async (flags, cmd) => {
16008
16024
  const { opts, cfg } = await ctx(cmd);
16009
- emit(opts, await request(cfg, "/admin/providers", { query: { org: flags.org } }));
16025
+ const org = cmd.optsWithGlobals().org;
16026
+ if (!org) throw new UsageError("--org <org> is required");
16027
+ emit(opts, await request(cfg, "/admin/providers", { query: { org } }));
16010
16028
  });
16011
16029
  providers.command("create").description("create a provider instance (POST /admin/providers, body {org, type, model, label?, credentials?})").argument("[fields...]", "key=value pairs (e.g. org=my-org type=anthropic-api model=opus)").option("--file <path>", "JSON body from file ('-' for stdin)").action(async (fields, flags, cmd) => {
16012
16030
  const { opts, cfg } = await ctx(cmd);
@@ -16113,12 +16131,12 @@ function registerAssets(program3) {
16113
16131
  }
16114
16132
 
16115
16133
  // src/index.mts
16116
- var VERSION = true ? "0.1.4" : "0.0.0-dev";
16134
+ var VERSION = true ? "0.1.5" : "0.0.0-dev";
16117
16135
  installEpipeGuard();
16118
16136
  var program2 = new Command();
16119
16137
  program2.name("pylot").description(
16120
16138
  "Pylot gateway CLI \u2014 dispatch missions, tail logs, manage teams/admin/secrets without curl.\nJSON on non-TTY stdout (pipe-friendly for agents and jq); tables on TTY."
16121
- ).allowExcessArguments(false).configureHelp({ sortSubcommands: true }).version(VERSION).option("--url <url>", "gateway base URL (default: PYLOT_DISPATCH_URL / PYLOT_API_URL / PYLOT_GATEWAY_URL)").option("--token <token>", "bearer token (default: PYLOT_API_TOKEN / PYLOT_SESSION_JWT / PYLOT_BROKER_TOKEN / PYLOT_DISPATCH_TOKEN)").option("--env <env>", "target environment: prod | staging (staging uses PYLOT_STAGING_URL + PYLOT_STAGING_DISPATCH_TOKEN)").option("--json", "force JSON output even on a TTY").option("-q, --quiet", "suppress progress notes on stderr");
16139
+ ).allowExcessArguments(false).configureHelp({ sortSubcommands: true }).version(VERSION).option("--url <url>", "gateway base URL (default: PYLOT_DISPATCH_URL / PYLOT_API_URL / PYLOT_GATEWAY_URL)").option("--token <token>", "bearer token (default: PYLOT_API_TOKEN / PYLOT_SESSION_JWT / PYLOT_BROKER_TOKEN / PYLOT_DISPATCH_TOKEN)").option("--org <org>", "default org for credential resolution (selects ~/.pylot/credentials entry; overrides pylot config set org)").option("--env <env>", "target environment: prod | staging (staging uses PYLOT_STAGING_URL + PYLOT_STAGING_DISPATCH_TOKEN)").option("--json", "force JSON output even on a TTY").option("-q, --quiet", "suppress progress notes on stderr");
16122
16140
  program2.exitOverride();
16123
16141
  program2.configureOutput({ outputError: (str, write) => write(str) });
16124
16142
  registerCore(program2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pylot-cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "description": "pylot \u2014 first-class CLI for the Pylot gateway API. Replaces curl workflows for operators and humans.",
6
6
  "bin": {