@hasna/skills 0.5.0 → 0.5.1
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 +44 -0
- package/bin/index.js +927 -393
- package/bin/mcp.js +409 -110
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/cli/commands/workspace-selection.d.ts +11 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +337 -123
- package/dist/lib/remote-auth.d.ts +15 -9
- package/dist/lib/remote-client.d.ts +11 -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 +3 -0
- package/dist/sdk/index.js +281 -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.1",
|
|
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,7 +52353,7 @@ 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)
|
|
@@ -52430,11 +52524,11 @@ function parseUpdatedProfile(value) {
|
|
|
52430
52524
|
return { user: { id: user.id, email: user.email, displayName: user.displayName, role: user.role } };
|
|
52431
52525
|
}
|
|
52432
52526
|
function parseUpdatedWorkspace(value) {
|
|
52433
|
-
const
|
|
52434
|
-
if (!isRecord5(
|
|
52527
|
+
const organization2 = isRecord5(value) && value.organization;
|
|
52528
|
+
if (!isRecord5(organization2) || !string(organization2.id) || !string(organization2.slug) || !string(organization2.name)) {
|
|
52435
52529
|
throw new Error("The server returned an invalid workspace.");
|
|
52436
52530
|
}
|
|
52437
|
-
return { organization: { id:
|
|
52531
|
+
return { organization: { id: organization2.id, slug: organization2.slug, name: organization2.name } };
|
|
52438
52532
|
}
|
|
52439
52533
|
|
|
52440
52534
|
// src/lib/remote-client.ts
|
|
@@ -52472,6 +52566,16 @@ class RemoteWorkspaceMemberError extends RemoteRequestError {
|
|
|
52472
52566
|
}
|
|
52473
52567
|
}
|
|
52474
52568
|
|
|
52569
|
+
class RemoteWorkspaceSelectionError extends RemoteRequestError {
|
|
52570
|
+
code;
|
|
52571
|
+
constructor(path, code) {
|
|
52572
|
+
super(path, workspaceSelectionFailures[code][0]);
|
|
52573
|
+
this.code = code;
|
|
52574
|
+
this.name = "RemoteWorkspaceSelectionError";
|
|
52575
|
+
this.message = workspaceSelectionFailures[code][1];
|
|
52576
|
+
}
|
|
52577
|
+
}
|
|
52578
|
+
|
|
52475
52579
|
class RemoteCapabilityUnavailableError extends RemoteRequestError {
|
|
52476
52580
|
code = "SUBSCRIPTION_CHECKOUT_UNAVAILABLE";
|
|
52477
52581
|
constructor() {
|
|
@@ -52493,6 +52597,7 @@ class RemoteSkillsClient {
|
|
|
52493
52597
|
return fetch(`${this.apiUrl}${path}`, {
|
|
52494
52598
|
...options,
|
|
52495
52599
|
redirect: "error",
|
|
52600
|
+
credentials: "omit",
|
|
52496
52601
|
signal: options?.signal ?? AbortSignal.timeout(15000),
|
|
52497
52602
|
headers: {
|
|
52498
52603
|
Authorization: `Bearer ${this.apiKey}`,
|
|
@@ -52599,6 +52704,65 @@ class RemoteSkillsClient {
|
|
|
52599
52704
|
async getIdentity() {
|
|
52600
52705
|
return (await this.requestNewRoute("/api/auth/whoami")).json();
|
|
52601
52706
|
}
|
|
52707
|
+
async listAccountWorkspaces(expectedUserId) {
|
|
52708
|
+
const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
|
|
52709
|
+
const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
|
|
52710
|
+
let identity;
|
|
52711
|
+
if (expected !== undefined) {
|
|
52712
|
+
const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
|
|
52713
|
+
if (!value || typeof value !== "object" || value.authMethod !== "jwt")
|
|
52714
|
+
throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces", "INTERACTIVE_SESSION_REQUIRED");
|
|
52715
|
+
identity = parseWorkspaceIdentity(value, expected);
|
|
52716
|
+
}
|
|
52717
|
+
const result = parseAccountWorkspaces(await connection.requestWorkspaceSelection("/api/v1/account/workspaces"));
|
|
52718
|
+
const current = result.workspaces.find((workspace) => workspace.current);
|
|
52719
|
+
if (identity && (current.membershipId !== identity.user.membershipId || current.organization.id !== identity.organization.id))
|
|
52720
|
+
throw new WorkspaceIdentityMismatchError;
|
|
52721
|
+
return result;
|
|
52722
|
+
}
|
|
52723
|
+
async switchWorkspace(context) {
|
|
52724
|
+
const target = workspaceContext(context);
|
|
52725
|
+
const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
|
|
52726
|
+
const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
|
|
52727
|
+
if (!value || typeof value !== "object" || value.authMethod !== "jwt")
|
|
52728
|
+
throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces/switch", "INTERACTIVE_SESSION_REQUIRED");
|
|
52729
|
+
parseWorkspaceIdentity(value, target.userId);
|
|
52730
|
+
const selected = parseWorkspaceSession(await connection.requestWorkspaceSelection("/api/v1/account/workspaces/switch", {
|
|
52731
|
+
method: "POST",
|
|
52732
|
+
body: JSON.stringify({ membershipId: target.membershipId })
|
|
52733
|
+
}), target);
|
|
52734
|
+
const verified = await new RemoteSkillsClient(selected.token, connection.apiUrl).requestWorkspaceSelection("/api/auth/whoami");
|
|
52735
|
+
if (!verified || typeof verified !== "object" || verified.authMethod !== "jwt")
|
|
52736
|
+
throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces/switch", "INTERACTIVE_SESSION_REQUIRED");
|
|
52737
|
+
const identity = parseWorkspaceIdentity(verified, target.userId);
|
|
52738
|
+
if (identity.user.membershipId !== target.membershipId || identity.organization.id !== selected.organization.id)
|
|
52739
|
+
throw new WorkspaceIdentityMismatchError;
|
|
52740
|
+
return { token: selected.token, ...identity };
|
|
52741
|
+
}
|
|
52742
|
+
async requestWorkspaceSelection(path, options) {
|
|
52743
|
+
let response;
|
|
52744
|
+
try {
|
|
52745
|
+
response = await this.request(path, { ...options, credentials: "omit" });
|
|
52746
|
+
} catch {
|
|
52747
|
+
throw new Error("Unable to reach the Skills workspace API.");
|
|
52748
|
+
}
|
|
52749
|
+
let value;
|
|
52750
|
+
try {
|
|
52751
|
+
value = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, response.ok ? 1024 * 1024 : 4096)));
|
|
52752
|
+
} catch {
|
|
52753
|
+
if (response.ok)
|
|
52754
|
+
throw new Error(invalidWorkspaceResult);
|
|
52755
|
+
}
|
|
52756
|
+
if (!response.ok) {
|
|
52757
|
+
const code = workspaceSelectionFailure(value, response.status);
|
|
52758
|
+
if (code)
|
|
52759
|
+
throw new RemoteWorkspaceSelectionError(path, code);
|
|
52760
|
+
if (response.status === 404 || response.status === 405)
|
|
52761
|
+
throw new RemoteRouteUnsupportedError(path, response.status, this.apiUrl);
|
|
52762
|
+
throw new RemoteRequestError(path, response.status);
|
|
52763
|
+
}
|
|
52764
|
+
return value;
|
|
52765
|
+
}
|
|
52602
52766
|
async updateProfile(input) {
|
|
52603
52767
|
const body = customerNamePatch(input, "displayName");
|
|
52604
52768
|
return parseUpdatedProfile(await (await this.requestNewRoute("/api/v1/account/profile", { method: "PATCH", body: JSON.stringify(body) })).json());
|
|
@@ -52901,13 +53065,13 @@ class RemoteSkillsClient {
|
|
|
52901
53065
|
return normalizeUpdatedSincePage(await response.json());
|
|
52902
53066
|
}
|
|
52903
53067
|
}
|
|
52904
|
-
function requireOptionalString(
|
|
52905
|
-
if (
|
|
53068
|
+
function requireOptionalString(record3, field) {
|
|
53069
|
+
if (record3[field] === undefined)
|
|
52906
53070
|
return;
|
|
52907
|
-
if (typeof
|
|
53071
|
+
if (typeof record3[field] !== "string") {
|
|
52908
53072
|
throw new Error(`Remote payload did not match the expected contract (${field} must be a string when present)`);
|
|
52909
53073
|
}
|
|
52910
|
-
return
|
|
53074
|
+
return record3[field];
|
|
52911
53075
|
}
|
|
52912
53076
|
var INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
|
|
52913
53077
|
function isVersionRecord(value) {
|
|
@@ -52930,19 +53094,19 @@ function normalizePin(entry) {
|
|
|
52930
53094
|
if (!entry || typeof entry !== "object") {
|
|
52931
53095
|
throw new Error("Remote pin payload did not match the expected contract (expected an object)");
|
|
52932
53096
|
}
|
|
52933
|
-
const
|
|
52934
|
-
const slug = typeof
|
|
53097
|
+
const record3 = entry;
|
|
53098
|
+
const slug = typeof record3.slug === "string" && record3.slug.trim() ? record3.slug.trim() : undefined;
|
|
52935
53099
|
if (!slug) {
|
|
52936
53100
|
throw new Error("Remote pin payload did not match the expected contract (missing slug)");
|
|
52937
53101
|
}
|
|
52938
53102
|
let metadata;
|
|
52939
|
-
if (
|
|
52940
|
-
if (!
|
|
53103
|
+
if (record3.metadata !== undefined) {
|
|
53104
|
+
if (!record3.metadata || typeof record3.metadata !== "object" || Array.isArray(record3.metadata)) {
|
|
52941
53105
|
throw new Error("Remote pin payload did not match the expected contract (metadata must be a JSON object when present)");
|
|
52942
53106
|
}
|
|
52943
|
-
metadata =
|
|
53107
|
+
metadata = record3.metadata;
|
|
52944
53108
|
}
|
|
52945
|
-
const pinnedAt = requireOptionalString(
|
|
53109
|
+
const pinnedAt = requireOptionalString(record3, "pinnedAt");
|
|
52946
53110
|
return {
|
|
52947
53111
|
slug,
|
|
52948
53112
|
...pinnedAt !== undefined ? { pinnedAt } : {},
|
|
@@ -52959,16 +53123,16 @@ function normalizeSkillSummary(entry) {
|
|
|
52959
53123
|
if (!entry || typeof entry !== "object") {
|
|
52960
53124
|
throw new Error("Remote skill payload did not match the expected contract (expected an object)");
|
|
52961
53125
|
}
|
|
52962
|
-
const
|
|
52963
|
-
const slug = typeof
|
|
53126
|
+
const record3 = entry;
|
|
53127
|
+
const slug = typeof record3.slug === "string" && record3.slug.trim() ? record3.slug.trim() : undefined;
|
|
52964
53128
|
if (!slug) {
|
|
52965
53129
|
throw new Error("Remote skill payload did not match the expected contract (missing slug)");
|
|
52966
53130
|
}
|
|
52967
53131
|
return {
|
|
52968
53132
|
slug,
|
|
52969
|
-
...requireOptionalString(
|
|
52970
|
-
...requireOptionalString(
|
|
52971
|
-
...requireOptionalString(
|
|
53133
|
+
...requireOptionalString(record3, "name") !== undefined ? { name: requireOptionalString(record3, "name") } : {},
|
|
53134
|
+
...requireOptionalString(record3, "version") !== undefined ? { version: requireOptionalString(record3, "version") } : {},
|
|
53135
|
+
...requireOptionalString(record3, "updatedAt") !== undefined ? { updatedAt: requireOptionalString(record3, "updatedAt") } : {}
|
|
52972
53136
|
};
|
|
52973
53137
|
}
|
|
52974
53138
|
function normalizeSkillSummaryList(payload) {
|
|
@@ -53021,12 +53185,12 @@ function normalizeUpdatedSincePage(payload) {
|
|
|
53021
53185
|
if (!payload || typeof payload !== "object") {
|
|
53022
53186
|
throw new Error("Updated-since payload did not match the expected contract (expected an object)");
|
|
53023
53187
|
}
|
|
53024
|
-
const
|
|
53025
|
-
if (!Array.isArray(
|
|
53188
|
+
const record3 = payload;
|
|
53189
|
+
if (!Array.isArray(record3.skills)) {
|
|
53026
53190
|
throw new Error("Updated-since payload did not match the expected contract (missing skills array)");
|
|
53027
53191
|
}
|
|
53028
|
-
const skills =
|
|
53029
|
-
const nextCursor =
|
|
53192
|
+
const skills = record3.skills.map(normalizeSkillSummary);
|
|
53193
|
+
const nextCursor = record3.nextCursor === undefined || record3.nextCursor === null ? null : record3.nextCursor;
|
|
53030
53194
|
if (nextCursor !== null && typeof nextCursor !== "string") {
|
|
53031
53195
|
throw new Error("Updated-since payload did not match the expected contract (nextCursor must be a string or absent)");
|
|
53032
53196
|
}
|
|
@@ -53073,13 +53237,13 @@ async function requestAuthApi(instance, path, options) {
|
|
|
53073
53237
|
apiUrl: safeUrl
|
|
53074
53238
|
});
|
|
53075
53239
|
}
|
|
53076
|
-
const
|
|
53077
|
-
const body =
|
|
53240
|
+
const text2 = await res.text();
|
|
53241
|
+
const body = text2 ? parseJsonBody(text2) : {};
|
|
53078
53242
|
if (!res.ok) {
|
|
53079
|
-
const
|
|
53080
|
-
const detail = typeof
|
|
53081
|
-
const error = typeof
|
|
53082
|
-
const code = typeof
|
|
53243
|
+
const record3 = isRecord6(body) ? body : {};
|
|
53244
|
+
const detail = typeof record3.detail === "string" ? record3.detail : undefined;
|
|
53245
|
+
const error = typeof record3.error === "string" ? record3.error : undefined;
|
|
53246
|
+
const code = typeof record3.code === "string" ? record3.code : undefined;
|
|
53083
53247
|
throw new HostedApiError(detail || error || `${res.status} ${res.statusText}`, {
|
|
53084
53248
|
status: res.status,
|
|
53085
53249
|
code,
|
|
@@ -53090,15 +53254,15 @@ async function requestAuthApi(instance, path, options) {
|
|
|
53090
53254
|
}
|
|
53091
53255
|
return body;
|
|
53092
53256
|
}
|
|
53093
|
-
function parseJsonBody(
|
|
53257
|
+
function parseJsonBody(text2) {
|
|
53094
53258
|
try {
|
|
53095
|
-
return JSON.parse(
|
|
53259
|
+
return JSON.parse(text2);
|
|
53096
53260
|
} catch {
|
|
53097
|
-
return { detail: condenseErrorBody(
|
|
53261
|
+
return { detail: condenseErrorBody(text2) };
|
|
53098
53262
|
}
|
|
53099
53263
|
}
|
|
53100
|
-
function condenseErrorBody(
|
|
53101
|
-
const stripped = /<[a-z!/]/i.test(
|
|
53264
|
+
function condenseErrorBody(text2) {
|
|
53265
|
+
const stripped = /<[a-z!/]/i.test(text2) ? text2.replace(/<(script|style)[\s\S]*?<\/\1>/gi, " ").replace(/<[^>]*>/g, " ") : text2;
|
|
53102
53266
|
const collapsed = stripped.replace(/\s+/g, " ").trim();
|
|
53103
53267
|
if (collapsed.length <= MAX_ERROR_DETAIL_LENGTH)
|
|
53104
53268
|
return collapsed;
|
|
@@ -53125,7 +53289,12 @@ class RemoteSkillsAuthClient {
|
|
|
53125
53289
|
pollDevice(deviceCode) {
|
|
53126
53290
|
return this.request("/api/auth/device/token", { method: "POST", body: JSON.stringify({ deviceCode }) });
|
|
53127
53291
|
}
|
|
53128
|
-
async sessionClient(email, code) {
|
|
53292
|
+
async sessionClient(email, code, context) {
|
|
53293
|
+
if (context !== undefined) {
|
|
53294
|
+
const target = workspaceContext(context), apiOrigin2 = this.apiOrigin;
|
|
53295
|
+
const session = await this.switchWorkspace(email, code, target);
|
|
53296
|
+
return new RemoteSkillsClient(session.token, apiOrigin2);
|
|
53297
|
+
}
|
|
53129
53298
|
const apiOrigin = this.apiOrigin;
|
|
53130
53299
|
if (!email.includes("@") || !/^\d{6}$/.test(code))
|
|
53131
53300
|
throw new Error("Fresh email and six-digit verification code are required to manage this account");
|
|
@@ -53134,34 +53303,76 @@ class RemoteSkillsAuthClient {
|
|
|
53134
53303
|
throw new Error("The server did not return an authorized account session");
|
|
53135
53304
|
return new RemoteSkillsClient(login.token, apiOrigin);
|
|
53136
53305
|
}
|
|
53137
|
-
async
|
|
53138
|
-
|
|
53306
|
+
async listAccountWorkspaces(email, code, expectedUserId) {
|
|
53307
|
+
const login = await this.workspaceLogin(email, code, expectedUserId);
|
|
53308
|
+
const result = await new RemoteSkillsClient(login.token, login.apiOrigin).listAccountWorkspaces(login.userId);
|
|
53309
|
+
return { userId: login.userId, ...result };
|
|
53139
53310
|
}
|
|
53140
|
-
async
|
|
53141
|
-
|
|
53311
|
+
async switchWorkspace(email, code, context) {
|
|
53312
|
+
const target = workspaceContext(context);
|
|
53313
|
+
const login = await this.workspaceLogin(email, code, target.userId);
|
|
53314
|
+
return new RemoteSkillsClient(login.token, login.apiOrigin).switchWorkspace(target);
|
|
53142
53315
|
}
|
|
53143
|
-
async
|
|
53144
|
-
|
|
53316
|
+
async workspaceLogin(email, code, expectedUserId) {
|
|
53317
|
+
const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
|
|
53318
|
+
const apiOrigin = this.apiOrigin;
|
|
53319
|
+
if (typeof email !== "string" || !email.includes("@") || typeof code !== "string" || !/^\d{6}$/.test(code))
|
|
53320
|
+
throw new Error("Fresh email and six-digit verification code are required to manage this account");
|
|
53321
|
+
let response;
|
|
53322
|
+
try {
|
|
53323
|
+
response = await fetch(`${apiOrigin}/api/auth/verify`, {
|
|
53324
|
+
method: "POST",
|
|
53325
|
+
redirect: "error",
|
|
53326
|
+
credentials: "omit",
|
|
53327
|
+
signal: AbortSignal.timeout(15000),
|
|
53328
|
+
headers: { "Content-Type": "application/json" },
|
|
53329
|
+
body: JSON.stringify({ email, code })
|
|
53330
|
+
});
|
|
53331
|
+
} catch {
|
|
53332
|
+
throw new HostedApiError("Unable to verify the Skills account.");
|
|
53333
|
+
}
|
|
53334
|
+
if (!response.ok) {
|
|
53335
|
+
response.body?.cancel().catch(() => {});
|
|
53336
|
+
throw new HostedApiError("Unable to verify the Skills account.", { status: response.status });
|
|
53337
|
+
}
|
|
53338
|
+
let value;
|
|
53339
|
+
try {
|
|
53340
|
+
value = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, 64 * 1024)));
|
|
53341
|
+
} catch {
|
|
53342
|
+
throw new HostedApiError("The server returned an invalid account verification result.");
|
|
53343
|
+
}
|
|
53344
|
+
return { ...parseWorkspaceLogin(value, expected), apiOrigin };
|
|
53345
|
+
}
|
|
53346
|
+
async createApiKey(email, code, name, scopes, context) {
|
|
53347
|
+
const capturedScopes = scopes === undefined ? undefined : [...scopes];
|
|
53348
|
+
return (await this.sessionClient(email, code, context)).createApiKey(name, capturedScopes);
|
|
53349
|
+
}
|
|
53350
|
+
async listApiKeys(email, code, context) {
|
|
53351
|
+
return (await this.sessionClient(email, code, context)).listApiKeys();
|
|
53145
53352
|
}
|
|
53146
|
-
async
|
|
53147
|
-
|
|
53148
|
-
return (await this.sessionClient(email, code)).updateProfile(input);
|
|
53353
|
+
async revokeApiKey(email, code, keyId, context) {
|
|
53354
|
+
return (await this.sessionClient(email, code, context)).revokeApiKey(keyId);
|
|
53149
53355
|
}
|
|
53150
|
-
async
|
|
53151
|
-
customerNamePatch(input, "
|
|
53152
|
-
return (await this.sessionClient(email, code)).
|
|
53356
|
+
async updateProfile(email, code, input, context) {
|
|
53357
|
+
const body = customerNamePatch(input, "displayName");
|
|
53358
|
+
return (await this.sessionClient(email, code, context)).updateProfile({ displayName: body.displayName });
|
|
53359
|
+
}
|
|
53360
|
+
async updateCurrentWorkspace(email, code, input, context) {
|
|
53361
|
+
const body = customerNamePatch(input, "name");
|
|
53362
|
+
return (await this.sessionClient(email, code, context)).updateCurrentWorkspace({ name: body.name });
|
|
53153
53363
|
}
|
|
53154
|
-
async listWorkspaceMembers(email, code, options = {}) {
|
|
53364
|
+
async listWorkspaceMembers(email, code, options = {}, context) {
|
|
53155
53365
|
workspaceMembersQuery(options);
|
|
53156
|
-
|
|
53366
|
+
const captured = { ...options };
|
|
53367
|
+
return (await this.sessionClient(email, code, context)).listWorkspaceMembers(captured);
|
|
53157
53368
|
}
|
|
53158
|
-
async setWorkspaceMemberRole(email, code, membershipId, input) {
|
|
53369
|
+
async setWorkspaceMemberRole(email, code, membershipId, input, context) {
|
|
53159
53370
|
const captured = workspaceMemberRoleInput(membershipId, input);
|
|
53160
|
-
return (await this.sessionClient(email, code)).setWorkspaceMemberRole(captured.membershipId, captured.body);
|
|
53371
|
+
return (await this.sessionClient(email, code, context)).setWorkspaceMemberRole(captured.membershipId, captured.body);
|
|
53161
53372
|
}
|
|
53162
|
-
async removeWorkspaceMember(email, code, membershipId, input) {
|
|
53373
|
+
async removeWorkspaceMember(email, code, membershipId, input, context) {
|
|
53163
53374
|
const captured = workspaceMemberRemovalInput(membershipId, input);
|
|
53164
|
-
return (await this.sessionClient(email, code)).removeWorkspaceMember(captured.membershipId, captured.body);
|
|
53375
|
+
return (await this.sessionClient(email, code, context)).removeWorkspaceMember(captured.membershipId, captured.body);
|
|
53165
53376
|
}
|
|
53166
53377
|
request(path, options) {
|
|
53167
53378
|
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 +53453,8 @@ export {
|
|
|
53242
53453
|
assertDurableTarget,
|
|
53243
53454
|
assertDurableStore,
|
|
53244
53455
|
artifactStorageSeam,
|
|
53456
|
+
WorkspaceIdentityMismatchError,
|
|
53457
|
+
WorkspaceContextInputError,
|
|
53245
53458
|
SqliteSkillsStore,
|
|
53246
53459
|
SqliteRunExecutionStore,
|
|
53247
53460
|
SqliteGovernanceStore,
|
|
@@ -53250,6 +53463,7 @@ export {
|
|
|
53250
53463
|
SKILLS_APP,
|
|
53251
53464
|
SKILLS_API_URL_ENV,
|
|
53252
53465
|
SKILLS_API_KEY_ENV,
|
|
53466
|
+
RemoteWorkspaceSelectionError,
|
|
53253
53467
|
RemoteWorkspaceMemberError,
|
|
53254
53468
|
RemoteSkillsClient,
|
|
53255
53469
|
RemoteSkillsAuthClient,
|