@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.
- package/dist/bin.js +284 -30
- 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
|
-
|
|
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
|
-
|
|
3692
|
-
|
|
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
|
-
"
|
|
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
|
-
|
|
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(
|
|
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 [
|
|
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
|
-
{
|
|
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.
|
|
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(
|
|
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);
|