@antasphere/slideless 0.2.1 → 0.2.2

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/bin.js +284 -30
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -3109,6 +3109,42 @@ function redactKey(key) {
3109
3109
  return "****";
3110
3110
  return `${key.slice(0, 13)}\u2026${key.slice(-4)}`;
3111
3111
  }
3112
+ function lookupWorkspaceKey(profile, workspaceId) {
3113
+ if (!profile)
3114
+ return void 0;
3115
+ const entry = profile.workspaceKeys?.[workspaceId];
3116
+ if (entry?.apiKey)
3117
+ return entry.apiKey;
3118
+ if (profile.apiKey && profile.workspaceId === workspaceId)
3119
+ return profile.apiKey;
3120
+ return void 0;
3121
+ }
3122
+ function storeWorkspaceKey(env, tool, profileName, workspaceId, entry) {
3123
+ const config = loadConfig(env, tool);
3124
+ const profile = config.profiles[profileName] ?? {};
3125
+ config.profiles[profileName] = {
3126
+ ...profile,
3127
+ workspaceKeys: { ...profile.workspaceKeys, [workspaceId]: entry }
3128
+ };
3129
+ if (!config.activeProfile)
3130
+ config.activeProfile = profileName;
3131
+ return saveConfig(env, tool, config);
3132
+ }
3133
+ function removeWorkspaceKey(env, tool, profileName, workspaceId) {
3134
+ const config = loadConfig(env, tool);
3135
+ const profile = config.profiles[profileName];
3136
+ if (!profile?.workspaceKeys || !(workspaceId in profile.workspaceKeys))
3137
+ return false;
3138
+ const { [workspaceId]: _dropped, ...rest } = profile.workspaceKeys;
3139
+ if (Object.keys(rest).length === 0) {
3140
+ const { workspaceKeys: _empty, ...bare } = profile;
3141
+ config.profiles[profileName] = bare;
3142
+ } else {
3143
+ config.profiles[profileName] = { ...profile, workspaceKeys: rest };
3144
+ }
3145
+ saveConfig(env, tool, config);
3146
+ return true;
3147
+ }
3112
3148
 
3113
3149
  // ../../node_modules/.pnpm/@antasphere+cli-core@0.2.0/node_modules/@antasphere/cli-core/dist/io.js
