@antasphere/slideless 0.2.0 → 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 +298 -35
  2. package/package.json +12 -11
package/dist/bin.js CHANGED
@@ -3042,7 +3042,7 @@ var {
3042
3042
  Help
3043
3043
  } = import_index.default;
3044
3044
 
3045
- // ../../node_modules/.pnpm/@antasphere+cli-core@0.1.0/node_modules/@antasphere/cli-core/dist/config.js
3045
+ // ../../node_modules/.pnpm/@antasphere+cli-core@0.2.0/node_modules/@antasphere/cli-core/dist/config.js
3046
3046
  import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3047
3047
  import { dirname, join } from "node:path";
3048
3048
  var HUB_TOOL = "hub";
@@ -3109,16 +3109,53 @@ 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
- // ../../node_modules/.pnpm/@antasphere+cli-core@0.1.0/node_modules/@antasphere/cli-core/dist/io.js
3149
+ // ../../node_modules/.pnpm/@antasphere+cli-core@0.2.0/node_modules/@antasphere/cli-core/dist/io.js
3114
3150
  function printJson(io, value) {
3115
3151
  io.out.write(`${JSON.stringify(value, null, 2)}
3116
3152
  `);
3117
3153
  }
3118
3154
 
3119
- // ../../node_modules/.pnpm/@antasphere+cli-core@0.1.0/node_modules/@antasphere/cli-core/dist/resolve.js
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];
@@ -3142,7 +3179,7 @@ function resolveApiKey(opts) {
3142
3179
  return opts.flag ?? opts.env[opts.envVar] ?? opts.profile?.apiKey;
3143
3180
  }
3144
3181
 
3145
- // ../../node_modules/.pnpm/@antasphere+cli-core@0.1.0/node_modules/@antasphere/cli-core/dist/auth.js
3182
+ // ../../node_modules/.pnpm/@antasphere+cli-core@0.2.0/node_modules/@antasphere/cli-core/dist/auth.js
3146
3183
  var CliAuthError = class extends Error {
3147
3184
  status;
3148
3185
  code;
@@ -3197,7 +3234,81 @@ var CliAuthClient = class {
3197
3234
  }
3198
3235
  };
3199
3236
 
3200
- // ../../node_modules/.pnpm/@antasphere+cli-core@0.1.0/node_modules/@antasphere/cli-core/dist/migrate.js
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
+
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";
3203
3314
  function migrateLegacyConfig(env, spec) {
@@ -3337,6 +3448,15 @@ var PlatformClient = class {
3337
3448
  cliAuthComplete(req) {
3338
3449
  return this.request("POST", "/cli/auth/complete", req);
3339
3450
  }
3451
+ /**
3452
+ * Public, CLOUD EDITION only (404 on oss): exchange a hub-minted 120 s
3453
+ * connect JWT (the hub's /sso/tool-token response) for an `slk_` API key
3454
+ * bound to the projected workspace — same grant and one-shot response
3455
+ * shape as cliAuthComplete. Each token works exactly once (jti).
3456
+ */
3457
+ ssoCliConnect(req) {
3458
+ return this.request("POST", "/sso/cli-connect", req);
3459
+ }
3340
3460
  // ── Members ───────────────────────────────────────────────────────────────
3341
3461
  members(params = {}) {
3342
3462
  return this.request("GET", this.pathWithQuery("/members", params));
@@ -3648,6 +3768,12 @@ function saveConfig2(env, config) {
3648
3768
  function clearConfig2(env) {
3649
3769
  clearConfig(env, TOOL);
3650
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
+ }
3651
3777
 
3652
3778
  // src/context.ts
3653
3779
  function resolveProfile2(config, requested) {
@@ -3667,25 +3793,95 @@ function resolveContext(cmd, io) {
3667
3793
  profile,
3668
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."
3669
3795
  });
3670
- 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
+ }
3671
3804
  const fetchImpl = io.fetch ?? globalThis.fetch.bind(globalThis);
3672
3805
  return {
3673
3806
  client: new PlatformClient({ baseUrl, ...apiKey ? { apiKey } : {}, fetch: fetchImpl }),
3674
3807
  baseUrl,
3675
3808
  apiKey,
3809
+ org,
3676
3810
  json: Boolean(opts.json),
3677
3811
  io,
3678
3812
  profileName,
3679
3813
  config
3680
3814
  };
3681
3815
  }
3682
- function requireApiKey(ctx) {
3683
- 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) {
3684
3837
  throw new CliUsageError(
3685
- "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."
3686
3839
  );
3687
3840
  }
3688
- 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;
3689
3885
  }
3690
3886
  function table(rows) {
3691
3887
  if (rows.length === 0) return "";
@@ -3791,7 +3987,9 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
3791
3987
  `
3792
3988
  );
3793
3989
  });
3794
- 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) => {
3795
3993
  const globals = cmd.optsWithGlobals();
3796
3994
  const config2 = loadConfig2(io.env);
3797
3995
  const profileName = globals.profile ?? config2.activeProfile;
@@ -3799,6 +3997,53 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
3799
3997
  if (!profileName || !profile) {
3800
3998
  throw new CliUsageError("No profile to log out of.");
3801
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
+ }
3802
4047
  delete profile.apiKey;
3803
4048
  if (!profile.baseUrl) delete config2.profiles[profileName];
3804
4049
  saveConfig2(io.env, config2);
@@ -3810,7 +4055,7 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
3810
4055
  });
3811
4056
  program2.command("whoami").description("Show the identity behind the resolved API key").action(async (_opts, cmd) => {
3812
4057
  const ctx = resolveContext(cmd, io);
3813
- requireApiKey(ctx);
4058
+ await requireApiKey(ctx);
3814
4059
  const me = await ctx.client.me();
3815
4060
  if (ctx.json) return printJson(io, me);
3816
4061
  io.out.write(
@@ -3825,7 +4070,7 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
3825
4070
  });
3826
4071
  program2.command("verify").description("Check that the resolved instance + key work (exit 0 = ok)").action(async (_opts, cmd) => {
3827
4072
  const ctx = resolveContext(cmd, io);
3828
- requireApiKey(ctx);
4073
+ await requireApiKey(ctx);
3829
4074
  const me = await ctx.client.me();
3830
4075
  if (ctx.json) return printJson(io, { ok: true, baseUrl: ctx.baseUrl, user: me.user });
3831
4076
  io.out.write(`ok \u2014 ${me.user.email} (${me.role}) @ ${ctx.baseUrl}
@@ -3853,7 +4098,14 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
3853
4098
  profiles: Object.fromEntries(
3854
4099
  names.map((n) => {
3855
4100
  const p = config2.profiles[n];
3856
- 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
+ ];
3857
4109
  })
3858
4110
  )
3859
4111
  });
