@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/sdk/index.js
CHANGED
|
@@ -25462,7 +25462,7 @@ class MissingSkillsFleetError extends Error {
|
|
|
25462
25462
|
// package.json
|
|
25463
25463
|
var package_default = {
|
|
25464
25464
|
name: "@hasna/skills",
|
|
25465
|
-
version: "0.5.
|
|
25465
|
+
version: "0.5.2",
|
|
25466
25466
|
description: "Skills library for AI coding agents",
|
|
25467
25467
|
type: "module",
|
|
25468
25468
|
bin: {
|
|
@@ -52164,12 +52164,106 @@ function createOfflineGate(options) {
|
|
|
52164
52164
|
}
|
|
52165
52165
|
};
|
|
52166
52166
|
}
|
|
52167
|
+
// src/lib/remote-workspace-selection.ts
|
|
52168
|
+
var record = (v2) => !!v2 && typeof v2 === "object" && !Array.isArray(v2);
|
|
52169
|
+
var uuid = (v2) => typeof v2 === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v2);
|
|
52170
|
+
var text = (v2, max = 1024) => typeof v2 === "string" && !!v2.trim() && v2.length <= max && !/[\p{Cc}\p{Cs}\u2028\u2029]/u.test(v2);
|
|
52171
|
+
var role = (v2) => typeof v2 === "string" && ["owner", "admin", "member", "viewer"].includes(v2);
|
|
52172
|
+
var invalidWorkspaceResult = "The server returned an invalid workspace selection result.";
|
|
52173
|
+
|
|
52174
|
+
class WorkspaceContextInputError extends Error {
|
|
52175
|
+
constructor() {
|
|
52176
|
+
super("Provide the observed user ID and exact lowercase membership ID.");
|
|
52177
|
+
this.name = "WorkspaceContextInputError";
|
|
52178
|
+
}
|
|
52179
|
+
}
|
|
52180
|
+
|
|
52181
|
+
class WorkspaceIdentityMismatchError extends Error {
|
|
52182
|
+
constructor() {
|
|
52183
|
+
super("The verified account does not match the requested workspace context.");
|
|
52184
|
+
this.name = "WorkspaceIdentityMismatchError";
|
|
52185
|
+
}
|
|
52186
|
+
}
|
|
52187
|
+
function workspaceExpectedUserId(value) {
|
|
52188
|
+
if (!uuid(value))
|
|
52189
|
+
throw new WorkspaceContextInputError;
|
|
52190
|
+
return value;
|
|
52191
|
+
}
|
|
52192
|
+
function workspaceContext(value) {
|
|
52193
|
+
if (!record(value) || Object.keys(value).sort().join(",") !== "membershipId,userId" || !uuid(value.userId) || !uuid(value.membershipId))
|
|
52194
|
+
throw new WorkspaceContextInputError;
|
|
52195
|
+
return { userId: value.userId, membershipId: value.membershipId };
|
|
52196
|
+
}
|
|
52197
|
+
function invalid() {
|
|
52198
|
+
throw new Error(invalidWorkspaceResult);
|
|
52199
|
+
}
|
|
52200
|
+
function organization(v2) {
|
|
52201
|
+
if (!record(v2) || !uuid(v2.id) || !text(v2.slug) || !text(v2.name))
|
|
52202
|
+
return invalid();
|
|
52203
|
+
return { id: v2.id, slug: v2.slug, name: v2.name };
|
|
52204
|
+
}
|
|
52205
|
+
function parseAccountWorkspaces(value) {
|
|
52206
|
+
if (!record(value) || !Array.isArray(value.workspaces) || !value.workspaces.length || value.workspaces.length > 1000)
|
|
52207
|
+
return invalid();
|
|
52208
|
+
const workspaces = value.workspaces.map((v2) => {
|
|
52209
|
+
if (!record(v2) || !uuid(v2.membershipId) || !role(v2.role) || typeof v2.current !== "boolean")
|
|
52210
|
+
return invalid();
|
|
52211
|
+
return { membershipId: v2.membershipId, organization: organization(v2.organization), role: v2.role, current: v2.current };
|
|
52212
|
+
});
|
|
52213
|
+
if (workspaces.filter((w2) => w2.current).length !== 1 || new Set(workspaces.map((w2) => w2.membershipId)).size !== workspaces.length || new Set(workspaces.map((w2) => w2.organization.id)).size !== workspaces.length)
|
|
52214
|
+
return invalid();
|
|
52215
|
+
return { workspaces };
|
|
52216
|
+
}
|
|
52217
|
+
function parseWorkspaceIdentity(value, expectedUserId) {
|
|
52218
|
+
if (!record(value))
|
|
52219
|
+
return invalid();
|
|
52220
|
+
const user = value.user;
|
|
52221
|
+
if (!record(user) || !uuid(user.id) || !uuid(user.membershipId) || !text(user.email, 320) || !(user.displayName === null || text(user.displayName)) || !role(user.role))
|
|
52222
|
+
return invalid();
|
|
52223
|
+
if (user.id !== expectedUserId)
|
|
52224
|
+
throw new WorkspaceIdentityMismatchError;
|
|
52225
|
+
return { user: { id: user.id, membershipId: user.membershipId, email: user.email, displayName: user.displayName, role: user.role }, organization: organization(value.organization) };
|
|
52226
|
+
}
|
|
52227
|
+
function sessionToken(value) {
|
|
52228
|
+
if (typeof value !== "string" || !value || value.length > 8192 || /[^\x21-\x7e]/.test(value) || value.startsWith("sk_"))
|
|
52229
|
+
return invalid();
|
|
52230
|
+
return value;
|
|
52231
|
+
}
|
|
52232
|
+
function parseWorkspaceSession(value, expected) {
|
|
52233
|
+
const identity = parseWorkspaceIdentity(value, expected.userId);
|
|
52234
|
+
if (identity.user.membershipId !== expected.membershipId)
|
|
52235
|
+
throw new WorkspaceIdentityMismatchError;
|
|
52236
|
+
return { token: sessionToken(value.token), ...identity };
|
|
52237
|
+
}
|
|
52238
|
+
function parseWorkspaceLogin(value, expectedUserId) {
|
|
52239
|
+
const user = record(value) && value.user;
|
|
52240
|
+
if (!record(value) || !record(user) || !uuid(user.id))
|
|
52241
|
+
return invalid();
|
|
52242
|
+
if (expectedUserId !== undefined && user.id !== expectedUserId)
|
|
52243
|
+
throw new WorkspaceIdentityMismatchError;
|
|
52244
|
+
return { token: sessionToken(value.token), userId: user.id };
|
|
52245
|
+
}
|
|
52246
|
+
var workspaceSelectionFailures = {
|
|
52247
|
+
INVALID_WORKSPACE_SELECTION: [400, "Provide only the exact membership ID from your workspace list."],
|
|
52248
|
+
SESSION_EXPIRED: [401, "Sign in again before selecting a workspace."],
|
|
52249
|
+
ACCOUNT_UNAVAILABLE: [403, "Account is unavailable."],
|
|
52250
|
+
INTERACTIVE_SESSION_REQUIRED: [403, "Interactive sign-in is required to select a workspace."],
|
|
52251
|
+
WORKSPACE_UNAVAILABLE: [404, "Workspace is unavailable. Refresh your workspace list."],
|
|
52252
|
+
WORKSPACE_BUSY: [503, "Workspace is busy. Refresh before retrying."]
|
|
52253
|
+
};
|
|
52254
|
+
function workspaceSelectionFailure(value, status) {
|
|
52255
|
+
if (!record(value) || typeof value.code !== "string" || !Object.hasOwn(workspaceSelectionFailures, value.code))
|
|
52256
|
+
return null;
|
|
52257
|
+
const code = value.code;
|
|
52258
|
+
return workspaceSelectionFailures[code][0] === status ? code : null;
|
|
52259
|
+
}
|
|
52260
|
+
|
|
52167
52261
|
// src/lib/remote-workspace.ts
|
|
52168
|
-
var
|
|
52262
|
+
var record2 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
52169
52263
|
var cursor = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,512}$/.test(value);
|
|
52170
|
-
var
|
|
52264
|
+
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);
|
|
52171
52265
|
function workspaceMembersQuery(options = {}) {
|
|
52172
|
-
if (!
|
|
52266
|
+
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))
|
|
52173
52267
|
throw new Error("Use a roster limit from 1 to 100 and an unchanged continuation cursor.");
|
|
52174
52268
|
const query = new URLSearchParams;
|
|
52175
52269
|
if (options.limit !== undefined)
|
|
@@ -52185,7 +52279,7 @@ function timestamp(value) {
|
|
|
52185
52279
|
return Number.isFinite(time) && new Date(time).toISOString().slice(0, 23) === value.slice(0, 23);
|
|
52186
52280
|
}
|
|
52187
52281
|
function parseMember(row, fail) {
|
|
52188
|
-
if (!
|
|
52282
|
+
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))
|
|
52189
52283
|
return fail();
|
|
52190
52284
|
return {
|
|
52191
52285
|
membershipId: row.membershipId,
|
|
@@ -52205,12 +52299,12 @@ class WorkspaceMemberInputError extends Error {
|
|
|
52205
52299
|
}
|
|
52206
52300
|
}
|
|
52207
52301
|
function mutationInput(membershipId, input, roleChange) {
|
|
52208
|
-
if (typeof membershipId !== "string" || !
|
|
52302
|
+
if (typeof membershipId !== "string" || !uuid2(membershipId) || membershipId !== membershipId.toLowerCase() || !record2(input) || Object.keys(input).sort().join(",") !== (roleChange ? "expectedRole,role" : "expectedRole"))
|
|
52209
52303
|
throw new WorkspaceMemberInputError;
|
|
52210
|
-
const expectedRole = input.expectedRole,
|
|
52211
|
-
if (!isRole(expectedRole) || roleChange && !isRole(
|
|
52304
|
+
const expectedRole = input.expectedRole, role2 = roleChange ? input.role : undefined;
|
|
52305
|
+
if (!isRole(expectedRole) || roleChange && !isRole(role2))
|
|
52212
52306
|
throw new WorkspaceMemberInputError;
|
|
52213
|
-
return { membershipId, role, expectedRole };
|
|
52307
|
+
return { membershipId, role: role2, expectedRole };
|
|
52214
52308
|
}
|
|
52215
52309
|
function workspaceMemberRoleInput(membershipId, input) {
|
|
52216
52310
|
const value = mutationInput(membershipId, input, true);
|
|
@@ -52221,19 +52315,19 @@ function workspaceMemberRemovalInput(membershipId, input) {
|
|
|
52221
52315
|
return { membershipId: value.membershipId, body: { expectedRole: value.expectedRole } };
|
|
52222
52316
|
}
|
|
52223
52317
|
var invalidMemberResult = "The server returned an invalid workspace member result. Refresh the roster before another action.";
|
|
52224
|
-
function parseWorkspaceMemberRoleResult(value, membershipId,
|
|
52318
|
+
function parseWorkspaceMemberRoleResult(value, membershipId, role2) {
|
|
52225
52319
|
const fail = () => {
|
|
52226
52320
|
throw new Error(invalidMemberResult);
|
|
52227
52321
|
};
|
|
52228
|
-
if (!
|
|
52322
|
+
if (!record2(value) || !uuid2(value.organizationId) || typeof value.changed !== "boolean")
|
|
52229
52323
|
return fail();
|
|
52230
52324
|
const member = parseMember(value.member, fail);
|
|
52231
|
-
if (member.membershipId !== membershipId || member.role !==
|
|
52325
|
+
if (member.membershipId !== membershipId || member.role !== role2)
|
|
52232
52326
|
return fail();
|
|
52233
52327
|
return { organizationId: value.organizationId, member, changed: value.changed };
|
|
52234
52328
|
}
|
|
52235
52329
|
function parseWorkspaceMemberRemovalResult(value, membershipId) {
|
|
52236
|
-
if (!
|
|
52330
|
+
if (!record2(value) || !uuid2(value.organizationId) || value.membershipId !== membershipId || value.removed !== true || typeof value.alreadyRemoved !== "boolean")
|
|
52237
52331
|
throw new Error(invalidMemberResult);
|
|
52238
52332
|
return { organizationId: value.organizationId, membershipId, removed: true, alreadyRemoved: value.alreadyRemoved };
|
|
52239
52333
|
}
|
|
@@ -52250,7 +52344,7 @@ var workspaceMemberFailures = {
|
|
|
52250
52344
|
MEMBERSHIP_BUSY: [503, "Membership is busy. Refresh the roster before another action."]
|
|
52251
52345
|
};
|
|
52252
52346
|
function workspaceMemberFailure(value, status) {
|
|
52253
|
-
if (!
|
|
52347
|
+
if (!record2(value) || typeof value.code !== "string" || !Object.hasOwn(workspaceMemberFailures, value.code))
|
|
52254
52348
|
return null;
|
|
52255
52349
|
const code = value.code;
|
|
52256
52350
|
return workspaceMemberFailures[code][0] === status ? code : null;
|
|
@@ -52259,13 +52353,72 @@ function parseWorkspaceMembersPage(value) {
|
|
|
52259
52353
|
const fail = () => {
|
|
52260
52354
|
throw new Error("The server returned an invalid workspace roster.");
|
|
52261
52355
|
};
|
|
52262
|
-
if (!
|
|
52356
|
+
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)
|
|
52263
52357
|
return fail();
|
|
52264
52358
|
const members = value.members.map((row) => parseMember(row, fail));
|
|
52265
52359
|
if (new Set(members.map((row) => row.membershipId)).size !== members.length)
|
|
52266
52360
|
return fail();
|
|
52267
52361
|
return { organizationId: value.organizationId, members, nextCursor: value.nextCursor };
|
|
52268
52362
|
}
|
|
52363
|
+
|
|
52364
|
+
// src/lib/remote-workspace-leave.ts
|
|
52365
|
+
class WorkspaceLeaveInputError extends Error {
|
|
52366
|
+
constructor() {
|
|
52367
|
+
super("Confirm leaving the exact observed user and membership with its expected role.");
|
|
52368
|
+
this.name = "WorkspaceLeaveInputError";
|
|
52369
|
+
}
|
|
52370
|
+
}
|
|
52371
|
+
function workspaceLeaveInput(context, input) {
|
|
52372
|
+
const target = workspaceContext(context);
|
|
52373
|
+
if (!input || typeof input !== "object" || Array.isArray(input) || Object.keys(input).sort().join(",") !== "confirm,expectedRole" || input.confirm !== true)
|
|
52374
|
+
throw new WorkspaceLeaveInputError;
|
|
52375
|
+
const captured = workspaceMemberRemovalInput(target.membershipId, { expectedRole: input.expectedRole });
|
|
52376
|
+
return {
|
|
52377
|
+
context: target,
|
|
52378
|
+
input: { expectedRole: captured.body.expectedRole, confirm: true },
|
|
52379
|
+
body: { membershipId: target.membershipId, expectedRole: captured.body.expectedRole }
|
|
52380
|
+
};
|
|
52381
|
+
}
|
|
52382
|
+
var workspaceLeaveFailures = {
|
|
52383
|
+
INVALID_REQUEST: [400, "Provide the exact current membership and expected role."],
|
|
52384
|
+
ACCOUNT_UNAVAILABLE: [403, "Account is unavailable."],
|
|
52385
|
+
INTERACTIVE_SESSION_REQUIRED: [403, "Fresh interactive sign-in is required to leave a workspace."],
|
|
52386
|
+
MEMBERSHIP_ROLE_CHANGED: [409, "Your role changed. Sign in and inspect the workspace before leaving."],
|
|
52387
|
+
LAST_OWNER_REQUIRED: [409, "The workspace must retain another active owner."],
|
|
52388
|
+
LAST_WORKSPACE_REQUIRED: [409, "Another available workspace is required before leaving."],
|
|
52389
|
+
MEMBERSHIP_BUSY: [503, "Membership is busy. Inspect the workspace before another leave action."]
|
|
52390
|
+
};
|
|
52391
|
+
|
|
52392
|
+
class RemoteWorkspaceLeaveError extends Error {
|
|
52393
|
+
code;
|
|
52394
|
+
status;
|
|
52395
|
+
constructor(code) {
|
|
52396
|
+
super(workspaceLeaveFailures[code][1]);
|
|
52397
|
+
this.code = code;
|
|
52398
|
+
this.name = "RemoteWorkspaceLeaveError";
|
|
52399
|
+
this.status = workspaceLeaveFailures[code][0];
|
|
52400
|
+
}
|
|
52401
|
+
}
|
|
52402
|
+
|
|
52403
|
+
class RemoteWorkspaceLeaveUnconfirmedError extends Error {
|
|
52404
|
+
code = "WORKSPACE_LEAVE_UNCONFIRMED";
|
|
52405
|
+
constructor() {
|
|
52406
|
+
super("The leave outcome is unconfirmed. Sign in again and inspect available memberships before another action. Do not retry automatically; saved credentials are unchanged.");
|
|
52407
|
+
this.name = "RemoteWorkspaceLeaveUnconfirmedError";
|
|
52408
|
+
}
|
|
52409
|
+
}
|
|
52410
|
+
function workspaceLeaveFailure(value, status) {
|
|
52411
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
52412
|
+
return null;
|
|
52413
|
+
const code = value.code;
|
|
52414
|
+
return typeof code === "string" && Object.hasOwn(workspaceLeaveFailures, code) && workspaceLeaveFailures[code][0] === status ? code : null;
|
|
52415
|
+
}
|
|
52416
|
+
function parseWorkspaceLeaveResult(value, membershipId, organizationId) {
|
|
52417
|
+
const row = value;
|
|
52418
|
+
if (!row || typeof row !== "object" || Array.isArray(row) || row.membershipId !== membershipId || row.organizationId !== organizationId || row.removed !== true || row.signInRequired !== true)
|
|
52419
|
+
throw new RemoteWorkspaceLeaveUnconfirmedError;
|
|
52420
|
+
return { membershipId, organizationId, removed: true, signInRequired: true };
|
|
52421
|
+
}
|
|
52269
52422
|
// src/lib/auth-store.ts
|
|
52270
52423
|
function getApiUrl(action, env = process.env, options = {}) {
|
|
52271
52424
|
return requireSkillsApiOrigin(action, env, options);
|
|
@@ -52430,11 +52583,11 @@ function parseUpdatedProfile(value) {
|
|
|
52430
52583
|
return { user: { id: user.id, email: user.email, displayName: user.displayName, role: user.role } };
|
|
52431
52584
|
}
|
|
52432
52585
|
function parseUpdatedWorkspace(value) {
|
|
52433
|
-
const
|
|
52434
|
-
if (!isRecord5(
|
|
52586
|
+
const organization2 = isRecord5(value) && value.organization;
|
|
52587
|
+
if (!isRecord5(organization2) || !string(organization2.id) || !string(organization2.slug) || !string(organization2.name)) {
|
|
52435
52588
|
throw new Error("The server returned an invalid workspace.");
|
|
52436
52589
|
}
|
|
52437
|
-
return { organization: { id:
|
|
52590
|
+
return { organization: { id: organization2.id, slug: organization2.slug, name: organization2.name } };
|
|
52438
52591
|
}
|
|
52439
52592
|
|
|
52440
52593
|
// src/lib/remote-client.ts
|
|
@@ -52472,6 +52625,16 @@ class RemoteWorkspaceMemberError extends RemoteRequestError {
|
|
|
52472
52625
|
}
|
|
52473
52626
|
}
|
|
52474
52627
|
|
|
52628
|
+
class RemoteWorkspaceSelectionError extends RemoteRequestError {
|
|
52629
|
+
code;
|
|
52630
|
+
constructor(path, code) {
|
|
52631
|
+
super(path, workspaceSelectionFailures[code][0]);
|
|
52632
|
+
this.code = code;
|
|
52633
|
+
this.name = "RemoteWorkspaceSelectionError";
|
|
52634
|
+
this.message = workspaceSelectionFailures[code][1];
|
|
52635
|
+
}
|
|
52636
|
+
}
|
|
52637
|
+
|
|
52475
52638
|
class RemoteCapabilityUnavailableError extends RemoteRequestError {
|
|
52476
52639
|
code = "SUBSCRIPTION_CHECKOUT_UNAVAILABLE";
|
|
52477
52640
|
constructor() {
|
|
@@ -52493,6 +52656,7 @@ class RemoteSkillsClient {
|
|
|
52493
52656
|
return fetch(`${this.apiUrl}${path}`, {
|
|
52494
52657
|
...options,
|
|
52495
52658
|
redirect: "error",
|
|
52659
|
+
credentials: "omit",
|
|
52496
52660
|
signal: options?.signal ?? AbortSignal.timeout(15000),
|
|
52497
52661
|
headers: {
|
|
52498
52662
|
Authorization: `Bearer ${this.apiKey}`,
|
|
@@ -52599,6 +52763,65 @@ class RemoteSkillsClient {
|
|
|
52599
52763
|
async getIdentity() {
|
|
52600
52764
|
return (await this.requestNewRoute("/api/auth/whoami")).json();
|
|
52601
52765
|
}
|
|
52766
|
+
async listAccountWorkspaces(expectedUserId) {
|
|
52767
|
+
const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
|
|
52768
|
+
const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
|
|
52769
|
+
let identity;
|
|
52770
|
+
if (expected !== undefined) {
|
|
52771
|
+
const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
|
|
52772
|
+
if (!value || typeof value !== "object" || value.authMethod !== "jwt")
|
|
52773
|
+
throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces", "INTERACTIVE_SESSION_REQUIRED");
|
|
52774
|
+
identity = parseWorkspaceIdentity(value, expected);
|
|
52775
|
+
}
|
|
52776
|
+
const result = parseAccountWorkspaces(await connection.requestWorkspaceSelection("/api/v1/account/workspaces"));
|
|
52777
|
+
const current = result.workspaces.find((workspace) => workspace.current);
|
|
52778
|
+
if (identity && (current.membershipId !== identity.user.membershipId || current.organization.id !== identity.organization.id))
|
|
52779
|
+
throw new WorkspaceIdentityMismatchError;
|
|
52780
|
+
return result;
|
|
52781
|
+
}
|
|
52782
|
+
async switchWorkspace(context) {
|
|
52783
|
+
const target = workspaceContext(context);
|
|
52784
|
+
const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
|
|
52785
|
+
const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
|
|
52786
|
+
if (!value || typeof value !== "object" || value.authMethod !== "jwt")
|
|
52787
|
+
throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces/switch", "INTERACTIVE_SESSION_REQUIRED");
|
|
52788
|
+
parseWorkspaceIdentity(value, target.userId);
|
|
52789
|
+
const selected = parseWorkspaceSession(await connection.requestWorkspaceSelection("/api/v1/account/workspaces/switch", {
|
|
52790
|
+
method: "POST",
|
|
52791
|
+
body: JSON.stringify({ membershipId: target.membershipId })
|
|
52792
|
+
}), target);
|
|
52793
|
+
const verified = await new RemoteSkillsClient(selected.token, connection.apiUrl).requestWorkspaceSelection("/api/auth/whoami");
|
|
52794
|
+
if (!verified || typeof verified !== "object" || verified.authMethod !== "jwt")
|
|
52795
|
+
throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces/switch", "INTERACTIVE_SESSION_REQUIRED");
|
|
52796
|
+
const identity = parseWorkspaceIdentity(verified, target.userId);
|
|
52797
|
+
if (identity.user.membershipId !== target.membershipId || identity.organization.id !== selected.organization.id)
|
|
52798
|
+
throw new WorkspaceIdentityMismatchError;
|
|
52799
|
+
return { token: selected.token, ...identity };
|
|
52800
|
+
}
|
|
52801
|
+
async requestWorkspaceSelection(path, options) {
|
|
52802
|
+
let response;
|
|
52803
|
+
try {
|
|
52804
|
+
response = await this.request(path, { ...options, credentials: "omit" });
|
|
52805
|
+
} catch {
|
|
52806
|
+
throw new Error("Unable to reach the Skills workspace API.");
|
|
52807
|
+
}
|
|
52808
|
+
let value;
|
|
52809
|
+
try {
|
|
52810
|
+
value = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, response.ok ? 1024 * 1024 : 4096)));
|
|
52811
|
+
} catch {
|
|
52812
|
+
if (response.ok)
|
|
52813
|
+
throw new Error(invalidWorkspaceResult);
|
|
52814
|
+
}
|
|
52815
|
+
if (!response.ok) {
|
|
52816
|
+
const code = workspaceSelectionFailure(value, response.status);
|
|
52817
|
+
if (code)
|
|
52818
|
+
throw new RemoteWorkspaceSelectionError(path, code);
|
|
52819
|
+
if (response.status === 404 || response.status === 405)
|
|
52820
|
+
throw new RemoteRouteUnsupportedError(path, response.status, this.apiUrl);
|
|
52821
|
+
throw new RemoteRequestError(path, response.status);
|
|
52822
|
+
}
|
|
52823
|
+
return value;
|
|
52824
|
+
}
|
|
52602
52825
|
async updateProfile(input) {
|
|
52603
52826
|
const body = customerNamePatch(input, "displayName");
|
|
52604
52827
|
return parseUpdatedProfile(await (await this.requestNewRoute("/api/v1/account/profile", { method: "PATCH", body: JSON.stringify(body) })).json());
|
|
@@ -52651,6 +52874,30 @@ class RemoteSkillsClient {
|
|
|
52651
52874
|
}
|
|
52652
52875
|
return value;
|
|
52653
52876
|
}
|
|
52877
|
+
async leaveWorkspace(context, input) {
|
|
52878
|
+
const captured = workspaceLeaveInput(context, input);
|
|
52879
|
+
const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
|
|
52880
|
+
const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
|
|
52881
|
+
if (!value || typeof value !== "object" || value.authMethod !== "jwt")
|
|
52882
|
+
throw new RemoteWorkspaceLeaveError("INTERACTIVE_SESSION_REQUIRED");
|
|
52883
|
+
const identity = parseWorkspaceIdentity(value, captured.context.userId);
|
|
52884
|
+
if (identity.user.membershipId !== captured.context.membershipId)
|
|
52885
|
+
throw new WorkspaceIdentityMismatchError;
|
|
52886
|
+
let response, body;
|
|
52887
|
+
try {
|
|
52888
|
+
response = await connection.request("/api/v1/account/workspaces/leave", { method: "POST", body: JSON.stringify(captured.body) });
|
|
52889
|
+
body = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, 4096)));
|
|
52890
|
+
} catch {
|
|
52891
|
+
throw new RemoteWorkspaceLeaveUnconfirmedError;
|
|
52892
|
+
}
|
|
52893
|
+
if (!response.ok) {
|
|
52894
|
+
const code = workspaceLeaveFailure(body, response.status);
|
|
52895
|
+
if (code)
|
|
52896
|
+
throw new RemoteWorkspaceLeaveError(code);
|
|
52897
|
+
throw new RemoteWorkspaceLeaveUnconfirmedError;
|
|
52898
|
+
}
|
|
52899
|
+
return parseWorkspaceLeaveResult(body, captured.context.membershipId, identity.organization.id);
|
|
52900
|
+
}
|
|
52654
52901
|
async listApiKeys() {
|
|
52655
52902
|
return this.arrayResponse("/api/auth/keys");
|
|
52656
52903
|
}
|
|
@@ -52901,13 +53148,13 @@ class RemoteSkillsClient {
|
|
|
52901
53148
|
return normalizeUpdatedSincePage(await response.json());
|
|
52902
53149
|
}
|
|
52903
53150
|
}
|
|
52904
|
-
function requireOptionalString(
|
|
52905
|
-
if (
|
|
53151
|
+
function requireOptionalString(record3, field) {
|
|
53152
|
+
if (record3[field] === undefined)
|
|
52906
53153
|
return;
|
|
52907
|
-
if (typeof
|
|
53154
|
+
if (typeof record3[field] !== "string") {
|
|
52908
53155
|
throw new Error(`Remote payload did not match the expected contract (${field} must be a string when present)`);
|
|
52909
53156
|
}
|
|
52910
|
-
return
|
|
53157
|
+
return record3[field];
|
|
52911
53158
|
}
|
|
52912
53159
|
var INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
|
|
52913
53160
|
function isVersionRecord(value) {
|
|
@@ -52930,19 +53177,19 @@ function normalizePin(entry) {
|
|
|
52930
53177
|
if (!entry || typeof entry !== "object") {
|
|
52931
53178
|
throw new Error("Remote pin payload did not match the expected contract (expected an object)");
|
|
52932
53179
|
}
|
|
52933
|
-
const
|
|
52934
|
-
const slug = typeof
|
|
53180
|
+
const record3 = entry;
|
|
53181
|
+
const slug = typeof record3.slug === "string" && record3.slug.trim() ? record3.slug.trim() : undefined;
|
|
52935
53182
|
if (!slug) {
|
|
52936
53183
|
throw new Error("Remote pin payload did not match the expected contract (missing slug)");
|
|
52937
53184
|
}
|
|
52938
53185
|
let metadata;
|
|
52939
|
-
if (
|
|
52940
|
-
if (!
|
|
53186
|
+
if (record3.metadata !== undefined) {
|
|
53187
|
+
if (!record3.metadata || typeof record3.metadata !== "object" || Array.isArray(record3.metadata)) {
|
|
52941
53188
|
throw new Error("Remote pin payload did not match the expected contract (metadata must be a JSON object when present)");
|
|
52942
53189
|
}
|
|
52943
|
-
metadata =
|
|
53190
|
+
metadata = record3.metadata;
|
|
52944
53191
|
}
|
|
52945
|
-
const pinnedAt = requireOptionalString(
|
|
53192
|
+
const pinnedAt = requireOptionalString(record3, "pinnedAt");
|
|
52946
53193
|
return {
|
|
52947
53194
|
slug,
|
|
52948
53195
|
...pinnedAt !== undefined ? { pinnedAt } : {},
|
|
@@ -52959,16 +53206,16 @@ function normalizeSkillSummary(entry) {
|
|
|
52959
53206
|
if (!entry || typeof entry !== "object") {
|
|
52960
53207
|
throw new Error("Remote skill payload did not match the expected contract (expected an object)");
|
|
52961
53208
|
}
|
|
52962
|
-
const
|
|
52963
|
-
const slug = typeof
|
|
53209
|
+
const record3 = entry;
|
|
53210
|
+
const slug = typeof record3.slug === "string" && record3.slug.trim() ? record3.slug.trim() : undefined;
|
|
52964
53211
|
if (!slug) {
|
|
52965
53212
|
throw new Error("Remote skill payload did not match the expected contract (missing slug)");
|
|
52966
53213
|
}
|
|
52967
53214
|
return {
|
|
52968
53215
|
slug,
|
|
52969
|
-
...requireOptionalString(
|
|
52970
|
-
...requireOptionalString(
|
|
52971
|
-
...requireOptionalString(
|
|
53216
|
+
...requireOptionalString(record3, "name") !== undefined ? { name: requireOptionalString(record3, "name") } : {},
|
|
53217
|
+
...requireOptionalString(record3, "version") !== undefined ? { version: requireOptionalString(record3, "version") } : {},
|
|
53218
|
+
...requireOptionalString(record3, "updatedAt") !== undefined ? { updatedAt: requireOptionalString(record3, "updatedAt") } : {}
|
|
52972
53219
|
};
|
|
52973
53220
|
}
|
|
52974
53221
|
function normalizeSkillSummaryList(payload) {
|
|
@@ -53021,12 +53268,12 @@ function normalizeUpdatedSincePage(payload) {
|
|
|
53021
53268
|
if (!payload || typeof payload !== "object") {
|
|
53022
53269
|
throw new Error("Updated-since payload did not match the expected contract (expected an object)");
|
|
53023
53270
|
}
|
|
53024
|
-
const
|
|
53025
|
-
if (!Array.isArray(
|
|
53271
|
+
const record3 = payload;
|
|
53272
|
+
if (!Array.isArray(record3.skills)) {
|
|
53026
53273
|
throw new Error("Updated-since payload did not match the expected contract (missing skills array)");
|
|
53027
53274
|
}
|
|
53028
|
-
const skills =
|
|
53029
|
-
const nextCursor =
|
|
53275
|
+
const skills = record3.skills.map(normalizeSkillSummary);
|
|
53276
|
+
const nextCursor = record3.nextCursor === undefined || record3.nextCursor === null ? null : record3.nextCursor;
|
|
53030
53277
|
if (nextCursor !== null && typeof nextCursor !== "string") {
|
|
53031
53278
|
throw new Error("Updated-since payload did not match the expected contract (nextCursor must be a string or absent)");
|
|
53032
53279
|
}
|
|
@@ -53073,13 +53320,13 @@ async function requestAuthApi(instance, path, options) {
|
|
|
53073
53320
|
apiUrl: safeUrl
|
|
53074
53321
|
});
|
|
53075
53322
|
}
|
|
53076
|
-
const
|
|
53077
|
-
const body =
|
|
53323
|
+
const text2 = await res.text();
|
|
53324
|
+
const body = text2 ? parseJsonBody(text2) : {};
|
|
53078
53325
|
if (!res.ok) {
|
|
53079
|
-
const
|
|
53080
|
-
const detail = typeof
|
|
53081
|
-
const error = typeof
|
|
53082
|
-
const code = typeof
|
|
53326
|
+
const record3 = isRecord6(body) ? body : {};
|
|
53327
|
+
const detail = typeof record3.detail === "string" ? record3.detail : undefined;
|
|
53328
|
+
const error = typeof record3.error === "string" ? record3.error : undefined;
|
|
53329
|
+
const code = typeof record3.code === "string" ? record3.code : undefined;
|
|
53083
53330
|
throw new HostedApiError(detail || error || `${res.status} ${res.statusText}`, {
|
|
53084
53331
|
status: res.status,
|
|
53085
53332
|
code,
|
|
@@ -53090,15 +53337,15 @@ async function requestAuthApi(instance, path, options) {
|
|
|
53090
53337
|
}
|
|
53091
53338
|
return body;
|
|
53092
53339
|
}
|
|
53093
|
-
function parseJsonBody(
|
|
53340
|
+
function parseJsonBody(text2) {
|
|
53094
53341
|
try {
|
|
53095
|
-
return JSON.parse(
|
|
53342
|
+
return JSON.parse(text2);
|
|
53096
53343
|
} catch {
|
|
53097
|
-
return { detail: condenseErrorBody(
|
|
53344
|
+
return { detail: condenseErrorBody(text2) };
|
|
53098
53345
|
}
|
|
53099
53346
|
}
|
|
53100
|
-
function condenseErrorBody(
|
|
53101
|
-
const stripped = /<[a-z!/]/i.test(
|
|
53347
|
+
function condenseErrorBody(text2) {
|
|
53348
|
+
const stripped = /<[a-z!/]/i.test(text2) ? text2.replace(/<(script|style)[\s\S]*?<\/\1>/gi, " ").replace(/<[^>]*>/g, " ") : text2;
|
|
53102
53349
|
const collapsed = stripped.replace(/\s+/g, " ").trim();
|
|
53103
53350
|
if (collapsed.length <= MAX_ERROR_DETAIL_LENGTH)
|
|
53104
53351
|
return collapsed;
|
|
@@ -53125,7 +53372,12 @@ class RemoteSkillsAuthClient {
|
|
|
53125
53372
|
pollDevice(deviceCode) {
|
|
53126
53373
|
return this.request("/api/auth/device/token", { method: "POST", body: JSON.stringify({ deviceCode }) });
|
|
53127
53374
|
}
|
|
53128
|
-
async sessionClient(email, code) {
|
|
53375
|
+
async sessionClient(email, code, context) {
|
|
53376
|
+
if (context !== undefined) {
|
|
53377
|
+
const target = workspaceContext(context), apiOrigin2 = this.apiOrigin;
|
|
53378
|
+
const session = await this.switchWorkspace(email, code, target);
|
|
53379
|
+
return new RemoteSkillsClient(session.token, apiOrigin2);
|
|
53380
|
+
}
|
|
53129
53381
|
const apiOrigin = this.apiOrigin;
|
|
53130
53382
|
if (!email.includes("@") || !/^\d{6}$/.test(code))
|
|
53131
53383
|
throw new Error("Fresh email and six-digit verification code are required to manage this account");
|
|
@@ -53134,34 +53386,80 @@ class RemoteSkillsAuthClient {
|
|
|
53134
53386
|
throw new Error("The server did not return an authorized account session");
|
|
53135
53387
|
return new RemoteSkillsClient(login.token, apiOrigin);
|
|
53136
53388
|
}
|
|
53137
|
-
async
|
|
53138
|
-
|
|
53389
|
+
async listAccountWorkspaces(email, code, expectedUserId) {
|
|
53390
|
+
const login = await this.workspaceLogin(email, code, expectedUserId);
|
|
53391
|
+
const result = await new RemoteSkillsClient(login.token, login.apiOrigin).listAccountWorkspaces(login.userId);
|
|
53392
|
+
return { userId: login.userId, ...result };
|
|
53139
53393
|
}
|
|
53140
|
-
async
|
|
53141
|
-
|
|
53394
|
+
async switchWorkspace(email, code, context) {
|
|
53395
|
+
const target = workspaceContext(context);
|
|
53396
|
+
const login = await this.workspaceLogin(email, code, target.userId);
|
|
53397
|
+
return new RemoteSkillsClient(login.token, login.apiOrigin).switchWorkspace(target);
|
|
53142
53398
|
}
|
|
53143
|
-
async
|
|
53144
|
-
|
|
53399
|
+
async workspaceLogin(email, code, expectedUserId) {
|
|
53400
|
+
const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
|
|
53401
|
+
const apiOrigin = this.apiOrigin;
|
|
53402
|
+
if (typeof email !== "string" || !email.includes("@") || typeof code !== "string" || !/^\d{6}$/.test(code))
|
|
53403
|
+
throw new Error("Fresh email and six-digit verification code are required to manage this account");
|
|
53404
|
+
let response;
|
|
53405
|
+
try {
|
|
53406
|
+
response = await fetch(`${apiOrigin}/api/auth/verify`, {
|
|
53407
|
+
method: "POST",
|
|
53408
|
+
redirect: "error",
|
|
53409
|
+
credentials: "omit",
|
|
53410
|
+
signal: AbortSignal.timeout(15000),
|
|
53411
|
+
headers: { "Content-Type": "application/json" },
|
|
53412
|
+
body: JSON.stringify({ email, code })
|
|
53413
|
+
});
|
|
53414
|
+
} catch {
|
|
53415
|
+
throw new HostedApiError("Unable to verify the Skills account.");
|
|
53416
|
+
}
|
|
53417
|
+
if (!response.ok) {
|
|
53418
|
+
response.body?.cancel().catch(() => {});
|
|
53419
|
+
throw new HostedApiError("Unable to verify the Skills account.", { status: response.status });
|
|
53420
|
+
}
|
|
53421
|
+
let value;
|
|
53422
|
+
try {
|
|
53423
|
+
value = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, 64 * 1024)));
|
|
53424
|
+
} catch {
|
|
53425
|
+
throw new HostedApiError("The server returned an invalid account verification result.");
|
|
53426
|
+
}
|
|
53427
|
+
return { ...parseWorkspaceLogin(value, expected), apiOrigin };
|
|
53428
|
+
}
|
|
53429
|
+
async createApiKey(email, code, name, scopes, context) {
|
|
53430
|
+
const capturedScopes = scopes === undefined ? undefined : [...scopes];
|
|
53431
|
+
return (await this.sessionClient(email, code, context)).createApiKey(name, capturedScopes);
|
|
53145
53432
|
}
|
|
53146
|
-
async
|
|
53147
|
-
|
|
53148
|
-
return (await this.sessionClient(email, code)).updateProfile(input);
|
|
53433
|
+
async listApiKeys(email, code, context) {
|
|
53434
|
+
return (await this.sessionClient(email, code, context)).listApiKeys();
|
|
53149
53435
|
}
|
|
53150
|
-
async
|
|
53151
|
-
|
|
53152
|
-
return (await this.sessionClient(email, code)).updateCurrentWorkspace(input);
|
|
53436
|
+
async revokeApiKey(email, code, keyId, context) {
|
|
53437
|
+
return (await this.sessionClient(email, code, context)).revokeApiKey(keyId);
|
|
53153
53438
|
}
|
|
53154
|
-
async
|
|
53439
|
+
async updateProfile(email, code, input, context) {
|
|
53440
|
+
const body = customerNamePatch(input, "displayName");
|
|
53441
|
+
return (await this.sessionClient(email, code, context)).updateProfile({ displayName: body.displayName });
|
|
53442
|
+
}
|
|
53443
|
+
async updateCurrentWorkspace(email, code, input, context) {
|
|
53444
|
+
const body = customerNamePatch(input, "name");
|
|
53445
|
+
return (await this.sessionClient(email, code, context)).updateCurrentWorkspace({ name: body.name });
|
|
53446
|
+
}
|
|
53447
|
+
async listWorkspaceMembers(email, code, options = {}, context) {
|
|
53155
53448
|
workspaceMembersQuery(options);
|
|
53156
|
-
|
|
53449
|
+
const captured = { ...options };
|
|
53450
|
+
return (await this.sessionClient(email, code, context)).listWorkspaceMembers(captured);
|
|
53157
53451
|
}
|
|
53158
|
-
async setWorkspaceMemberRole(email, code, membershipId, input) {
|
|
53452
|
+
async setWorkspaceMemberRole(email, code, membershipId, input, context) {
|
|
53159
53453
|
const captured = workspaceMemberRoleInput(membershipId, input);
|
|
53160
|
-
return (await this.sessionClient(email, code)).setWorkspaceMemberRole(captured.membershipId, captured.body);
|
|
53454
|
+
return (await this.sessionClient(email, code, context)).setWorkspaceMemberRole(captured.membershipId, captured.body);
|
|
53455
|
+
}
|
|
53456
|
+
async leaveWorkspace(email, code, context, input) {
|
|
53457
|
+
const captured = workspaceLeaveInput(context, input);
|
|
53458
|
+
return (await this.sessionClient(email, code, captured.context)).leaveWorkspace(captured.context, captured.input);
|
|
53161
53459
|
}
|
|
53162
|
-
async removeWorkspaceMember(email, code, membershipId, input) {
|
|
53460
|
+
async removeWorkspaceMember(email, code, membershipId, input, context) {
|
|
53163
53461
|
const captured = workspaceMemberRemovalInput(membershipId, input);
|
|
53164
|
-
return (await this.sessionClient(email, code)).removeWorkspaceMember(captured.membershipId, captured.body);
|
|
53462
|
+
return (await this.sessionClient(email, code, context)).removeWorkspaceMember(captured.membershipId, captured.body);
|
|
53165
53463
|
}
|
|
53166
53464
|
request(path, options) {
|
|
53167
53465
|
if (!["/api/auth/login", "/api/auth/verify", "/api/auth/device/start", "/api/auth/device/token", "/api/auth/keys", "/api/auth/whoami"].includes(path))
|
|
@@ -53242,6 +53540,9 @@ export {
|
|
|
53242
53540
|
assertDurableTarget,
|
|
53243
53541
|
assertDurableStore,
|
|
53244
53542
|
artifactStorageSeam,
|
|
53543
|
+
WorkspaceLeaveInputError,
|
|
53544
|
+
WorkspaceIdentityMismatchError,
|
|
53545
|
+
WorkspaceContextInputError,
|
|
53245
53546
|
SqliteSkillsStore,
|
|
53246
53547
|
SqliteRunExecutionStore,
|
|
53247
53548
|
SqliteGovernanceStore,
|
|
@@ -53250,7 +53551,10 @@ export {
|
|
|
53250
53551
|
SKILLS_APP,
|
|
53251
53552
|
SKILLS_API_URL_ENV,
|
|
53252
53553
|
SKILLS_API_KEY_ENV,
|
|
53554
|
+
RemoteWorkspaceSelectionError,
|
|
53253
53555
|
RemoteWorkspaceMemberError,
|
|
53556
|
+
RemoteWorkspaceLeaveUnconfirmedError,
|
|
53557
|
+
RemoteWorkspaceLeaveError,
|
|
53254
53558
|
RemoteSkillsClient,
|
|
53255
53559
|
RemoteSkillsAuthClient,
|
|
53256
53560
|
RemoteRouteUnsupportedError,
|