@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/bin/mcp.js
CHANGED
|
@@ -8016,9 +8016,14 @@ var init_fleet_credentials = __esm(() => {
|
|
|
8016
8016
|
|
|
8017
8017
|
// src/lib/auth-store.ts
|
|
8018
8018
|
import { chmodSync, existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync11, renameSync as renameSync2, statSync as statSync7, unlinkSync, writeFileSync as writeFileSync5 } from "fs";
|
|
8019
|
+
import { basename as basename3, dirname as dirname5, join as join13 } from "path";
|
|
8019
8020
|
function getAuthFilePath(env = process.env) {
|
|
8020
8021
|
return skillsCredentialFilePath(env);
|
|
8021
8022
|
}
|
|
8023
|
+
function getIdentityFilePath(env = process.env) {
|
|
8024
|
+
const file = skillsCredentialFilePath(env);
|
|
8025
|
+
return join13(dirname5(file), basename3(file).replace(/^credentials/, "identity") + ".json");
|
|
8026
|
+
}
|
|
8022
8027
|
function getApiUrl(action, env = process.env, options = {}) {
|
|
8023
8028
|
return requireSkillsApiOrigin(action, env, options);
|
|
8024
8029
|
}
|
|
@@ -8295,9 +8300,99 @@ var init_remote_files = __esm(() => {
|
|
|
8295
8300
|
MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
|
|
8296
8301
|
});
|
|
8297
8302
|
|
|
8303
|
+
// src/lib/remote-workspace-selection.ts
|
|
8304
|
+
function workspaceExpectedUserId(value) {
|
|
8305
|
+
if (!uuid2(value))
|
|
8306
|
+
throw new WorkspaceContextInputError;
|
|
8307
|
+
return value;
|
|
8308
|
+
}
|
|
8309
|
+
function workspaceContext(value) {
|
|
8310
|
+
if (!record3(value) || Object.keys(value).sort().join(",") !== "membershipId,userId" || !uuid2(value.userId) || !uuid2(value.membershipId))
|
|
8311
|
+
throw new WorkspaceContextInputError;
|
|
8312
|
+
return { userId: value.userId, membershipId: value.membershipId };
|
|
8313
|
+
}
|
|
8314
|
+
function invalid() {
|
|
8315
|
+
throw new Error(invalidWorkspaceResult);
|
|
8316
|
+
}
|
|
8317
|
+
function organization(v) {
|
|
8318
|
+
if (!record3(v) || !uuid2(v.id) || !text(v.slug) || !text(v.name))
|
|
8319
|
+
return invalid();
|
|
8320
|
+
return { id: v.id, slug: v.slug, name: v.name };
|
|
8321
|
+
}
|
|
8322
|
+
function parseAccountWorkspaces(value) {
|
|
8323
|
+
if (!record3(value) || !Array.isArray(value.workspaces) || !value.workspaces.length || value.workspaces.length > 1000)
|
|
8324
|
+
return invalid();
|
|
8325
|
+
const workspaces = value.workspaces.map((v) => {
|
|
8326
|
+
if (!record3(v) || !uuid2(v.membershipId) || !role(v.role) || typeof v.current !== "boolean")
|
|
8327
|
+
return invalid();
|
|
8328
|
+
return { membershipId: v.membershipId, organization: organization(v.organization), role: v.role, current: v.current };
|
|
8329
|
+
});
|
|
8330
|
+
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)
|
|
8331
|
+
return invalid();
|
|
8332
|
+
return { workspaces };
|
|
8333
|
+
}
|
|
8334
|
+
function parseWorkspaceIdentity(value, expectedUserId) {
|
|
8335
|
+
if (!record3(value))
|
|
8336
|
+
return invalid();
|
|
8337
|
+
const user = value.user;
|
|
8338
|
+
if (!record3(user) || !uuid2(user.id) || !uuid2(user.membershipId) || !text(user.email, 320) || !(user.displayName === null || text(user.displayName)) || !role(user.role))
|
|
8339
|
+
return invalid();
|
|
8340
|
+
if (user.id !== expectedUserId)
|
|
8341
|
+
throw new WorkspaceIdentityMismatchError;
|
|
8342
|
+
return { user: { id: user.id, membershipId: user.membershipId, email: user.email, displayName: user.displayName, role: user.role }, organization: organization(value.organization) };
|
|
8343
|
+
}
|
|
8344
|
+
function sessionToken(value) {
|
|
8345
|
+
if (typeof value !== "string" || !value || value.length > 8192 || /[^\x21-\x7e]/.test(value) || value.startsWith("sk_"))
|
|
8346
|
+
return invalid();
|
|
8347
|
+
return value;
|
|
8348
|
+
}
|
|
8349
|
+
function parseWorkspaceSession(value, expected) {
|
|
8350
|
+
const identity = parseWorkspaceIdentity(value, expected.userId);
|
|
8351
|
+
if (identity.user.membershipId !== expected.membershipId)
|
|
8352
|
+
throw new WorkspaceIdentityMismatchError;
|
|
8353
|
+
return { token: sessionToken(value.token), ...identity };
|
|
8354
|
+
}
|
|
8355
|
+
function parseWorkspaceLogin(value, expectedUserId) {
|
|
8356
|
+
const user = record3(value) && value.user;
|
|
8357
|
+
if (!record3(value) || !record3(user) || !uuid2(user.id))
|
|
8358
|
+
return invalid();
|
|
8359
|
+
if (expectedUserId !== undefined && user.id !== expectedUserId)
|
|
8360
|
+
throw new WorkspaceIdentityMismatchError;
|
|
8361
|
+
return { token: sessionToken(value.token), userId: user.id };
|
|
8362
|
+
}
|
|
8363
|
+
function workspaceSelectionFailure(value, status) {
|
|
8364
|
+
if (!record3(value) || typeof value.code !== "string" || !Object.hasOwn(workspaceSelectionFailures, value.code))
|
|
8365
|
+
return null;
|
|
8366
|
+
const code = value.code;
|
|
8367
|
+
return workspaceSelectionFailures[code][0] === status ? code : null;
|
|
8368
|
+
}
|
|
8369
|
+
var record3 = (v) => !!v && typeof v === "object" && !Array.isArray(v), uuid2 = (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), text = (v, max = 1024) => typeof v === "string" && !!v.trim() && v.length <= max && !/[\p{Cc}\p{Cs}\u2028\u2029]/u.test(v), role = (v) => typeof v === "string" && ["owner", "admin", "member", "viewer"].includes(v), invalidWorkspaceResult = "The server returned an invalid workspace selection result.", WorkspaceContextInputError, WorkspaceIdentityMismatchError, workspaceSelectionFailures;
|
|
8370
|
+
var init_remote_workspace_selection = __esm(() => {
|
|
8371
|
+
WorkspaceContextInputError = class WorkspaceContextInputError extends Error {
|
|
8372
|
+
constructor() {
|
|
8373
|
+
super("Provide the observed user ID and exact lowercase membership ID.");
|
|
8374
|
+
this.name = "WorkspaceContextInputError";
|
|
8375
|
+
}
|
|
8376
|
+
};
|
|
8377
|
+
WorkspaceIdentityMismatchError = class WorkspaceIdentityMismatchError extends Error {
|
|
8378
|
+
constructor() {
|
|
8379
|
+
super("The verified account does not match the requested workspace context.");
|
|
8380
|
+
this.name = "WorkspaceIdentityMismatchError";
|
|
8381
|
+
}
|
|
8382
|
+
};
|
|
8383
|
+
workspaceSelectionFailures = {
|
|
8384
|
+
INVALID_WORKSPACE_SELECTION: [400, "Provide only the exact membership ID from your workspace list."],
|
|
8385
|
+
SESSION_EXPIRED: [401, "Sign in again before selecting a workspace."],
|
|
8386
|
+
ACCOUNT_UNAVAILABLE: [403, "Account is unavailable."],
|
|
8387
|
+
INTERACTIVE_SESSION_REQUIRED: [403, "Interactive sign-in is required to select a workspace."],
|
|
8388
|
+
WORKSPACE_UNAVAILABLE: [404, "Workspace is unavailable. Refresh your workspace list."],
|
|
8389
|
+
WORKSPACE_BUSY: [503, "Workspace is busy. Refresh before retrying."]
|
|
8390
|
+
};
|
|
8391
|
+
});
|
|
8392
|
+
|
|
8298
8393
|
// src/lib/remote-workspace.ts
|
|
8299
8394
|
function workspaceMembersQuery(options = {}) {
|
|
8300
|
-
if (!
|
|
8395
|
+
if (!record4(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))
|
|
8301
8396
|
throw new Error("Use a roster limit from 1 to 100 and an unchanged continuation cursor.");
|
|
8302
8397
|
const query = new URLSearchParams;
|
|
8303
8398
|
if (options.limit !== undefined)
|
|
@@ -8313,7 +8408,7 @@ function timestamp(value) {
|
|
|
8313
8408
|
return Number.isFinite(time3) && new Date(time3).toISOString().slice(0, 23) === value.slice(0, 23);
|
|
8314
8409
|
}
|
|
8315
8410
|
function parseMember(row, fail) {
|
|
8316
|
-
if (!
|
|
8411
|
+
if (!record4(row) || !uuid3(row.membershipId) || !uuid3(row.userId) || typeof row.email !== "string" || !row.email || !(row.displayName === null || typeof row.displayName === "string") || !isRole(row.role) || !timestamp(row.createdAt))
|
|
8317
8412
|
return fail();
|
|
8318
8413
|
return {
|
|
8319
8414
|
membershipId: row.membershipId,
|
|
@@ -8325,12 +8420,12 @@ function parseMember(row, fail) {
|
|
|
8325
8420
|
};
|
|
8326
8421
|
}
|
|
8327
8422
|
function mutationInput(membershipId, input, roleChange) {
|
|
8328
|
-
if (typeof membershipId !== "string" || !
|
|
8423
|
+
if (typeof membershipId !== "string" || !uuid3(membershipId) || membershipId !== membershipId.toLowerCase() || !record4(input) || Object.keys(input).sort().join(",") !== (roleChange ? "expectedRole,role" : "expectedRole"))
|
|
8329
8424
|
throw new WorkspaceMemberInputError;
|
|
8330
|
-
const expectedRole = input.expectedRole,
|
|
8331
|
-
if (!isRole(expectedRole) || roleChange && !isRole(
|
|
8425
|
+
const expectedRole = input.expectedRole, role2 = roleChange ? input.role : undefined;
|
|
8426
|
+
if (!isRole(expectedRole) || roleChange && !isRole(role2))
|
|
8332
8427
|
throw new WorkspaceMemberInputError;
|
|
8333
|
-
return { membershipId, role, expectedRole };
|
|
8428
|
+
return { membershipId, role: role2, expectedRole };
|
|
8334
8429
|
}
|
|
8335
8430
|
function workspaceMemberRoleInput(membershipId, input) {
|
|
8336
8431
|
const value = mutationInput(membershipId, input, true);
|
|
@@ -8340,24 +8435,24 @@ function workspaceMemberRemovalInput(membershipId, input) {
|
|
|
8340
8435
|
const value = mutationInput(membershipId, input, false);
|
|
8341
8436
|
return { membershipId: value.membershipId, body: { expectedRole: value.expectedRole } };
|
|
8342
8437
|
}
|
|
8343
|
-
function parseWorkspaceMemberRoleResult(value, membershipId,
|
|
8438
|
+
function parseWorkspaceMemberRoleResult(value, membershipId, role2) {
|
|
8344
8439
|
const fail = () => {
|
|
8345
8440
|
throw new Error(invalidMemberResult);
|
|
8346
8441
|
};
|
|
8347
|
-
if (!
|
|
8442
|
+
if (!record4(value) || !uuid3(value.organizationId) || typeof value.changed !== "boolean")
|
|
8348
8443
|
return fail();
|
|
8349
8444
|
const member = parseMember(value.member, fail);
|
|
8350
|
-
if (member.membershipId !== membershipId || member.role !==
|
|
8445
|
+
if (member.membershipId !== membershipId || member.role !== role2)
|
|
8351
8446
|
return fail();
|
|
8352
8447
|
return { organizationId: value.organizationId, member, changed: value.changed };
|
|
8353
8448
|
}
|
|
8354
8449
|
function parseWorkspaceMemberRemovalResult(value, membershipId) {
|
|
8355
|
-
if (!
|
|
8450
|
+
if (!record4(value) || !uuid3(value.organizationId) || value.membershipId !== membershipId || value.removed !== true || typeof value.alreadyRemoved !== "boolean")
|
|
8356
8451
|
throw new Error(invalidMemberResult);
|
|
8357
8452
|
return { organizationId: value.organizationId, membershipId, removed: true, alreadyRemoved: value.alreadyRemoved };
|
|
8358
8453
|
}
|
|
8359
8454
|
function workspaceMemberFailure(value, status) {
|
|
8360
|
-
if (!
|
|
8455
|
+
if (!record4(value) || typeof value.code !== "string" || !Object.hasOwn(workspaceMemberFailures, value.code))
|
|
8361
8456
|
return null;
|
|
8362
8457
|
const code = value.code;
|
|
8363
8458
|
return workspaceMemberFailures[code][0] === status ? code : null;
|
|
@@ -8366,14 +8461,14 @@ function parseWorkspaceMembersPage(value) {
|
|
|
8366
8461
|
const fail = () => {
|
|
8367
8462
|
throw new Error("The server returned an invalid workspace roster.");
|
|
8368
8463
|
};
|
|
8369
|
-
if (!
|
|
8464
|
+
if (!record4(value) || !uuid3(value.organizationId) || !Array.isArray(value.members) || value.members.length > 100 || !(value.nextCursor === null || cursor(value.nextCursor)) || !value.members.length && value.nextCursor !== null)
|
|
8370
8465
|
return fail();
|
|
8371
8466
|
const members = value.members.map((row) => parseMember(row, fail));
|
|
8372
8467
|
if (new Set(members.map((row) => row.membershipId)).size !== members.length)
|
|
8373
8468
|
return fail();
|
|
8374
8469
|
return { organizationId: value.organizationId, members, nextCursor: value.nextCursor };
|
|
8375
8470
|
}
|
|
8376
|
-
var
|
|
8471
|
+
var record4 = (value) => !!value && typeof value === "object" && !Array.isArray(value), cursor = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,512}$/.test(value), uuid3 = (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), isRole = (value) => typeof value === "string" && ["owner", "admin", "member", "viewer"].includes(value), WorkspaceMemberInputError, invalidMemberResult = "The server returned an invalid workspace member result. Refresh the roster before another action.", workspaceMemberFailures;
|
|
8377
8472
|
var init_remote_workspace = __esm(() => {
|
|
8378
8473
|
WorkspaceMemberInputError = class WorkspaceMemberInputError extends Error {
|
|
8379
8474
|
constructor() {
|
|
@@ -8395,6 +8490,75 @@ var init_remote_workspace = __esm(() => {
|
|
|
8395
8490
|
};
|
|
8396
8491
|
});
|
|
8397
8492
|
|
|
8493
|
+
// src/lib/remote-workspace-leave.ts
|
|
8494
|
+
function workspaceLeaveInput(context, input) {
|
|
8495
|
+
const target = workspaceContext(context);
|
|
8496
|
+
if (!input || typeof input !== "object" || Array.isArray(input) || Object.keys(input).sort().join(",") !== "confirm,expectedRole" || input.confirm !== true)
|
|
8497
|
+
throw new WorkspaceLeaveInputError;
|
|
8498
|
+
const captured = workspaceMemberRemovalInput(target.membershipId, { expectedRole: input.expectedRole });
|
|
8499
|
+
return {
|
|
8500
|
+
context: target,
|
|
8501
|
+
input: { expectedRole: captured.body.expectedRole, confirm: true },
|
|
8502
|
+
body: { membershipId: target.membershipId, expectedRole: captured.body.expectedRole }
|
|
8503
|
+
};
|
|
8504
|
+
}
|
|
8505
|
+
function workspaceLeaveFailure(value, status) {
|
|
8506
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
8507
|
+
return null;
|
|
8508
|
+
const code = value.code;
|
|
8509
|
+
return typeof code === "string" && Object.hasOwn(workspaceLeaveFailures, code) && workspaceLeaveFailures[code][0] === status ? code : null;
|
|
8510
|
+
}
|
|
8511
|
+
function parseWorkspaceLeaveResult(value, membershipId, organizationId) {
|
|
8512
|
+
const row = value;
|
|
8513
|
+
if (!row || typeof row !== "object" || Array.isArray(row) || row.membershipId !== membershipId || row.organizationId !== organizationId || row.removed !== true || row.signInRequired !== true)
|
|
8514
|
+
throw new RemoteWorkspaceLeaveUnconfirmedError;
|
|
8515
|
+
return { membershipId, organizationId, removed: true, signInRequired: true };
|
|
8516
|
+
}
|
|
8517
|
+
function workspaceLeaveProfileContext(membershipId, userId, profile) {
|
|
8518
|
+
const observed = profile === undefined ? undefined : workspaceContext(profile);
|
|
8519
|
+
const target = workspaceContext({ userId: userId ?? observed?.userId, membershipId });
|
|
8520
|
+
if (observed && (observed.userId !== target.userId || observed.membershipId !== target.membershipId))
|
|
8521
|
+
throw new WorkspaceIdentityMismatchError;
|
|
8522
|
+
return target;
|
|
8523
|
+
}
|
|
8524
|
+
var WorkspaceLeaveInputError, workspaceLeaveFailures, RemoteWorkspaceLeaveError, RemoteWorkspaceLeaveUnconfirmedError;
|
|
8525
|
+
var init_remote_workspace_leave = __esm(() => {
|
|
8526
|
+
init_remote_workspace_selection();
|
|
8527
|
+
init_remote_workspace();
|
|
8528
|
+
WorkspaceLeaveInputError = class WorkspaceLeaveInputError extends Error {
|
|
8529
|
+
constructor() {
|
|
8530
|
+
super("Confirm leaving the exact observed user and membership with its expected role.");
|
|
8531
|
+
this.name = "WorkspaceLeaveInputError";
|
|
8532
|
+
}
|
|
8533
|
+
};
|
|
8534
|
+
workspaceLeaveFailures = {
|
|
8535
|
+
INVALID_REQUEST: [400, "Provide the exact current membership and expected role."],
|
|
8536
|
+
ACCOUNT_UNAVAILABLE: [403, "Account is unavailable."],
|
|
8537
|
+
INTERACTIVE_SESSION_REQUIRED: [403, "Fresh interactive sign-in is required to leave a workspace."],
|
|
8538
|
+
MEMBERSHIP_ROLE_CHANGED: [409, "Your role changed. Sign in and inspect the workspace before leaving."],
|
|
8539
|
+
LAST_OWNER_REQUIRED: [409, "The workspace must retain another active owner."],
|
|
8540
|
+
LAST_WORKSPACE_REQUIRED: [409, "Another available workspace is required before leaving."],
|
|
8541
|
+
MEMBERSHIP_BUSY: [503, "Membership is busy. Inspect the workspace before another leave action."]
|
|
8542
|
+
};
|
|
8543
|
+
RemoteWorkspaceLeaveError = class RemoteWorkspaceLeaveError extends Error {
|
|
8544
|
+
code;
|
|
8545
|
+
status;
|
|
8546
|
+
constructor(code) {
|
|
8547
|
+
super(workspaceLeaveFailures[code][1]);
|
|
8548
|
+
this.code = code;
|
|
8549
|
+
this.name = "RemoteWorkspaceLeaveError";
|
|
8550
|
+
this.status = workspaceLeaveFailures[code][0];
|
|
8551
|
+
}
|
|
8552
|
+
};
|
|
8553
|
+
RemoteWorkspaceLeaveUnconfirmedError = class RemoteWorkspaceLeaveUnconfirmedError extends Error {
|
|
8554
|
+
code = "WORKSPACE_LEAVE_UNCONFIRMED";
|
|
8555
|
+
constructor() {
|
|
8556
|
+
super("The leave outcome is unconfirmed. Sign in again and inspect available memberships before another action. Do not retry automatically; saved credentials are unchanged.");
|
|
8557
|
+
this.name = "RemoteWorkspaceLeaveUnconfirmedError";
|
|
8558
|
+
}
|
|
8559
|
+
};
|
|
8560
|
+
});
|
|
8561
|
+
|
|
8398
8562
|
// src/lib/remote-account.ts
|
|
8399
8563
|
function creditCount(value) {
|
|
8400
8564
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0 || value > 2147483647) {
|
|
@@ -8498,11 +8662,11 @@ function parseUpdatedProfile(value) {
|
|
|
8498
8662
|
return { user: { id: user.id, email: user.email, displayName: user.displayName, role: user.role } };
|
|
8499
8663
|
}
|
|
8500
8664
|
function parseUpdatedWorkspace(value) {
|
|
8501
|
-
const
|
|
8502
|
-
if (!isRecord4(
|
|
8665
|
+
const organization2 = isRecord4(value) && value.organization;
|
|
8666
|
+
if (!isRecord4(organization2) || !string4(organization2.id) || !string4(organization2.slug) || !string4(organization2.name)) {
|
|
8503
8667
|
throw new Error("The server returned an invalid workspace.");
|
|
8504
8668
|
}
|
|
8505
|
-
return { organization: { id:
|
|
8669
|
+
return { organization: { id: organization2.id, slug: organization2.slug, name: organization2.name } };
|
|
8506
8670
|
}
|
|
8507
8671
|
|
|
8508
8672
|
// src/lib/remote-client.ts
|
|
@@ -8510,6 +8674,7 @@ var exports_remote_client = {};
|
|
|
8510
8674
|
__export(exports_remote_client, {
|
|
8511
8675
|
createRemoteSkillsClientReadOnly: () => createRemoteSkillsClientReadOnly,
|
|
8512
8676
|
createRemoteSkillsClient: () => createRemoteSkillsClient,
|
|
8677
|
+
RemoteWorkspaceSelectionError: () => RemoteWorkspaceSelectionError,
|
|
8513
8678
|
RemoteWorkspaceMemberError: () => RemoteWorkspaceMemberError,
|
|
8514
8679
|
RemoteSkillsClient: () => RemoteSkillsClient,
|
|
8515
8680
|
RemoteRouteUnsupportedError: () => RemoteRouteUnsupportedError,
|
|
@@ -8529,6 +8694,7 @@ class RemoteSkillsClient {
|
|
|
8529
8694
|
return fetch(`${this.apiUrl}${path}`, {
|
|
8530
8695
|
...options,
|
|
8531
8696
|
redirect: "error",
|
|
8697
|
+
credentials: "omit",
|
|
8532
8698
|
signal: options?.signal ?? AbortSignal.timeout(15000),
|
|
8533
8699
|
headers: {
|
|
8534
8700
|
Authorization: `Bearer ${this.apiKey}`,
|
|
@@ -8635,6 +8801,65 @@ class RemoteSkillsClient {
|
|
|
8635
8801
|
async getIdentity() {
|
|
8636
8802
|
return (await this.requestNewRoute("/api/auth/whoami")).json();
|
|
8637
8803
|
}
|
|
8804
|
+
async listAccountWorkspaces(expectedUserId) {
|
|
8805
|
+
const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
|
|
8806
|
+
const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
|
|
8807
|
+
let identity;
|
|
8808
|
+
if (expected !== undefined) {
|
|
8809
|
+
const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
|
|
8810
|
+
if (!value || typeof value !== "object" || value.authMethod !== "jwt")
|
|
8811
|
+
throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces", "INTERACTIVE_SESSION_REQUIRED");
|
|
8812
|
+
identity = parseWorkspaceIdentity(value, expected);
|
|
8813
|
+
}
|
|
8814
|
+
const result = parseAccountWorkspaces(await connection.requestWorkspaceSelection("/api/v1/account/workspaces"));
|
|
8815
|
+
const current = result.workspaces.find((workspace) => workspace.current);
|
|
8816
|
+
if (identity && (current.membershipId !== identity.user.membershipId || current.organization.id !== identity.organization.id))
|
|
8817
|
+
throw new WorkspaceIdentityMismatchError;
|
|
8818
|
+
return result;
|
|
8819
|
+
}
|
|
8820
|
+
async switchWorkspace(context) {
|
|
8821
|
+
const target = workspaceContext(context);
|
|
8822
|
+
const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
|
|
8823
|
+
const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
|
|
8824
|
+
if (!value || typeof value !== "object" || value.authMethod !== "jwt")
|
|
8825
|
+
throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces/switch", "INTERACTIVE_SESSION_REQUIRED");
|
|
8826
|
+
parseWorkspaceIdentity(value, target.userId);
|
|
8827
|
+
const selected = parseWorkspaceSession(await connection.requestWorkspaceSelection("/api/v1/account/workspaces/switch", {
|
|
8828
|
+
method: "POST",
|
|
8829
|
+
body: JSON.stringify({ membershipId: target.membershipId })
|
|
8830
|
+
}), target);
|
|
8831
|
+
const verified = await new RemoteSkillsClient(selected.token, connection.apiUrl).requestWorkspaceSelection("/api/auth/whoami");
|
|
8832
|
+
if (!verified || typeof verified !== "object" || verified.authMethod !== "jwt")
|
|
8833
|
+
throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces/switch", "INTERACTIVE_SESSION_REQUIRED");
|
|
8834
|
+
const identity = parseWorkspaceIdentity(verified, target.userId);
|
|
8835
|
+
if (identity.user.membershipId !== target.membershipId || identity.organization.id !== selected.organization.id)
|
|
8836
|
+
throw new WorkspaceIdentityMismatchError;
|
|
8837
|
+
return { token: selected.token, ...identity };
|
|
8838
|
+
}
|
|
8839
|
+
async requestWorkspaceSelection(path, options) {
|
|
8840
|
+
let response;
|
|
8841
|
+
try {
|
|
8842
|
+
response = await this.request(path, { ...options, credentials: "omit" });
|
|
8843
|
+
} catch {
|
|
8844
|
+
throw new Error("Unable to reach the Skills workspace API.");
|
|
8845
|
+
}
|
|
8846
|
+
let value;
|
|
8847
|
+
try {
|
|
8848
|
+
value = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, response.ok ? 1024 * 1024 : 4096)));
|
|
8849
|
+
} catch {
|
|
8850
|
+
if (response.ok)
|
|
8851
|
+
throw new Error(invalidWorkspaceResult);
|
|
8852
|
+
}
|
|
8853
|
+
if (!response.ok) {
|
|
8854
|
+
const code = workspaceSelectionFailure(value, response.status);
|
|
8855
|
+
if (code)
|
|
8856
|
+
throw new RemoteWorkspaceSelectionError(path, code);
|
|
8857
|
+
if (response.status === 404 || response.status === 405)
|
|
8858
|
+
throw new RemoteRouteUnsupportedError(path, response.status, this.apiUrl);
|
|
8859
|
+
throw new RemoteRequestError(path, response.status);
|
|
8860
|
+
}
|
|
8861
|
+
return value;
|
|
8862
|
+
}
|
|
8638
8863
|
async updateProfile(input) {
|
|
8639
8864
|
const body = customerNamePatch(input, "displayName");
|
|
8640
8865
|
return parseUpdatedProfile(await (await this.requestNewRoute("/api/v1/account/profile", { method: "PATCH", body: JSON.stringify(body) })).json());
|
|
@@ -8687,6 +8912,30 @@ class RemoteSkillsClient {
|
|
|
8687
8912
|
}
|
|
8688
8913
|
return value;
|
|
8689
8914
|
}
|
|
8915
|
+
async leaveWorkspace(context, input) {
|
|
8916
|
+
const captured = workspaceLeaveInput(context, input);
|
|
8917
|
+
const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
|
|
8918
|
+
const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
|
|
8919
|
+
if (!value || typeof value !== "object" || value.authMethod !== "jwt")
|
|
8920
|
+
throw new RemoteWorkspaceLeaveError("INTERACTIVE_SESSION_REQUIRED");
|
|
8921
|
+
const identity = parseWorkspaceIdentity(value, captured.context.userId);
|
|
8922
|
+
if (identity.user.membershipId !== captured.context.membershipId)
|
|
8923
|
+
throw new WorkspaceIdentityMismatchError;
|
|
8924
|
+
let response, body;
|
|
8925
|
+
try {
|
|
8926
|
+
response = await connection.request("/api/v1/account/workspaces/leave", { method: "POST", body: JSON.stringify(captured.body) });
|
|
8927
|
+
body = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, 4096)));
|
|
8928
|
+
} catch {
|
|
8929
|
+
throw new RemoteWorkspaceLeaveUnconfirmedError;
|
|
8930
|
+
}
|
|
8931
|
+
if (!response.ok) {
|
|
8932
|
+
const code = workspaceLeaveFailure(body, response.status);
|
|
8933
|
+
if (code)
|
|
8934
|
+
throw new RemoteWorkspaceLeaveError(code);
|
|
8935
|
+
throw new RemoteWorkspaceLeaveUnconfirmedError;
|
|
8936
|
+
}
|
|
8937
|
+
return parseWorkspaceLeaveResult(body, captured.context.membershipId, identity.organization.id);
|
|
8938
|
+
}
|
|
8690
8939
|
async listApiKeys() {
|
|
8691
8940
|
return this.arrayResponse("/api/auth/keys");
|
|
8692
8941
|
}
|
|
@@ -8937,13 +9186,13 @@ class RemoteSkillsClient {
|
|
|
8937
9186
|
return normalizeUpdatedSincePage(await response.json());
|
|
8938
9187
|
}
|
|
8939
9188
|
}
|
|
8940
|
-
function requireOptionalString(
|
|
8941
|
-
if (
|
|
9189
|
+
function requireOptionalString(record5, field) {
|
|
9190
|
+
if (record5[field] === undefined)
|
|
8942
9191
|
return;
|
|
8943
|
-
if (typeof
|
|
9192
|
+
if (typeof record5[field] !== "string") {
|
|
8944
9193
|
throw new Error(`Remote payload did not match the expected contract (${field} must be a string when present)`);
|
|
8945
9194
|
}
|
|
8946
|
-
return
|
|
9195
|
+
return record5[field];
|
|
8947
9196
|
}
|
|
8948
9197
|
function isVersionRecord(value) {
|
|
8949
9198
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -8965,19 +9214,19 @@ function normalizePin(entry) {
|
|
|
8965
9214
|
if (!entry || typeof entry !== "object") {
|
|
8966
9215
|
throw new Error("Remote pin payload did not match the expected contract (expected an object)");
|
|
8967
9216
|
}
|
|
8968
|
-
const
|
|
8969
|
-
const slug = typeof
|
|
9217
|
+
const record5 = entry;
|
|
9218
|
+
const slug = typeof record5.slug === "string" && record5.slug.trim() ? record5.slug.trim() : undefined;
|
|
8970
9219
|
if (!slug) {
|
|
8971
9220
|
throw new Error("Remote pin payload did not match the expected contract (missing slug)");
|
|
8972
9221
|
}
|
|
8973
9222
|
let metadata;
|
|
8974
|
-
if (
|
|
8975
|
-
if (!
|
|
9223
|
+
if (record5.metadata !== undefined) {
|
|
9224
|
+
if (!record5.metadata || typeof record5.metadata !== "object" || Array.isArray(record5.metadata)) {
|
|
8976
9225
|
throw new Error("Remote pin payload did not match the expected contract (metadata must be a JSON object when present)");
|
|
8977
9226
|
}
|
|
8978
|
-
metadata =
|
|
9227
|
+
metadata = record5.metadata;
|
|
8979
9228
|
}
|
|
8980
|
-
const pinnedAt = requireOptionalString(
|
|
9229
|
+
const pinnedAt = requireOptionalString(record5, "pinnedAt");
|
|
8981
9230
|
return {
|
|
8982
9231
|
slug,
|
|
8983
9232
|
...pinnedAt !== undefined ? { pinnedAt } : {},
|
|
@@ -8994,16 +9243,16 @@ function normalizeSkillSummary(entry) {
|
|
|
8994
9243
|
if (!entry || typeof entry !== "object") {
|
|
8995
9244
|
throw new Error("Remote skill payload did not match the expected contract (expected an object)");
|
|
8996
9245
|
}
|
|
8997
|
-
const
|
|
8998
|
-
const slug = typeof
|
|
9246
|
+
const record5 = entry;
|
|
9247
|
+
const slug = typeof record5.slug === "string" && record5.slug.trim() ? record5.slug.trim() : undefined;
|
|
8999
9248
|
if (!slug) {
|
|
9000
9249
|
throw new Error("Remote skill payload did not match the expected contract (missing slug)");
|
|
9001
9250
|
}
|
|
9002
9251
|
return {
|
|
9003
9252
|
slug,
|
|
9004
|
-
...requireOptionalString(
|
|
9005
|
-
...requireOptionalString(
|
|
9006
|
-
...requireOptionalString(
|
|
9253
|
+
...requireOptionalString(record5, "name") !== undefined ? { name: requireOptionalString(record5, "name") } : {},
|
|
9254
|
+
...requireOptionalString(record5, "version") !== undefined ? { version: requireOptionalString(record5, "version") } : {},
|
|
9255
|
+
...requireOptionalString(record5, "updatedAt") !== undefined ? { updatedAt: requireOptionalString(record5, "updatedAt") } : {}
|
|
9007
9256
|
};
|
|
9008
9257
|
}
|
|
9009
9258
|
function normalizeSkillSummaryList(payload) {
|
|
@@ -9056,12 +9305,12 @@ function normalizeUpdatedSincePage(payload) {
|
|
|
9056
9305
|
if (!payload || typeof payload !== "object") {
|
|
9057
9306
|
throw new Error("Updated-since payload did not match the expected contract (expected an object)");
|
|
9058
9307
|
}
|
|
9059
|
-
const
|
|
9060
|
-
if (!Array.isArray(
|
|
9308
|
+
const record5 = payload;
|
|
9309
|
+
if (!Array.isArray(record5.skills)) {
|
|
9061
9310
|
throw new Error("Updated-since payload did not match the expected contract (missing skills array)");
|
|
9062
9311
|
}
|
|
9063
|
-
const skills =
|
|
9064
|
-
const nextCursor =
|
|
9312
|
+
const skills = record5.skills.map(normalizeSkillSummary);
|
|
9313
|
+
const nextCursor = record5.nextCursor === undefined || record5.nextCursor === null ? null : record5.nextCursor;
|
|
9065
9314
|
if (nextCursor !== null && typeof nextCursor !== "string") {
|
|
9066
9315
|
throw new Error("Updated-since payload did not match the expected contract (nextCursor must be a string or absent)");
|
|
9067
9316
|
}
|
|
@@ -9074,8 +9323,10 @@ async function createRemoteSkillsClient(env = process.env) {
|
|
|
9074
9323
|
function createRemoteSkillsClientReadOnly(env = process.env) {
|
|
9075
9324
|
return createRemoteSkillsClient(env);
|
|
9076
9325
|
}
|
|
9077
|
-
var RemoteRouteUnsupportedError, RemoteRequestError, RemoteWorkspaceMemberError, RemoteCapabilityUnavailableError, INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
|
|
9326
|
+
var RemoteRouteUnsupportedError, RemoteRequestError, RemoteWorkspaceMemberError, RemoteWorkspaceSelectionError, RemoteCapabilityUnavailableError, INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
|
|
9078
9327
|
var init_remote_client = __esm(() => {
|
|
9328
|
+
init_remote_workspace_leave();
|
|
9329
|
+
init_remote_workspace_selection();
|
|
9079
9330
|
init_remote_workspace();
|
|
9080
9331
|
init_remote_workspace();
|
|
9081
9332
|
init_auth_store();
|
|
@@ -9113,6 +9364,15 @@ var init_remote_client = __esm(() => {
|
|
|
9113
9364
|
this.message = workspaceMemberFailures[code][1];
|
|
9114
9365
|
}
|
|
9115
9366
|
};
|
|
9367
|
+
RemoteWorkspaceSelectionError = class RemoteWorkspaceSelectionError extends RemoteRequestError {
|
|
9368
|
+
code;
|
|
9369
|
+
constructor(path, code) {
|
|
9370
|
+
super(path, workspaceSelectionFailures[code][0]);
|
|
9371
|
+
this.code = code;
|
|
9372
|
+
this.name = "RemoteWorkspaceSelectionError";
|
|
9373
|
+
this.message = workspaceSelectionFailures[code][1];
|
|
9374
|
+
}
|
|
9375
|
+
};
|
|
9116
9376
|
RemoteCapabilityUnavailableError = class RemoteCapabilityUnavailableError extends RemoteRequestError {
|
|
9117
9377
|
code = "SUBSCRIPTION_CHECKOUT_UNAVAILABLE";
|
|
9118
9378
|
constructor() {
|
|
@@ -14639,7 +14899,7 @@ class StdioServerTransport {
|
|
|
14639
14899
|
// package.json
|
|
14640
14900
|
var package_default = {
|
|
14641
14901
|
name: "@hasna/skills",
|
|
14642
|
-
version: "0.5.
|
|
14902
|
+
version: "0.5.2",
|
|
14643
14903
|
description: "Skills library for AI coding agents",
|
|
14644
14904
|
type: "module",
|
|
14645
14905
|
bin: {
|
|
@@ -27945,7 +28205,7 @@ function registerDiscoveryTools(server) {
|
|
|
27945
28205
|
|
|
27946
28206
|
// src/mcp/operation-tools.ts
|
|
27947
28207
|
import { existsSync as existsSync14, readdirSync as readdirSync8, statSync as statSync9 } from "fs";
|
|
27948
|
-
import { join as
|
|
28208
|
+
import { join as join15 } from "path";
|
|
27949
28209
|
|
|
27950
28210
|
// src/lib/credential-state.ts
|
|
27951
28211
|
init_auth_store();
|
|
@@ -27999,19 +28259,19 @@ function describeCredentialState() {
|
|
|
27999
28259
|
// src/lib/run-state.ts
|
|
28000
28260
|
import { createHash as createHash2, randomBytes } from "crypto";
|
|
28001
28261
|
import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync12, readdirSync as readdirSync7, statSync as statSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
28002
|
-
import { extname, join as
|
|
28262
|
+
import { extname, join as join14, relative as relative2 } from "path";
|
|
28003
28263
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
28004
28264
|
const now = new Date;
|
|
28005
28265
|
const id = createRunId(now);
|
|
28006
28266
|
const day = now.toISOString().slice(0, 10);
|
|
28007
28267
|
const skillName = normalizeSkillName(params.skill);
|
|
28008
28268
|
const root = getProjectStateDir(targetDir);
|
|
28009
|
-
const runDir =
|
|
28010
|
-
const logsDir =
|
|
28011
|
-
const exportDir =
|
|
28269
|
+
const runDir = join14(root, "runs", day, id);
|
|
28270
|
+
const logsDir = join14(runDir, "logs");
|
|
28271
|
+
const exportDir = join14(root, "exports", skillName, id);
|
|
28012
28272
|
mkdirSync6(logsDir, { recursive: true });
|
|
28013
28273
|
mkdirSync6(exportDir, { recursive: true });
|
|
28014
|
-
mkdirSync6(
|
|
28274
|
+
mkdirSync6(join14(root, "tmp"), { recursive: true });
|
|
28015
28275
|
const record3 = {
|
|
28016
28276
|
id,
|
|
28017
28277
|
skill: skillName,
|
|
@@ -28063,22 +28323,22 @@ function updateSkillRun(context, patch) {
|
|
|
28063
28323
|
return context.record;
|
|
28064
28324
|
}
|
|
28065
28325
|
function writeRunLogs(context, stdout = "", stderr = "") {
|
|
28066
|
-
writeFileSync6(
|
|
28067
|
-
writeFileSync6(
|
|
28326
|
+
writeFileSync6(join14(context.logsDir, "stdout.log"), stdout);
|
|
28327
|
+
writeFileSync6(join14(context.logsDir, "stderr.log"), stderr);
|
|
28068
28328
|
}
|
|
28069
28329
|
function appendRunEvent(context, event, data = {}) {
|
|
28070
28330
|
const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
|
|
28071
28331
|
`;
|
|
28072
|
-
const path =
|
|
28332
|
+
const path = join14(context.runDir, "events.ndjson");
|
|
28073
28333
|
const previous = existsSync13(path) ? readFileSync12(path, "utf-8") : "";
|
|
28074
28334
|
writeFileSync6(path, previous + line);
|
|
28075
28335
|
}
|
|
28076
28336
|
function findSkillRun(runId, targetDir = process.cwd()) {
|
|
28077
|
-
const runsRoot =
|
|
28337
|
+
const runsRoot = join14(getProjectStateDir(targetDir), "runs");
|
|
28078
28338
|
if (!existsSync13(runsRoot))
|
|
28079
28339
|
return null;
|
|
28080
28340
|
for (const day of readdirSync7(runsRoot)) {
|
|
28081
|
-
const record3 = readRunRecord(
|
|
28341
|
+
const record3 = readRunRecord(join14(runsRoot, day, runId));
|
|
28082
28342
|
if (record3)
|
|
28083
28343
|
return record3;
|
|
28084
28344
|
}
|
|
@@ -28095,11 +28355,11 @@ function skillRunEnv(context) {
|
|
|
28095
28355
|
};
|
|
28096
28356
|
}
|
|
28097
28357
|
function writeRunRecord(context) {
|
|
28098
|
-
writeFileSync6(
|
|
28358
|
+
writeFileSync6(join14(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
|
|
28099
28359
|
`);
|
|
28100
28360
|
}
|
|
28101
28361
|
function writeArtifactsManifest(context, artifacts) {
|
|
28102
|
-
writeFileSync6(
|
|
28362
|
+
writeFileSync6(join14(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
|
|
28103
28363
|
`);
|
|
28104
28364
|
}
|
|
28105
28365
|
function collectRunArtifacts(context) {
|
|
@@ -28119,7 +28379,7 @@ function collectRunArtifacts(context) {
|
|
|
28119
28379
|
return artifacts.sort((a, b) => a.path.localeCompare(b.path));
|
|
28120
28380
|
}
|
|
28121
28381
|
function readRunRecord(runDir) {
|
|
28122
|
-
const path =
|
|
28382
|
+
const path = join14(runDir, "run.json");
|
|
28123
28383
|
if (!existsSync13(path))
|
|
28124
28384
|
return null;
|
|
28125
28385
|
try {
|
|
@@ -28131,7 +28391,7 @@ function readRunRecord(runDir) {
|
|
|
28131
28391
|
function walkFiles(dir) {
|
|
28132
28392
|
const files = [];
|
|
28133
28393
|
for (const entry of readdirSync7(dir)) {
|
|
28134
|
-
const full =
|
|
28394
|
+
const full = join14(dir, entry);
|
|
28135
28395
|
if (statSync8(full).isDirectory())
|
|
28136
28396
|
files.push(...walkFiles(full));
|
|
28137
28397
|
else
|
|
@@ -28638,7 +28898,7 @@ function registerOperationTools(server) {
|
|
|
28638
28898
|
if (exists) {
|
|
28639
28899
|
try {
|
|
28640
28900
|
skillCount = readdirSync8(agentSkillsPath).filter((f) => {
|
|
28641
|
-
const full =
|
|
28901
|
+
const full = join15(agentSkillsPath, f);
|
|
28642
28902
|
return !f.startsWith(".") && statSync9(full).isDirectory();
|
|
28643
28903
|
}).length;
|
|
28644
28904
|
} catch {}
|
|
@@ -28686,14 +28946,14 @@ function compactRunToolPayload(payload, detailHint) {
|
|
|
28686
28946
|
|
|
28687
28947
|
// src/lib/feedback.ts
|
|
28688
28948
|
import { appendFileSync, existsSync as existsSync15, mkdirSync as mkdirSync7 } from "fs";
|
|
28689
|
-
import { dirname as
|
|
28949
|
+
import { dirname as dirname6, join as join16 } from "path";
|
|
28690
28950
|
import { Database } from "bun:sqlite";
|
|
28691
28951
|
function getFeedbackDbPath() {
|
|
28692
|
-
return
|
|
28952
|
+
return join16(getDataDir(), "skills.db");
|
|
28693
28953
|
}
|
|
28694
28954
|
function getFeedbackDb() {
|
|
28695
28955
|
const dbPath = getFeedbackDbPath();
|
|
28696
|
-
const dir =
|
|
28956
|
+
const dir = dirname6(dbPath);
|
|
28697
28957
|
if (!existsSync15(dir))
|
|
28698
28958
|
mkdirSync7(dir, { recursive: true });
|
|
28699
28959
|
const db = new Database(dbPath);
|
|
@@ -28721,8 +28981,8 @@ function saveFeedback(input) {
|
|
|
28721
28981
|
throw new Error("Feedback message is required");
|
|
28722
28982
|
const category = input.category ?? "general";
|
|
28723
28983
|
if (isApiMode()) {
|
|
28724
|
-
const path =
|
|
28725
|
-
const dir =
|
|
28984
|
+
const path = join16(getDataDir(), "feedback.jsonl");
|
|
28985
|
+
const dir = dirname6(path);
|
|
28726
28986
|
if (!existsSync15(dir))
|
|
28727
28987
|
mkdirSync7(dir, { recursive: true });
|
|
28728
28988
|
appendFileSync(path, JSON.stringify({ message, category, email: input.email ?? null, agent: input.agent ?? null, version: input.version ?? null, createdAt: new Date().toISOString() }) + `
|
|
@@ -28870,9 +29130,9 @@ function registerResourceMetaTools(server) {
|
|
|
28870
29130
|
|
|
28871
29131
|
// src/lib/scheduler.ts
|
|
28872
29132
|
import { existsSync as existsSync16, readFileSync as readFileSync13, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8 } from "fs";
|
|
28873
|
-
import { join as
|
|
29133
|
+
import { join as join17 } from "path";
|
|
28874
29134
|
function getSchedulesPath(targetDir = process.cwd()) {
|
|
28875
|
-
return
|
|
29135
|
+
return join17(targetDir, ".skills", "schedules.json");
|
|
28876
29136
|
}
|
|
28877
29137
|
function loadSchedules(targetDir = process.cwd()) {
|
|
28878
29138
|
const path = getSchedulesPath(targetDir);
|
|
@@ -28885,7 +29145,7 @@ function loadSchedules(targetDir = process.cwd()) {
|
|
|
28885
29145
|
}
|
|
28886
29146
|
function saveSchedules(data, targetDir = process.cwd()) {
|
|
28887
29147
|
const path = getSchedulesPath(targetDir);
|
|
28888
|
-
const dir =
|
|
29148
|
+
const dir = join17(targetDir, ".skills");
|
|
28889
29149
|
if (!existsSync16(dir))
|
|
28890
29150
|
mkdirSync8(dir, { recursive: true });
|
|
28891
29151
|
writeFileSync7(path, JSON.stringify(data, null, 2));
|
|
@@ -29155,7 +29415,7 @@ import {
|
|
|
29155
29415
|
statSync as statSync10,
|
|
29156
29416
|
writeFileSync as writeFileSync8
|
|
29157
29417
|
} from "fs";
|
|
29158
|
-
import { dirname as
|
|
29418
|
+
import { dirname as dirname7, join as join18, normalize as normalize3, relative as relative3, sep as sep2 } from "path";
|
|
29159
29419
|
var SKILLS_STORAGE_TABLES = [
|
|
29160
29420
|
"skills_sync_records",
|
|
29161
29421
|
"skills_sync_cursors"
|
|
@@ -29231,7 +29491,7 @@ function getSkillsNativeStorageStatus(options = {}) {
|
|
|
29231
29491
|
local: {
|
|
29232
29492
|
dataDir: getDataDir(),
|
|
29233
29493
|
projectStateDir: getProjectStateDir(targetDir),
|
|
29234
|
-
feedbackDbPath:
|
|
29494
|
+
feedbackDbPath: join18(getDataDir(), "skills.db")
|
|
29235
29495
|
},
|
|
29236
29496
|
remote: {
|
|
29237
29497
|
databaseConfigured: Boolean(config2.databaseUrl),
|
|
@@ -29335,7 +29595,7 @@ function parsePositiveInteger(value) {
|
|
|
29335
29595
|
function walkFiles2(dir) {
|
|
29336
29596
|
const files = [];
|
|
29337
29597
|
for (const entry of readdirSync9(dir)) {
|
|
29338
|
-
const full =
|
|
29598
|
+
const full = join18(dir, entry);
|
|
29339
29599
|
const stats = statSync10(full);
|
|
29340
29600
|
if (stats.isDirectory())
|
|
29341
29601
|
files.push(...walkFiles2(full));
|
|
@@ -29391,6 +29651,9 @@ function registerStorageTools(server) {
|
|
|
29391
29651
|
}
|
|
29392
29652
|
|
|
29393
29653
|
// src/lib/remote-auth.ts
|
|
29654
|
+
init_remote_workspace_leave();
|
|
29655
|
+
init_remote_workspace_selection();
|
|
29656
|
+
init_remote_files();
|
|
29394
29657
|
init_remote_workspace();
|
|
29395
29658
|
init_remote_workspace();
|
|
29396
29659
|
init_remote_client();
|
|
@@ -29431,13 +29694,13 @@ async function requestAuthApi(instance, path, options) {
|
|
|
29431
29694
|
apiUrl: safeUrl
|
|
29432
29695
|
});
|
|
29433
29696
|
}
|
|
29434
|
-
const
|
|
29435
|
-
const body =
|
|
29697
|
+
const text2 = await res.text();
|
|
29698
|
+
const body = text2 ? parseJsonBody(text2) : {};
|
|
29436
29699
|
if (!res.ok) {
|
|
29437
|
-
const
|
|
29438
|
-
const detail = typeof
|
|
29439
|
-
const error2 = typeof
|
|
29440
|
-
const code = typeof
|
|
29700
|
+
const record5 = isRecord5(body) ? body : {};
|
|
29701
|
+
const detail = typeof record5.detail === "string" ? record5.detail : undefined;
|
|
29702
|
+
const error2 = typeof record5.error === "string" ? record5.error : undefined;
|
|
29703
|
+
const code = typeof record5.code === "string" ? record5.code : undefined;
|
|
29441
29704
|
throw new HostedApiError(detail || error2 || `${res.status} ${res.statusText}`, {
|
|
29442
29705
|
status: res.status,
|
|
29443
29706
|
code,
|
|
@@ -29448,15 +29711,15 @@ async function requestAuthApi(instance, path, options) {
|
|
|
29448
29711
|
}
|
|
29449
29712
|
return body;
|
|
29450
29713
|
}
|
|
29451
|
-
function parseJsonBody(
|
|
29714
|
+
function parseJsonBody(text2) {
|
|
29452
29715
|
try {
|
|
29453
|
-
return JSON.parse(
|
|
29716
|
+
return JSON.parse(text2);
|
|
29454
29717
|
} catch {
|
|
29455
|
-
return { detail: condenseErrorBody(
|
|
29718
|
+
return { detail: condenseErrorBody(text2) };
|
|
29456
29719
|
}
|
|
29457
29720
|
}
|
|
29458
|
-
function condenseErrorBody(
|
|
29459
|
-
const stripped = /<[a-z!/]/i.test(
|
|
29721
|
+
function condenseErrorBody(text2) {
|
|
29722
|
+
const stripped = /<[a-z!/]/i.test(text2) ? text2.replace(/<(script|style)[\s\S]*?<\/\1>/gi, " ").replace(/<[^>]*>/g, " ") : text2;
|
|
29460
29723
|
const collapsed = stripped.replace(/\s+/g, " ").trim();
|
|
29461
29724
|
if (collapsed.length <= MAX_ERROR_DETAIL_LENGTH)
|
|
29462
29725
|
return collapsed;
|
|
@@ -29483,7 +29746,12 @@ class RemoteSkillsAuthClient {
|
|
|
29483
29746
|
pollDevice(deviceCode) {
|
|
29484
29747
|
return this.request("/api/auth/device/token", { method: "POST", body: JSON.stringify({ deviceCode }) });
|
|
29485
29748
|
}
|
|
29486
|
-
async sessionClient(email2, code) {
|
|
29749
|
+
async sessionClient(email2, code, context) {
|
|
29750
|
+
if (context !== undefined) {
|
|
29751
|
+
const target = workspaceContext(context), apiOrigin2 = this.apiOrigin;
|
|
29752
|
+
const session = await this.switchWorkspace(email2, code, target);
|
|
29753
|
+
return new RemoteSkillsClient(session.token, apiOrigin2);
|
|
29754
|
+
}
|
|
29487
29755
|
const apiOrigin = this.apiOrigin;
|
|
29488
29756
|
if (!email2.includes("@") || !/^\d{6}$/.test(code))
|
|
29489
29757
|
throw new Error("Fresh email and six-digit verification code are required to manage this account");
|
|
@@ -29492,34 +29760,80 @@ class RemoteSkillsAuthClient {
|
|
|
29492
29760
|
throw new Error("The server did not return an authorized account session");
|
|
29493
29761
|
return new RemoteSkillsClient(login.token, apiOrigin);
|
|
29494
29762
|
}
|
|
29495
|
-
async
|
|
29496
|
-
|
|
29763
|
+
async listAccountWorkspaces(email2, code, expectedUserId) {
|
|
29764
|
+
const login = await this.workspaceLogin(email2, code, expectedUserId);
|
|
29765
|
+
const result = await new RemoteSkillsClient(login.token, login.apiOrigin).listAccountWorkspaces(login.userId);
|
|
29766
|
+
return { userId: login.userId, ...result };
|
|
29497
29767
|
}
|
|
29498
|
-
async
|
|
29499
|
-
|
|
29768
|
+
async switchWorkspace(email2, code, context) {
|
|
29769
|
+
const target = workspaceContext(context);
|
|
29770
|
+
const login = await this.workspaceLogin(email2, code, target.userId);
|
|
29771
|
+
return new RemoteSkillsClient(login.token, login.apiOrigin).switchWorkspace(target);
|
|
29500
29772
|
}
|
|
29501
|
-
async
|
|
29502
|
-
|
|
29773
|
+
async workspaceLogin(email2, code, expectedUserId) {
|
|
29774
|
+
const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
|
|
29775
|
+
const apiOrigin = this.apiOrigin;
|
|
29776
|
+
if (typeof email2 !== "string" || !email2.includes("@") || typeof code !== "string" || !/^\d{6}$/.test(code))
|
|
29777
|
+
throw new Error("Fresh email and six-digit verification code are required to manage this account");
|
|
29778
|
+
let response;
|
|
29779
|
+
try {
|
|
29780
|
+
response = await fetch(`${apiOrigin}/api/auth/verify`, {
|
|
29781
|
+
method: "POST",
|
|
29782
|
+
redirect: "error",
|
|
29783
|
+
credentials: "omit",
|
|
29784
|
+
signal: AbortSignal.timeout(15000),
|
|
29785
|
+
headers: { "Content-Type": "application/json" },
|
|
29786
|
+
body: JSON.stringify({ email: email2, code })
|
|
29787
|
+
});
|
|
29788
|
+
} catch {
|
|
29789
|
+
throw new HostedApiError("Unable to verify the Skills account.");
|
|
29790
|
+
}
|
|
29791
|
+
if (!response.ok) {
|
|
29792
|
+
response.body?.cancel().catch(() => {});
|
|
29793
|
+
throw new HostedApiError("Unable to verify the Skills account.", { status: response.status });
|
|
29794
|
+
}
|
|
29795
|
+
let value;
|
|
29796
|
+
try {
|
|
29797
|
+
value = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, 64 * 1024)));
|
|
29798
|
+
} catch {
|
|
29799
|
+
throw new HostedApiError("The server returned an invalid account verification result.");
|
|
29800
|
+
}
|
|
29801
|
+
return { ...parseWorkspaceLogin(value, expected), apiOrigin };
|
|
29802
|
+
}
|
|
29803
|
+
async createApiKey(email2, code, name, scopes, context) {
|
|
29804
|
+
const capturedScopes = scopes === undefined ? undefined : [...scopes];
|
|
29805
|
+
return (await this.sessionClient(email2, code, context)).createApiKey(name, capturedScopes);
|
|
29806
|
+
}
|
|
29807
|
+
async listApiKeys(email2, code, context) {
|
|
29808
|
+
return (await this.sessionClient(email2, code, context)).listApiKeys();
|
|
29809
|
+
}
|
|
29810
|
+
async revokeApiKey(email2, code, keyId, context) {
|
|
29811
|
+
return (await this.sessionClient(email2, code, context)).revokeApiKey(keyId);
|
|
29503
29812
|
}
|
|
29504
|
-
async updateProfile(email2, code, input) {
|
|
29505
|
-
customerNamePatch(input, "displayName");
|
|
29506
|
-
return (await this.sessionClient(email2, code)).updateProfile(
|
|
29813
|
+
async updateProfile(email2, code, input, context) {
|
|
29814
|
+
const body = customerNamePatch(input, "displayName");
|
|
29815
|
+
return (await this.sessionClient(email2, code, context)).updateProfile({ displayName: body.displayName });
|
|
29507
29816
|
}
|
|
29508
|
-
async updateCurrentWorkspace(email2, code, input) {
|
|
29509
|
-
customerNamePatch(input, "name");
|
|
29510
|
-
return (await this.sessionClient(email2, code)).updateCurrentWorkspace(
|
|
29817
|
+
async updateCurrentWorkspace(email2, code, input, context) {
|
|
29818
|
+
const body = customerNamePatch(input, "name");
|
|
29819
|
+
return (await this.sessionClient(email2, code, context)).updateCurrentWorkspace({ name: body.name });
|
|
29511
29820
|
}
|
|
29512
|
-
async listWorkspaceMembers(email2, code, options = {}) {
|
|
29821
|
+
async listWorkspaceMembers(email2, code, options = {}, context) {
|
|
29513
29822
|
workspaceMembersQuery(options);
|
|
29514
|
-
|
|
29823
|
+
const captured = { ...options };
|
|
29824
|
+
return (await this.sessionClient(email2, code, context)).listWorkspaceMembers(captured);
|
|
29515
29825
|
}
|
|
29516
|
-
async setWorkspaceMemberRole(email2, code, membershipId, input) {
|
|
29826
|
+
async setWorkspaceMemberRole(email2, code, membershipId, input, context) {
|
|
29517
29827
|
const captured = workspaceMemberRoleInput(membershipId, input);
|
|
29518
|
-
return (await this.sessionClient(email2, code)).setWorkspaceMemberRole(captured.membershipId, captured.body);
|
|
29828
|
+
return (await this.sessionClient(email2, code, context)).setWorkspaceMemberRole(captured.membershipId, captured.body);
|
|
29519
29829
|
}
|
|
29520
|
-
async
|
|
29830
|
+
async leaveWorkspace(email2, code, context, input) {
|
|
29831
|
+
const captured = workspaceLeaveInput(context, input);
|
|
29832
|
+
return (await this.sessionClient(email2, code, captured.context)).leaveWorkspace(captured.context, captured.input);
|
|
29833
|
+
}
|
|
29834
|
+
async removeWorkspaceMember(email2, code, membershipId, input, context) {
|
|
29521
29835
|
const captured = workspaceMemberRemovalInput(membershipId, input);
|
|
29522
|
-
return (await this.sessionClient(email2, code)).removeWorkspaceMember(captured.membershipId, captured.body);
|
|
29836
|
+
return (await this.sessionClient(email2, code, context)).removeWorkspaceMember(captured.membershipId, captured.body);
|
|
29523
29837
|
}
|
|
29524
29838
|
request(path, options) {
|
|
29525
29839
|
if (!["/api/auth/login", "/api/auth/verify", "/api/auth/device/start", "/api/auth/device/token", "/api/auth/keys", "/api/auth/whoami"].includes(path))
|
|
@@ -29528,9 +29842,89 @@ class RemoteSkillsAuthClient {
|
|
|
29528
29842
|
}
|
|
29529
29843
|
}
|
|
29530
29844
|
|
|
29531
|
-
// src/
|
|
29845
|
+
// src/lib/workspace-profile.ts
|
|
29846
|
+
import { constants as constants2, closeSync as closeSync3, fstatSync as fstatSync3, lstatSync as lstatSync4, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync2, openSync as openSync3, readFileSync as readFileSync15, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync9 } from "fs";
|
|
29532
29847
|
init_auth_store();
|
|
29848
|
+
init_fleet_credentials();
|
|
29849
|
+
init_instance_credentials();
|
|
29533
29850
|
init_remote_client();
|
|
29851
|
+
init_remote_workspace_selection();
|
|
29852
|
+
|
|
29853
|
+
class WorkspaceProfileError extends Error {
|
|
29854
|
+
}
|
|
29855
|
+
var fail = (message) => {
|
|
29856
|
+
throw new WorkspaceProfileError(message);
|
|
29857
|
+
};
|
|
29858
|
+
function stat(path) {
|
|
29859
|
+
try {
|
|
29860
|
+
return lstatSync4(path);
|
|
29861
|
+
} catch (error2) {
|
|
29862
|
+
if (error2.code === "ENOENT")
|
|
29863
|
+
return null;
|
|
29864
|
+
throw error2;
|
|
29865
|
+
}
|
|
29866
|
+
}
|
|
29867
|
+
function safeText(file) {
|
|
29868
|
+
if (stat(file) === null)
|
|
29869
|
+
return null;
|
|
29870
|
+
const fd = openSync3(file, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
|
|
29871
|
+
try {
|
|
29872
|
+
const s = fstatSync3(fd);
|
|
29873
|
+
if (!s.isFile() || s.size > 65536 || ![256, 384].includes(s.mode & 4095) || process.getuid && s.uid !== process.getuid())
|
|
29874
|
+
return fail("The selected profile must use bounded owner-only regular files.");
|
|
29875
|
+
return readFileSync15(fd, "utf8");
|
|
29876
|
+
} finally {
|
|
29877
|
+
closeSync3(fd);
|
|
29878
|
+
}
|
|
29879
|
+
}
|
|
29880
|
+
function checkIdentityMetadata(file, identity) {
|
|
29881
|
+
const text2 = safeText(file);
|
|
29882
|
+
if (text2 === null)
|
|
29883
|
+
return;
|
|
29884
|
+
let value;
|
|
29885
|
+
try {
|
|
29886
|
+
value = JSON.parse(text2);
|
|
29887
|
+
} catch {
|
|
29888
|
+
return fail("The profile identity metadata is invalid. Sign in again before managing this workspace.");
|
|
29889
|
+
}
|
|
29890
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
29891
|
+
return fail("The profile identity metadata is invalid.");
|
|
29892
|
+
for (const [key, expected] of Object.entries({ userId: identity.user.id, orgId: identity.organization.id })) {
|
|
29893
|
+
if (value[key] !== undefined && value[key] !== expected)
|
|
29894
|
+
return fail("The profile identity metadata does not match its authenticated key. Sign in again before managing this workspace.");
|
|
29895
|
+
}
|
|
29896
|
+
}
|
|
29897
|
+
async function keyIdentity(key, origin) {
|
|
29898
|
+
const value = await new RemoteSkillsClient(key, origin).getIdentity();
|
|
29899
|
+
if (value.authMethod !== "api_key")
|
|
29900
|
+
return fail("The selected credential is not a workspace API key.");
|
|
29901
|
+
const user = value.user;
|
|
29902
|
+
return parseWorkspaceIdentity(value, workspaceExpectedUserId(user?.id));
|
|
29903
|
+
}
|
|
29904
|
+
function prepareProfileWorkspace(action, source = process.env) {
|
|
29905
|
+
const env = { ...source }, origin = getApiUrl(action, env), profile = selectedSkillsProfile(env);
|
|
29906
|
+
const unchanged2 = captureSkillsCredentialFiles(skillsProfileCredentialFiles(env));
|
|
29907
|
+
return { origin, async resolve() {
|
|
29908
|
+
unchanged2();
|
|
29909
|
+
if (!profile)
|
|
29910
|
+
return { origin, context: undefined, unchanged: unchanged2 };
|
|
29911
|
+
const connection = await resolveSkillsConnection(env);
|
|
29912
|
+
if (!connection || connection.apiOrigin !== origin)
|
|
29913
|
+
return fail("The selected profile has no usable credential for this server.");
|
|
29914
|
+
const identity = await keyIdentity(connection.apiKey, origin);
|
|
29915
|
+
checkIdentityMetadata(getIdentityFilePath(env), identity);
|
|
29916
|
+
unchanged2();
|
|
29917
|
+
const context = { userId: identity.user.id, membershipId: identity.user.membershipId };
|
|
29918
|
+
return { origin, context, unchanged: unchanged2 };
|
|
29919
|
+
} };
|
|
29920
|
+
}
|
|
29921
|
+
async function captureProfileWorkspace(action, source = process.env) {
|
|
29922
|
+
return prepareProfileWorkspace(action, source).resolve();
|
|
29923
|
+
}
|
|
29924
|
+
|
|
29925
|
+
// src/mcp/remote-customer-tools.ts
|
|
29926
|
+
init_remote_client();
|
|
29927
|
+
init_remote_workspace_leave();
|
|
29534
29928
|
function registerRemoteCustomerTools(server) {
|
|
29535
29929
|
const memberRole = exports_external.enum(["owner", "admin", "member", "viewer"]);
|
|
29536
29930
|
const memberInput = {
|
|
@@ -29539,13 +29933,25 @@ function registerRemoteCustomerTools(server) {
|
|
|
29539
29933
|
email: exports_external.string().email(),
|
|
29540
29934
|
code: exports_external.string().regex(/^\d{6}$/)
|
|
29541
29935
|
};
|
|
29936
|
+
server.registerTool("leave_workspace", {
|
|
29937
|
+
title: "Leave Current Workspace",
|
|
29938
|
+
description: "Leave exactly the observed user and membership with confirm=true and fresh verification. The server enforces last-owner and last-workspace safeguards. Sign in again afterwards; no automatic retry or saved-profile deletion.",
|
|
29939
|
+
annotations: { destructiveHint: true, idempotentHint: false, readOnlyHint: false },
|
|
29940
|
+
inputSchema: exports_external.object({ ...memberInput, userId: memberInput.membershipId, confirm: exports_external.literal(true) }).strict()
|
|
29941
|
+
}, async ({ membershipId, userId, expectedRole, email: email2, code, confirm }) => {
|
|
29942
|
+
try {
|
|
29943
|
+
return mcpJson(await freshAccount("Leave workspace", (client, context) => client.leaveWorkspace(email2, code, workspaceLeaveProfileContext(membershipId, userId, context), { expectedRole, confirm })));
|
|
29944
|
+
} catch (error2) {
|
|
29945
|
+
return error2 instanceof RemoteWorkspaceLeaveError || error2 instanceof RemoteWorkspaceLeaveUnconfirmedError ? mcpError(error2.code, error2.message) : mcpError("WORKSPACE_LEAVE_UNCONFIRMED", "Leaving could not be confirmed. Check the selected profile and exact membership, sign in again and inspect available workspaces before another action. Do not retry automatically; saved credentials are unchanged.");
|
|
29946
|
+
}
|
|
29947
|
+
});
|
|
29542
29948
|
server.registerTool("set_workspace_member_role", {
|
|
29543
29949
|
title: "Set Current Workspace Member Role",
|
|
29544
29950
|
description: "Change exactly this membership incarnation with its observed expectedRole and fresh verification. The server enforces owner/admin policy. No automatic refresh or retry; saved credentials stay unchanged.",
|
|
29545
29951
|
inputSchema: exports_external.object({ ...memberInput, role: memberRole }).strict()
|
|
29546
|
-
}, async ({ membershipId, role, expectedRole, email: email2, code }) => {
|
|
29952
|
+
}, async ({ membershipId, role: role2, expectedRole, email: email2, code }) => {
|
|
29547
29953
|
try {
|
|
29548
|
-
return mcpJson(await
|
|
29954
|
+
return mcpJson(await freshAccount("Set workspace member role", (client, context) => client.setWorkspaceMemberRole(email2, code, membershipId, { role: role2, expectedRole }, context)));
|
|
29549
29955
|
} catch (error2) {
|
|
29550
29956
|
return memberError(error2);
|
|
29551
29957
|
}
|
|
@@ -29556,7 +29962,7 @@ function registerRemoteCustomerTools(server) {
|
|
|
29556
29962
|
inputSchema: exports_external.object(memberInput).strict()
|
|
29557
29963
|
}, async ({ membershipId, expectedRole, email: email2, code }) => {
|
|
29558
29964
|
try {
|
|
29559
|
-
return mcpJson(await
|
|
29965
|
+
return mcpJson(await freshAccount("Remove workspace member", (client, context) => client.removeWorkspaceMember(email2, code, membershipId, { expectedRole }, context)));
|
|
29560
29966
|
} catch (error2) {
|
|
29561
29967
|
return memberError(error2);
|
|
29562
29968
|
}
|
|
@@ -29572,7 +29978,7 @@ function registerRemoteCustomerTools(server) {
|
|
|
29572
29978
|
}).strict()
|
|
29573
29979
|
}, async ({ email: email2, code, limit, cursor: cursor2 }) => {
|
|
29574
29980
|
try {
|
|
29575
|
-
return mcpJson(await
|
|
29981
|
+
return mcpJson(await freshAccount("List workspace members", (client, context) => client.listWorkspaceMembers(email2, code, { limit, cursor: cursor2 }, context)));
|
|
29576
29982
|
} catch {
|
|
29577
29983
|
return mcpError("WORKSPACE_MEMBERS_FAILED", "Unable to list workspace members. Check the selected server, owner/admin permissions, pagination and fresh verification code.");
|
|
29578
29984
|
}
|
|
@@ -29584,8 +29990,7 @@ function registerRemoteCustomerTools(server) {
|
|
|
29584
29990
|
inputSchema: exports_external.object({ name: exports_external.string().min(1), email: exports_external.string().email(), code: exports_external.string().regex(/^\d{6}$/) }).strict()
|
|
29585
29991
|
}, async ({ name, email: email2, code }) => {
|
|
29586
29992
|
try {
|
|
29587
|
-
|
|
29588
|
-
return mcpJson(kind === "profile" ? await client.updateProfile(email2, code, { displayName: name }) : await client.updateCurrentWorkspace(email2, code, { name }));
|
|
29993
|
+
return mcpJson(await freshAccount("Update customer name", async (client, context) => kind === "profile" ? client.updateProfile(email2, code, { displayName: name }, context) : client.updateCurrentWorkspace(email2, code, { name }, context)));
|
|
29589
29994
|
} catch {
|
|
29590
29995
|
return mcpError("NAME_UPDATE_FAILED", "Unable to update the name. Check the selected server, name, permissions and fresh verification code.");
|
|
29591
29996
|
}
|
|
@@ -29607,9 +30012,9 @@ function registerRemoteCustomerTools(server) {
|
|
|
29607
30012
|
inputSchema: { email: exports_external.string().email(), code: exports_external.string().regex(/^\d{6}$/) }
|
|
29608
30013
|
}, async ({ email: email2, code }) => {
|
|
29609
30014
|
try {
|
|
29610
|
-
return mcpJson(await
|
|
29611
|
-
} catch
|
|
29612
|
-
return mcpError("KEY_LIST_FAILED",
|
|
30015
|
+
return mcpJson(await freshAccount("List API keys", (client, context) => client.listApiKeys(email2, code, context)));
|
|
30016
|
+
} catch {
|
|
30017
|
+
return mcpError("KEY_LIST_FAILED", "Unable to list API keys. Check the selected profile, server, account and fresh verification code.");
|
|
29613
30018
|
}
|
|
29614
30019
|
});
|
|
29615
30020
|
server.registerTool("revoke_api_key", {
|
|
@@ -29618,9 +30023,9 @@ function registerRemoteCustomerTools(server) {
|
|
|
29618
30023
|
inputSchema: { key_id: exports_external.string().min(1), email: exports_external.string().email(), code: exports_external.string().regex(/^\d{6}$/) }
|
|
29619
30024
|
}, async ({ key_id, email: email2, code }) => {
|
|
29620
30025
|
try {
|
|
29621
|
-
return mcpJson(await
|
|
29622
|
-
} catch
|
|
29623
|
-
return mcpError("KEY_REVOKE_FAILED",
|
|
30026
|
+
return mcpJson(await freshAccount("Revoke API key", (client, context) => client.revokeApiKey(email2, code, key_id, context)));
|
|
30027
|
+
} catch {
|
|
30028
|
+
return mcpError("KEY_REVOKE_FAILED", "Unable to revoke this API key. Check the selected profile, key, account and fresh verification code.");
|
|
29624
30029
|
}
|
|
29625
30030
|
});
|
|
29626
30031
|
server.registerTool("create_api_key", {
|
|
@@ -29628,10 +30033,11 @@ function registerRemoteCustomerTools(server) {
|
|
|
29628
30033
|
description: "Create an API key using fresh email OTP reauthentication; returns its secret once. A stored API key cannot grant this authority.",
|
|
29629
30034
|
inputSchema: { name: exports_external.string().min(1), email: exports_external.string().email(), code: exports_external.string().regex(/^\d{6}$/), scopes: exports_external.array(exports_external.string()).optional() }
|
|
29630
30035
|
}, async ({ name, email: email2, code, scopes }) => {
|
|
30036
|
+
const capturedScopes = scopes === undefined ? undefined : [...scopes];
|
|
29631
30037
|
try {
|
|
29632
|
-
return mcpJson(await
|
|
29633
|
-
} catch
|
|
29634
|
-
return mcpError("KEY_CREATION_FAILED",
|
|
30038
|
+
return mcpJson(await freshAccount("Create API key", (client, context) => client.createApiKey(email2, code, name, capturedScopes, context)));
|
|
30039
|
+
} catch {
|
|
30040
|
+
return mcpError("KEY_CREATION_FAILED", "API key creation could not be confirmed. Check the selected profile and workspace keys before retrying; a lost response may still have created a key.");
|
|
29635
30041
|
}
|
|
29636
30042
|
});
|
|
29637
30043
|
server.registerTool("quote_skill", {
|
|
@@ -29665,6 +30071,11 @@ async function callRemote(action) {
|
|
|
29665
30071
|
return mcpError("REMOTE_REQUEST_FAILED", error2 instanceof Error ? error2.message : "Skills server request failed");
|
|
29666
30072
|
}
|
|
29667
30073
|
}
|
|
30074
|
+
async function freshAccount(action, operation) {
|
|
30075
|
+
const target = await captureProfileWorkspace(action);
|
|
30076
|
+
target.unchanged();
|
|
30077
|
+
return operation(new RemoteSkillsAuthClient(target.origin), target.context);
|
|
30078
|
+
}
|
|
29668
30079
|
|
|
29669
30080
|
// src/mcp/server.ts
|
|
29670
30081
|
function buildServer() {
|