@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.
- package/dist/bin.js +298 -35
- 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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
3683
|
-
|
|
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
|
-
"
|
|
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
|
-
|
|
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(
|
|
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 [
|
|
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
|
-
{
|
|
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.
|
|
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(
|
|
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.
|
|
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
|
+
}
|