@hasna/skills 0.5.0 → 0.5.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/README.md +72 -1
- package/bin/index.js +1083 -388
- package/bin/mcp.js +521 -110
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/cli/commands/workspace-leave.d.ts +2 -0
- package/dist/cli/commands/workspace-selection.d.ts +11 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +427 -123
- package/dist/lib/remote-auth.d.ts +18 -9
- package/dist/lib/remote-client.d.ts +14 -0
- package/dist/lib/remote-workspace-leave.d.ts +52 -0
- package/dist/lib/remote-workspace-selection.d.ts +58 -0
- package/dist/lib/workspace-profile.d.ts +49 -0
- package/dist/sdk/index.d.ts +5 -0
- package/dist/sdk/index.js +371 -67
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10254,12 +10254,106 @@ function primitiveHaystack(primitive) {
|
|
|
10254
10254
|
function clone(value) {
|
|
10255
10255
|
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
10256
10256
|
}
|
|
10257
|
+
// src/lib/remote-workspace-selection.ts
|
|
10258
|
+
var record = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
10259
|
+
var uuid = (v) => typeof v === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v);
|
|
10260
|
+
var text = (v, max = 1024) => typeof v === "string" && !!v.trim() && v.length <= max && !/[\p{Cc}\p{Cs}\u2028\u2029]/u.test(v);
|
|
10261
|
+
var role = (v) => typeof v === "string" && ["owner", "admin", "member", "viewer"].includes(v);
|
|
10262
|
+
var invalidWorkspaceResult = "The server returned an invalid workspace selection result.";
|
|
10263
|
+
|
|
10264
|
+
class WorkspaceContextInputError extends Error {
|
|
10265
|
+
constructor() {
|
|
10266
|
+
super("Provide the observed user ID and exact lowercase membership ID.");
|
|
10267
|
+
this.name = "WorkspaceContextInputError";
|
|
10268
|
+
}
|
|
10269
|
+
}
|
|
10270
|
+
|
|
10271
|
+
class WorkspaceIdentityMismatchError extends Error {
|
|
10272
|
+
constructor() {
|
|
10273
|
+
super("The verified account does not match the requested workspace context.");
|
|
10274
|
+
this.name = "WorkspaceIdentityMismatchError";
|
|
10275
|
+
}
|
|
10276
|
+
}
|
|
10277
|
+
function workspaceExpectedUserId(value) {
|
|
10278
|
+
if (!uuid(value))
|
|
10279
|
+
throw new WorkspaceContextInputError;
|
|
10280
|
+
return value;
|
|
10281
|
+
}
|
|
10282
|
+
function workspaceContext(value) {
|
|
10283
|
+
if (!record(value) || Object.keys(value).sort().join(",") !== "membershipId,userId" || !uuid(value.userId) || !uuid(value.membershipId))
|
|
10284
|
+
throw new WorkspaceContextInputError;
|
|
10285
|
+
return { userId: value.userId, membershipId: value.membershipId };
|
|
10286
|
+
}
|
|
10287
|
+
function invalid() {
|
|
10288
|
+
throw new Error(invalidWorkspaceResult);
|
|
10289
|
+
}
|
|
10290
|
+
function organization(v) {
|
|
10291
|
+
if (!record(v) || !uuid(v.id) || !text(v.slug) || !text(v.name))
|
|
10292
|
+
return invalid();
|
|
10293
|
+
return { id: v.id, slug: v.slug, name: v.name };
|
|
10294
|
+
}
|
|
10295
|
+
function parseAccountWorkspaces(value) {
|
|
10296
|
+
if (!record(value) || !Array.isArray(value.workspaces) || !value.workspaces.length || value.workspaces.length > 1000)
|
|
10297
|
+
return invalid();
|
|
10298
|
+
const workspaces = value.workspaces.map((v) => {
|
|
10299
|
+
if (!record(v) || !uuid(v.membershipId) || !role(v.role) || typeof v.current !== "boolean")
|
|
10300
|
+
return invalid();
|
|
10301
|
+
return { membershipId: v.membershipId, organization: organization(v.organization), role: v.role, current: v.current };
|
|
10302
|
+
});
|
|
10303
|
+
if (workspaces.filter((w) => w.current).length !== 1 || new Set(workspaces.map((w) => w.membershipId)).size !== workspaces.length || new Set(workspaces.map((w) => w.organization.id)).size !== workspaces.length)
|
|
10304
|
+
return invalid();
|
|
10305
|
+
return { workspaces };
|
|
10306
|
+
}
|
|
10307
|
+
function parseWorkspaceIdentity(value, expectedUserId) {
|
|
10308
|
+
if (!record(value))
|
|
10309
|
+
return invalid();
|
|
10310
|
+
const user = value.user;
|
|
10311
|
+
if (!record(user) || !uuid(user.id) || !uuid(user.membershipId) || !text(user.email, 320) || !(user.displayName === null || text(user.displayName)) || !role(user.role))
|
|
10312
|
+
return invalid();
|
|
10313
|
+
if (user.id !== expectedUserId)
|
|
10314
|
+
throw new WorkspaceIdentityMismatchError;
|
|
10315
|
+
return { user: { id: user.id, membershipId: user.membershipId, email: user.email, displayName: user.displayName, role: user.role }, organization: organization(value.organization) };
|
|
10316
|
+
}
|
|
10317
|
+
function sessionToken(value) {
|
|
10318
|
+
if (typeof value !== "string" || !value || value.length > 8192 || /[^\x21-\x7e]/.test(value) || value.startsWith("sk_"))
|
|
10319
|
+
return invalid();
|
|
10320
|
+
return value;
|
|
10321
|
+
}
|
|
10322
|
+
function parseWorkspaceSession(value, expected) {
|
|
10323
|
+
const identity = parseWorkspaceIdentity(value, expected.userId);
|
|
10324
|
+
if (identity.user.membershipId !== expected.membershipId)
|
|
10325
|
+
throw new WorkspaceIdentityMismatchError;
|
|
10326
|
+
return { token: sessionToken(value.token), ...identity };
|
|
10327
|
+
}
|
|
10328
|
+
function parseWorkspaceLogin(value, expectedUserId) {
|
|
10329
|
+
const user = record(value) && value.user;
|
|
10330
|
+
if (!record(value) || !record(user) || !uuid(user.id))
|
|
10331
|
+
return invalid();
|
|
10332
|
+
if (expectedUserId !== undefined && user.id !== expectedUserId)
|
|
10333
|
+
throw new WorkspaceIdentityMismatchError;
|
|
10334
|
+
return { token: sessionToken(value.token), userId: user.id };
|
|
10335
|
+
}
|
|
10336
|
+
var workspaceSelectionFailures = {
|
|
10337
|
+
INVALID_WORKSPACE_SELECTION: [400, "Provide only the exact membership ID from your workspace list."],
|
|
10338
|
+
SESSION_EXPIRED: [401, "Sign in again before selecting a workspace."],
|
|
10339
|
+
ACCOUNT_UNAVAILABLE: [403, "Account is unavailable."],
|
|
10340
|
+
INTERACTIVE_SESSION_REQUIRED: [403, "Interactive sign-in is required to select a workspace."],
|
|
10341
|
+
WORKSPACE_UNAVAILABLE: [404, "Workspace is unavailable. Refresh your workspace list."],
|
|
10342
|
+
WORKSPACE_BUSY: [503, "Workspace is busy. Refresh before retrying."]
|
|
10343
|
+
};
|
|
10344
|
+
function workspaceSelectionFailure(value, status) {
|
|
10345
|
+
if (!record(value) || typeof value.code !== "string" || !Object.hasOwn(workspaceSelectionFailures, value.code))
|
|
10346
|
+
return null;
|
|
10347
|
+
const code = value.code;
|
|
10348
|
+
return workspaceSelectionFailures[code][0] === status ? code : null;
|
|
10349
|
+
}
|
|
10350
|
+
|
|
10257
10351
|
// src/lib/remote-workspace.ts
|
|
10258
|
-
var
|
|
10352
|
+
var record2 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
10259
10353
|
var cursor = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,512}$/.test(value);
|
|
10260
|
-
var
|
|
10354
|
+
var uuid2 = (value) => typeof value === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(value);
|
|
10261
10355
|
function workspaceMembersQuery(options = {}) {
|
|
10262
|
-
if (!
|
|
10356
|
+
if (!record2(options) || Object.keys(options).some((key) => key !== "limit" && key !== "cursor") || options.limit !== undefined && (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > 100) || options.cursor !== undefined && !cursor(options.cursor))
|
|
10263
10357
|
throw new Error("Use a roster limit from 1 to 100 and an unchanged continuation cursor.");
|
|
10264
10358
|
const query = new URLSearchParams;
|
|
10265
10359
|
if (options.limit !== undefined)
|
|
@@ -10275,7 +10369,7 @@ function timestamp(value) {
|
|
|
10275
10369
|
return Number.isFinite(time) && new Date(time).toISOString().slice(0, 23) === value.slice(0, 23);
|
|
10276
10370
|
}
|
|
10277
10371
|
function parseMember(row, fail) {
|
|
10278
|
-
if (!
|
|
10372
|
+
if (!record2(row) || !uuid2(row.membershipId) || !uuid2(row.userId) || typeof row.email !== "string" || !row.email || !(row.displayName === null || typeof row.displayName === "string") || !isRole(row.role) || !timestamp(row.createdAt))
|
|
10279
10373
|
return fail();
|
|
10280
10374
|
return {
|
|
10281
10375
|
membershipId: row.membershipId,
|
|
@@ -10295,12 +10389,12 @@ class WorkspaceMemberInputError extends Error {
|
|
|
10295
10389
|
}
|
|
10296
10390
|
}
|
|
10297
10391
|
function mutationInput(membershipId, input, roleChange) {
|
|
10298
|
-
if (typeof membershipId !== "string" || !
|
|
10392
|
+
if (typeof membershipId !== "string" || !uuid2(membershipId) || membershipId !== membershipId.toLowerCase() || !record2(input) || Object.keys(input).sort().join(",") !== (roleChange ? "expectedRole,role" : "expectedRole"))
|
|
10299
10393
|
throw new WorkspaceMemberInputError;
|
|
10300
|
-
const expectedRole = input.expectedRole,
|
|
10301
|
-
if (!isRole(expectedRole) || roleChange && !isRole(
|
|
10394
|
+
const expectedRole = input.expectedRole, role2 = roleChange ? input.role : undefined;
|
|
10395
|
+
if (!isRole(expectedRole) || roleChange && !isRole(role2))
|
|
10302
10396
|
throw new WorkspaceMemberInputError;
|
|
10303
|
-
return { membershipId, role, expectedRole };
|
|
10397
|
+
return { membershipId, role: role2, expectedRole };
|
|
10304
10398
|
}
|
|
10305
10399
|
function workspaceMemberRoleInput(membershipId, input) {
|
|
10306
10400
|
const value = mutationInput(membershipId, input, true);
|
|
@@ -10311,19 +10405,19 @@ function workspaceMemberRemovalInput(membershipId, input) {
|
|
|
10311
10405
|
return { membershipId: value.membershipId, body: { expectedRole: value.expectedRole } };
|
|
10312
10406
|
}
|
|
10313
10407
|
var invalidMemberResult = "The server returned an invalid workspace member result. Refresh the roster before another action.";
|
|
10314
|
-
function parseWorkspaceMemberRoleResult(value, membershipId,
|
|
10408
|
+
function parseWorkspaceMemberRoleResult(value, membershipId, role2) {
|
|
10315
10409
|
const fail = () => {
|
|
10316
10410
|
throw new Error(invalidMemberResult);
|
|
10317
10411
|
};
|
|
10318
|
-
if (!
|
|
10412
|
+
if (!record2(value) || !uuid2(value.organizationId) || typeof value.changed !== "boolean")
|
|
10319
10413
|
return fail();
|
|
10320
10414
|
const member = parseMember(value.member, fail);
|
|
10321
|
-
if (member.membershipId !== membershipId || member.role !==
|
|
10415
|
+
if (member.membershipId !== membershipId || member.role !== role2)
|
|
10322
10416
|
return fail();
|
|
10323
10417
|
return { organizationId: value.organizationId, member, changed: value.changed };
|
|
10324
10418
|
}
|
|
10325
10419
|
function parseWorkspaceMemberRemovalResult(value, membershipId) {
|
|
10326
|
-
if (!
|
|
10420
|
+
if (!record2(value) || !uuid2(value.organizationId) || value.membershipId !== membershipId || value.removed !== true || typeof value.alreadyRemoved !== "boolean")
|
|
10327
10421
|
throw new Error(invalidMemberResult);
|
|
10328
10422
|
return { organizationId: value.organizationId, membershipId, removed: true, alreadyRemoved: value.alreadyRemoved };
|
|
10329
10423
|
}
|
|
@@ -10340,7 +10434,7 @@ var workspaceMemberFailures = {
|
|
|
10340
10434
|
MEMBERSHIP_BUSY: [503, "Membership is busy. Refresh the roster before another action."]
|
|
10341
10435
|
};
|
|
10342
10436
|
function workspaceMemberFailure(value, status) {
|
|
10343
|
-
if (!
|
|
10437
|
+
if (!record2(value) || typeof value.code !== "string" || !Object.hasOwn(workspaceMemberFailures, value.code))
|
|
10344
10438
|
return null;
|
|
10345
10439
|
const code = value.code;
|
|
10346
10440
|
return workspaceMemberFailures[code][0] === status ? code : null;
|
|
@@ -10349,13 +10443,72 @@ function parseWorkspaceMembersPage(value) {
|
|
|
10349
10443
|
const fail = () => {
|
|
10350
10444
|
throw new Error("The server returned an invalid workspace roster.");
|
|
10351
10445
|
};
|
|
10352
|
-
if (!
|
|
10446
|
+
if (!record2(value) || !uuid2(value.organizationId) || !Array.isArray(value.members) || value.members.length > 100 || !(value.nextCursor === null || cursor(value.nextCursor)) || !value.members.length && value.nextCursor !== null)
|
|
10353
10447
|
return fail();
|
|
10354
10448
|
const members = value.members.map((row) => parseMember(row, fail));
|
|
10355
10449
|
if (new Set(members.map((row) => row.membershipId)).size !== members.length)
|
|
10356
10450
|
return fail();
|
|
10357
10451
|
return { organizationId: value.organizationId, members, nextCursor: value.nextCursor };
|
|
10358
10452
|
}
|
|
10453
|
+
|
|
10454
|
+
// src/lib/remote-workspace-leave.ts
|
|
10455
|
+
class WorkspaceLeaveInputError extends Error {
|
|
10456
|
+
constructor() {
|
|
10457
|
+
super("Confirm leaving the exact observed user and membership with its expected role.");
|
|
10458
|
+
this.name = "WorkspaceLeaveInputError";
|
|
10459
|
+
}
|
|
10460
|
+
}
|
|
10461
|
+
function workspaceLeaveInput(context, input) {
|
|
10462
|
+
const target = workspaceContext(context);
|
|
10463
|
+
if (!input || typeof input !== "object" || Array.isArray(input) || Object.keys(input).sort().join(",") !== "confirm,expectedRole" || input.confirm !== true)
|
|
10464
|
+
throw new WorkspaceLeaveInputError;
|
|
10465
|
+
const captured = workspaceMemberRemovalInput(target.membershipId, { expectedRole: input.expectedRole });
|
|
10466
|
+
return {
|
|
10467
|
+
context: target,
|
|
10468
|
+
input: { expectedRole: captured.body.expectedRole, confirm: true },
|
|
10469
|
+
body: { membershipId: target.membershipId, expectedRole: captured.body.expectedRole }
|
|
10470
|
+
};
|
|
10471
|
+
}
|
|
10472
|
+
var workspaceLeaveFailures = {
|
|
10473
|
+
INVALID_REQUEST: [400, "Provide the exact current membership and expected role."],
|
|
10474
|
+
ACCOUNT_UNAVAILABLE: [403, "Account is unavailable."],
|
|
10475
|
+
INTERACTIVE_SESSION_REQUIRED: [403, "Fresh interactive sign-in is required to leave a workspace."],
|
|
10476
|
+
MEMBERSHIP_ROLE_CHANGED: [409, "Your role changed. Sign in and inspect the workspace before leaving."],
|
|
10477
|
+
LAST_OWNER_REQUIRED: [409, "The workspace must retain another active owner."],
|
|
10478
|
+
LAST_WORKSPACE_REQUIRED: [409, "Another available workspace is required before leaving."],
|
|
10479
|
+
MEMBERSHIP_BUSY: [503, "Membership is busy. Inspect the workspace before another leave action."]
|
|
10480
|
+
};
|
|
10481
|
+
|
|
10482
|
+
class RemoteWorkspaceLeaveError extends Error {
|
|
10483
|
+
code;
|
|
10484
|
+
status;
|
|
10485
|
+
constructor(code) {
|
|
10486
|
+
super(workspaceLeaveFailures[code][1]);
|
|
10487
|
+
this.code = code;
|
|
10488
|
+
this.name = "RemoteWorkspaceLeaveError";
|
|
10489
|
+
this.status = workspaceLeaveFailures[code][0];
|
|
10490
|
+
}
|
|
10491
|
+
}
|
|
10492
|
+
|
|
10493
|
+
class RemoteWorkspaceLeaveUnconfirmedError extends Error {
|
|
10494
|
+
code = "WORKSPACE_LEAVE_UNCONFIRMED";
|
|
10495
|
+
constructor() {
|
|
10496
|
+
super("The leave outcome is unconfirmed. Sign in again and inspect available memberships before another action. Do not retry automatically; saved credentials are unchanged.");
|
|
10497
|
+
this.name = "RemoteWorkspaceLeaveUnconfirmedError";
|
|
10498
|
+
}
|
|
10499
|
+
}
|
|
10500
|
+
function workspaceLeaveFailure(value, status) {
|
|
10501
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
10502
|
+
return null;
|
|
10503
|
+
const code = value.code;
|
|
10504
|
+
return typeof code === "string" && Object.hasOwn(workspaceLeaveFailures, code) && workspaceLeaveFailures[code][0] === status ? code : null;
|
|
10505
|
+
}
|
|
10506
|
+
function parseWorkspaceLeaveResult(value, membershipId, organizationId) {
|
|
10507
|
+
const row = value;
|
|
10508
|
+
if (!row || typeof row !== "object" || Array.isArray(row) || row.membershipId !== membershipId || row.organizationId !== organizationId || row.removed !== true || row.signInRequired !== true)
|
|
10509
|
+
throw new RemoteWorkspaceLeaveUnconfirmedError;
|
|
10510
|
+
return { membershipId, organizationId, removed: true, signInRequired: true };
|
|
10511
|
+
}
|
|
10359
10512
|
// src/lib/auth-store.ts
|
|
10360
10513
|
function getApiUrl(action, env = process.env, options = {}) {
|
|
10361
10514
|
return requireSkillsApiOrigin(action, env, options);
|
|
@@ -10364,44 +10517,44 @@ function getApiUrl(action, env = process.env, options = {}) {
|
|
|
10364
10517
|
// src/lib/remote-run-contract.ts
|
|
10365
10518
|
var REMOTE_SKILL_RUN_CONTRACT_VERSION = 1;
|
|
10366
10519
|
function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
|
|
10367
|
-
const
|
|
10520
|
+
const record3 = isRecord3(payload) ? payload : {};
|
|
10368
10521
|
return {
|
|
10369
10522
|
contractVersion: REMOTE_SKILL_RUN_CONTRACT_VERSION,
|
|
10370
|
-
...pickString(
|
|
10371
|
-
skill: pickStringValue(
|
|
10372
|
-
...pickString(
|
|
10373
|
-
...pickString(
|
|
10374
|
-
...pickNumber(
|
|
10375
|
-
...pickString(
|
|
10376
|
-
...pickString(
|
|
10377
|
-
...pickString(
|
|
10378
|
-
...pickString(
|
|
10379
|
-
...pickNumber(
|
|
10380
|
-
...pickString(
|
|
10381
|
-
...hasOwn(
|
|
10382
|
-
...pickString(
|
|
10383
|
-
...pickString(
|
|
10384
|
-
...pickString(
|
|
10385
|
-
...pickString(
|
|
10386
|
-
...hasOwn(
|
|
10523
|
+
...pickString(record3, "id"),
|
|
10524
|
+
skill: pickStringValue(record3, "skill") ?? fallbackSkill,
|
|
10525
|
+
...pickString(record3, "requestedSlug"),
|
|
10526
|
+
...pickString(record3, "status"),
|
|
10527
|
+
...pickNumber(record3, "exitCode"),
|
|
10528
|
+
...pickString(record3, "correlationId"),
|
|
10529
|
+
...pickString(record3, "createdAt"),
|
|
10530
|
+
...pickString(record3, "startedAt"),
|
|
10531
|
+
...pickString(record3, "completedAt"),
|
|
10532
|
+
...pickNumber(record3, "durationMs"),
|
|
10533
|
+
...pickString(record3, "outputType"),
|
|
10534
|
+
...hasOwn(record3, "outputPreview") ? { outputPreview: record3.outputPreview } : {},
|
|
10535
|
+
...pickString(record3, "errorCode"),
|
|
10536
|
+
...pickString(record3, "errorMessage"),
|
|
10537
|
+
...pickString(record3, "error"),
|
|
10538
|
+
...pickString(record3, "code"),
|
|
10539
|
+
...hasOwn(record3, "details") ? { details: record3.details } : {}
|
|
10387
10540
|
};
|
|
10388
10541
|
}
|
|
10389
10542
|
function isRecord3(value) {
|
|
10390
10543
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
10391
10544
|
}
|
|
10392
|
-
function hasOwn(
|
|
10393
|
-
return Object.prototype.hasOwnProperty.call(
|
|
10545
|
+
function hasOwn(record3, key) {
|
|
10546
|
+
return Object.prototype.hasOwnProperty.call(record3, key);
|
|
10394
10547
|
}
|
|
10395
|
-
function pickString(
|
|
10396
|
-
const value = pickStringValue(
|
|
10548
|
+
function pickString(record3, key) {
|
|
10549
|
+
const value = pickStringValue(record3, key);
|
|
10397
10550
|
return value === undefined ? {} : { [key]: value };
|
|
10398
10551
|
}
|
|
10399
|
-
function pickStringValue(
|
|
10400
|
-
const value =
|
|
10552
|
+
function pickStringValue(record3, key) {
|
|
10553
|
+
const value = record3[key];
|
|
10401
10554
|
return typeof value === "string" ? value : undefined;
|
|
10402
10555
|
}
|
|
10403
|
-
function pickNumber(
|
|
10404
|
-
const value =
|
|
10556
|
+
function pickNumber(record3, key) {
|
|
10557
|
+
const value = record3[key];
|
|
10405
10558
|
return typeof value === "number" && Number.isFinite(value) ? { [key]: value } : {};
|
|
10406
10559
|
}
|
|
10407
10560
|
|
|
@@ -10564,11 +10717,11 @@ function parseUpdatedProfile(value) {
|
|
|
10564
10717
|
return { user: { id: user.id, email: user.email, displayName: user.displayName, role: user.role } };
|
|
10565
10718
|
}
|
|
10566
10719
|
function parseUpdatedWorkspace(value) {
|
|
10567
|
-
const
|
|
10568
|
-
if (!isRecord4(
|
|
10720
|
+
const organization2 = isRecord4(value) && value.organization;
|
|
10721
|
+
if (!isRecord4(organization2) || !string(organization2.id) || !string(organization2.slug) || !string(organization2.name)) {
|
|
10569
10722
|
throw new Error("The server returned an invalid workspace.");
|
|
10570
10723
|
}
|
|
10571
|
-
return { organization: { id:
|
|
10724
|
+
return { organization: { id: organization2.id, slug: organization2.slug, name: organization2.name } };
|
|
10572
10725
|
}
|
|
10573
10726
|
|
|
10574
10727
|
// src/lib/remote-client.ts
|
|
@@ -10606,6 +10759,16 @@ class RemoteWorkspaceMemberError extends RemoteRequestError {
|
|
|
10606
10759
|
}
|
|
10607
10760
|
}
|
|
10608
10761
|
|
|
10762
|
+
class RemoteWorkspaceSelectionError extends RemoteRequestError {
|
|
10763
|
+
code;
|
|
10764
|
+
constructor(path, code) {
|
|
10765
|
+
super(path, workspaceSelectionFailures[code][0]);
|
|
10766
|
+
this.code = code;
|
|
10767
|
+
this.name = "RemoteWorkspaceSelectionError";
|
|
10768
|
+
this.message = workspaceSelectionFailures[code][1];
|
|
10769
|
+
}
|
|
10770
|
+
}
|
|
10771
|
+
|
|
10609
10772
|
class RemoteCapabilityUnavailableError extends RemoteRequestError {
|
|
10610
10773
|
code = "SUBSCRIPTION_CHECKOUT_UNAVAILABLE";
|
|
10611
10774
|
constructor() {
|
|
@@ -10627,6 +10790,7 @@ class RemoteSkillsClient {
|
|
|
10627
10790
|
return fetch(`${this.apiUrl}${path}`, {
|
|
10628
10791
|
...options,
|
|
10629
10792
|
redirect: "error",
|
|
10793
|
+
credentials: "omit",
|
|
10630
10794
|
signal: options?.signal ?? AbortSignal.timeout(15000),
|
|
10631
10795
|
headers: {
|
|
10632
10796
|
Authorization: `Bearer ${this.apiKey}`,
|
|
@@ -10733,6 +10897,65 @@ class RemoteSkillsClient {
|
|
|
10733
10897
|
async getIdentity() {
|
|
10734
10898
|
return (await this.requestNewRoute("/api/auth/whoami")).json();
|
|
10735
10899
|
}
|
|
10900
|
+
async listAccountWorkspaces(expectedUserId) {
|
|
10901
|
+
const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
|
|
10902
|
+
const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
|
|
10903
|
+
let identity;
|
|
10904
|
+
if (expected !== undefined) {
|
|
10905
|
+
const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
|
|
10906
|
+
if (!value || typeof value !== "object" || value.authMethod !== "jwt")
|
|
10907
|
+
throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces", "INTERACTIVE_SESSION_REQUIRED");
|
|
10908
|
+
identity = parseWorkspaceIdentity(value, expected);
|
|
10909
|
+
}
|
|
10910
|
+
const result = parseAccountWorkspaces(await connection.requestWorkspaceSelection("/api/v1/account/workspaces"));
|
|
10911
|
+
const current = result.workspaces.find((workspace) => workspace.current);
|
|
10912
|
+
if (identity && (current.membershipId !== identity.user.membershipId || current.organization.id !== identity.organization.id))
|
|
10913
|
+
throw new WorkspaceIdentityMismatchError;
|
|
10914
|
+
return result;
|
|
10915
|
+
}
|
|
10916
|
+
async switchWorkspace(context) {
|
|
10917
|
+
const target = workspaceContext(context);
|
|
10918
|
+
const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
|
|
10919
|
+
const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
|
|
10920
|
+
if (!value || typeof value !== "object" || value.authMethod !== "jwt")
|
|
10921
|
+
throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces/switch", "INTERACTIVE_SESSION_REQUIRED");
|
|
10922
|
+
parseWorkspaceIdentity(value, target.userId);
|
|
10923
|
+
const selected = parseWorkspaceSession(await connection.requestWorkspaceSelection("/api/v1/account/workspaces/switch", {
|
|
10924
|
+
method: "POST",
|
|
10925
|
+
body: JSON.stringify({ membershipId: target.membershipId })
|
|
10926
|
+
}), target);
|
|
10927
|
+
const verified = await new RemoteSkillsClient(selected.token, connection.apiUrl).requestWorkspaceSelection("/api/auth/whoami");
|
|
10928
|
+
if (!verified || typeof verified !== "object" || verified.authMethod !== "jwt")
|
|
10929
|
+
throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces/switch", "INTERACTIVE_SESSION_REQUIRED");
|
|
10930
|
+
const identity = parseWorkspaceIdentity(verified, target.userId);
|
|
10931
|
+
if (identity.user.membershipId !== target.membershipId || identity.organization.id !== selected.organization.id)
|
|
10932
|
+
throw new WorkspaceIdentityMismatchError;
|
|
10933
|
+
return { token: selected.token, ...identity };
|
|
10934
|
+
}
|
|
10935
|
+
async requestWorkspaceSelection(path, options) {
|
|
10936
|
+
let response;
|
|
10937
|
+
try {
|
|
10938
|
+
response = await this.request(path, { ...options, credentials: "omit" });
|
|
10939
|
+
} catch {
|
|
10940
|
+
throw new Error("Unable to reach the Skills workspace API.");
|
|
10941
|
+
}
|
|
10942
|
+
let value;
|
|
10943
|
+
try {
|
|
10944
|
+
value = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, response.ok ? 1024 * 1024 : 4096)));
|
|
10945
|
+
} catch {
|
|
10946
|
+
if (response.ok)
|
|
10947
|
+
throw new Error(invalidWorkspaceResult);
|
|
10948
|
+
}
|
|
10949
|
+
if (!response.ok) {
|
|
10950
|
+
const code = workspaceSelectionFailure(value, response.status);
|
|
10951
|
+
if (code)
|
|
10952
|
+
throw new RemoteWorkspaceSelectionError(path, code);
|
|
10953
|
+
if (response.status === 404 || response.status === 405)
|
|
10954
|
+
throw new RemoteRouteUnsupportedError(path, response.status, this.apiUrl);
|
|
10955
|
+
throw new RemoteRequestError(path, response.status);
|
|
10956
|
+
}
|
|
10957
|
+
return value;
|
|
10958
|
+
}
|
|
10736
10959
|
async updateProfile(input) {
|
|
10737
10960
|
const body = customerNamePatch(input, "displayName");
|
|
10738
10961
|
return parseUpdatedProfile(await (await this.requestNewRoute("/api/v1/account/profile", { method: "PATCH", body: JSON.stringify(body) })).json());
|
|
@@ -10785,6 +11008,30 @@ class RemoteSkillsClient {
|
|
|
10785
11008
|
}
|
|
10786
11009
|
return value;
|
|
10787
11010
|
}
|
|
11011
|
+
async leaveWorkspace(context, input) {
|
|
11012
|
+
const captured = workspaceLeaveInput(context, input);
|
|
11013
|
+
const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
|
|
11014
|
+
const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
|
|
11015
|
+
if (!value || typeof value !== "object" || value.authMethod !== "jwt")
|
|
11016
|
+
throw new RemoteWorkspaceLeaveError("INTERACTIVE_SESSION_REQUIRED");
|
|
11017
|
+
const identity = parseWorkspaceIdentity(value, captured.context.userId);
|
|
11018
|
+
if (identity.user.membershipId !== captured.context.membershipId)
|
|
11019
|
+
throw new WorkspaceIdentityMismatchError;
|
|
11020
|
+
let response, body;
|
|
11021
|
+
try {
|
|
11022
|
+
response = await connection.request("/api/v1/account/workspaces/leave", { method: "POST", body: JSON.stringify(captured.body) });
|
|
11023
|
+
body = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, 4096)));
|
|
11024
|
+
} catch {
|
|
11025
|
+
throw new RemoteWorkspaceLeaveUnconfirmedError;
|
|
11026
|
+
}
|
|
11027
|
+
if (!response.ok) {
|
|
11028
|
+
const code = workspaceLeaveFailure(body, response.status);
|
|
11029
|
+
if (code)
|
|
11030
|
+
throw new RemoteWorkspaceLeaveError(code);
|
|
11031
|
+
throw new RemoteWorkspaceLeaveUnconfirmedError;
|
|
11032
|
+
}
|
|
11033
|
+
return parseWorkspaceLeaveResult(body, captured.context.membershipId, identity.organization.id);
|
|
11034
|
+
}
|
|
10788
11035
|
async listApiKeys() {
|
|
10789
11036
|
return this.arrayResponse("/api/auth/keys");
|
|
10790
11037
|
}
|
|
@@ -11035,13 +11282,13 @@ class RemoteSkillsClient {
|
|
|
11035
11282
|
return normalizeUpdatedSincePage(await response.json());
|
|
11036
11283
|
}
|
|
11037
11284
|
}
|
|
11038
|
-
function requireOptionalString(
|
|
11039
|
-
if (
|
|
11285
|
+
function requireOptionalString(record3, field) {
|
|
11286
|
+
if (record3[field] === undefined)
|
|
11040
11287
|
return;
|
|
11041
|
-
if (typeof
|
|
11288
|
+
if (typeof record3[field] !== "string") {
|
|
11042
11289
|
throw new Error(`Remote payload did not match the expected contract (${field} must be a string when present)`);
|
|
11043
11290
|
}
|
|
11044
|
-
return
|
|
11291
|
+
return record3[field];
|
|
11045
11292
|
}
|
|
11046
11293
|
var INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
|
|
11047
11294
|
function isVersionRecord(value) {
|
|
@@ -11064,19 +11311,19 @@ function normalizePin(entry) {
|
|
|
11064
11311
|
if (!entry || typeof entry !== "object") {
|
|
11065
11312
|
throw new Error("Remote pin payload did not match the expected contract (expected an object)");
|
|
11066
11313
|
}
|
|
11067
|
-
const
|
|
11068
|
-
const slug = typeof
|
|
11314
|
+
const record3 = entry;
|
|
11315
|
+
const slug = typeof record3.slug === "string" && record3.slug.trim() ? record3.slug.trim() : undefined;
|
|
11069
11316
|
if (!slug) {
|
|
11070
11317
|
throw new Error("Remote pin payload did not match the expected contract (missing slug)");
|
|
11071
11318
|
}
|
|
11072
11319
|
let metadata;
|
|
11073
|
-
if (
|
|
11074
|
-
if (!
|
|
11320
|
+
if (record3.metadata !== undefined) {
|
|
11321
|
+
if (!record3.metadata || typeof record3.metadata !== "object" || Array.isArray(record3.metadata)) {
|
|
11075
11322
|
throw new Error("Remote pin payload did not match the expected contract (metadata must be a JSON object when present)");
|
|
11076
11323
|
}
|
|
11077
|
-
metadata =
|
|
11324
|
+
metadata = record3.metadata;
|
|
11078
11325
|
}
|
|
11079
|
-
const pinnedAt = requireOptionalString(
|
|
11326
|
+
const pinnedAt = requireOptionalString(record3, "pinnedAt");
|
|
11080
11327
|
return {
|
|
11081
11328
|
slug,
|
|
11082
11329
|
...pinnedAt !== undefined ? { pinnedAt } : {},
|
|
@@ -11093,16 +11340,16 @@ function normalizeSkillSummary(entry) {
|
|
|
11093
11340
|
if (!entry || typeof entry !== "object") {
|
|
11094
11341
|
throw new Error("Remote skill payload did not match the expected contract (expected an object)");
|
|
11095
11342
|
}
|
|
11096
|
-
const
|
|
11097
|
-
const slug = typeof
|
|
11343
|
+
const record3 = entry;
|
|
11344
|
+
const slug = typeof record3.slug === "string" && record3.slug.trim() ? record3.slug.trim() : undefined;
|
|
11098
11345
|
if (!slug) {
|
|
11099
11346
|
throw new Error("Remote skill payload did not match the expected contract (missing slug)");
|
|
11100
11347
|
}
|
|
11101
11348
|
return {
|
|
11102
11349
|
slug,
|
|
11103
|
-
...requireOptionalString(
|
|
11104
|
-
...requireOptionalString(
|
|
11105
|
-
...requireOptionalString(
|
|
11350
|
+
...requireOptionalString(record3, "name") !== undefined ? { name: requireOptionalString(record3, "name") } : {},
|
|
11351
|
+
...requireOptionalString(record3, "version") !== undefined ? { version: requireOptionalString(record3, "version") } : {},
|
|
11352
|
+
...requireOptionalString(record3, "updatedAt") !== undefined ? { updatedAt: requireOptionalString(record3, "updatedAt") } : {}
|
|
11106
11353
|
};
|
|
11107
11354
|
}
|
|
11108
11355
|
function normalizeSkillSummaryList(payload) {
|
|
@@ -11155,12 +11402,12 @@ function normalizeUpdatedSincePage(payload) {
|
|
|
11155
11402
|
if (!payload || typeof payload !== "object") {
|
|
11156
11403
|
throw new Error("Updated-since payload did not match the expected contract (expected an object)");
|
|
11157
11404
|
}
|
|
11158
|
-
const
|
|
11159
|
-
if (!Array.isArray(
|
|
11405
|
+
const record3 = payload;
|
|
11406
|
+
if (!Array.isArray(record3.skills)) {
|
|
11160
11407
|
throw new Error("Updated-since payload did not match the expected contract (missing skills array)");
|
|
11161
11408
|
}
|
|
11162
|
-
const skills =
|
|
11163
|
-
const nextCursor =
|
|
11409
|
+
const skills = record3.skills.map(normalizeSkillSummary);
|
|
11410
|
+
const nextCursor = record3.nextCursor === undefined || record3.nextCursor === null ? null : record3.nextCursor;
|
|
11164
11411
|
if (nextCursor !== null && typeof nextCursor !== "string") {
|
|
11165
11412
|
throw new Error("Updated-since payload did not match the expected contract (nextCursor must be a string or absent)");
|
|
11166
11413
|
}
|
|
@@ -11397,8 +11644,8 @@ function revisionIdOf(content) {
|
|
|
11397
11644
|
});
|
|
11398
11645
|
return createHash4("sha256").update(canonical).digest("hex");
|
|
11399
11646
|
}
|
|
11400
|
-
function revisionIdOfRecord(
|
|
11401
|
-
return revisionIdOf(
|
|
11647
|
+
function revisionIdOfRecord(record3) {
|
|
11648
|
+
return revisionIdOf(record3);
|
|
11402
11649
|
}
|
|
11403
11650
|
|
|
11404
11651
|
// src/lib/skill-bundle.ts
|
|
@@ -12105,16 +12352,16 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
|
|
|
12105
12352
|
}
|
|
12106
12353
|
return { path: target, created };
|
|
12107
12354
|
}
|
|
12108
|
-
function writePullMarker(dir,
|
|
12355
|
+
function writePullMarker(dir, record3) {
|
|
12109
12356
|
const marker = {
|
|
12110
12357
|
managedBy: "@hasna/skills",
|
|
12111
|
-
skill:
|
|
12112
|
-
source:
|
|
12113
|
-
...
|
|
12114
|
-
...
|
|
12115
|
-
...
|
|
12116
|
-
...
|
|
12117
|
-
...
|
|
12358
|
+
skill: record3.skill,
|
|
12359
|
+
source: record3.source ?? "pull",
|
|
12360
|
+
...record3.version ? { version: record3.version } : {},
|
|
12361
|
+
...record3.contentHash ? { contentHash: record3.contentHash } : {},
|
|
12362
|
+
...record3.sourceCommit ? { sourceCommit: record3.sourceCommit } : {},
|
|
12363
|
+
...record3.signature ? { signature: record3.signature } : {},
|
|
12364
|
+
...record3.revisionId ? { revisionId: record3.revisionId } : {},
|
|
12118
12365
|
syncedAt: new Date().toISOString()
|
|
12119
12366
|
};
|
|
12120
12367
|
writeFileSync8(join17(dir, PULL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
|
|
@@ -12129,19 +12376,19 @@ async function safeMeta(client, slug) {
|
|
|
12129
12376
|
}
|
|
12130
12377
|
if (!raw || typeof raw !== "object")
|
|
12131
12378
|
return null;
|
|
12132
|
-
const
|
|
12133
|
-
const kind =
|
|
12134
|
-
const tags = Array.isArray(
|
|
12379
|
+
const record3 = raw;
|
|
12380
|
+
const kind = record3.kind === "instruction" || record3.kind === "executable" ? record3.kind : undefined;
|
|
12381
|
+
const tags = Array.isArray(record3.tags) ? record3.tags.filter((tag) => typeof tag === "string" && tag.trim().length > 0) : undefined;
|
|
12135
12382
|
return {
|
|
12136
|
-
...str(
|
|
12137
|
-
...str(
|
|
12138
|
-
...str(
|
|
12383
|
+
...str(record3.displayName) ? { displayName: str(record3.displayName) } : {},
|
|
12384
|
+
...str(record3.description) ? { description: str(record3.description) } : {},
|
|
12385
|
+
...str(record3.category) ? { category: str(record3.category) } : {},
|
|
12139
12386
|
...tags && tags.length ? { tags } : {},
|
|
12140
|
-
...str(
|
|
12387
|
+
...str(record3.version) ? { version: str(record3.version) } : {},
|
|
12141
12388
|
...kind ? { kind } : {},
|
|
12142
|
-
...REVISION_ID_PATTERN.test(str(
|
|
12143
|
-
...typeof
|
|
12144
|
-
...str(
|
|
12389
|
+
...REVISION_ID_PATTERN.test(str(record3.revisionId) ?? "") ? { revisionId: str(record3.revisionId) } : {},
|
|
12390
|
+
...typeof record3.skillMd === "string" && record3.skillMd.length > 0 ? { skillMd: record3.skillMd } : {},
|
|
12391
|
+
...str(record3.publishedSource) ? { publishedSource: str(record3.publishedSource) } : {}
|
|
12145
12392
|
};
|
|
12146
12393
|
}
|
|
12147
12394
|
function pickCorpusOptions(options) {
|
|
@@ -12150,8 +12397,8 @@ function pickCorpusOptions(options) {
|
|
|
12150
12397
|
function extractSlug(entry) {
|
|
12151
12398
|
if (!entry || typeof entry !== "object")
|
|
12152
12399
|
return;
|
|
12153
|
-
const
|
|
12154
|
-
return str(
|
|
12400
|
+
const record3 = entry;
|
|
12401
|
+
return str(record3.slug) ?? str(record3.name);
|
|
12155
12402
|
}
|
|
12156
12403
|
function dedupe(values) {
|
|
12157
12404
|
return [...new Set(values)];
|
|
@@ -12277,7 +12524,7 @@ import { dirname as dirname7, relative as relative4 } from "path";
|
|
|
12277
12524
|
// package.json
|
|
12278
12525
|
var package_default = {
|
|
12279
12526
|
name: "@hasna/skills",
|
|
12280
|
-
version: "0.5.
|
|
12527
|
+
version: "0.5.2",
|
|
12281
12528
|
description: "Skills library for AI coding agents",
|
|
12282
12529
|
type: "module",
|
|
12283
12530
|
bin: {
|
|
@@ -13788,7 +14035,7 @@ class SkillsPostgresSyncStore {
|
|
|
13788
14035
|
}
|
|
13789
14036
|
async upsertRecords(records) {
|
|
13790
14037
|
let count = 0;
|
|
13791
|
-
for (const
|
|
14038
|
+
for (const record3 of records) {
|
|
13792
14039
|
await this.client.query([
|
|
13793
14040
|
"INSERT INTO skills_sync_records",
|
|
13794
14041
|
"(scope, kind, id, updated_at, deleted_at, source, payload)",
|
|
@@ -13799,13 +14046,13 @@ class SkillsPostgresSyncStore {
|
|
|
13799
14046
|
"source = EXCLUDED.source,",
|
|
13800
14047
|
"payload = EXCLUDED.payload"
|
|
13801
14048
|
].join(" "), [
|
|
13802
|
-
|
|
13803
|
-
|
|
13804
|
-
|
|
13805
|
-
|
|
13806
|
-
|
|
13807
|
-
|
|
13808
|
-
JSON.stringify(
|
|
14049
|
+
record3.scope,
|
|
14050
|
+
record3.kind,
|
|
14051
|
+
record3.id,
|
|
14052
|
+
record3.updatedAt,
|
|
14053
|
+
record3.deletedAt ?? null,
|
|
14054
|
+
record3.source ?? null,
|
|
14055
|
+
JSON.stringify(record3.payload)
|
|
13809
14056
|
]);
|
|
13810
14057
|
count += 1;
|
|
13811
14058
|
}
|
|
@@ -14784,13 +15031,13 @@ async function requestAuthApi(instance, path, options) {
|
|
|
14784
15031
|
apiUrl: safeUrl
|
|
14785
15032
|
});
|
|
14786
15033
|
}
|
|
14787
|
-
const
|
|
14788
|
-
const body =
|
|
15034
|
+
const text2 = await res.text();
|
|
15035
|
+
const body = text2 ? parseJsonBody(text2) : {};
|
|
14789
15036
|
if (!res.ok) {
|
|
14790
|
-
const
|
|
14791
|
-
const detail = typeof
|
|
14792
|
-
const error = typeof
|
|
14793
|
-
const code = typeof
|
|
15037
|
+
const record3 = isRecord5(body) ? body : {};
|
|
15038
|
+
const detail = typeof record3.detail === "string" ? record3.detail : undefined;
|
|
15039
|
+
const error = typeof record3.error === "string" ? record3.error : undefined;
|
|
15040
|
+
const code = typeof record3.code === "string" ? record3.code : undefined;
|
|
14794
15041
|
throw new HostedApiError(detail || error || `${res.status} ${res.statusText}`, {
|
|
14795
15042
|
status: res.status,
|
|
14796
15043
|
code,
|
|
@@ -14801,15 +15048,15 @@ async function requestAuthApi(instance, path, options) {
|
|
|
14801
15048
|
}
|
|
14802
15049
|
return body;
|
|
14803
15050
|
}
|
|
14804
|
-
function parseJsonBody(
|
|
15051
|
+
function parseJsonBody(text2) {
|
|
14805
15052
|
try {
|
|
14806
|
-
return JSON.parse(
|
|
15053
|
+
return JSON.parse(text2);
|
|
14807
15054
|
} catch {
|
|
14808
|
-
return { detail: condenseErrorBody(
|
|
15055
|
+
return { detail: condenseErrorBody(text2) };
|
|
14809
15056
|
}
|
|
14810
15057
|
}
|
|
14811
|
-
function condenseErrorBody(
|
|
14812
|
-
const stripped = /<[a-z!/]/i.test(
|
|
15058
|
+
function condenseErrorBody(text2) {
|
|
15059
|
+
const stripped = /<[a-z!/]/i.test(text2) ? text2.replace(/<(script|style)[\s\S]*?<\/\1>/gi, " ").replace(/<[^>]*>/g, " ") : text2;
|
|
14813
15060
|
const collapsed = stripped.replace(/\s+/g, " ").trim();
|
|
14814
15061
|
if (collapsed.length <= MAX_ERROR_DETAIL_LENGTH)
|
|
14815
15062
|
return collapsed;
|
|
@@ -14836,7 +15083,12 @@ class RemoteSkillsAuthClient {
|
|
|
14836
15083
|
pollDevice(deviceCode) {
|
|
14837
15084
|
return this.request("/api/auth/device/token", { method: "POST", body: JSON.stringify({ deviceCode }) });
|
|
14838
15085
|
}
|
|
14839
|
-
async sessionClient(email, code) {
|
|
15086
|
+
async sessionClient(email, code, context) {
|
|
15087
|
+
if (context !== undefined) {
|
|
15088
|
+
const target = workspaceContext(context), apiOrigin2 = this.apiOrigin;
|
|
15089
|
+
const session = await this.switchWorkspace(email, code, target);
|
|
15090
|
+
return new RemoteSkillsClient(session.token, apiOrigin2);
|
|
15091
|
+
}
|
|
14840
15092
|
const apiOrigin = this.apiOrigin;
|
|
14841
15093
|
if (!email.includes("@") || !/^\d{6}$/.test(code))
|
|
14842
15094
|
throw new Error("Fresh email and six-digit verification code are required to manage this account");
|
|
@@ -14845,34 +15097,80 @@ class RemoteSkillsAuthClient {
|
|
|
14845
15097
|
throw new Error("The server did not return an authorized account session");
|
|
14846
15098
|
return new RemoteSkillsClient(login.token, apiOrigin);
|
|
14847
15099
|
}
|
|
14848
|
-
async
|
|
14849
|
-
|
|
15100
|
+
async listAccountWorkspaces(email, code, expectedUserId) {
|
|
15101
|
+
const login = await this.workspaceLogin(email, code, expectedUserId);
|
|
15102
|
+
const result = await new RemoteSkillsClient(login.token, login.apiOrigin).listAccountWorkspaces(login.userId);
|
|
15103
|
+
return { userId: login.userId, ...result };
|
|
14850
15104
|
}
|
|
14851
|
-
async
|
|
14852
|
-
|
|
15105
|
+
async switchWorkspace(email, code, context) {
|
|
15106
|
+
const target = workspaceContext(context);
|
|
15107
|
+
const login = await this.workspaceLogin(email, code, target.userId);
|
|
15108
|
+
return new RemoteSkillsClient(login.token, login.apiOrigin).switchWorkspace(target);
|
|
14853
15109
|
}
|
|
14854
|
-
async
|
|
14855
|
-
|
|
15110
|
+
async workspaceLogin(email, code, expectedUserId) {
|
|
15111
|
+
const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
|
|
15112
|
+
const apiOrigin = this.apiOrigin;
|
|
15113
|
+
if (typeof email !== "string" || !email.includes("@") || typeof code !== "string" || !/^\d{6}$/.test(code))
|
|
15114
|
+
throw new Error("Fresh email and six-digit verification code are required to manage this account");
|
|
15115
|
+
let response;
|
|
15116
|
+
try {
|
|
15117
|
+
response = await fetch(`${apiOrigin}/api/auth/verify`, {
|
|
15118
|
+
method: "POST",
|
|
15119
|
+
redirect: "error",
|
|
15120
|
+
credentials: "omit",
|
|
15121
|
+
signal: AbortSignal.timeout(15000),
|
|
15122
|
+
headers: { "Content-Type": "application/json" },
|
|
15123
|
+
body: JSON.stringify({ email, code })
|
|
15124
|
+
});
|
|
15125
|
+
} catch {
|
|
15126
|
+
throw new HostedApiError("Unable to verify the Skills account.");
|
|
15127
|
+
}
|
|
15128
|
+
if (!response.ok) {
|
|
15129
|
+
response.body?.cancel().catch(() => {});
|
|
15130
|
+
throw new HostedApiError("Unable to verify the Skills account.", { status: response.status });
|
|
15131
|
+
}
|
|
15132
|
+
let value;
|
|
15133
|
+
try {
|
|
15134
|
+
value = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, 64 * 1024)));
|
|
15135
|
+
} catch {
|
|
15136
|
+
throw new HostedApiError("The server returned an invalid account verification result.");
|
|
15137
|
+
}
|
|
15138
|
+
return { ...parseWorkspaceLogin(value, expected), apiOrigin };
|
|
15139
|
+
}
|
|
15140
|
+
async createApiKey(email, code, name, scopes, context) {
|
|
15141
|
+
const capturedScopes = scopes === undefined ? undefined : [...scopes];
|
|
15142
|
+
return (await this.sessionClient(email, code, context)).createApiKey(name, capturedScopes);
|
|
14856
15143
|
}
|
|
14857
|
-
async
|
|
14858
|
-
|
|
14859
|
-
return (await this.sessionClient(email, code)).updateProfile(input);
|
|
15144
|
+
async listApiKeys(email, code, context) {
|
|
15145
|
+
return (await this.sessionClient(email, code, context)).listApiKeys();
|
|
14860
15146
|
}
|
|
14861
|
-
async
|
|
14862
|
-
|
|
14863
|
-
return (await this.sessionClient(email, code)).updateCurrentWorkspace(input);
|
|
15147
|
+
async revokeApiKey(email, code, keyId, context) {
|
|
15148
|
+
return (await this.sessionClient(email, code, context)).revokeApiKey(keyId);
|
|
14864
15149
|
}
|
|
14865
|
-
async
|
|
15150
|
+
async updateProfile(email, code, input, context) {
|
|
15151
|
+
const body = customerNamePatch(input, "displayName");
|
|
15152
|
+
return (await this.sessionClient(email, code, context)).updateProfile({ displayName: body.displayName });
|
|
15153
|
+
}
|
|
15154
|
+
async updateCurrentWorkspace(email, code, input, context) {
|
|
15155
|
+
const body = customerNamePatch(input, "name");
|
|
15156
|
+
return (await this.sessionClient(email, code, context)).updateCurrentWorkspace({ name: body.name });
|
|
15157
|
+
}
|
|
15158
|
+
async listWorkspaceMembers(email, code, options = {}, context) {
|
|
14866
15159
|
workspaceMembersQuery(options);
|
|
14867
|
-
|
|
15160
|
+
const captured = { ...options };
|
|
15161
|
+
return (await this.sessionClient(email, code, context)).listWorkspaceMembers(captured);
|
|
14868
15162
|
}
|
|
14869
|
-
async setWorkspaceMemberRole(email, code, membershipId, input) {
|
|
15163
|
+
async setWorkspaceMemberRole(email, code, membershipId, input, context) {
|
|
14870
15164
|
const captured = workspaceMemberRoleInput(membershipId, input);
|
|
14871
|
-
return (await this.sessionClient(email, code)).setWorkspaceMemberRole(captured.membershipId, captured.body);
|
|
15165
|
+
return (await this.sessionClient(email, code, context)).setWorkspaceMemberRole(captured.membershipId, captured.body);
|
|
15166
|
+
}
|
|
15167
|
+
async leaveWorkspace(email, code, context, input) {
|
|
15168
|
+
const captured = workspaceLeaveInput(context, input);
|
|
15169
|
+
return (await this.sessionClient(email, code, captured.context)).leaveWorkspace(captured.context, captured.input);
|
|
14872
15170
|
}
|
|
14873
|
-
async removeWorkspaceMember(email, code, membershipId, input) {
|
|
15171
|
+
async removeWorkspaceMember(email, code, membershipId, input, context) {
|
|
14874
15172
|
const captured = workspaceMemberRemovalInput(membershipId, input);
|
|
14875
|
-
return (await this.sessionClient(email, code)).removeWorkspaceMember(captured.membershipId, captured.body);
|
|
15173
|
+
return (await this.sessionClient(email, code, context)).removeWorkspaceMember(captured.membershipId, captured.body);
|
|
14876
15174
|
}
|
|
14877
15175
|
request(path, options) {
|
|
14878
15176
|
if (!["/api/auth/login", "/api/auth/verify", "/api/auth/device/start", "/api/auth/device/token", "/api/auth/keys", "/api/auth/whoami"].includes(path))
|
|
@@ -15062,6 +15360,9 @@ export {
|
|
|
15062
15360
|
agentGlobalSkillsDir,
|
|
15063
15361
|
addSchedule,
|
|
15064
15362
|
adaptSkillMdForAgent,
|
|
15363
|
+
WorkspaceLeaveInputError,
|
|
15364
|
+
WorkspaceIdentityMismatchError,
|
|
15365
|
+
WorkspaceContextInputError,
|
|
15065
15366
|
TOOL_PRIMITIVE_SCHEMA_VERSION,
|
|
15066
15367
|
TOOL_PRIMITIVES,
|
|
15067
15368
|
StationSnapshotError,
|
|
@@ -15093,7 +15394,10 @@ export {
|
|
|
15093
15394
|
SKILLS_API_KEY_ENV_KEYS,
|
|
15094
15395
|
SKILLS_API_KEY_ENV,
|
|
15095
15396
|
SKILLS,
|
|
15397
|
+
RemoteWorkspaceSelectionError,
|
|
15096
15398
|
RemoteWorkspaceMemberError,
|
|
15399
|
+
RemoteWorkspaceLeaveUnconfirmedError,
|
|
15400
|
+
RemoteWorkspaceLeaveError,
|
|
15097
15401
|
RemoteSkillsClient,
|
|
15098
15402
|
RemoteSkillsAuthClient,
|
|
15099
15403
|
RemoteRouteUnsupportedError,
|