@antasphere/slideless 0.2.2 → 0.2.4
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 +237 -167
- package/package.json +2 -2
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.4.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,50 +3109,43 @@ function redactKey(key) {
|
|
|
3109
3109
|
return "****";
|
|
3110
3110
|
return `${key.slice(0, 13)}\u2026${key.slice(-4)}`;
|
|
3111
3111
|
}
|
|
3112
|
-
function
|
|
3113
|
-
|
|
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;
|
|
3112
|
+
function lookupConnectKey(profile, hubProfileName) {
|
|
3113
|
+
return profile?.connectKeys?.[hubProfileName]?.apiKey;
|
|
3121
3114
|
}
|
|
3122
|
-
function
|
|
3115
|
+
function storeConnectKey(env, tool, profileName, hubProfileName, entry) {
|
|
3123
3116
|
const config = loadConfig(env, tool);
|
|
3124
3117
|
const profile = config.profiles[profileName] ?? {};
|
|
3125
3118
|
config.profiles[profileName] = {
|
|
3126
3119
|
...profile,
|
|
3127
|
-
|
|
3120
|
+
connectKeys: { ...profile.connectKeys, [hubProfileName]: entry }
|
|
3128
3121
|
};
|
|
3129
3122
|
if (!config.activeProfile)
|
|
3130
3123
|
config.activeProfile = profileName;
|
|
3131
3124
|
return saveConfig(env, tool, config);
|
|
3132
3125
|
}
|
|
3133
|
-
function
|
|
3126
|
+
function removeConnectKey(env, tool, profileName, hubProfileName) {
|
|
3134
3127
|
const config = loadConfig(env, tool);
|
|
3135
3128
|
const profile = config.profiles[profileName];
|
|
3136
|
-
if (!profile?.
|
|
3129
|
+
if (!profile?.connectKeys || !(hubProfileName in profile.connectKeys))
|
|
3137
3130
|
return false;
|
|
3138
|
-
const { [
|
|
3131
|
+
const { [hubProfileName]: _dropped, ...rest } = profile.connectKeys;
|
|
3139
3132
|
if (Object.keys(rest).length === 0) {
|
|
3140
|
-
const {
|
|
3133
|
+
const { connectKeys: _empty, ...bare } = profile;
|
|
3141
3134
|
config.profiles[profileName] = bare;
|
|
3142
3135
|
} else {
|
|
3143
|
-
config.profiles[profileName] = { ...profile,
|
|
3136
|
+
config.profiles[profileName] = { ...profile, connectKeys: rest };
|
|
3144
3137
|
}
|
|
3145
3138
|
saveConfig(env, tool, config);
|
|
3146
3139
|
return true;
|
|
3147
3140
|
}
|
|
3148
3141
|
|
|
3149
|
-
// ../../node_modules/.pnpm/@antasphere+cli-core@0.
|
|
3142
|
+
// ../../node_modules/.pnpm/@antasphere+cli-core@0.4.0/node_modules/@antasphere/cli-core/dist/io.js
|
|
3150
3143
|
function printJson(io, value) {
|
|
3151
3144
|
io.out.write(`${JSON.stringify(value, null, 2)}
|
|
3152
3145
|
`);
|
|
3153
3146
|
}
|
|
3154
3147
|
|
|
3155
|
-
// ../../node_modules/.pnpm/@antasphere+cli-core@0.
|
|
3148
|
+
// ../../node_modules/.pnpm/@antasphere+cli-core@0.4.0/node_modules/@antasphere/cli-core/dist/resolve.js
|
|
3156
3149
|
var CliUsageError = class extends Error {
|
|
3157
3150
|
};
|
|
3158
3151
|
var DEFAULT_PROFILE = "default";
|
|
@@ -3179,7 +3172,7 @@ function resolveApiKey(opts) {
|
|
|
3179
3172
|
return opts.flag ?? opts.env[opts.envVar] ?? opts.profile?.apiKey;
|
|
3180
3173
|
}
|
|
3181
3174
|
|
|
3182
|
-
// ../../node_modules/.pnpm/@antasphere+cli-core@0.
|
|
3175
|
+
// ../../node_modules/.pnpm/@antasphere+cli-core@0.4.0/node_modules/@antasphere/cli-core/dist/auth.js
|
|
3183
3176
|
var CliAuthError = class extends Error {
|
|
3184
3177
|
status;
|
|
3185
3178
|
code;
|
|
@@ -3234,7 +3227,7 @@ var CliAuthClient = class {
|
|
|
3234
3227
|
}
|
|
3235
3228
|
};
|
|
3236
3229
|
|
|
3237
|
-
// ../../node_modules/.pnpm/@antasphere+cli-core@0.
|
|
3230
|
+
// ../../node_modules/.pnpm/@antasphere+cli-core@0.4.0/node_modules/@antasphere/cli-core/dist/connect.js
|
|
3238
3231
|
var CliConnectError = class extends CliAuthError {
|
|
3239
3232
|
stage;
|
|
3240
3233
|
constructor(stage, status, code, message, details) {
|
|
@@ -3268,19 +3261,27 @@ var CliConnectClient = class {
|
|
|
3268
3261
|
toolToken(hubApiKey, body) {
|
|
3269
3262
|
return this.call("hub", `${this.hubBaseUrl}/api/v1/sso/tool-token`, body, hubApiKey);
|
|
3270
3263
|
}
|
|
3271
|
-
/**
|
|
3272
|
-
|
|
3273
|
-
|
|
3264
|
+
/**
|
|
3265
|
+
* Step 2 — the tool verifies the JWT (its only credential) and mints its
|
|
3266
|
+
* key. `hubRefreshToken` is the SAME response's offline grant, relayed so
|
|
3267
|
+
* the tool can store it (a grant-less connect is refused for users with
|
|
3268
|
+
* none already stored — `hub_grant_missing`).
|
|
3269
|
+
*/
|
|
3270
|
+
cliConnect(token, hubRefreshToken) {
|
|
3271
|
+
return this.call("tool", `${this.toolBaseUrl}/api/v1/sso/cli-connect`, {
|
|
3272
|
+
token,
|
|
3273
|
+
...hubRefreshToken ? { hubRefreshToken } : {}
|
|
3274
|
+
});
|
|
3274
3275
|
}
|
|
3275
|
-
/** The full two-call exchange. The hub key goes to the hub ONLY
|
|
3276
|
+
/** The full two-call exchange. The hub key goes to the hub ONLY; the
|
|
3277
|
+
* offline grant goes to the TOOL only — neither is ever persisted here. */
|
|
3276
3278
|
async exchange(hubApiKey, body) {
|
|
3277
3279
|
const minted = await this.toolToken(hubApiKey, body);
|
|
3278
|
-
const connected = await this.cliConnect(minted.token);
|
|
3280
|
+
const connected = await this.cliConnect(minted.token, minted.hubRefreshToken);
|
|
3279
3281
|
return {
|
|
3280
3282
|
key: connected.key,
|
|
3281
3283
|
apiKey: connected.apiKey,
|
|
3282
3284
|
user: connected.user,
|
|
3283
|
-
hubWorkspaceId: minted.workspaceId,
|
|
3284
3285
|
toolWorkspaceId: connected.workspaceId
|
|
3285
3286
|
};
|
|
3286
3287
|
}
|
|
@@ -3299,16 +3300,106 @@ var CliConnectClient = class {
|
|
|
3299
3300
|
}
|
|
3300
3301
|
};
|
|
3301
3302
|
|
|
3302
|
-
// ../../node_modules/.pnpm/@antasphere+cli-core@0.
|
|
3303
|
-
function
|
|
3304
|
-
const
|
|
3305
|
-
|
|
3303
|
+
// ../../node_modules/.pnpm/@antasphere+cli-core@0.4.0/node_modules/@antasphere/cli-core/dist/connect-on-demand.js
|
|
3304
|
+
async function probeToolInstance(toolBaseUrl, fetchImpl) {
|
|
3305
|
+
const f = fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
3306
|
+
try {
|
|
3307
|
+
const res = await f(`${toolBaseUrl.replace(/\/+$/, "")}/api/v1/instance`, {
|
|
3308
|
+
method: "GET",
|
|
3309
|
+
// Discovery is served cacheable for CLI/MCP clients, but the probe
|
|
3310
|
+
// must see live state (mirrors the tool SDKs' discovery call).
|
|
3311
|
+
cache: "no-store"
|
|
3312
|
+
});
|
|
3313
|
+
if (!res.ok)
|
|
3314
|
+
return null;
|
|
3315
|
+
const info = await res.json().catch(() => null);
|
|
3316
|
+
const methods = info?.auth?.methods;
|
|
3317
|
+
if (!Array.isArray(methods))
|
|
3318
|
+
return null;
|
|
3319
|
+
const authMethods = methods.filter((m) => typeof m === "string");
|
|
3320
|
+
const edition = typeof info?.edition === "string" ? info.edition : void 0;
|
|
3321
|
+
return { cloud: authMethods.includes("antasphere") || edition === "cloud", edition, authMethods };
|
|
3322
|
+
} catch {
|
|
3323
|
+
return null;
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
function activeHubProfile(env) {
|
|
3327
|
+
return resolveProfile(loadConfig(env, HUB_TOOL), void 0);
|
|
3328
|
+
}
|
|
3329
|
+
function defaultMissingHubLogin(tool) {
|
|
3330
|
+
const envVar = `${tool.replace(/-/g, "_").toUpperCase()}_API_KEY`;
|
|
3331
|
+
return `This ${tool} instance signs in through the Antasphere hub. Run \`antasphere login\` once, then retry \u2014 or pass --api-key / set ${envVar}.`;
|
|
3306
3332
|
}
|
|
3307
|
-
|
|
3308
|
-
|
|
3333
|
+
var DEFAULT_HUB_LOGIN_REJECTED = "The stored Antasphere hub credential was rejected \u2014 run `antasphere login` again, then retry.";
|
|
3334
|
+
function defaultConnected(facts) {
|
|
3335
|
+
return `Connected to ${facts.toolBaseUrl} as ${facts.email} via Antasphere` + (facts.stored ? "" : ` \u2014 key NOT cached: profile '${facts.profileName}' points at ${facts.profileBaseUrl}`) + ".\n";
|
|
3336
|
+
}
|
|
3337
|
+
async function connectOnDemand(opts) {
|
|
3338
|
+
assertToolNamespace(opts.tool);
|
|
3339
|
+
const toolBaseUrl = opts.toolBaseUrl.replace(/\/+$/, "");
|
|
3340
|
+
const messages = opts.messages ?? {};
|
|
3341
|
+
const profileName = opts.profileName ?? DEFAULT_PROFILE;
|
|
3342
|
+
const hub = activeHubProfile(opts.env);
|
|
3343
|
+
const existing = loadConfig(opts.env, opts.tool).profiles[profileName];
|
|
3344
|
+
const pinnedHere = existing?.baseUrl?.replace(/\/+$/, "") === toolBaseUrl;
|
|
3345
|
+
if (pinnedHere && hub.name) {
|
|
3346
|
+
const cachedKey = lookupConnectKey(existing, hub.name);
|
|
3347
|
+
if (cachedKey)
|
|
3348
|
+
return { outcome: "connected", key: cachedKey, cached: true };
|
|
3349
|
+
}
|
|
3350
|
+
const probe = await probeToolInstance(toolBaseUrl, opts.fetch);
|
|
3351
|
+
if (!probe?.cloud)
|
|
3352
|
+
return { outcome: "not_cloud" };
|
|
3353
|
+
const hubKey = hub.profile?.apiKey;
|
|
3354
|
+
const hubBaseUrl = hub.profile?.baseUrl;
|
|
3355
|
+
if (!hubKey || !hubBaseUrl || !hub.name) {
|
|
3356
|
+
throw new CliUsageError(messages.missingHubLogin ?? defaultMissingHubLogin(opts.tool));
|
|
3357
|
+
}
|
|
3358
|
+
const resource = opts.resource ?? `${toolBaseUrl}/mcp`;
|
|
3359
|
+
const connect = new CliConnectClient({
|
|
3360
|
+
hubBaseUrl,
|
|
3361
|
+
toolBaseUrl,
|
|
3362
|
+
...opts.fetch ? { fetch: opts.fetch } : {}
|
|
3363
|
+
});
|
|
3364
|
+
const body = { resource };
|
|
3365
|
+
let result;
|
|
3366
|
+
try {
|
|
3367
|
+
result = await connect.exchange(hubKey, body);
|
|
3368
|
+
} catch (e) {
|
|
3369
|
+
if (e instanceof CliConnectError && e.needsReExchange) {
|
|
3370
|
+
result = await connect.exchange(hubKey, body);
|
|
3371
|
+
} else if (e instanceof CliConnectError && e.needsHubLogin) {
|
|
3372
|
+
throw new CliUsageError(messages.hubLoginRejected ?? DEFAULT_HUB_LOGIN_REJECTED);
|
|
3373
|
+
} else {
|
|
3374
|
+
throw e;
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
const stored = !existing?.baseUrl || pinnedHere;
|
|
3378
|
+
if (stored) {
|
|
3379
|
+
storeConnectKey(opts.env, opts.tool, profileName, hub.name, {
|
|
3380
|
+
apiKey: result.key,
|
|
3381
|
+
email: result.user.email,
|
|
3382
|
+
mintedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3383
|
+
});
|
|
3384
|
+
if (!existing?.baseUrl) {
|
|
3385
|
+
const cfg = loadConfig(opts.env, opts.tool);
|
|
3386
|
+
cfg.profiles[profileName] = { ...cfg.profiles[profileName], baseUrl: toolBaseUrl };
|
|
3387
|
+
saveConfig(opts.env, opts.tool, cfg);
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
const facts = {
|
|
3391
|
+
toolBaseUrl,
|
|
3392
|
+
email: result.user.email,
|
|
3393
|
+
stored,
|
|
3394
|
+
profileName,
|
|
3395
|
+
// `stored` is false only when the profile names another instance.
|
|
3396
|
+
...stored ? {} : { profileBaseUrl: existing?.baseUrl }
|
|
3397
|
+
};
|
|
3398
|
+
opts.notify?.((messages.connected ?? defaultConnected)(facts));
|
|
3399
|
+
return { outcome: "connected", key: result.key, result, cached: false, stored };
|
|
3309
3400
|
}
|
|
3310
3401
|
|
|
3311
|
-
// ../../node_modules/.pnpm/@antasphere+cli-core@0.
|
|
3402
|
+
// ../../node_modules/.pnpm/@antasphere+cli-core@0.4.0/node_modules/@antasphere/cli-core/dist/migrate.js
|
|
3312
3403
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
3313
3404
|
import { join as join2 } from "node:path";
|
|
3314
3405
|
function migrateLegacyConfig(env, spec) {
|
|
@@ -3426,6 +3517,14 @@ var PlatformClient = class {
|
|
|
3426
3517
|
me() {
|
|
3427
3518
|
return this.request("GET", "/me");
|
|
3428
3519
|
}
|
|
3520
|
+
/**
|
|
3521
|
+
* CLOUD EDITION only (404 on oss); sessions only (machines 403): record
|
|
3522
|
+
* the explicit first-run-welcome dismissal — flips /me's
|
|
3523
|
+
* `firstRunPending` to false, permanently. Idempotent.
|
|
3524
|
+
*/
|
|
3525
|
+
dismissOnboarding() {
|
|
3526
|
+
return this.request("POST", "/me/onboarding/dismiss");
|
|
3527
|
+
}
|
|
3429
3528
|
/** Better Auth session probe — null when signed out. */
|
|
3430
3529
|
async session() {
|
|
3431
3530
|
return this.request("GET", "/auth/get-session");
|
|
@@ -3448,6 +3547,15 @@ var PlatformClient = class {
|
|
|
3448
3547
|
cliAuthComplete(req) {
|
|
3449
3548
|
return this.request("POST", "/cli/auth/complete", req);
|
|
3450
3549
|
}
|
|
3550
|
+
/**
|
|
3551
|
+
* Revoke the PRESENTING API key (CLI logout). Self-revocation only: the
|
|
3552
|
+
* route names no key, so the credential can kill exactly itself. Requires
|
|
3553
|
+
* an API-key credential with presentations:write (sessions and OAuth
|
|
3554
|
+
* bearers are refused — the dashboard is their key-management surface).
|
|
3555
|
+
*/
|
|
3556
|
+
cliAuthRevoke() {
|
|
3557
|
+
return this.request("DELETE", "/cli/auth/key");
|
|
3558
|
+
}
|
|
3451
3559
|
/**
|
|
3452
3560
|
* Public, CLOUD EDITION only (404 on oss): exchange a hub-minted 120 s
|
|
3453
3561
|
* connect JWT (the hub's /sso/tool-token response) for an `slk_` API key
|
|
@@ -3457,6 +3565,17 @@ var PlatformClient = class {
|
|
|
3457
3565
|
ssoCliConnect(req) {
|
|
3458
3566
|
return this.request("POST", "/sso/cli-connect", req);
|
|
3459
3567
|
}
|
|
3568
|
+
/**
|
|
3569
|
+
* CLOUD EDITION only (404 on oss); sessions only (machines 403 — the
|
|
3570
|
+
* path is unlisted in the scope allowlist): single logout. The server
|
|
3571
|
+
* revokes the local session and clears the SSO hint cookie in this very
|
|
3572
|
+
* response; `url` is the hub end-session URL the browser must then VISIT
|
|
3573
|
+
* to end the hub anchor session (null = local signout only — go straight
|
|
3574
|
+
* to /login?signed_out=1).
|
|
3575
|
+
*/
|
|
3576
|
+
ssoLogout() {
|
|
3577
|
+
return this.request("POST", "/sso/logout");
|
|
3578
|
+
}
|
|
3460
3579
|
// ── Members ───────────────────────────────────────────────────────────────
|
|
3461
3580
|
members(params = {}) {
|
|
3462
3581
|
return this.request("GET", this.pathWithQuery("/members", params));
|
|
@@ -3529,15 +3648,6 @@ var PlatformClient = class {
|
|
|
3529
3648
|
acceptInvitation(req) {
|
|
3530
3649
|
return this.request("POST", "/invitations/accept", req);
|
|
3531
3650
|
}
|
|
3532
|
-
// ── OAuth consent ─────────────────────────────────────────────────────────
|
|
3533
|
-
/**
|
|
3534
|
-
* Park the workspace the upcoming OAuth consent should bind (ADR 014).
|
|
3535
|
-
* Sessions only; the consent page calls it right before approving when
|
|
3536
|
-
* the user picked a non-default workspace.
|
|
3537
|
-
*/
|
|
3538
|
-
oauthConsentWorkspace(workspaceId) {
|
|
3539
|
-
return this.request("POST", "/oauth/consent-workspace", { workspaceId });
|
|
3540
|
-
}
|
|
3541
3651
|
// ── Audit ─────────────────────────────────────────────────────────────────
|
|
3542
3652
|
audit(params = {}) {
|
|
3543
3653
|
return this.request("GET", this.pathWithQuery("/audit", params));
|
|
@@ -3768,11 +3878,8 @@ function saveConfig2(env, config) {
|
|
|
3768
3878
|
function clearConfig2(env) {
|
|
3769
3879
|
clearConfig(env, TOOL);
|
|
3770
3880
|
}
|
|
3771
|
-
function
|
|
3772
|
-
return
|
|
3773
|
-
}
|
|
3774
|
-
function removeWorkspaceKey2(env, profileName, workspaceId) {
|
|
3775
|
-
return removeWorkspaceKey(env, TOOL, profileName, workspaceId);
|
|
3881
|
+
function removeConnectKey2(env, profileName, hubProfileName) {
|
|
3882
|
+
return removeConnectKey(env, TOOL, profileName, hubProfileName);
|
|
3776
3883
|
}
|
|
3777
3884
|
|
|
3778
3885
|
// src/context.ts
|
|
@@ -3794,19 +3901,15 @@ function resolveContext(cmd, io) {
|
|
|
3794
3901
|
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."
|
|
3795
3902
|
});
|
|
3796
3903
|
let apiKey = resolveApiKey({ flag: opts.apiKey, env: io.env, envVar: "SLIDELESS_API_KEY", profile });
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
if (org && profile?.baseUrl?.replace(/\/+$/, "") === baseUrl) {
|
|
3801
|
-
apiKey = lookupWorkspaceKey(profile, org);
|
|
3802
|
-
}
|
|
3904
|
+
if (!apiKey && profile?.baseUrl?.replace(/\/+$/, "") === baseUrl) {
|
|
3905
|
+
const hub = activeHubProfile(io.env);
|
|
3906
|
+
if (hub.name) apiKey = lookupConnectKey(profile, hub.name);
|
|
3803
3907
|
}
|
|
3804
3908
|
const fetchImpl = io.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3805
3909
|
return {
|
|
3806
3910
|
client: new PlatformClient({ baseUrl, ...apiKey ? { apiKey } : {}, fetch: fetchImpl }),
|
|
3807
3911
|
baseUrl,
|
|
3808
3912
|
apiKey,
|
|
3809
|
-
org,
|
|
3810
3913
|
json: Boolean(opts.json),
|
|
3811
3914
|
io,
|
|
3812
3915
|
profileName,
|
|
@@ -3814,74 +3917,28 @@ function resolveContext(cmd, io) {
|
|
|
3814
3917
|
};
|
|
3815
3918
|
}
|
|
3816
3919
|
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.";
|
|
3920
|
+
var MISSING_HUB_LOGIN_MESSAGE = "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.";
|
|
3817
3921
|
async function requireApiKey(ctx) {
|
|
3818
3922
|
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
3923
|
const { io } = ctx;
|
|
3825
|
-
|
|
3826
|
-
|
|
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) {
|
|
3837
|
-
throw new CliUsageError(
|
|
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."
|
|
3839
|
-
);
|
|
3840
|
-
}
|
|
3841
|
-
const resource = `${ctx.baseUrl}/mcp`;
|
|
3842
|
-
const connect = new CliConnectClient({
|
|
3843
|
-
hubBaseUrl,
|
|
3924
|
+
const outcome = await connectOnDemand({
|
|
3925
|
+
tool: "slideless",
|
|
3844
3926
|
toolBaseUrl: ctx.baseUrl,
|
|
3845
|
-
...
|
|
3927
|
+
...ctx.profileName !== void 0 ? { profileName: ctx.profileName } : {},
|
|
3928
|
+
env: io.env,
|
|
3929
|
+
// Thread the injected fetch so the probe + exchange stay on the test
|
|
3930
|
+
// harness wire (and any proxying the runner set up).
|
|
3931
|
+
...io.fetch ? { fetch: io.fetch } : {},
|
|
3932
|
+
notify: (line) => io.err.write(line),
|
|
3933
|
+
messages: { missingHubLogin: MISSING_HUB_LOGIN_MESSAGE }
|
|
3846
3934
|
});
|
|
3847
|
-
|
|
3848
|
-
|
|
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
|
-
}
|
|
3935
|
+
if (outcome.outcome === "not_cloud") {
|
|
3936
|
+
throw new CliUsageError(NO_KEY_MESSAGE);
|
|
3877
3937
|
}
|
|
3878
|
-
|
|
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;
|
|
3938
|
+
ctx.apiKey = outcome.key;
|
|
3882
3939
|
const fetchImpl = io.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3883
|
-
ctx.client = new PlatformClient({ baseUrl: ctx.baseUrl, apiKey:
|
|
3884
|
-
return
|
|
3940
|
+
ctx.client = new PlatformClient({ baseUrl: ctx.baseUrl, apiKey: outcome.key, fetch: fetchImpl });
|
|
3941
|
+
return outcome.key;
|
|
3885
3942
|
}
|
|
3886
3943
|
function table(rows) {
|
|
3887
3944
|
if (rows.length === 0) return "";
|
|
@@ -3917,10 +3974,26 @@ async function readLineFromStdin() {
|
|
|
3917
3974
|
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
3918
3975
|
return Buffer.concat(chunks).toString("utf8").split("\n")[0]?.trim() ?? "";
|
|
3919
3976
|
}
|
|
3977
|
+
async function refuseOtpLoginOnCloud(baseUrl, io) {
|
|
3978
|
+
let cloud = false;
|
|
3979
|
+
try {
|
|
3980
|
+
const fetchImpl = io.fetch ?? globalThis.fetch.bind(globalThis);
|
|
3981
|
+
const info = await new PlatformClient({ baseUrl, fetch: fetchImpl }).instance();
|
|
3982
|
+
cloud = info.auth.methods.includes("antasphere") || info.edition === "cloud";
|
|
3983
|
+
} catch {
|
|
3984
|
+
return;
|
|
3985
|
+
}
|
|
3986
|
+
if (cloud) {
|
|
3987
|
+
throw new CliUsageError(
|
|
3988
|
+
`${baseUrl} is an Antasphere-cloud instance \u2014 it signs in at the hub, not with its own email codes. Run \`antasphere login\` once; \`slideless\` then connects automatically (or pass --api-key <slk_\u2026> / set SLIDELESS_API_KEY).`
|
|
3989
|
+
);
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3920
3992
|
function registerAuthCommands(program2, io) {
|
|
3921
3993
|
const auth = program2.command("auth").description("Sign in over email OTP (mints an API key)");
|
|
3922
3994
|
auth.command("login-request").description("Email a 6-digit sign-in code (existing accounts only \u2014 sign-up stays closed)").requiredOption("--email <email>", "account email on the instance").action(async (opts, cmd) => {
|
|
3923
3995
|
const baseUrl = resolveAuthUrl(cmd, io);
|
|
3996
|
+
await refuseOtpLoginOnCloud(baseUrl, io);
|
|
3924
3997
|
const client = new CliAuthClient({ baseUrl, ...io.fetch ? { fetch: io.fetch } : {} });
|
|
3925
3998
|
const result = await client.request({ email: opts.email });
|
|
3926
3999
|
const json = Boolean(cmd.optsWithGlobals().json);
|
|
@@ -3939,6 +4012,7 @@ Complete with: slideless auth login-complete --email ${opts.email} --code <code>
|
|
|
3939
4012
|
async (opts, cmd) => {
|
|
3940
4013
|
const globals = cmd.optsWithGlobals();
|
|
3941
4014
|
const baseUrl = resolveAuthUrl(cmd, io);
|
|
4015
|
+
await refuseOtpLoginOnCloud(baseUrl, io);
|
|
3942
4016
|
const client = new CliAuthClient({ baseUrl, ...io.fetch ? { fetch: io.fetch } : {} });
|
|
3943
4017
|
const result = await client.complete({
|
|
3944
4018
|
email: opts.email,
|
|
@@ -3988,7 +4062,7 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
|
|
|
3988
4062
|
);
|
|
3989
4063
|
});
|
|
3990
4064
|
program2.command("logout").description(
|
|
3991
|
-
"Forget the stored API key of the active (or named) profile;
|
|
4065
|
+
"Forget the stored API key of the active (or named) profile; a hub-connected profile disconnects from Antasphere (revokes + evicts the cached user-scoped key)"
|
|
3992
4066
|
).action(async (_opts, cmd) => {
|
|
3993
4067
|
const globals = cmd.optsWithGlobals();
|
|
3994
4068
|
const config2 = loadConfig2(io.env);
|
|
@@ -3997,49 +4071,46 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
|
|
|
3997
4071
|
if (!profileName || !profile) {
|
|
3998
4072
|
throw new CliUsageError("No profile to log out of.");
|
|
3999
4073
|
}
|
|
4000
|
-
const
|
|
4001
|
-
const
|
|
4002
|
-
if (
|
|
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
|
-
}
|
|
4074
|
+
const connectKeys = profile.connectKeys ?? {};
|
|
4075
|
+
const slots = Object.entries(connectKeys);
|
|
4076
|
+
if (!profile.apiKey && slots.length > 0) {
|
|
4016
4077
|
const rawUrl = profile.baseUrl;
|
|
4017
|
-
let revoked =
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4078
|
+
let revoked = true;
|
|
4079
|
+
for (const [slot, entry] of slots) {
|
|
4080
|
+
if (rawUrl) {
|
|
4081
|
+
const baseUrl = rawUrl.replace(/\/+$/, "");
|
|
4082
|
+
try {
|
|
4083
|
+
const client = new CliAuthClient({ baseUrl, ...io.fetch ? { fetch: io.fetch } : {} });
|
|
4084
|
+
await client.revoke(entry.apiKey);
|
|
4085
|
+
} catch (e) {
|
|
4086
|
+
revoked = false;
|
|
4087
|
+
if (e instanceof CliAuthError && e.status === 401) {
|
|
4088
|
+
io.err.write("The cached key was already unusable \u2014 evicting it anyway.\n");
|
|
4089
|
+
} else if (e instanceof CliAuthError && e.status === 403) {
|
|
4090
|
+
io.err.write(
|
|
4091
|
+
`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
4092
|
`
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4093
|
+
);
|
|
4094
|
+
} else {
|
|
4095
|
+
throw e;
|
|
4096
|
+
}
|
|
4034
4097
|
}
|
|
4098
|
+
} else {
|
|
4099
|
+
revoked = false;
|
|
4100
|
+
io.err.write("No instance URL known for this profile \u2014 evicting the cached key locally only.\n");
|
|
4035
4101
|
}
|
|
4036
|
-
|
|
4037
|
-
|
|
4102
|
+
removeConnectKey2(io.env, profileName, slot);
|
|
4103
|
+
}
|
|
4104
|
+
if (globals.json) {
|
|
4105
|
+
return printJson(io, {
|
|
4106
|
+
profile: profileName,
|
|
4107
|
+
hubProfiles: slots.map(([slot]) => slot),
|
|
4108
|
+
revoked,
|
|
4109
|
+
evicted: true
|
|
4110
|
+
});
|
|
4038
4111
|
}
|
|
4039
|
-
removeWorkspaceKey2(io.env, profileName, org);
|
|
4040
|
-
if (globals.json) return printJson(io, { profile: profileName, org, revoked, evicted: true });
|
|
4041
4112
|
io.out.write(
|
|
4042
|
-
`
|
|
4113
|
+
`Disconnected from Antasphere on profile "${profileName}"${revoked ? " (key revoked server-side)" : ""}.
|
|
4043
4114
|
`
|
|
4044
4115
|
);
|
|
4045
4116
|
return;
|
|
@@ -4061,8 +4132,8 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
|
|
|
4061
4132
|
io.out.write(
|
|
4062
4133
|
`${me.user.name} <${me.user.email}>
|
|
4063
4134
|
instance: ${ctx.baseUrl}
|
|
4064
|
-
workspace: ${me.workspace
|
|
4065
|
-
role: ${me.role} (via ${me.via})
|
|
4135
|
+
workspace: ${me.workspace?.name ?? "(none)"}
|
|
4136
|
+
role: ${me.role ?? "(none)"} (via ${me.via})
|
|
4066
4137
|
scopes: ${me.scopes ? me.scopes.join(", ") : "full (session)"}
|
|
4067
4138
|
` + (me.via === "api_key" ? ` key expires: ${me.apiKeyExpiresAt ?? "never"}
|
|
4068
4139
|
` : "")
|
|
@@ -4103,7 +4174,9 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
|
|
|
4103
4174
|
{
|
|
4104
4175
|
baseUrl: p.baseUrl ?? null,
|
|
4105
4176
|
apiKey: p.apiKey ? redactKey(p.apiKey) : null,
|
|
4106
|
-
|
|
4177
|
+
// Hub profiles this tool profile is connected through
|
|
4178
|
+
// (one user-scoped key each, valid for every org).
|
|
4179
|
+
hubProfiles: Object.keys(p.connectKeys ?? {})
|
|
4107
4180
|
}
|
|
4108
4181
|
];
|
|
4109
4182
|
})
|
|
@@ -4120,12 +4193,12 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
|
|
|
4120
4193
|
table(
|
|
4121
4194
|
names.map((n) => {
|
|
4122
4195
|
const p = config2.profiles[n];
|
|
4123
|
-
const
|
|
4196
|
+
const hubProfiles = Object.keys(p.connectKeys ?? {});
|
|
4124
4197
|
return [
|
|
4125
4198
|
config2.activeProfile === n ? "*" : " ",
|
|
4126
4199
|
n,
|
|
4127
4200
|
p.baseUrl ?? "(no url)",
|
|
4128
|
-
p.apiKey ? redactKey(p.apiKey) :
|
|
4201
|
+
p.apiKey ? redactKey(p.apiKey) : hubProfiles.length > 0 ? `(hub: ${hubProfiles.join(", ")})` : "(no key)"
|
|
4129
4202
|
];
|
|
4130
4203
|
})
|
|
4131
4204
|
)
|
|
@@ -4145,9 +4218,9 @@ API key "${result.apiKey.name}" (${redactKey(result.key)}) saved to profile "${p
|
|
|
4145
4218
|
{
|
|
4146
4219
|
baseUrl: p.baseUrl ?? null,
|
|
4147
4220
|
apiKey: p.apiKey ? redactKey(p.apiKey) : null,
|
|
4148
|
-
// Hub-connected
|
|
4149
|
-
|
|
4150
|
-
Object.entries(p.
|
|
4221
|
+
// Hub-connected keys (one per hub profile), redacted like the rest.
|
|
4222
|
+
connectKeys: Object.fromEntries(
|
|
4223
|
+
Object.entries(p.connectKeys ?? {}).map(([slot, entry]) => [slot, redactKey(entry.apiKey)])
|
|
4151
4224
|
)
|
|
4152
4225
|
}
|
|
4153
4226
|
])
|
|
@@ -5084,13 +5157,10 @@ function registerCompletionCommand(program2, io) {
|
|
|
5084
5157
|
}
|
|
5085
5158
|
|
|
5086
5159
|
// src/index.ts
|
|
5087
|
-
var VERSION = "0.2.
|
|
5160
|
+
var VERSION = "0.2.4";
|
|
5088
5161
|
function buildProgram(io) {
|
|
5089
5162
|
const program2 = new Command();
|
|
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);
|
|
5163
|
+
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);
|
|
5094
5164
|
registerAuthCommands(program2, io);
|
|
5095
5165
|
registerDeckCommands(program2, io);
|
|
5096
5166
|
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.4",
|
|
4
4
|
"description": "The Slideless CLI — push, share, pull, and preview decks on a Slideless instance",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"test": "vitest run --passWithNoTests"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@antasphere/cli-core": "^0.
|
|
33
|
+
"@antasphere/cli-core": "^0.4.0",
|
|
34
34
|
"@slideless/contract": "workspace:*",
|
|
35
35
|
"@slideless/sdk": "workspace:*",
|
|
36
36
|
"@types/node": "^22.15.0",
|