3114
3150
  function printJson(io, value) {
@@ -3119,6 +3155,7 @@ function printJson(io, value) {
3119
3155
  // ../../node_modules/.pnpm/@antasphere+cli-core@0.2.0/node_modules/@antasphere/cli-core/dist/resolve.js
3120
3156
  var CliUsageError = class extends Error {
3121
3157
  };
3158
+ var DEFAULT_PROFILE = "default";
3122
3159
  function resolveProfile(config, requested, opts = {}) {
3123
3160
  if (requested) {
3124
3161
  const profile2 = config.profiles[requested];
@@ -3197,6 +3234,80 @@ var CliAuthClient = class {
3197
3234
  }
3198
3235
  };
3199
3236
 
3237
+ // ../../node_modules/.pnpm/@antasphere+cli-core@0.2.0/node_modules/@antasphere/cli-core/dist/connect.js
3238
+ var CliConnectError = class extends CliAuthError {
3239
+ stage;
3240
+ constructor(stage, status, code, message, details) {
3241
+ super(status, code, message, details);
3242
+ this.stage = stage;
3243
+ this.name = "CliConnectError";
3244
+ }
3245
+ /**
3246
+ * The hub rejected the key itself: invalid/revoked (401), or a key
3247
+ * minted before the `sso:exchange` scope existed (403 insufficient_scope
3248
+ * — old keys simply lack it). The fix is a fresh `antasphere login`.
3249
+ */
3250
+ get needsHubLogin() {
3251
+ return this.stage === "hub" && (this.status === 401 || this.status === 403 && this.code === "insufficient_scope");
3252
+ }
3253
+ /** The tool rejected the JWT (expired/replayed) — run the exchange again. */
3254
+ get needsReExchange() {
3255
+ return this.stage === "tool" && this.status === 401;
3256
+ }
3257
+ };
3258
+ var CliConnectClient = class {
3259
+ hubBaseUrl;
3260
+ toolBaseUrl;
3261
+ fetchImpl;
3262
+ constructor(options) {
3263
+ this.hubBaseUrl = options.hubBaseUrl.replace(/\/+$/, "");
3264
+ this.toolBaseUrl = options.toolBaseUrl.replace(/\/+$/, "");
3265
+ this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
3266
+ }
3267
+ /** Step 1 — the hub mints the 120 s single-org JWT for the tool's resource. */
3268
+ toolToken(hubApiKey, body) {
3269
+ return this.call("hub", `${this.hubBaseUrl}/api/v1/sso/tool-token`, body, hubApiKey);
3270
+ }
3271
+ /** Step 2 — the tool verifies the JWT (its only credential) and mints its key. */
3272
+ cliConnect(token) {
3273
+ return this.call("tool", `${this.toolBaseUrl}/api/v1/sso/cli-connect`, { token });
3274
+ }
3275
+ /** The full two-call exchange. The hub key goes to the hub ONLY. */
3276
+ async exchange(hubApiKey, body) {
3277
+ const minted = await this.toolToken(hubApiKey, body);
3278
+ const connected = await this.cliConnect(minted.token);
3279
+ return {
3280
+ key: connected.key,
3281
+ apiKey: connected.apiKey,
3282
+ user: connected.user,
3283
+ hubWorkspaceId: minted.workspaceId,
3284
+ toolWorkspaceId: connected.workspaceId
3285
+ };
3286
+ }
3287
+ /** Mirrors CliAuthClient's parse: `{ error: { code, message, details } }` envelopes. */
3288
+ async call(stage, url, body, apiKey) {
3289
+ const headers = { "content-type": "application/json" };
3290
+ if (apiKey)
3291
+ headers["authorization"] = `Bearer ${apiKey}`;
3292
+ const res = await this.fetchImpl(url, { method: "POST", headers, body: JSON.stringify(body) });
3293
+ const json = await res.json().catch(() => null);
3294
+ if (!res.ok) {
3295
+ const errBody = json ?? {};
3296
+ throw new CliConnectError(stage, res.status, errBody.error?.code ?? "unknown_error", errBody.error?.message ?? `Request failed with ${res.status}`, errBody.error?.details);
3297
+ }
3298
+ return json;
3299
+ }
3300
+ };
3301
+
3302
+ // ../../node_modules/.pnpm/@antasphere+cli-core@0.2.0/node_modules/@antasphere/cli-core/dist/org.js
3303
+ function getActiveWorkspace(env, opts = {}) {
3304
+ const { profile } = resolveProfile(loadConfig(env, HUB_TOOL), opts.profile);
3305
+ return profile?.activeWorkspaceId ?? profile?.workspaceId;
3306
+ }
3307
+ function resolveTargetWorkspace(env, opts = {}) {
3308
+ return opts.override ?? getActiveWorkspace(env, opts);
3309
+ }
3310
+
3200
3311
  // ../../node_modules/.pnpm/@antasphere+cli-core@0.2.0/node_modules/@antasphere/cli-core/dist/migrate.js
3201
3312
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
3202
3313
  import { join as join2 } from "node:path";
@@ -3657,6 +3768,12 @@ function saveConfig2(env, config) {
3657
3768
  function clearConfig2(env) {
3658
3769
  clearConfig(env, TOOL);
3659
3770
  }
3771
+ function storeWorkspaceKey2(env, profileName, workspaceId, entry) {
3772
+ return storeWorkspaceKey(env, TOOL, profileName, workspaceId, entry);
3773
+ }
3774
+ function removeWorkspaceKey2(env, profileName, workspaceId) {
3775
+ return removeWorkspaceKey(env, TOOL, profileName, workspaceId);
3776
+ }
3660
3777
 
3661
3778
  // src/context.ts
3662
3779
  function resolveProfile2(config, requested) {
@@ -3676,25 +3793,95 @@ function resolveContext(cmd, io) {
3676
3793
  profile,
3677
3794
  missingMessage: "No instance configured. Pass --api-url <url>, set SLIDELESS_URL, or sign in once with `slideless auth login-request --api-url <url> --email <you>` to save a profile."
3678
3795
  });
3679
- const apiKey = resolveApiKey({ flag: opts.apiKey, env: io.env, envVar: "SLIDELESS_API_KEY", profile });
3796
+ let apiKey = resolveApiKey({ flag: opts.apiKey, env: io.env, envVar: "SLIDELESS_API_KEY", profile });
3797
+ let org;
3798
+ if (!apiKey) {
3799
+ org = resolveTargetWorkspace(io.env, opts.org ? { override: opts.org } : {});
3800
+ if (org && profile?.baseUrl?.replace(/\/+$/, "") === baseUrl) {
3801
+ apiKey = lookupWorkspaceKey(profile, org);
3802
+ }
3803
+ }
3680
3804
  const fetchImpl = io.fetch ?? globalThis.fetch.bind(globalThis);
3681
3805
  return {
3682
3806
  client: new PlatformClient({ baseUrl, ...apiKey ? { apiKey } : {}, fetch: fetchImpl }),
3683
3807
  baseUrl,
3684
3808
  apiKey,
3809
+ org,
3685
3810
  json: Boolean(opts.json),
3686
3811
  io,
3687
3812
  profileName,
3688
3813
  config
3689
3814
  };
3690
3815
  }
3691
- function requireApiKey(ctx) {
3692
- if (!ctx.apiKey) {
3816
+ var NO_KEY_MESSAGE = "An API key is required. Sign in (`slideless auth login-request --email <you>`), paste one (`slideless login`), pass --api-key, or set SLIDELESS_API_KEY.";
3817
+ async function requireApiKey(ctx) {
3818
+ if (ctx.apiKey) return ctx.apiKey;
3819
+ const key = await connectViaHub(ctx);
3820
+ if (!key) throw new CliUsageError(NO_KEY_MESSAGE);
3821
+ return key;
3822
+ }
3823
+ async function connectViaHub(ctx) {
3824
+ const { io } = ctx;
3825
+ let cloud = false;
3826
+ try {
3827
+ const info = await ctx.client.instance();
3828
+ cloud = info.auth.methods.includes("antasphere") || info.edition === "cloud";
3829
+ } catch {
3830
+ return void 0;
3831
+ }
3832
+ if (!cloud) return void 0;
3833
+ const { profile: hubProfile } = resolveProfile(loadConfig(io.env, HUB_TOOL), void 0);
3834
+ const hubKey = hubProfile?.apiKey;
3835
+ const hubBaseUrl = hubProfile?.baseUrl;
3836
+ if (!hubKey || !hubBaseUrl) {
3693
3837
  throw new CliUsageError(
3694
- "An API key is required. Sign in (`slideless auth login-request --email <you>`), paste one (`slideless login`), pass --api-key, or set SLIDELESS_API_KEY."
3838
+ "This Slideless instance signs in through the Antasphere hub. Run `antasphere login` once, then retry \u2014 or pass --api-key <slk_\u2026> / set SLIDELESS_API_KEY."
3695
3839
  );
3696
3840
  }
3697
- return ctx.apiKey;
3841
+ const resource = `${ctx.baseUrl}/mcp`;
3842
+ const connect = new CliConnectClient({
3843
+ hubBaseUrl,
3844
+ toolBaseUrl: ctx.baseUrl,
3845
+ ...io.fetch ? { fetch: io.fetch } : {}
3846
+ });
3847
+ const body = { resource, ...ctx.org ? { workspaceId: ctx.org } : {} };
3848
+ let result;
3849
+ try {
3850
+ result = await connect.exchange(hubKey, body);
3851
+ } catch (e) {
3852
+ if (e instanceof CliConnectError && e.needsReExchange) {
3853
+ result = await connect.exchange(hubKey, body);
3854
+ } else if (e instanceof CliConnectError && e.needsHubLogin) {
3855
+ throw new CliUsageError(
3856
+ "The stored Antasphere hub credential was rejected \u2014 run `antasphere login` again, then retry."
3857
+ );
3858
+ } else {
3859
+ throw e;
3860
+ }
3861
+ }
3862
+ const profileName = ctx.profileName ?? DEFAULT_PROFILE;
3863
+ const existing = ctx.config.profiles[profileName];
3864
+ const cacheable = !existing?.baseUrl || existing.baseUrl.replace(/\/+$/, "") === ctx.baseUrl;
3865
+ if (cacheable) {
3866
+ storeWorkspaceKey2(io.env, profileName, result.hubWorkspaceId, {
3867
+ apiKey: result.key,
3868
+ toolWorkspaceId: result.toolWorkspaceId,
3869
+ email: result.user.email,
3870
+ mintedAt: (/* @__PURE__ */ new Date()).toISOString()
3871
+ });
3872
+ if (!existing?.baseUrl) {
3873
+ const cfg = loadConfig2(io.env);
3874
+ cfg.profiles[profileName] = { ...cfg.profiles[profileName], baseUrl: ctx.baseUrl };
3875
+ saveConfig2(io.env, cfg);
3876
+ }
3877
+ }
3878
+ io.err.write(
3879
+ `Connected to ${ctx.baseUrl} as ${result.user.email} via Antasphere (org ${result.hubWorkspaceId})` + (cacheable ? "" : ` \u2014 key NOT cached: profile '${profileName}' points at ${existing?.baseUrl}`) + ".\n"
3880
+ );
3881
+ ctx.apiKey = result.key;
3882
+ const fetchImpl = io.fetch ?? globalThis.fetch.bind(globalThis);
3883
+ ctx.client = new PlatformClient({ baseUrl: ctx.baseUrl, apiKey: result.key, fetch: fetchImpl });
3884
+ return result.key;
3698
3885
  }
3699
3886
  function table(rows) {
3700
3887
  if (rows.length === 0) return "";
@@ -3800,7 +3987,9 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
3800
3987
  `
3801
3988
  );
3802
3989
  });
3803
- program2.command("logout").description("Forget the stored API key of the active (or named) profile").action(async (_opts, cmd) => {
3990
+ program2.command("logout").description(
3991
+ "Forget the stored API key of the active (or named) profile; --org logs one connected hub org out instead"
3992
+ ).action(async (_opts, cmd) => {
3804
3993
  const globals = cmd.optsWithGlobals();
3805
3994
  const config2 = loadConfig2(io.env);
3806
3995
  const profileName = globals.profile ?? config2.activeProfile;
@@ -3808,6 +3997,53 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
3808
3997
  if (!profileName || !profile) {
3809
3998
  throw new CliUsageError("No profile to log out of.");
3810
3999
  }
4000
+ const hubKeys = profile.workspaceKeys ?? {};
4001
+ const perOrg = globals.org !== void 0 || !profile.apiKey && Object.keys(hubKeys).length > 0;
4002
+ if (perOrg) {
4003
+ const org = globals.org ?? resolveTargetWorkspace(io.env, {});
4004
+ if (!org) {
4005
+ throw new CliUsageError(
4006
+ "Pass --org <id> to say which hub org to log out of (or set one with `antasphere org use`)."
4007
+ );
4008
+ }
4009
+ const entry = hubKeys[org];
4010
+ if (!entry) {
4011
+ const cached = Object.keys(hubKeys).join(", ") || "(none)";
4012
+ throw new CliUsageError(
4013
+ `No connected key for hub org ${org} on profile "${profileName}". Connected orgs: ${cached}.`
4014
+ );
4015
+ }
4016
+ const rawUrl = profile.baseUrl;
4017
+ let revoked = false;
4018
+ if (rawUrl) {
4019
+ const baseUrl = rawUrl.replace(/\/+$/, "");
4020
+ try {
4021
+ const client = new CliAuthClient({ baseUrl, ...io.fetch ? { fetch: io.fetch } : {} });
4022
+ await client.revoke(entry.apiKey);
4023
+ revoked = true;
4024
+ } catch (e) {
4025
+ if (e instanceof CliAuthError && e.status === 401) {
4026
+ io.err.write("The cached key was already unusable \u2014 evicting it anyway.\n");
4027
+ } else if (e instanceof CliAuthError && e.status === 403) {
4028
+ io.err.write(
4029
+ `This instance refused the self-revoke (${e.code}) \u2014 the key STAYS VALID server-side; revoke it from the dashboard. Evicting the cached copy.
4030
+ `
4031
+ );
4032
+ } else {
4033
+ throw e;
4034
+ }
4035
+ }
4036
+ } else {
4037
+ io.err.write("No instance URL known for this profile \u2014 evicting the cached key locally only.\n");
4038
+ }
4039
+ removeWorkspaceKey2(io.env, profileName, org);
4040
+ if (globals.json) return printJson(io, { profile: profileName, org, revoked, evicted: true });
4041
+ io.out.write(
4042
+ `Logged out of hub org ${org} on profile "${profileName}"${revoked ? " (key revoked server-side)" : ""}.
4043
+ `
4044
+ );
4045
+ return;
4046
+ }
3811
4047
  delete profile.apiKey;
3812
4048
  if (!profile.baseUrl) delete config2.profiles[profileName];
3813
4049
  saveConfig2(io.env, config2);
@@ -3819,7 +4055,7 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
3819
4055
  });
3820
4056
  program2.command("whoami").description("Show the identity behind the resolved API key").action(async (_opts, cmd) => {
3821
4057
  const ctx = resolveContext(cmd, io);
3822
- requireApiKey(ctx);
4058
+ await requireApiKey(ctx);
3823
4059
  const me = await ctx.client.me();
3824
4060
  if (ctx.json) return printJson(io, me);
3825
4061
  io.out.write(
@@ -3834,7 +4070,7 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
3834
4070
  });
3835
4071
  program2.command("verify").description("Check that the resolved instance + key work (exit 0 = ok)").action(async (_opts, cmd) => {
3836
4072
  const ctx = resolveContext(cmd, io);
3837
- requireApiKey(ctx);
4073
+ await requireApiKey(ctx);
3838
4074
  const me = await ctx.client.me();
3839
4075
  if (ctx.json) return printJson(io, { ok: true, baseUrl: ctx.baseUrl, user: me.user });
3840
4076
  io.out.write(`ok \u2014 ${me.user.email} (${me.role}) @ ${ctx.baseUrl}
@@ -3862,7 +4098,14 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
3862
4098
  profiles: Object.fromEntries(
3863
4099
  names.map((n) => {
3864
4100
  const p = config2.profiles[n];
3865
- return [n, { baseUrl: p.baseUrl ?? null, apiKey: p.apiKey ? redactKey(p.apiKey) : null }];
4101
+ return [
4102
+ n,
4103
+ {
4104
+ baseUrl: p.baseUrl ?? null,
4105
+ apiKey: p.apiKey ? redactKey(p.apiKey) : null,
4106
+ hubOrgs: Object.keys(p.workspaceKeys ?? {})
4107
+ }
4108
+ ];
3866
4109
  })
3867
4110
  )
3868
4111
  });
@@ -3877,11 +4120,12 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
3877
4120
  table(
3878
4121
  names.map((n) => {
3879
4122
  const p = config2.profiles[n];
4123
+ const hubOrgs = Object.keys(p.workspaceKeys ?? {});
3880
4124
  return [
3881
4125
  config2.activeProfile === n ? "*" : " ",
3882
4126
  n,
3883
4127
  p.baseUrl ?? "(no url)",
3884
- p.apiKey ? redactKey(p.apiKey) : "(no key)"
4128
+ p.apiKey ? redactKey(p.apiKey) : hubOrgs.length > 0 ? `(hub: ${hubOrgs.join(", ")})` : "(no key)"
3885
4129
  ];
3886
4130
  })
3887
4131
  )
@@ -3898,7 +4142,14 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
3898
4142
  profiles: Object.fromEntries(
3899
4143
  Object.entries(cfg.profiles).map(([n, p]) => [
3900
4144
  n,
3901
- { baseUrl: p.baseUrl ?? null, apiKey: p.apiKey ? redactKey(p.apiKey) : null }
4145
+ {
4146
+ baseUrl: p.baseUrl ?? null,
4147
+ apiKey: p.apiKey ? redactKey(p.apiKey) : null,
4148
+ // Hub-connected orgs: cached exchange keys, redacted like the rest.
4149
+ workspaceKeys: Object.fromEntries(
4150
+ Object.entries(p.workspaceKeys ?? {}).map(([org, entry]) => [org, redactKey(entry.apiKey)])
4151
+ )
4152
+ }
3902
4153
  ])
3903
4154
  )
3904
4155
  };
@@ -3920,7 +4171,7 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
3920
4171
  function registerDeckCommands(program2, io) {
3921
4172
  program2.command("list").description("List presentations (newest first, cursor-paginated)").option("--cursor <cursor>", "resume from a previous nextCursor").option("--limit <n>", "page size (1-100)", (v) => parseInt(v, 10)).option("--all", "follow nextCursor until every page is fetched", false).action(async (opts, cmd) => {
3922
4173
  const ctx = resolveContext(cmd, io);
3923
- requireApiKey(ctx);
4174
+ await requireApiKey(ctx);
3924
4175
  const params = {};
3925
4176
  if (opts.cursor) params.cursor = opts.cursor;
3926
4177
  if (opts.limit !== void 0) params.limit = opts.limit;
@@ -3948,7 +4199,7 @@ function registerDeckCommands(program2, io) {
3948
4199
  });
3949
4200
  program2.command("get <id>").description("Show one presentation (metadata + latest version)").action(async (id, _opts, cmd) => {
3950
4201
  const ctx = resolveContext(cmd, io);
3951
- requireApiKey(ctx);
4202
+ await requireApiKey(ctx);
3952
4203
  const deck = await ctx.client.presentation(id);
3953
4204
  if (ctx.json) return printJson(io, deck);
3954
4205
  io.out.write(
@@ -3965,7 +4216,7 @@ function registerDeckCommands(program2, io) {
3965
4216
  });
3966
4217
  program2.command("delete <id>").description("Delete a presentation (soft delete; versions and share links stop resolving)").action(async (id, _opts, cmd) => {
3967
4218
  const ctx = resolveContext(cmd, io);
3968
- requireApiKey(ctx);
4219
+ await requireApiKey(ctx);
3969
4220
  const deck = await ctx.client.deletePresentation(id);
3970
4221
  if (ctx.json) return printJson(io, deck);
3971
4222
  io.out.write(`Deleted "${deck.title}" (${deck.id}).
@@ -4316,7 +4567,7 @@ function registerContentCommands(program2, io) {
4316
4567
  ).option("--title <title>", "deck title (default: existing title, or the folder name)").option("--entry <path>", "entry document (default: index.html, or the only .html)").option("--kind <kind>", "presentation | app | plan (new decks only)", "presentation").option("--interactive", "mark the deck as embedding interactive content (new decks only)", false).option("--id <deckId>", "push a new version of this existing deck").option("--new", `force a NEW deck even when ${LINK_FILENAME} links one`, false).action(
4317
4568
  async (path, opts, cmd) => {
4318
4569
  const ctx = resolveContext(cmd, io);
4319
- requireApiKey(ctx);
4570
+ await requireApiKey(ctx);
4320
4571
  const target = path ?? ".";
4321
4572
  const scan = await scanDeck(target);
4322
4573
  const entryPath = detectEntry(scan, opts.entry);
@@ -4392,7 +4643,7 @@ function registerContentCommands(program2, io) {
4392
4643
  `Download a deck version to a folder (id defaults to the ${LINK_FILENAME} link in the target)`
4393
4644
  ).option("--at <version>", "pull this version instead of the latest", (v) => parseInt(v, 10)).action(async (id, path, opts, cmd) => {
4394
4645
  const ctx = resolveContext(cmd, io);
4395
- requireApiKey(ctx);
4646
+ await requireApiKey(ctx);
4396
4647
  let deckId = id ?? null;
4397
4648
  let dest = path ?? null;
4398
4649
  if (!deckId) {
@@ -4437,7 +4688,7 @@ function registerContentCommands(program2, io) {
4437
4688
  program2.command("pull-annotations [id]").description(`List a deck's annotations (id defaults to the ${LINK_FILENAME} link in .)`).option("--version <n>", "only notes anchored to this version", (v) => parseInt(v, 10)).option("--status <status>", "open | resolved").option("--out <file>", "write the annotations as JSON to this file").action(
4438
4689
  async (id, opts, cmd) => {
4439
4690
  const ctx = resolveContext(cmd, io);
4440
- requireApiKey(ctx);
4691
+ await requireApiKey(ctx);
4441
4692
  let deckId = id ?? null;
4442
4693
  if (!deckId) {
4443
4694
  const link = await readLink(resolve3("."));
@@ -4550,7 +4801,7 @@ function registerSharingCommands(program2, io) {
4550
4801
  program2.command("share <id>").description("Create a per-recipient share link (prints the viewer URL \u2014 shown once)").option("--name <name>", "owner-facing recipient label", "cli").option("--to-version <n>", "pin the recipient to this version", (v) => parseInt(v, 10)).option("--annotator", "let the recipient annotate", false).option("--expires <datetime>", "ISO expiry, e.g. 2026-12-31T23:59:59Z").option("--password <password>", "viewer password (min 4 chars)").action(
4551
4802
  async (id, opts, cmd) => {
4552
4803
  const ctx = resolveContext(cmd, io);
4553
- requireApiKey(ctx);
4804
+ await requireApiKey(ctx);
4554
4805
  const created = await ctx.client.createShareToken(id, shareOptionsOf(opts));
4555
4806
  if (ctx.json) return printJson(io, created);
4556
4807
  io.out.write(
@@ -4563,7 +4814,7 @@ function registerSharingCommands(program2, io) {
4563
4814
  );
4564
4815
  program2.command("unshare <id>").description("Revoke one share token (--token) or ALL active tokens of the deck").option("--token <tokenId>", "revoke only this token").action(async (id, opts, cmd) => {
4565
4816
  const ctx = resolveContext(cmd, io);
4566
- requireApiKey(ctx);
4817
+ await requireApiKey(ctx);
4567
4818
  if (opts.token) {
4568
4819
  const revoked2 = await ctx.client.revokeShareToken(id, opts.token);
4569
4820
  if (ctx.json) return printJson(io, revoked2);
@@ -4592,7 +4843,7 @@ function registerSharingCommands(program2, io) {
4592
4843
  program2.command("share-email <id>").description("Create a personal share link per recipient and email it (one token per address)").requiredOption("--to <email...>", "recipient email(s)").option("--to-version <n>", "pin recipients to this version", (v) => parseInt(v, 10)).option("--annotator", "let recipients annotate", false).option("--expires <datetime>", "ISO expiry").option("--password <password>", "viewer password (tell recipients separately)").option("--message <text>", "personal note included in the email").action(
4593
4844
  async (id, opts, cmd) => {
4594
4845
  const ctx = resolveContext(cmd, io);
4595
- requireApiKey(ctx);
4846
+ await requireApiKey(ctx);
4596
4847
  const results = [];
4597
4848
  for (const email of opts.to) {
4598
4849
  const created = await ctx.client.createShareToken(id, shareOptionsOf({ ...opts, name: email }));
@@ -4611,7 +4862,7 @@ function registerSharingCommands(program2, io) {
4611
4862
  program2.command("pin <id> <tokenId>").description("Pin a share token to a version, or set it back to following the latest").option("--to-version <n>", "freeze the recipient on this version", (v) => parseInt(v, 10)).option("--latest", "follow the latest version again", false).action(
4612
4863
  async (id, tokenId, opts, cmd) => {
4613
4864
  const ctx = resolveContext(cmd, io);
4614
- requireApiKey(ctx);
4865
+ await requireApiKey(ctx);
4615
4866
  if (opts.latest === (opts.toVersion !== void 0)) {
4616
4867
  throw new CliUsageError("Pass exactly one of --to-version <n> or --latest.");
4617
4868
  }
@@ -4627,7 +4878,7 @@ function registerSharingCommands(program2, io) {
4627
4878
  );
4628
4879
  program2.command("invite <id>").description("Invite a dev collaborator to one deck (prints the claim link)").requiredOption("--email <email>", "invitee email").action(async (id, opts, cmd) => {
4629
4880
  const ctx = resolveContext(cmd, io);
4630
- requireApiKey(ctx);
4881
+ await requireApiKey(ctx);
4631
4882
  const invited = await ctx.client.inviteCollaborator(id, { email: opts.email });
4632
4883
  if (ctx.json) return printJson(io, invited);
4633
4884
  io.out.write(
@@ -4639,7 +4890,7 @@ function registerSharingCommands(program2, io) {
4639
4890
  });
4640
4891
  program2.command("uninvite <id> <collaboratorId>").description("Revoke a collaborator grant (pending or active)").action(async (id, collaboratorId, _opts, cmd) => {
4641
4892
  const ctx = resolveContext(cmd, io);
4642
- requireApiKey(ctx);
4893
+ await requireApiKey(ctx);
4643
4894
  const revoked = await ctx.client.removeCollaborator(id, collaboratorId);
4644
4895
  if (ctx.json) return printJson(io, revoked);
4645
4896
  io.out.write(`Revoked collaborator grant ${revoked.id} (${revoked.email}).
@@ -4672,7 +4923,7 @@ function registerFileCommands(program2, io) {
4672
4923
  });
4673
4924
  program2.command("export").description("Download the full workspace export as a zip (key needs data:export)").option("-o, --out <path>", "write to this path (defaults to export-<date>.zip)").action(async (opts, cmd) => {
4674
4925
  const ctx = resolveContext(cmd, io);
4675
- requireApiKey(ctx);
4926
+ await requireApiKey(ctx);
4676
4927
  const res = await ctx.client.downloadExport();
4677
4928
  if (!res.body) {
4678
4929
  throw new Error("The export response carried no body");
@@ -4686,7 +4937,7 @@ function registerFileCommands(program2, io) {
4686
4937
  const files = program2.command("files").description("Manage workspace files");
4687
4938
  files.command("list").description("List files in the workspace (newest first, cursor-paginated)").option("--cursor <cursor>", "resume from a previous nextCursor").option("--limit <n>", "page size (1-100)", (v) => parseInt(v, 10)).option("--all", "follow nextCursor until every page is fetched", false).action(async (opts, cmd) => {
4688
4939
  const ctx = resolveContext(cmd, io);
4689
- requireApiKey(ctx);
4940
+ await requireApiKey(ctx);
4690
4941
  const params = {};
4691
4942
  if (opts.cursor) params.cursor = opts.cursor;
4692
4943
  if (opts.limit !== void 0) params.limit = opts.limit;
@@ -4717,7 +4968,7 @@ function registerFileCommands(program2, io) {
4717
4968
  });
4718
4969
  files.command("upload <path>").description("Upload a local file").option("--name <name>", "stored file name (defaults to the local basename)").option("--content-type <type>", "content type", "application/octet-stream").action(async (path, opts, cmd) => {
4719
4970
  const ctx = resolveContext(cmd, io);
4720
- requireApiKey(ctx);
4971
+ await requireApiKey(ctx);
4721
4972
  const bytes = await readFile4(path);
4722
4973
  const name = opts.name ?? basename2(path);
4723
4974
  const result = await ctx.client.uploadFile(name, new Uint8Array(bytes), opts.contentType);
@@ -4730,7 +4981,7 @@ function registerFileCommands(program2, io) {
4730
4981
  });
4731
4982
  files.command("download <id>").description("Download a file by id").option("--out <path>", "write to this path (defaults to the stored name)").action(async (id, opts, cmd) => {
4732
4983
  const ctx = resolveContext(cmd, io);
4733
- const apiKey = requireApiKey(ctx);
4984
+ const apiKey = await requireApiKey(ctx);
4734
4985
  const meta = await ctx.client.file(id);
4735
4986
  const fetchImpl = io.fetch ?? globalThis.fetch.bind(globalThis);
4736
4987
  const res = await fetchImpl(ctx.client.fileContentUrl(id), {
@@ -4747,7 +4998,7 @@ function registerFileCommands(program2, io) {
4747
4998
  });
4748
4999
  files.command("rm <id>").description("Delete a file by id").action(async (id, _opts, cmd) => {
4749
5000
  const ctx = resolveContext(cmd, io);
4750
- requireApiKey(ctx);
5001
+ await requireApiKey(ctx);
4751
5002
  await ctx.client.deleteFile(id);
4752
5003
  if (ctx.json) return printJson(io, { deleted: id });
4753
5004
  io.out.write(`Deleted ${id}
@@ -4833,10 +5084,13 @@ function registerCompletionCommand(program2, io) {
4833
5084
  }
4834
5085
 
4835
5086
  // src/index.ts
4836
- var VERSION = "0.2.1";
5087
+ var VERSION = "0.2.2";
4837
5088
  function buildProgram(io) {
4838
5089
  const program2 = new Command();
4839
- program2.name("slideless").description("Command-line client for a Slideless instance (push, share, pull, preview)").version(VERSION).option("--api-url <url>", "instance base URL (or SLIDELESS_URL / profile baseUrl)").option("--url <url>", "alias of --api-url").option("--api-key <key>", "API key (or SLIDELESS_API_KEY / profile apiKey)").option("--profile <name>", "use this saved profile instead of the active one").option("--json", "machine-readable JSON output", false);
5090
+ program2.name("slideless").description("Command-line client for a Slideless instance (push, share, pull, preview)").version(VERSION).option("--api-url <url>", "instance base URL (or SLIDELESS_URL / profile baseUrl)").option("--url <url>", "alias of --api-url").option("--api-key <key>", "API key (or SLIDELESS_API_KEY / profile apiKey)").option(
5091
+ "--org <id>",
5092
+ "hub org to connect as on a cloud instance (default: `antasphere org use`; ignored when a direct API key resolves)"
5093
+ ).option("--profile <name>", "use this saved profile instead of the active one").option("--json", "machine-readable JSON output", false);
4840
5094
  registerAuthCommands(program2, io);
4841
5095
  registerDeckCommands(program2, io);
4842
5096
  registerContentCommands(program2, io);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antasphere/slideless",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "The Slideless CLI — push, share, pull, and preview decks on a Slideless instance",
5
5
  "license": "MIT",
6
6
  "type": "module",