@@ -3868,11 +4120,12 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
3868
4120
  table(
3869
4121
  names.map((n) => {
3870
4122
  const p = config2.profiles[n];
4123
+ const hubOrgs = Object.keys(p.workspaceKeys ?? {});
3871
4124
  return [
3872
4125
  config2.activeProfile === n ? "*" : " ",
3873
4126
  n,
3874
4127
  p.baseUrl ?? "(no url)",
3875
- p.apiKey ? redactKey(p.apiKey) : "(no key)"
4128
+ p.apiKey ? redactKey(p.apiKey) : hubOrgs.length > 0 ? `(hub: ${hubOrgs.join(", ")})` : "(no key)"
3876
4129
  ];
3877
4130
  })
3878
4131
  )
@@ -3889,7 +4142,14 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
3889
4142
  profiles: Object.fromEntries(
3890
4143
  Object.entries(cfg.profiles).map(([n, p]) => [
3891
4144
  n,
3892
- { 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
+ }
3893
4153
  ])
3894
4154
  )
3895
4155
  };
@@ -3911,7 +4171,7 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
3911
4171
  function registerDeckCommands(program2, io) {
3912
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) => {
3913
4173
  const ctx = resolveContext(cmd, io);
3914
- requireApiKey(ctx);
4174
+ await requireApiKey(ctx);
3915
4175
  const params = {};
3916
4176
  if (opts.cursor) params.cursor = opts.cursor;
3917
4177
  if (opts.limit !== void 0) params.limit = opts.limit;
@@ -3939,7 +4199,7 @@ function registerDeckCommands(program2, io) {
3939
4199
  });
