@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/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 (!record3(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))
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 (!record3(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))
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" || !uuid2(membershipId) || membershipId !== membershipId.toLowerCase() || !record3(input) || Object.keys(input).sort().join(",") !== (roleChange ? "expectedRole,role" : "expectedRole"))
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, role = roleChange ? input.role : undefined;
8331
- if (!isRole(expectedRole) || roleChange && !isRole(role))
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, role) {
8438
+ function parseWorkspaceMemberRoleResult(value, membershipId, role2) {
8344
8439
  const fail = () => {
8345
8440
  throw new Error(invalidMemberResult);
8346
8441
  };
8347
- if (!record3(value) || !uuid2(value.organizationId) || typeof value.changed !== "boolean")
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 !== 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 (!record3(value) || !uuid2(value.organizationId) || value.membershipId !== membershipId || value.removed !== true || typeof value.alreadyRemoved !== "boolean")
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 (!record3(value) || typeof value.code !== "string" || !Object.hasOwn(workspaceMemberFailures, value.code))
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 (!record3(value) || !uuid2(value.organizationId) || !Array.isArray(value.members) || value.members.length > 100 || !(value.nextCursor === null || cursor(value.nextCursor)) || !value.members.length && value.nextCursor !== null)
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 record3 = (value) => !!value && typeof value === "object" && !Array.isArray(value), cursor = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,512}$/.test(value), 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), 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;
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() {
@@ -8498,11 +8593,11 @@ function parseUpdatedProfile(value) {
8498
8593
  return { user: { id: user.id, email: user.email, displayName: user.displayName, role: user.role } };
8499
8594
  }
8500
8595
  function parseUpdatedWorkspace(value) {
8501
- const organization = isRecord4(value) && value.organization;
8502
- if (!isRecord4(organization) || !string4(organization.id) || !string4(organization.slug) || !string4(organization.name)) {
8596
+ const organization2 = isRecord4(value) && value.organization;
8597
+ if (!isRecord4(organization2) || !string4(organization2.id) || !string4(organization2.slug) || !string4(organization2.name)) {
8503
8598
  throw new Error("The server returned an invalid workspace.");
8504
8599
  }
8505
- return { organization: { id: organization.id, slug: organization.slug, name: organization.name } };
8600
+ return { organization: { id: organization2.id, slug: organization2.slug, name: organization2.name } };
8506
8601
  }
8507
8602
 
8508
8603
  // src/lib/remote-client.ts
@@ -8510,6 +8605,7 @@ var exports_remote_client = {};
8510
8605
  __export(exports_remote_client, {
8511
8606
  createRemoteSkillsClientReadOnly: () => createRemoteSkillsClientReadOnly,
8512
8607
  createRemoteSkillsClient: () => createRemoteSkillsClient,
8608
+ RemoteWorkspaceSelectionError: () => RemoteWorkspaceSelectionError,
8513
8609
  RemoteWorkspaceMemberError: () => RemoteWorkspaceMemberError,
8514
8610
  RemoteSkillsClient: () => RemoteSkillsClient,
8515
8611
  RemoteRouteUnsupportedError: () => RemoteRouteUnsupportedError,
@@ -8529,6 +8625,7 @@ class RemoteSkillsClient {
8529
8625
  return fetch(`${this.apiUrl}${path}`, {
8530
8626
  ...options,
8531
8627
  redirect: "error",
8628
+ credentials: "omit",
8532
8629
  signal: options?.signal ?? AbortSignal.timeout(15000),
8533
8630
  headers: {
8534
8631
  Authorization: `Bearer ${this.apiKey}`,
@@ -8635,6 +8732,65 @@ class RemoteSkillsClient {
8635
8732
  async getIdentity() {
8636
8733
  return (await this.requestNewRoute("/api/auth/whoami")).json();
8637
8734
  }
8735
+ async listAccountWorkspaces(expectedUserId) {
8736
+ const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
8737
+ const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
8738
+ let identity;
8739
+ if (expected !== undefined) {
8740
+ const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
8741
+ if (!value || typeof value !== "object" || value.authMethod !== "jwt")
8742
+ throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces", "INTERACTIVE_SESSION_REQUIRED");
8743
+ identity = parseWorkspaceIdentity(value, expected);
8744
+ }
8745
+ const result = parseAccountWorkspaces(await connection.requestWorkspaceSelection("/api/v1/account/workspaces"));
8746
+ const current = result.workspaces.find((workspace) => workspace.current);
8747
+ if (identity && (current.membershipId !== identity.user.membershipId || current.organization.id !== identity.organization.id))
8748
+ throw new WorkspaceIdentityMismatchError;
8749
+ return result;
8750
+ }
8751
+ async switchWorkspace(context) {
8752
+ const target = workspaceContext(context);
8753
+ const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
8754
+ const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
8755
+ if (!value || typeof value !== "object" || value.authMethod !== "jwt")
8756
+ throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces/switch", "INTERACTIVE_SESSION_REQUIRED");
8757
+ parseWorkspaceIdentity(value, target.userId);
8758
+ const selected = parseWorkspaceSession(await connection.requestWorkspaceSelection("/api/v1/account/workspaces/switch", {
8759
+ method: "POST",
8760
+ body: JSON.stringify({ membershipId: target.membershipId })
8761
+ }), target);
8762
+ const verified = await new RemoteSkillsClient(selected.token, connection.apiUrl).requestWorkspaceSelection("/api/auth/whoami");
8763
+ if (!verified || typeof verified !== "object" || verified.authMethod !== "jwt")
8764
+ throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces/switch", "INTERACTIVE_SESSION_REQUIRED");
8765
+ const identity = parseWorkspaceIdentity(verified, target.userId);
8766
+ if (identity.user.membershipId !== target.membershipId || identity.organization.id !== selected.organization.id)
8767
+ throw new WorkspaceIdentityMismatchError;
8768
+ return { token: selected.token, ...identity };
8769
+ }
8770
+ async requestWorkspaceSelection(path, options) {
8771
+ let response;
8772
+ try {
8773
+ response = await this.request(path, { ...options, credentials: "omit" });
8774
+ } catch {
8775
+ throw new Error("Unable to reach the Skills workspace API.");
8776
+ }
8777
+ let value;
8778
+ try {
8779
+ value = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, response.ok ? 1024 * 1024 : 4096)));
8780
+ } catch {
8781
+ if (response.ok)
8782
+ throw new Error(invalidWorkspaceResult);
8783
+ }
8784
+ if (!response.ok) {
8785
+ const code = workspaceSelectionFailure(value, response.status);
8786
+ if (code)
8787
+ throw new RemoteWorkspaceSelectionError(path, code);
8788
+ if (response.status === 404 || response.status === 405)
8789
+ throw new RemoteRouteUnsupportedError(path, response.status, this.apiUrl);
8790
+ throw new RemoteRequestError(path, response.status);
8791
+ }
8792
+ return value;
8793
+ }
8638
8794
  async updateProfile(input) {
8639
8795
  const body = customerNamePatch(input, "displayName");
8640
8796
  return parseUpdatedProfile(await (await this.requestNewRoute("/api/v1/account/profile", { method: "PATCH", body: JSON.stringify(body) })).json());
@@ -8937,13 +9093,13 @@ class RemoteSkillsClient {
8937
9093
  return normalizeUpdatedSincePage(await response.json());
8938
9094
  }
8939
9095
  }
8940
- function requireOptionalString(record4, field) {
8941
- if (record4[field] === undefined)
9096
+ function requireOptionalString(record5, field) {
9097
+ if (record5[field] === undefined)
8942
9098
  return;
8943
- if (typeof record4[field] !== "string") {
9099
+ if (typeof record5[field] !== "string") {
8944
9100
  throw new Error(`Remote payload did not match the expected contract (${field} must be a string when present)`);
8945
9101
  }
8946
- return record4[field];
9102
+ return record5[field];
8947
9103
  }
8948
9104
  function isVersionRecord(value) {
8949
9105
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -8965,19 +9121,19 @@ function normalizePin(entry) {
8965
9121
  if (!entry || typeof entry !== "object") {
8966
9122
  throw new Error("Remote pin payload did not match the expected contract (expected an object)");
8967
9123
  }
8968
- const record4 = entry;
8969
- const slug = typeof record4.slug === "string" && record4.slug.trim() ? record4.slug.trim() : undefined;
9124
+ const record5 = entry;
9125
+ const slug = typeof record5.slug === "string" && record5.slug.trim() ? record5.slug.trim() : undefined;
8970
9126
  if (!slug) {
8971
9127
  throw new Error("Remote pin payload did not match the expected contract (missing slug)");
8972
9128
  }
8973
9129
  let metadata;
8974
- if (record4.metadata !== undefined) {
8975
- if (!record4.metadata || typeof record4.metadata !== "object" || Array.isArray(record4.metadata)) {
9130
+ if (record5.metadata !== undefined) {
9131
+ if (!record5.metadata || typeof record5.metadata !== "object" || Array.isArray(record5.metadata)) {
8976
9132
  throw new Error("Remote pin payload did not match the expected contract (metadata must be a JSON object when present)");
8977
9133
  }
8978
- metadata = record4.metadata;
9134
+ metadata = record5.metadata;
8979
9135
  }
8980
- const pinnedAt = requireOptionalString(record4, "pinnedAt");
9136
+ const pinnedAt = requireOptionalString(record5, "pinnedAt");
8981
9137
  return {
8982
9138
  slug,
8983
9139
  ...pinnedAt !== undefined ? { pinnedAt } : {},
@@ -8994,16 +9150,16 @@ function normalizeSkillSummary(entry) {
8994
9150
  if (!entry || typeof entry !== "object") {
8995
9151
  throw new Error("Remote skill payload did not match the expected contract (expected an object)");
8996
9152
  }
8997
- const record4 = entry;
8998
- const slug = typeof record4.slug === "string" && record4.slug.trim() ? record4.slug.trim() : undefined;
9153
+ const record5 = entry;
9154
+ const slug = typeof record5.slug === "string" && record5.slug.trim() ? record5.slug.trim() : undefined;
8999
9155
  if (!slug) {
9000
9156
  throw new Error("Remote skill payload did not match the expected contract (missing slug)");
9001
9157
  }
9002
9158
  return {
9003
9159
  slug,
9004
- ...requireOptionalString(record4, "name") !== undefined ? { name: requireOptionalString(record4, "name") } : {},
9005
- ...requireOptionalString(record4, "version") !== undefined ? { version: requireOptionalString(record4, "version") } : {},
9006
- ...requireOptionalString(record4, "updatedAt") !== undefined ? { updatedAt: requireOptionalString(record4, "updatedAt") } : {}
9160
+ ...requireOptionalString(record5, "name") !== undefined ? { name: requireOptionalString(record5, "name") } : {},
9161
+ ...requireOptionalString(record5, "version") !== undefined ? { version: requireOptionalString(record5, "version") } : {},
9162
+ ...requireOptionalString(record5, "updatedAt") !== undefined ? { updatedAt: requireOptionalString(record5, "updatedAt") } : {}
9007
9163
  };
9008
9164
  }
9009
9165
  function normalizeSkillSummaryList(payload) {
@@ -9056,12 +9212,12 @@ function normalizeUpdatedSincePage(payload) {
9056
9212
  if (!payload || typeof payload !== "object") {
9057
9213
  throw new Error("Updated-since payload did not match the expected contract (expected an object)");
9058
9214
  }
9059
- const record4 = payload;
9060
- if (!Array.isArray(record4.skills)) {
9215
+ const record5 = payload;
9216
+ if (!Array.isArray(record5.skills)) {
9061
9217
  throw new Error("Updated-since payload did not match the expected contract (missing skills array)");
9062
9218
  }
9063
- const skills = record4.skills.map(normalizeSkillSummary);
9064
- const nextCursor = record4.nextCursor === undefined || record4.nextCursor === null ? null : record4.nextCursor;
9219
+ const skills = record5.skills.map(normalizeSkillSummary);
9220
+ const nextCursor = record5.nextCursor === undefined || record5.nextCursor === null ? null : record5.nextCursor;
9065
9221
  if (nextCursor !== null && typeof nextCursor !== "string") {
9066
9222
  throw new Error("Updated-since payload did not match the expected contract (nextCursor must be a string or absent)");
9067
9223
  }
@@ -9074,8 +9230,9 @@ async function createRemoteSkillsClient(env = process.env) {
9074
9230
  function createRemoteSkillsClientReadOnly(env = process.env) {
9075
9231
  return createRemoteSkillsClient(env);
9076
9232
  }
9077
- var RemoteRouteUnsupportedError, RemoteRequestError, RemoteWorkspaceMemberError, RemoteCapabilityUnavailableError, INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
9233
+ var RemoteRouteUnsupportedError, RemoteRequestError, RemoteWorkspaceMemberError, RemoteWorkspaceSelectionError, RemoteCapabilityUnavailableError, INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
9078
9234
  var init_remote_client = __esm(() => {
9235
+ init_remote_workspace_selection();
9079
9236
  init_remote_workspace();
9080
9237
  init_remote_workspace();
9081
9238
  init_auth_store();
@@ -9113,6 +9270,15 @@ var init_remote_client = __esm(() => {
9113
9270
  this.message = workspaceMemberFailures[code][1];
9114
9271
  }
9115
9272
  };
9273
+ RemoteWorkspaceSelectionError = class RemoteWorkspaceSelectionError extends RemoteRequestError {
9274
+ code;
9275
+ constructor(path, code) {
9276
+ super(path, workspaceSelectionFailures[code][0]);
9277
+ this.code = code;
9278
+ this.name = "RemoteWorkspaceSelectionError";
9279
+ this.message = workspaceSelectionFailures[code][1];
9280
+ }
9281
+ };
9116
9282
  RemoteCapabilityUnavailableError = class RemoteCapabilityUnavailableError extends RemoteRequestError {
9117
9283
  code = "SUBSCRIPTION_CHECKOUT_UNAVAILABLE";
9118
9284
  constructor() {
@@ -14639,7 +14805,7 @@ class StdioServerTransport {
14639
14805
  // package.json
14640
14806
  var package_default = {
14641
14807
  name: "@hasna/skills",
14642
- version: "0.5.0",
14808
+ version: "0.5.1",
14643
14809
  description: "Skills library for AI coding agents",
14644
14810
  type: "module",
14645
14811
  bin: {
@@ -27945,7 +28111,7 @@ function registerDiscoveryTools(server) {
27945
28111
 
27946
28112
  // src/mcp/operation-tools.ts
27947
28113
  import { existsSync as existsSync14, readdirSync as readdirSync8, statSync as statSync9 } from "fs";
27948
- import { join as join14 } from "path";
28114
+ import { join as join15 } from "path";
27949
28115
 
27950
28116
  // src/lib/credential-state.ts
27951
28117
  init_auth_store();
@@ -27999,19 +28165,19 @@ function describeCredentialState() {
27999
28165
  // src/lib/run-state.ts
28000
28166
  import { createHash as createHash2, randomBytes } from "crypto";
28001
28167
  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 join13, relative as relative2 } from "path";
28168
+ import { extname, join as join14, relative as relative2 } from "path";
28003
28169
  function createSkillRun(params, targetDir = process.cwd()) {
28004
28170
  const now = new Date;
28005
28171
  const id = createRunId(now);
28006
28172
  const day = now.toISOString().slice(0, 10);
28007
28173
  const skillName = normalizeSkillName(params.skill);
28008
28174
  const root = getProjectStateDir(targetDir);
28009
- const runDir = join13(root, "runs", day, id);
28010
- const logsDir = join13(runDir, "logs");
28011
- const exportDir = join13(root, "exports", skillName, id);
28175
+ const runDir = join14(root, "runs", day, id);
28176
+ const logsDir = join14(runDir, "logs");
28177
+ const exportDir = join14(root, "exports", skillName, id);
28012
28178
  mkdirSync6(logsDir, { recursive: true });
28013
28179
  mkdirSync6(exportDir, { recursive: true });
28014
- mkdirSync6(join13(root, "tmp"), { recursive: true });
28180
+ mkdirSync6(join14(root, "tmp"), { recursive: true });
28015
28181
  const record3 = {
28016
28182
  id,
28017
28183
  skill: skillName,
@@ -28063,22 +28229,22 @@ function updateSkillRun(context, patch) {
28063
28229
  return context.record;
28064
28230
  }
28065
28231
  function writeRunLogs(context, stdout = "", stderr = "") {
28066
- writeFileSync6(join13(context.logsDir, "stdout.log"), stdout);
28067
- writeFileSync6(join13(context.logsDir, "stderr.log"), stderr);
28232
+ writeFileSync6(join14(context.logsDir, "stdout.log"), stdout);
28233
+ writeFileSync6(join14(context.logsDir, "stderr.log"), stderr);
28068
28234
  }
28069
28235
  function appendRunEvent(context, event, data = {}) {
28070
28236
  const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
28071
28237
  `;
28072
- const path = join13(context.runDir, "events.ndjson");
28238
+ const path = join14(context.runDir, "events.ndjson");
28073
28239
  const previous = existsSync13(path) ? readFileSync12(path, "utf-8") : "";
28074
28240
  writeFileSync6(path, previous + line);
28075
28241
  }
28076
28242
  function findSkillRun(runId, targetDir = process.cwd()) {
28077
- const runsRoot = join13(getProjectStateDir(targetDir), "runs");
28243
+ const runsRoot = join14(getProjectStateDir(targetDir), "runs");
28078
28244
  if (!existsSync13(runsRoot))
28079
28245
  return null;
28080
28246
  for (const day of readdirSync7(runsRoot)) {
28081
- const record3 = readRunRecord(join13(runsRoot, day, runId));
28247
+ const record3 = readRunRecord(join14(runsRoot, day, runId));
28082
28248
  if (record3)
28083
28249
  return record3;
28084
28250
  }
@@ -28095,11 +28261,11 @@ function skillRunEnv(context) {
28095
28261
  };
28096
28262
  }
28097
28263
  function writeRunRecord(context) {
28098
- writeFileSync6(join13(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
28264
+ writeFileSync6(join14(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
28099
28265
  `);
28100
28266
  }
28101
28267
  function writeArtifactsManifest(context, artifacts) {
28102
- writeFileSync6(join13(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
28268
+ writeFileSync6(join14(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
28103
28269
  `);
28104
28270
  }
28105
28271
  function collectRunArtifacts(context) {
@@ -28119,7 +28285,7 @@ function collectRunArtifacts(context) {
28119
28285
  return artifacts.sort((a, b) => a.path.localeCompare(b.path));
28120
28286
  }
28121
28287
  function readRunRecord(runDir) {
28122
- const path = join13(runDir, "run.json");
28288
+ const path = join14(runDir, "run.json");
28123
28289
  if (!existsSync13(path))
28124
28290
  return null;
28125
28291
  try {
@@ -28131,7 +28297,7 @@ function readRunRecord(runDir) {
28131
28297
  function walkFiles(dir) {
28132
28298
  const files = [];
28133
28299
  for (const entry of readdirSync7(dir)) {
28134
- const full = join13(dir, entry);
28300
+ const full = join14(dir, entry);
28135
28301
  if (statSync8(full).isDirectory())
28136
28302
  files.push(...walkFiles(full));
28137
28303
  else
@@ -28638,7 +28804,7 @@ function registerOperationTools(server) {
28638
28804
  if (exists) {
28639
28805
  try {
28640
28806
  skillCount = readdirSync8(agentSkillsPath).filter((f) => {
28641
- const full = join14(agentSkillsPath, f);
28807
+ const full = join15(agentSkillsPath, f);
28642
28808
  return !f.startsWith(".") && statSync9(full).isDirectory();
28643
28809
  }).length;
28644
28810
  } catch {}
@@ -28686,14 +28852,14 @@ function compactRunToolPayload(payload, detailHint) {
28686
28852
 
28687
28853
  // src/lib/feedback.ts
28688
28854
  import { appendFileSync, existsSync as existsSync15, mkdirSync as mkdirSync7 } from "fs";
28689
- import { dirname as dirname5, join as join15 } from "path";
28855
+ import { dirname as dirname6, join as join16 } from "path";
28690
28856
  import { Database } from "bun:sqlite";
28691
28857
  function getFeedbackDbPath() {
28692
- return join15(getDataDir(), "skills.db");
28858
+ return join16(getDataDir(), "skills.db");
28693
28859
  }
28694
28860
  function getFeedbackDb() {
28695
28861
  const dbPath = getFeedbackDbPath();
28696
- const dir = dirname5(dbPath);
28862
+ const dir = dirname6(dbPath);
28697
28863
  if (!existsSync15(dir))
28698
28864
  mkdirSync7(dir, { recursive: true });
28699
28865
  const db = new Database(dbPath);
@@ -28721,8 +28887,8 @@ function saveFeedback(input) {
28721
28887
  throw new Error("Feedback message is required");
28722
28888
  const category = input.category ?? "general";
28723
28889
  if (isApiMode()) {
28724
- const path = join15(getDataDir(), "feedback.jsonl");
28725
- const dir = dirname5(path);
28890
+ const path = join16(getDataDir(), "feedback.jsonl");
28891
+ const dir = dirname6(path);
28726
28892
  if (!existsSync15(dir))
28727
28893
  mkdirSync7(dir, { recursive: true });
28728
28894
  appendFileSync(path, JSON.stringify({ message, category, email: input.email ?? null, agent: input.agent ?? null, version: input.version ?? null, createdAt: new Date().toISOString() }) + `
@@ -28870,9 +29036,9 @@ function registerResourceMetaTools(server) {
28870
29036
 
28871
29037
  // src/lib/scheduler.ts
28872
29038
  import { existsSync as existsSync16, readFileSync as readFileSync13, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8 } from "fs";
28873
- import { join as join16 } from "path";
29039
+ import { join as join17 } from "path";
28874
29040
  function getSchedulesPath(targetDir = process.cwd()) {
28875
- return join16(targetDir, ".skills", "schedules.json");
29041
+ return join17(targetDir, ".skills", "schedules.json");
28876
29042
  }
28877
29043
  function loadSchedules(targetDir = process.cwd()) {
28878
29044
  const path = getSchedulesPath(targetDir);
@@ -28885,7 +29051,7 @@ function loadSchedules(targetDir = process.cwd()) {
28885
29051
  }
28886
29052
  function saveSchedules(data, targetDir = process.cwd()) {
28887
29053
  const path = getSchedulesPath(targetDir);
28888
- const dir = join16(targetDir, ".skills");
29054
+ const dir = join17(targetDir, ".skills");
28889
29055
  if (!existsSync16(dir))
28890
29056
  mkdirSync8(dir, { recursive: true });
28891
29057
  writeFileSync7(path, JSON.stringify(data, null, 2));
@@ -29155,7 +29321,7 @@ import {
29155
29321
  statSync as statSync10,
29156
29322
  writeFileSync as writeFileSync8
29157
29323
  } from "fs";
29158
- import { dirname as dirname6, join as join17, normalize as normalize3, relative as relative3, sep as sep2 } from "path";
29324
+ import { dirname as dirname7, join as join18, normalize as normalize3, relative as relative3, sep as sep2 } from "path";
29159
29325
  var SKILLS_STORAGE_TABLES = [
29160
29326
  "skills_sync_records",
29161
29327
  "skills_sync_cursors"
@@ -29231,7 +29397,7 @@ function getSkillsNativeStorageStatus(options = {}) {
29231
29397
  local: {
29232
29398
  dataDir: getDataDir(),
29233
29399
  projectStateDir: getProjectStateDir(targetDir),
29234
- feedbackDbPath: join17(getDataDir(), "skills.db")
29400
+ feedbackDbPath: join18(getDataDir(), "skills.db")
29235
29401
  },
29236
29402
  remote: {
29237
29403
  databaseConfigured: Boolean(config2.databaseUrl),
@@ -29335,7 +29501,7 @@ function parsePositiveInteger(value) {
29335
29501
  function walkFiles2(dir) {
29336
29502
  const files = [];
29337
29503
  for (const entry of readdirSync9(dir)) {
29338
- const full = join17(dir, entry);
29504
+ const full = join18(dir, entry);
29339
29505
  const stats = statSync10(full);
29340
29506
  if (stats.isDirectory())
29341
29507
  files.push(...walkFiles2(full));
@@ -29391,6 +29557,8 @@ function registerStorageTools(server) {
29391
29557
  }
29392
29558
 
29393
29559
  // src/lib/remote-auth.ts
29560
+ init_remote_workspace_selection();
29561
+ init_remote_files();
29394
29562
  init_remote_workspace();
29395
29563
  init_remote_workspace();
29396
29564
  init_remote_client();
@@ -29431,13 +29599,13 @@ async function requestAuthApi(instance, path, options) {
29431
29599
  apiUrl: safeUrl
29432
29600
  });
29433
29601
  }
29434
- const text = await res.text();
29435
- const body = text ? parseJsonBody(text) : {};
29602
+ const text2 = await res.text();
29603
+ const body = text2 ? parseJsonBody(text2) : {};
29436
29604
  if (!res.ok) {
29437
- const record4 = isRecord5(body) ? body : {};
29438
- const detail = typeof record4.detail === "string" ? record4.detail : undefined;
29439
- const error2 = typeof record4.error === "string" ? record4.error : undefined;
29440
- const code = typeof record4.code === "string" ? record4.code : undefined;
29605
+ const record5 = isRecord5(body) ? body : {};
29606
+ const detail = typeof record5.detail === "string" ? record5.detail : undefined;
29607
+ const error2 = typeof record5.error === "string" ? record5.error : undefined;
29608
+ const code = typeof record5.code === "string" ? record5.code : undefined;
29441
29609
  throw new HostedApiError(detail || error2 || `${res.status} ${res.statusText}`, {
29442
29610
  status: res.status,
29443
29611
  code,
@@ -29448,15 +29616,15 @@ async function requestAuthApi(instance, path, options) {
29448
29616
  }
29449
29617
  return body;
29450
29618
  }
29451
- function parseJsonBody(text) {
29619
+ function parseJsonBody(text2) {
29452
29620
  try {
29453
- return JSON.parse(text);
29621
+ return JSON.parse(text2);
29454
29622
  } catch {
29455
- return { detail: condenseErrorBody(text) };
29623
+ return { detail: condenseErrorBody(text2) };
29456
29624
  }
29457
29625
  }
29458
- function condenseErrorBody(text) {
29459
- const stripped = /<[a-z!/]/i.test(text) ? text.replace(/<(script|style)[\s\S]*?<\/\1>/gi, " ").replace(/<[^>]*>/g, " ") : text;
29626
+ function condenseErrorBody(text2) {
29627
+ const stripped = /<[a-z!/]/i.test(text2) ? text2.replace(/<(script|style)[\s\S]*?<\/\1>/gi, " ").replace(/<[^>]*>/g, " ") : text2;
29460
29628
  const collapsed = stripped.replace(/\s+/g, " ").trim();
29461
29629
  if (collapsed.length <= MAX_ERROR_DETAIL_LENGTH)
29462
29630
  return collapsed;
@@ -29483,7 +29651,12 @@ class RemoteSkillsAuthClient {
29483
29651
  pollDevice(deviceCode) {
29484
29652
  return this.request("/api/auth/device/token", { method: "POST", body: JSON.stringify({ deviceCode }) });
29485
29653
  }
29486
- async sessionClient(email2, code) {
29654
+ async sessionClient(email2, code, context) {
29655
+ if (context !== undefined) {
29656
+ const target = workspaceContext(context), apiOrigin2 = this.apiOrigin;
29657
+ const session = await this.switchWorkspace(email2, code, target);
29658
+ return new RemoteSkillsClient(session.token, apiOrigin2);
29659
+ }
29487
29660
  const apiOrigin = this.apiOrigin;
29488
29661
  if (!email2.includes("@") || !/^\d{6}$/.test(code))
29489
29662
  throw new Error("Fresh email and six-digit verification code are required to manage this account");
@@ -29492,34 +29665,76 @@ class RemoteSkillsAuthClient {
29492
29665
  throw new Error("The server did not return an authorized account session");
29493
29666
  return new RemoteSkillsClient(login.token, apiOrigin);
29494
29667
  }
29495
- async createApiKey(email2, code, name, scopes) {
29496
- return (await this.sessionClient(email2, code)).createApiKey(name, scopes);
29668
+ async listAccountWorkspaces(email2, code, expectedUserId) {
29669
+ const login = await this.workspaceLogin(email2, code, expectedUserId);
29670
+ const result = await new RemoteSkillsClient(login.token, login.apiOrigin).listAccountWorkspaces(login.userId);
29671
+ return { userId: login.userId, ...result };
29497
29672
  }
29498
- async listApiKeys(email2, code) {
29499
- return (await this.sessionClient(email2, code)).listApiKeys();
29673
+ async switchWorkspace(email2, code, context) {
29674
+ const target = workspaceContext(context);
29675
+ const login = await this.workspaceLogin(email2, code, target.userId);
29676
+ return new RemoteSkillsClient(login.token, login.apiOrigin).switchWorkspace(target);
29500
29677
  }
29501
- async revokeApiKey(email2, code, keyId) {
29502
- return (await this.sessionClient(email2, code)).revokeApiKey(keyId);
29678
+ async workspaceLogin(email2, code, expectedUserId) {
29679
+ const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
29680
+ const apiOrigin = this.apiOrigin;
29681
+ if (typeof email2 !== "string" || !email2.includes("@") || typeof code !== "string" || !/^\d{6}$/.test(code))
29682
+ throw new Error("Fresh email and six-digit verification code are required to manage this account");
29683
+ let response;
29684
+ try {
29685
+ response = await fetch(`${apiOrigin}/api/auth/verify`, {
29686
+ method: "POST",
29687
+ redirect: "error",
29688
+ credentials: "omit",
29689
+ signal: AbortSignal.timeout(15000),
29690
+ headers: { "Content-Type": "application/json" },
29691
+ body: JSON.stringify({ email: email2, code })
29692
+ });
29693
+ } catch {
29694
+ throw new HostedApiError("Unable to verify the Skills account.");
29695
+ }
29696
+ if (!response.ok) {
29697
+ response.body?.cancel().catch(() => {});
29698
+ throw new HostedApiError("Unable to verify the Skills account.", { status: response.status });
29699
+ }
29700
+ let value;
29701
+ try {
29702
+ value = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, 64 * 1024)));
29703
+ } catch {
29704
+ throw new HostedApiError("The server returned an invalid account verification result.");
29705
+ }
29706
+ return { ...parseWorkspaceLogin(value, expected), apiOrigin };
29707
+ }
29708
+ async createApiKey(email2, code, name, scopes, context) {
29709
+ const capturedScopes = scopes === undefined ? undefined : [...scopes];
29710
+ return (await this.sessionClient(email2, code, context)).createApiKey(name, capturedScopes);
29711
+ }
29712
+ async listApiKeys(email2, code, context) {
29713
+ return (await this.sessionClient(email2, code, context)).listApiKeys();
29714
+ }
29715
+ async revokeApiKey(email2, code, keyId, context) {
29716
+ return (await this.sessionClient(email2, code, context)).revokeApiKey(keyId);
29503
29717
  }
29504
- async updateProfile(email2, code, input) {
29505
- customerNamePatch(input, "displayName");
29506
- return (await this.sessionClient(email2, code)).updateProfile(input);
29718
+ async updateProfile(email2, code, input, context) {
29719
+ const body = customerNamePatch(input, "displayName");
29720
+ return (await this.sessionClient(email2, code, context)).updateProfile({ displayName: body.displayName });
29507
29721
  }
29508
- async updateCurrentWorkspace(email2, code, input) {
29509
- customerNamePatch(input, "name");
29510
- return (await this.sessionClient(email2, code)).updateCurrentWorkspace(input);
29722
+ async updateCurrentWorkspace(email2, code, input, context) {
29723
+ const body = customerNamePatch(input, "name");
29724
+ return (await this.sessionClient(email2, code, context)).updateCurrentWorkspace({ name: body.name });
29511
29725
  }
29512
- async listWorkspaceMembers(email2, code, options = {}) {
29726
+ async listWorkspaceMembers(email2, code, options = {}, context) {
29513
29727
  workspaceMembersQuery(options);
29514
- return (await this.sessionClient(email2, code)).listWorkspaceMembers(options);
29728
+ const captured = { ...options };
29729
+ return (await this.sessionClient(email2, code, context)).listWorkspaceMembers(captured);
29515
29730
  }
29516
- async setWorkspaceMemberRole(email2, code, membershipId, input) {
29731
+ async setWorkspaceMemberRole(email2, code, membershipId, input, context) {
29517
29732
  const captured = workspaceMemberRoleInput(membershipId, input);
29518
- return (await this.sessionClient(email2, code)).setWorkspaceMemberRole(captured.membershipId, captured.body);
29733
+ return (await this.sessionClient(email2, code, context)).setWorkspaceMemberRole(captured.membershipId, captured.body);
29519
29734
  }
29520
- async removeWorkspaceMember(email2, code, membershipId, input) {
29735
+ async removeWorkspaceMember(email2, code, membershipId, input, context) {
29521
29736
  const captured = workspaceMemberRemovalInput(membershipId, input);
29522
- return (await this.sessionClient(email2, code)).removeWorkspaceMember(captured.membershipId, captured.body);
29737
+ return (await this.sessionClient(email2, code, context)).removeWorkspaceMember(captured.membershipId, captured.body);
29523
29738
  }
29524
29739
  request(path, options) {
29525
29740
  if (!["/api/auth/login", "/api/auth/verify", "/api/auth/device/start", "/api/auth/device/token", "/api/auth/keys", "/api/auth/whoami"].includes(path))
@@ -29528,8 +29743,87 @@ class RemoteSkillsAuthClient {
29528
29743
  }
29529
29744
  }
29530
29745
 
29531
- // src/mcp/remote-customer-tools.ts
29746
+ // src/lib/workspace-profile.ts
29747
+ 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
29748
  init_auth_store();
29749
+ init_fleet_credentials();
29750
+ init_instance_credentials();
29751
+ init_remote_client();
29752
+ init_remote_workspace_selection();
29753
+
29754
+ class WorkspaceProfileError extends Error {
29755
+ }
29756
+ var fail = (message) => {
29757
+ throw new WorkspaceProfileError(message);
29758
+ };
29759
+ function stat(path) {
29760
+ try {
29761
+ return lstatSync4(path);
29762
+ } catch (error2) {
29763
+ if (error2.code === "ENOENT")
29764
+ return null;
29765
+ throw error2;
29766
+ }
29767
+ }
29768
+ function safeText(file) {
29769
+ if (stat(file) === null)
29770
+ return null;
29771
+ const fd = openSync3(file, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
29772
+ try {
29773
+ const s = fstatSync3(fd);
29774
+ if (!s.isFile() || s.size > 65536 || ![256, 384].includes(s.mode & 4095) || process.getuid && s.uid !== process.getuid())
29775
+ return fail("The selected profile must use bounded owner-only regular files.");
29776
+ return readFileSync15(fd, "utf8");
29777
+ } finally {
29778
+ closeSync3(fd);
29779
+ }
29780
+ }
29781
+ function checkIdentityMetadata(file, identity) {
29782
+ const text2 = safeText(file);
29783
+ if (text2 === null)
29784
+ return;
29785
+ let value;
29786
+ try {
29787
+ value = JSON.parse(text2);
29788
+ } catch {
29789
+ return fail("The profile identity metadata is invalid. Sign in again before managing this workspace.");
29790
+ }
29791
+ if (!value || typeof value !== "object" || Array.isArray(value))
29792
+ return fail("The profile identity metadata is invalid.");
29793
+ for (const [key, expected] of Object.entries({ userId: identity.user.id, orgId: identity.organization.id })) {
29794
+ if (value[key] !== undefined && value[key] !== expected)
29795
+ return fail("The profile identity metadata does not match its authenticated key. Sign in again before managing this workspace.");
29796
+ }
29797
+ }
29798
+ async function keyIdentity(key, origin) {
29799
+ const value = await new RemoteSkillsClient(key, origin).getIdentity();
29800
+ if (value.authMethod !== "api_key")
29801
+ return fail("The selected credential is not a workspace API key.");
29802
+ const user = value.user;
29803
+ return parseWorkspaceIdentity(value, workspaceExpectedUserId(user?.id));
29804
+ }
29805
+ function prepareProfileWorkspace(action, source = process.env) {
29806
+ const env = { ...source }, origin = getApiUrl(action, env), profile = selectedSkillsProfile(env);
29807
+ const unchanged2 = captureSkillsCredentialFiles(skillsProfileCredentialFiles(env));
29808
+ return { origin, async resolve() {
29809
+ unchanged2();
29810
+ if (!profile)
29811
+ return { origin, context: undefined, unchanged: unchanged2 };
29812
+ const connection = await resolveSkillsConnection(env);
29813
+ if (!connection || connection.apiOrigin !== origin)
29814
+ return fail("The selected profile has no usable credential for this server.");
29815
+ const identity = await keyIdentity(connection.apiKey, origin);
29816
+ checkIdentityMetadata(getIdentityFilePath(env), identity);
29817
+ unchanged2();
29818
+ const context = { userId: identity.user.id, membershipId: identity.user.membershipId };
29819
+ return { origin, context, unchanged: unchanged2 };
29820
+ } };
29821
+ }
29822
+ async function captureProfileWorkspace(action, source = process.env) {
29823
+ return prepareProfileWorkspace(action, source).resolve();
29824
+ }
29825
+
29826
+ // src/mcp/remote-customer-tools.ts
29533
29827
  init_remote_client();
29534
29828
  function registerRemoteCustomerTools(server) {
29535
29829
  const memberRole = exports_external.enum(["owner", "admin", "member", "viewer"]);
@@ -29543,9 +29837,9 @@ function registerRemoteCustomerTools(server) {
29543
29837
  title: "Set Current Workspace Member Role",
29544
29838
  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
29839
  inputSchema: exports_external.object({ ...memberInput, role: memberRole }).strict()
29546
- }, async ({ membershipId, role, expectedRole, email: email2, code }) => {
29840
+ }, async ({ membershipId, role: role2, expectedRole, email: email2, code }) => {
29547
29841
  try {
29548
- return mcpJson(await new RemoteSkillsAuthClient(getApiUrl("Set workspace member role")).setWorkspaceMemberRole(email2, code, membershipId, { role, expectedRole }));
29842
+ return mcpJson(await freshAccount("Set workspace member role", (client, context) => client.setWorkspaceMemberRole(email2, code, membershipId, { role: role2, expectedRole }, context)));
29549
29843
  } catch (error2) {
29550
29844
  return memberError(error2);
29551
29845
  }
@@ -29556,7 +29850,7 @@ function registerRemoteCustomerTools(server) {
29556
29850
  inputSchema: exports_external.object(memberInput).strict()
29557
29851
  }, async ({ membershipId, expectedRole, email: email2, code }) => {
29558
29852
  try {
29559
- return mcpJson(await new RemoteSkillsAuthClient(getApiUrl("Remove workspace member")).removeWorkspaceMember(email2, code, membershipId, { expectedRole }));
29853
+ return mcpJson(await freshAccount("Remove workspace member", (client, context) => client.removeWorkspaceMember(email2, code, membershipId, { expectedRole }, context)));
29560
29854
  } catch (error2) {
29561
29855
  return memberError(error2);
29562
29856
  }
@@ -29572,7 +29866,7 @@ function registerRemoteCustomerTools(server) {
29572
29866
  }).strict()
29573
29867
  }, async ({ email: email2, code, limit, cursor: cursor2 }) => {
29574
29868
  try {
29575
- return mcpJson(await new RemoteSkillsAuthClient(getApiUrl("List workspace members")).listWorkspaceMembers(email2, code, { limit, cursor: cursor2 }));
29869
+ return mcpJson(await freshAccount("List workspace members", (client, context) => client.listWorkspaceMembers(email2, code, { limit, cursor: cursor2 }, context)));
29576
29870
  } catch {
29577
29871
  return mcpError("WORKSPACE_MEMBERS_FAILED", "Unable to list workspace members. Check the selected server, owner/admin permissions, pagination and fresh verification code.");
29578
29872
  }
@@ -29584,8 +29878,7 @@ function registerRemoteCustomerTools(server) {
29584
29878
  inputSchema: exports_external.object({ name: exports_external.string().min(1), email: exports_external.string().email(), code: exports_external.string().regex(/^\d{6}$/) }).strict()
29585
29879
  }, async ({ name, email: email2, code }) => {
29586
29880
  try {
29587
- const client = new RemoteSkillsAuthClient(getApiUrl("Update customer name"));
29588
- return mcpJson(kind === "profile" ? await client.updateProfile(email2, code, { displayName: name }) : await client.updateCurrentWorkspace(email2, code, { name }));
29881
+ 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
29882
  } catch {
29590
29883
  return mcpError("NAME_UPDATE_FAILED", "Unable to update the name. Check the selected server, name, permissions and fresh verification code.");
29591
29884
  }
@@ -29607,9 +29900,9 @@ function registerRemoteCustomerTools(server) {
29607
29900
  inputSchema: { email: exports_external.string().email(), code: exports_external.string().regex(/^\d{6}$/) }
29608
29901
  }, async ({ email: email2, code }) => {
29609
29902
  try {
29610
- return mcpJson(await new RemoteSkillsAuthClient(getApiUrl("List API keys")).listApiKeys(email2, code));
29611
- } catch (error2) {
29612
- return mcpError("KEY_LIST_FAILED", error2.message);
29903
+ return mcpJson(await freshAccount("List API keys", (client, context) => client.listApiKeys(email2, code, context)));
29904
+ } catch {
29905
+ return mcpError("KEY_LIST_FAILED", "Unable to list API keys. Check the selected profile, server, account and fresh verification code.");
29613
29906
  }
29614
29907
  });
29615
29908
  server.registerTool("revoke_api_key", {
@@ -29618,9 +29911,9 @@ function registerRemoteCustomerTools(server) {
29618
29911
  inputSchema: { key_id: exports_external.string().min(1), email: exports_external.string().email(), code: exports_external.string().regex(/^\d{6}$/) }
29619
29912
  }, async ({ key_id, email: email2, code }) => {
29620
29913
  try {
29621
- return mcpJson(await new RemoteSkillsAuthClient(getApiUrl("Revoke API key")).revokeApiKey(email2, code, key_id));
29622
- } catch (error2) {
29623
- return mcpError("KEY_REVOKE_FAILED", error2.message);
29914
+ return mcpJson(await freshAccount("Revoke API key", (client, context) => client.revokeApiKey(email2, code, key_id, context)));
29915
+ } catch {
29916
+ return mcpError("KEY_REVOKE_FAILED", "Unable to revoke this API key. Check the selected profile, key, account and fresh verification code.");
29624
29917
  }
29625
29918
  });
29626
29919
  server.registerTool("create_api_key", {
@@ -29628,10 +29921,11 @@ function registerRemoteCustomerTools(server) {
29628
29921
  description: "Create an API key using fresh email OTP reauthentication; returns its secret once. A stored API key cannot grant this authority.",
29629
29922
  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
29923
  }, async ({ name, email: email2, code, scopes }) => {
29924
+ const capturedScopes = scopes === undefined ? undefined : [...scopes];
29631
29925
  try {
29632
- return mcpJson(await new RemoteSkillsAuthClient(getApiUrl("Create API key")).createApiKey(email2, code, name, scopes));
29633
- } catch (error2) {
29634
- return mcpError("KEY_CREATION_FAILED", error2.message);
29926
+ return mcpJson(await freshAccount("Create API key", (client, context) => client.createApiKey(email2, code, name, capturedScopes, context)));
29927
+ } catch {
29928
+ 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
29929
  }
29636
29930
  });
29637
29931
  server.registerTool("quote_skill", {
@@ -29665,6 +29959,11 @@ async function callRemote(action) {
29665
29959
  return mcpError("REMOTE_REQUEST_FAILED", error2 instanceof Error ? error2.message : "Skills server request failed");
29666
29960
  }
29667
29961
  }
29962
+ async function freshAccount(action, operation) {
29963
+ const target = await captureProfileWorkspace(action);
29964
+ target.unchanged();
29965
+ return operation(new RemoteSkillsAuthClient(target.origin), target.context);
29966
+ }
29668
29967
 
29669
29968
  // src/mcp/server.ts
29670
29969
  function buildServer() {