3940
4200
  program2.command("get <id>").description("Show one presentation (metadata + latest version)").action(async (id, _opts, cmd) => {
3941
4201
  const ctx = resolveContext(cmd, io);
3942
- requireApiKey(ctx);
4202
+ await requireApiKey(ctx);
3943
4203
  const deck = await ctx.client.presentation(id);
3944
4204
  if (ctx.json) return printJson(io, deck);
3945
4205
  io.out.write(
@@ -3956,7 +4216,7 @@ function registerDeckCommands(program2, io) {
3956
4216
  });
3957
4217
  program2.command("delete <id>").description("Delete a presentation (soft delete; versions and share links stop resolving)").action(async (id, _opts, cmd) => {
3958
4218
  const ctx = resolveContext(cmd, io);
3959
- requireApiKey(ctx);
4219
+ await requireApiKey(ctx);
3960
4220
  const deck = await ctx.client.deletePresentation(id);
3961
4221
  if (ctx.json) return printJson(io, deck);
3962
4222
  io.out.write(`Deleted "${deck.title}" (${deck.id}).
@@ -4307,7 +4567,7 @@ function registerContentCommands(program2, io) {
4307
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(
4308
4568
  async (path, opts, cmd) => {
4309
4569
  const ctx = resolveContext(cmd, io);
4310
- requireApiKey(ctx);
4570
+ await requireApiKey(ctx);
4311
4571
  const target = path ?? ".";
4312
4572
  const scan = await scanDeck(target);
4313
4573
  const entryPath = detectEntry(scan, opts.entry);
@@ -4383,7 +4643,7 @@ function registerContentCommands(program2, io) {
4383
4643
  `Download a deck version to a folder (id defaults to the ${LINK_FILENAME} link in the target)`
4384
4644
  ).option("--at <version>", "pull this version instead of the latest", (v) => parseInt(v, 10)).action(async (id, path, opts, cmd) => {
4385
4645
  const ctx = resolveContext(cmd, io);
4386
- requireApiKey(ctx);
4646
+ await requireApiKey(ctx);
4387
4647
  let deckId = id ?? null;
4388
4648
  let dest = path ?? null;
4389
4649
  if (!deckId) {
@@ -4428,7 +4688,7 @@ function registerContentCommands(program2, io) {
4428
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(
4429
4689
  async (id, opts, cmd) => {
4430
4690
  const ctx = resolveContext(cmd, io);
4431
- requireApiKey(ctx);
4691
+ await requireApiKey(ctx);
4432
4692
  let deckId = id ?? null;
4433
4693
  if (!deckId) {
4434
4694
  const link = await readLink(resolve3("."));
@@ -4541,7 +4801,7 @@ function registerSharingCommands(program2, io) {
4541
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(
4542
4802
  async (id, opts, cmd) => {
4543
4803
  const ctx = resolveContext(cmd, io);
4544
- requireApiKey(ctx);
4804
+ await requireApiKey(ctx);
4545
4805
  const created = await ctx.client.createShareToken(id, shareOptionsOf(opts));
4546
4806
  if (ctx.json) return printJson(io, created);
4547
4807
  io.out.write(
@@ -4554,7 +4814,7 @@ function registerSharingCommands(program2, io) {
4554
4814
  );
4555
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) => {
4556
4816
  const ctx = resolveContext(cmd, io);
4557
- requireApiKey(ctx);
4817
+ await requireApiKey(ctx);
4558
4818
  if (opts.token) {
4559
4819
  const revoked2 = await ctx.client.revokeShareToken(id, opts.token);
4560
4820
  if (ctx.json) return printJson(io, revoked2);
@@ -4583,7 +4843,7 @@ function registerSharingCommands(program2, io) {
4583
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(
4584
4844
  async (id, opts, cmd) => {
4585
4845
  const ctx = resolveContext(cmd, io);
4586
- requireApiKey(ctx);
4846
+ await requireApiKey(ctx);
4587
4847
  const results = [];
4588
4848
  for (const email of opts.to) {
4589
4849
  const created = await ctx.client.createShareToken(id, shareOptionsOf({ ...opts, name: email }));
@@ -4602,7 +4862,7 @@ function registerSharingCommands(program2, io) {
4602
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(
4603
4863
  async (id, tokenId, opts, cmd) => {
4604
4864
  const ctx = resolveContext(cmd, io);
4605
- requireApiKey(ctx);
4865
+ await requireApiKey(ctx);
4606
4866
  if (opts.latest === (opts.toVersion !== void 0)) {
4607
4867
  throw new CliUsageError("Pass exactly one of --to-version <n> or --latest.");
4608
4868
  }
@@ -4618,7 +4878,7 @@ function registerSharingCommands(program2, io) {
4618
4878
  );
4619
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) => {
4620
4880
  const ctx = resolveContext(cmd, io);
4621
- requireApiKey(ctx);
4881
+ await requireApiKey(ctx);
4622
4882
  const invited = await ctx.client.inviteCollaborator(id, { email: opts.email });
4623
4883
  if (ctx.json) return printJson(io, invited);
4624
4884
  io.out.write(
@@ -4630,7 +4890,7 @@ function registerSharingCommands(program2, io) {
4630
4890
  });
4631
4891
  program2.command("uninvite <id> <collaboratorId>").description("Revoke a collaborator grant (pending or active)").action(async (id, collaboratorId, _opts, cmd) => {
4632
4892
  const ctx = resolveContext(cmd, io);
4633
- requireApiKey(ctx);
4893
+ await requireApiKey(ctx);
4634
4894
  const revoked = await ctx.client.removeCollaborator(id, collaboratorId);
4635
4895
  if (ctx.json) return printJson(io, revoked);
4636
4896
  io.out.write(`Revoked collaborator grant ${revoked.id} (${revoked.email}).
@@ -4663,7 +4923,7 @@ function registerFileCommands(program2, io) {
4663
4923
  });
4664
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) => {
4665
4925
  const ctx = resolveContext(cmd, io);
4666
- requireApiKey(ctx);
4926
+ await requireApiKey(ctx);
4667
4927
  const res = await ctx.client.downloadExport();
4668
4928
  if (!res.body) {
4669
4929
  throw new Error("The export response carried no body");
@@ -4677,7 +4937,7 @@ function registerFileCommands(program2, io) {
4677
4937
  const files = program2.command("files").description("Manage workspace files");
4678
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) => {
4679
4939
  const ctx = resolveContext(cmd, io);
4680
- requireApiKey(ctx);
4940
+ await requireApiKey(ctx);
4681
4941
  const params = {};
4682
4942
  if (opts.cursor) params.cursor = opts.cursor;
4683
4943
  if (opts.limit !== void 0) params.limit = opts.limit;
@@ -4708,7 +4968,7 @@ function registerFileCommands(program2, io) {
4708
4968
  });
4709
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) => {
4710
4970
  const ctx = resolveContext(cmd, io);
4711
- requireApiKey(ctx);
4971
+ await requireApiKey(ctx);
4712
4972
  const bytes = await readFile4(path);
4713
4973
  const name = opts.name ?? basename2(path);
4714
4974
  const result = await ctx.client.uploadFile(name, new Uint8Array(bytes), opts.contentType);
@@ -4721,7 +4981,7 @@ function registerFileCommands(program2, io) {
4721
4981
  });
4722
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) => {
4723
4983
  const ctx = resolveContext(cmd, io);
4724
- const apiKey = requireApiKey(ctx);
4984
+ const apiKey = await requireApiKey(ctx);
4725
4985
  const meta = await ctx.client.file(id);
4726
4986
  const fetchImpl = io.fetch ?? globalThis.fetch.bind(globalThis);
4727
4987
  const res = await fetchImpl(ctx.client.fileContentUrl(id), {
@@ -4738,7 +4998,7 @@ function registerFileCommands(program2, io) {
4738
4998
  });
4739
4999
  files.command("rm <id>").description("Delete a file by id").action(async (id, _opts, cmd) => {
4740
5000
  const ctx = resolveContext(cmd, io);
4741
- requireApiKey(ctx);
5001
+ await requireApiKey(ctx);
4742
5002
  await ctx.client.deleteFile(id);
4743
5003
  if (ctx.json) return printJson(io, { deleted: id });
4744
5004
  io.out.write(`Deleted ${id}
@@ -4824,10 +5084,13 @@ function registerCompletionCommand(program2, io) {
4824
5084
  }
4825
5085
 
4826
5086
  // src/index.ts
4827
- var VERSION = "0.2.0";
5087
+ var VERSION = "0.2.2";
4828
5088
  function buildProgram(io) {
4829
5089
  const program2 = new Command();
4830
- 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);
4831
5094
  registerAuthCommands(program2, io);
4832
5095
  registerDeckCommands(program2, io);
4833
5096
  registerContentCommands(program2, io);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antasphere/slideless",
3
- "version": "0.2.0",
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",
@@ -22,19 +22,20 @@
22
22
  "files": [
23
23
  "dist"
24
24
  ],
25
- "devDependencies": {
26
- "@antasphere/cli-core": "^0.1.0",
27
- "@types/node": "^22.15.0",
28
- "commander": "^12.1.0",
29
- "esbuild": "^0.25.0",
30
- "vitest": "^3.2.0",
31
- "@slideless/contract": "0.1.0",
32
- "@slideless/sdk": "0.1.0"
33
- },
34
25
  "scripts": {
35
26
  "build": "rm -rf dist && esbuild src/bin.ts --bundle --platform=node --format=esm --target=node22 --outfile=dist/bin.js --log-level=warning --banner:js=\"import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);\"",
27
+ "prepack": "npm run build",
36
28
  "typecheck": "tsc -p tsconfig.json --noEmit",
37
29
  "lint": "eslint src",
38
30
  "test": "vitest run --passWithNoTests"
31
+ },
32
+ "devDependencies": {
33
+ "@antasphere/cli-core": "^0.2.0",
34
+ "@slideless/contract": "workspace:*",
35
+ "@slideless/sdk": "workspace:*",
36
+ "@types/node": "^22.15.0",
37
+ "commander": "^12.1.0",
38
+ "esbuild": "^0.25.0",
39
+ "vitest": "^3.2.0"
39
40
  }
40
- }
41
+ }