@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/bin/index.js CHANGED
@@ -36860,7 +36860,7 @@ var package_default;
36860
36860
  var init_package = __esm(() => {
36861
36861
  package_default = {
36862
36862
  name: "@hasna/skills",
36863
- version: "0.5.0",
36863
+ version: "0.5.2",
36864
36864
  description: "Skills library for AI coding agents",
36865
36865
  type: "module",
36866
36866
  bin: {
@@ -49045,9 +49045,99 @@ var init_read_access = __esm(() => {
49045
49045
  init_remote_registry();
49046
49046
  });
49047
49047
 
49048
+ // src/lib/remote-workspace-selection.ts
49049
+ function workspaceExpectedUserId(value) {
49050
+ if (!uuid(value))
49051
+ throw new WorkspaceContextInputError;
49052
+ return value;
49053
+ }
49054
+ function workspaceContext(value) {
49055
+ if (!record(value) || Object.keys(value).sort().join(",") !== "membershipId,userId" || !uuid(value.userId) || !uuid(value.membershipId))
49056
+ throw new WorkspaceContextInputError;
49057
+ return { userId: value.userId, membershipId: value.membershipId };
49058
+ }
49059
+ function invalid() {
49060
+ throw new Error(invalidWorkspaceResult);
49061
+ }
49062
+ function organization(v) {
49063
+ if (!record(v) || !uuid(v.id) || !text(v.slug) || !text(v.name))
49064
+ return invalid();
49065
+ return { id: v.id, slug: v.slug, name: v.name };
49066
+ }
49067
+ function parseAccountWorkspaces(value) {
49068
+ if (!record(value) || !Array.isArray(value.workspaces) || !value.workspaces.length || value.workspaces.length > 1000)
49069
+ return invalid();
49070
+ const workspaces = value.workspaces.map((v) => {
49071
+ if (!record(v) || !uuid(v.membershipId) || !role(v.role) || typeof v.current !== "boolean")
49072
+ return invalid();
49073
+ return { membershipId: v.membershipId, organization: organization(v.organization), role: v.role, current: v.current };
49074
+ });
49075
+ 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)
49076
+ return invalid();
49077
+ return { workspaces };
49078
+ }
49079
+ function parseWorkspaceIdentity(value, expectedUserId) {
49080
+ if (!record(value))
49081
+ return invalid();
49082
+ const user = value.user;
49083
+ if (!record(user) || !uuid(user.id) || !uuid(user.membershipId) || !text(user.email, 320) || !(user.displayName === null || text(user.displayName)) || !role(user.role))
49084
+ return invalid();
49085
+ if (user.id !== expectedUserId)
49086
+ throw new WorkspaceIdentityMismatchError;
49087
+ return { user: { id: user.id, membershipId: user.membershipId, email: user.email, displayName: user.displayName, role: user.role }, organization: organization(value.organization) };
49088
+ }
49089
+ function sessionToken(value) {
49090
+ if (typeof value !== "string" || !value || value.length > 8192 || /[^\x21-\x7e]/.test(value) || value.startsWith("sk_"))
49091
+ return invalid();
49092
+ return value;
49093
+ }
49094
+ function parseWorkspaceSession(value, expected) {
49095
+ const identity2 = parseWorkspaceIdentity(value, expected.userId);
49096
+ if (identity2.user.membershipId !== expected.membershipId)
49097
+ throw new WorkspaceIdentityMismatchError;
49098
+ return { token: sessionToken(value.token), ...identity2 };
49099
+ }
49100
+ function parseWorkspaceLogin(value, expectedUserId) {
49101
+ const user = record(value) && value.user;
49102
+ if (!record(value) || !record(user) || !uuid(user.id))
49103
+ return invalid();
49104
+ if (expectedUserId !== undefined && user.id !== expectedUserId)
49105
+ throw new WorkspaceIdentityMismatchError;
49106
+ return { token: sessionToken(value.token), userId: user.id };
49107
+ }
49108
+ function workspaceSelectionFailure(value, status) {
49109
+ if (!record(value) || typeof value.code !== "string" || !Object.hasOwn(workspaceSelectionFailures, value.code))
49110
+ return null;
49111
+ const code = value.code;
49112
+ return workspaceSelectionFailures[code][0] === status ? code : null;
49113
+ }
49114
+ var record = (v) => !!v && typeof v === "object" && !Array.isArray(v), uuid = (v) => typeof v === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v), text = (v, max2 = 1024) => typeof v === "string" && !!v.trim() && v.length <= max2 && !/[\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;
49115
+ var init_remote_workspace_selection = __esm(() => {
49116
+ WorkspaceContextInputError = class WorkspaceContextInputError extends Error {
49117
+ constructor() {
49118
+ super("Provide the observed user ID and exact lowercase membership ID.");
49119
+ this.name = "WorkspaceContextInputError";
49120
+ }
49121
+ };
49122
+ WorkspaceIdentityMismatchError = class WorkspaceIdentityMismatchError extends Error {
49123
+ constructor() {
49124
+ super("The verified account does not match the requested workspace context.");
49125
+ this.name = "WorkspaceIdentityMismatchError";
49126
+ }
49127
+ };
49128
+ workspaceSelectionFailures = {
49129
+ INVALID_WORKSPACE_SELECTION: [400, "Provide only the exact membership ID from your workspace list."],
49130
+ SESSION_EXPIRED: [401, "Sign in again before selecting a workspace."],
49131
+ ACCOUNT_UNAVAILABLE: [403, "Account is unavailable."],
49132
+ INTERACTIVE_SESSION_REQUIRED: [403, "Interactive sign-in is required to select a workspace."],
49133
+ WORKSPACE_UNAVAILABLE: [404, "Workspace is unavailable. Refresh your workspace list."],
49134
+ WORKSPACE_BUSY: [503, "Workspace is busy. Refresh before retrying."]
49135
+ };
49136
+ });
49137
+
49048
49138
  // src/lib/remote-workspace.ts
49049
49139
  function workspaceMembersQuery(options = {}) {
49050
- if (!record(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))
49140
+ if (!record2(options) || Object.keys(options).some((key) => key !== "limit" && key !== "cursor") || options.limit !== undefined && (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > 100) || options.cursor !== undefined && !cursor(options.cursor))
49051
49141
  throw new Error("Use a roster limit from 1 to 100 and an unchanged continuation cursor.");
49052
49142
  const query = new URLSearchParams;
49053
49143
  if (options.limit !== undefined)
@@ -49063,7 +49153,7 @@ function timestamp(value) {
49063
49153
  return Number.isFinite(time) && new Date(time).toISOString().slice(0, 23) === value.slice(0, 23);
49064
49154
  }
49065
49155
  function parseMember(row, fail2) {
49066
- if (!record(row) || !uuid(row.membershipId) || !uuid(row.userId) || typeof row.email !== "string" || !row.email || !(row.displayName === null || typeof row.displayName === "string") || !isRole(row.role) || !timestamp(row.createdAt))
49156
+ if (!record2(row) || !uuid2(row.membershipId) || !uuid2(row.userId) || typeof row.email !== "string" || !row.email || !(row.displayName === null || typeof row.displayName === "string") || !isRole(row.role) || !timestamp(row.createdAt))
49067
49157
  return fail2();
49068
49158
  return {
49069
49159
  membershipId: row.membershipId,
@@ -49075,12 +49165,12 @@ function parseMember(row, fail2) {
49075
49165
  };
49076
49166
  }
49077
49167
  function mutationInput(membershipId, input, roleChange) {
49078
- if (typeof membershipId !== "string" || !uuid(membershipId) || membershipId !== membershipId.toLowerCase() || !record(input) || Object.keys(input).sort().join(",") !== (roleChange ? "expectedRole,role" : "expectedRole"))
49168
+ if (typeof membershipId !== "string" || !uuid2(membershipId) || membershipId !== membershipId.toLowerCase() || !record2(input) || Object.keys(input).sort().join(",") !== (roleChange ? "expectedRole,role" : "expectedRole"))
49079
49169
  throw new WorkspaceMemberInputError;
49080
- const expectedRole = input.expectedRole, role = roleChange ? input.role : undefined;
49081
- if (!isRole(expectedRole) || roleChange && !isRole(role))
49170
+ const expectedRole = input.expectedRole, role2 = roleChange ? input.role : undefined;
49171
+ if (!isRole(expectedRole) || roleChange && !isRole(role2))
49082
49172
  throw new WorkspaceMemberInputError;
49083
- return { membershipId, role, expectedRole };
49173
+ return { membershipId, role: role2, expectedRole };
49084
49174
  }
49085
49175
  function workspaceMemberRoleInput(membershipId, input) {
49086
49176
  const value = mutationInput(membershipId, input, true);
@@ -49090,24 +49180,24 @@ function workspaceMemberRemovalInput(membershipId, input) {
49090
49180
  const value = mutationInput(membershipId, input, false);
49091
49181
  return { membershipId: value.membershipId, body: { expectedRole: value.expectedRole } };
49092
49182
  }
49093
- function parseWorkspaceMemberRoleResult(value, membershipId, role) {
49183
+ function parseWorkspaceMemberRoleResult(value, membershipId, role2) {
49094
49184
  const fail2 = () => {
49095
49185
  throw new Error(invalidMemberResult);
49096
49186
  };
49097
- if (!record(value) || !uuid(value.organizationId) || typeof value.changed !== "boolean")
49187
+ if (!record2(value) || !uuid2(value.organizationId) || typeof value.changed !== "boolean")
49098
49188
  return fail2();
49099
49189
  const member = parseMember(value.member, fail2);
49100
- if (member.membershipId !== membershipId || member.role !== role)
49190
+ if (member.membershipId !== membershipId || member.role !== role2)
49101
49191
  return fail2();
49102
49192
  return { organizationId: value.organizationId, member, changed: value.changed };
49103
49193
  }
49104
49194
  function parseWorkspaceMemberRemovalResult(value, membershipId) {
49105
- if (!record(value) || !uuid(value.organizationId) || value.membershipId !== membershipId || value.removed !== true || typeof value.alreadyRemoved !== "boolean")
49195
+ if (!record2(value) || !uuid2(value.organizationId) || value.membershipId !== membershipId || value.removed !== true || typeof value.alreadyRemoved !== "boolean")
49106
49196
  throw new Error(invalidMemberResult);
49107
49197
  return { organizationId: value.organizationId, membershipId, removed: true, alreadyRemoved: value.alreadyRemoved };
49108
49198
  }
49109
49199
  function workspaceMemberFailure(value, status) {
49110
- if (!record(value) || typeof value.code !== "string" || !Object.hasOwn(workspaceMemberFailures, value.code))
49200
+ if (!record2(value) || typeof value.code !== "string" || !Object.hasOwn(workspaceMemberFailures, value.code))
49111
49201
  return null;
49112
49202
  const code = value.code;
49113
49203
  return workspaceMemberFailures[code][0] === status ? code : null;
@@ -49116,14 +49206,14 @@ function parseWorkspaceMembersPage(value) {
49116
49206
  const fail2 = () => {
49117
49207
  throw new Error("The server returned an invalid workspace roster.");
49118
49208
  };
49119
- if (!record(value) || !uuid(value.organizationId) || !Array.isArray(value.members) || value.members.length > 100 || !(value.nextCursor === null || cursor(value.nextCursor)) || !value.members.length && value.nextCursor !== null)
49209
+ if (!record2(value) || !uuid2(value.organizationId) || !Array.isArray(value.members) || value.members.length > 100 || !(value.nextCursor === null || cursor(value.nextCursor)) || !value.members.length && value.nextCursor !== null)
49120
49210
  return fail2();
49121
49211
  const members = value.members.map((row) => parseMember(row, fail2));
49122
49212
  if (new Set(members.map((row) => row.membershipId)).size !== members.length)
49123
49213
  return fail2();
49124
49214
  return { organizationId: value.organizationId, members, nextCursor: value.nextCursor };
49125
49215
  }
49126
- var record = (value) => !!value && typeof value === "object" && !Array.isArray(value), cursor = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,512}$/.test(value), uuid = (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;
49216
+ var record2 = (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;
49127
49217
  var init_remote_workspace = __esm(() => {
49128
49218
  WorkspaceMemberInputError = class WorkspaceMemberInputError extends Error {
49129
49219
  constructor() {
@@ -49145,6 +49235,75 @@ var init_remote_workspace = __esm(() => {
49145
49235
  };
49146
49236
  });
49147
49237
 
49238
+ // src/lib/remote-workspace-leave.ts
49239
+ function workspaceLeaveInput(context, input) {
49240
+ const target = workspaceContext(context);
49241
+ if (!input || typeof input !== "object" || Array.isArray(input) || Object.keys(input).sort().join(",") !== "confirm,expectedRole" || input.confirm !== true)
49242
+ throw new WorkspaceLeaveInputError;
49243
+ const captured = workspaceMemberRemovalInput(target.membershipId, { expectedRole: input.expectedRole });
49244
+ return {
49245
+ context: target,
49246
+ input: { expectedRole: captured.body.expectedRole, confirm: true },
49247
+ body: { membershipId: target.membershipId, expectedRole: captured.body.expectedRole }
49248
+ };
49249
+ }
49250
+ function workspaceLeaveFailure(value, status) {
49251
+ if (!value || typeof value !== "object" || Array.isArray(value))
49252
+ return null;
49253
+ const code = value.code;
49254
+ return typeof code === "string" && Object.hasOwn(workspaceLeaveFailures, code) && workspaceLeaveFailures[code][0] === status ? code : null;
49255
+ }
49256
+ function parseWorkspaceLeaveResult(value, membershipId, organizationId) {
49257
+ const row = value;
49258
+ if (!row || typeof row !== "object" || Array.isArray(row) || row.membershipId !== membershipId || row.organizationId !== organizationId || row.removed !== true || row.signInRequired !== true)
49259
+ throw new RemoteWorkspaceLeaveUnconfirmedError;
49260
+ return { membershipId, organizationId, removed: true, signInRequired: true };
49261
+ }
49262
+ function workspaceLeaveProfileContext(membershipId, userId, profile) {
49263
+ const observed = profile === undefined ? undefined : workspaceContext(profile);
49264
+ const target = workspaceContext({ userId: userId ?? observed?.userId, membershipId });
49265
+ if (observed && (observed.userId !== target.userId || observed.membershipId !== target.membershipId))
49266
+ throw new WorkspaceIdentityMismatchError;
49267
+ return target;
49268
+ }
49269
+ var WorkspaceLeaveInputError, workspaceLeaveFailures, RemoteWorkspaceLeaveError, RemoteWorkspaceLeaveUnconfirmedError;
49270
+ var init_remote_workspace_leave = __esm(() => {
49271
+ init_remote_workspace_selection();
49272
+ init_remote_workspace();
49273
+ WorkspaceLeaveInputError = class WorkspaceLeaveInputError extends Error {
49274
+ constructor() {
49275
+ super("Confirm leaving the exact observed user and membership with its expected role.");
49276
+ this.name = "WorkspaceLeaveInputError";
49277
+ }
49278
+ };
49279
+ workspaceLeaveFailures = {
49280
+ INVALID_REQUEST: [400, "Provide the exact current membership and expected role."],
49281
+ ACCOUNT_UNAVAILABLE: [403, "Account is unavailable."],
49282
+ INTERACTIVE_SESSION_REQUIRED: [403, "Fresh interactive sign-in is required to leave a workspace."],
49283
+ MEMBERSHIP_ROLE_CHANGED: [409, "Your role changed. Sign in and inspect the workspace before leaving."],
49284
+ LAST_OWNER_REQUIRED: [409, "The workspace must retain another active owner."],
49285
+ LAST_WORKSPACE_REQUIRED: [409, "Another available workspace is required before leaving."],
49286
+ MEMBERSHIP_BUSY: [503, "Membership is busy. Inspect the workspace before another leave action."]
49287
+ };
49288
+ RemoteWorkspaceLeaveError = class RemoteWorkspaceLeaveError extends Error {
49289
+ code;
49290
+ status;
49291
+ constructor(code) {
49292
+ super(workspaceLeaveFailures[code][1]);
49293
+ this.code = code;
49294
+ this.name = "RemoteWorkspaceLeaveError";
49295
+ this.status = workspaceLeaveFailures[code][0];
49296
+ }
49297
+ };
49298
+ RemoteWorkspaceLeaveUnconfirmedError = class RemoteWorkspaceLeaveUnconfirmedError extends Error {
49299
+ code = "WORKSPACE_LEAVE_UNCONFIRMED";
49300
+ constructor() {
49301
+ super("The leave outcome is unconfirmed. Sign in again and inspect available memberships before another action. Do not retry automatically; saved credentials are unchanged.");
49302
+ this.name = "RemoteWorkspaceLeaveUnconfirmedError";
49303
+ }
49304
+ };
49305
+ });
49306
+
49148
49307
  // src/lib/auth-store.ts
49149
49308
  import { chmodSync, existsSync as existsSync17, mkdirSync as mkdirSync7, readFileSync as readFileSync12, renameSync as renameSync4, statSync as statSync9, unlinkSync, writeFileSync as writeFileSync7 } from "fs";
49150
49309
  import { basename as basename4, dirname as dirname6, join as join18 } from "path";
@@ -49163,14 +49322,14 @@ function readIdentity(env3 = process.env) {
49163
49322
  const parsed = JSON.parse(readFileSync12(getIdentityFilePath(env3), "utf-8"));
49164
49323
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
49165
49324
  return {};
49166
- const record2 = parsed;
49325
+ const record3 = parsed;
49167
49326
  const selected = resolveSkillsApiOrigin(env3)?.origin;
49168
- const bound = typeof record2.apiUrl === "string" ? record2.apiUrl : readCredentialValue(SKILLS_BOUND_API_URL, env3) ?? readStoredApiUrl(env3) ?? defaultFleetGatewayBaseUrl("skills");
49327
+ const bound = typeof record3.apiUrl === "string" ? record3.apiUrl : readCredentialValue(SKILLS_BOUND_API_URL, env3) ?? readStoredApiUrl(env3) ?? defaultFleetGatewayBaseUrl("skills");
49169
49328
  if (selected && normalizeSkillsApiOrigin(bound) !== selected)
49170
49329
  return {};
49171
49330
  const identity2 = {};
49172
49331
  for (const field of ["email", "orgId", "orgSlug", "userId"]) {
49173
- const value = record2[field];
49332
+ const value = record3[field];
49174
49333
  if (typeof value === "string" && value.length > 0)
49175
49334
  identity2[field] = value;
49176
49335
  }
@@ -49319,44 +49478,44 @@ var init_auth_store = __esm(() => {
49319
49478
 
49320
49479
  // src/lib/remote-run-contract.ts
49321
49480
  function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
49322
- const record2 = isRecord3(payload) ? payload : {};
49481
+ const record3 = isRecord3(payload) ? payload : {};
49323
49482
  return {
49324
49483
  contractVersion: REMOTE_SKILL_RUN_CONTRACT_VERSION,
49325
- ...pickString(record2, "id"),
49326
- skill: pickStringValue(record2, "skill") ?? fallbackSkill,
49327
- ...pickString(record2, "requestedSlug"),
49328
- ...pickString(record2, "status"),
49329
- ...pickNumber(record2, "exitCode"),
49330
- ...pickString(record2, "correlationId"),
49331
- ...pickString(record2, "createdAt"),
49332
- ...pickString(record2, "startedAt"),
49333
- ...pickString(record2, "completedAt"),
49334
- ...pickNumber(record2, "durationMs"),
49335
- ...pickString(record2, "outputType"),
49336
- ...hasOwn(record2, "outputPreview") ? { outputPreview: record2.outputPreview } : {},
49337
- ...pickString(record2, "errorCode"),
49338
- ...pickString(record2, "errorMessage"),
49339
- ...pickString(record2, "error"),
49340
- ...pickString(record2, "code"),
49341
- ...hasOwn(record2, "details") ? { details: record2.details } : {}
49484
+ ...pickString(record3, "id"),
49485
+ skill: pickStringValue(record3, "skill") ?? fallbackSkill,
49486
+ ...pickString(record3, "requestedSlug"),
49487
+ ...pickString(record3, "status"),
49488
+ ...pickNumber(record3, "exitCode"),
49489
+ ...pickString(record3, "correlationId"),
49490
+ ...pickString(record3, "createdAt"),
49491
+ ...pickString(record3, "startedAt"),
49492
+ ...pickString(record3, "completedAt"),
49493
+ ...pickNumber(record3, "durationMs"),
49494
+ ...pickString(record3, "outputType"),
49495
+ ...hasOwn(record3, "outputPreview") ? { outputPreview: record3.outputPreview } : {},
49496
+ ...pickString(record3, "errorCode"),
49497
+ ...pickString(record3, "errorMessage"),
49498
+ ...pickString(record3, "error"),
49499
+ ...pickString(record3, "code"),
49500
+ ...hasOwn(record3, "details") ? { details: record3.details } : {}
49342
49501
  };
49343
49502
  }
49344
49503
  function isRecord3(value) {
49345
49504
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
49346
49505
  }
49347
- function hasOwn(record2, key) {
49348
- return Object.prototype.hasOwnProperty.call(record2, key);
49506
+ function hasOwn(record3, key) {
49507
+ return Object.prototype.hasOwnProperty.call(record3, key);
49349
49508
  }
49350
- function pickString(record2, key) {
49351
- const value = pickStringValue(record2, key);
49509
+ function pickString(record3, key) {
49510
+ const value = pickStringValue(record3, key);
49352
49511
  return value === undefined ? {} : { [key]: value };
49353
49512
  }
49354
- function pickStringValue(record2, key) {
49355
- const value = record2[key];
49513
+ function pickStringValue(record3, key) {
49514
+ const value = record3[key];
49356
49515
  return typeof value === "string" ? value : undefined;
49357
49516
  }
49358
- function pickNumber(record2, key) {
49359
- const value = record2[key];
49517
+ function pickNumber(record3, key) {
49518
+ const value = record3[key];
49360
49519
  return typeof value === "number" && Number.isFinite(value) ? { [key]: value } : {};
49361
49520
  }
49362
49521
  var REMOTE_SKILL_RUN_CONTRACT_VERSION = 1;
@@ -49548,11 +49707,11 @@ function parseUpdatedProfile(value) {
49548
49707
  return { user: { id: user.id, email: user.email, displayName: user.displayName, role: user.role } };
49549
49708
  }
49550
49709
  function parseUpdatedWorkspace(value) {
49551
- const organization = isRecord4(value) && value.organization;
49552
- if (!isRecord4(organization) || !string(organization.id) || !string(organization.slug) || !string(organization.name)) {
49710
+ const organization2 = isRecord4(value) && value.organization;
49711
+ if (!isRecord4(organization2) || !string(organization2.id) || !string(organization2.slug) || !string(organization2.name)) {
49553
49712
  throw new Error("The server returned an invalid workspace.");
49554
49713
  }
49555
- return { organization: { id: organization.id, slug: organization.slug, name: organization.name } };
49714
+ return { organization: { id: organization2.id, slug: organization2.slug, name: organization2.name } };
49556
49715
  }
49557
49716
 
49558
49717
  // src/lib/remote-client.ts
@@ -49560,6 +49719,7 @@ var exports_remote_client = {};
49560
49719
  __export(exports_remote_client, {
49561
49720
  createRemoteSkillsClientReadOnly: () => createRemoteSkillsClientReadOnly,
49562
49721
  createRemoteSkillsClient: () => createRemoteSkillsClient,
49722
+ RemoteWorkspaceSelectionError: () => RemoteWorkspaceSelectionError,
49563
49723
  RemoteWorkspaceMemberError: () => RemoteWorkspaceMemberError,
49564
49724
  RemoteSkillsClient: () => RemoteSkillsClient,
49565
49725
  RemoteRouteUnsupportedError: () => RemoteRouteUnsupportedError,
@@ -49579,6 +49739,7 @@ class RemoteSkillsClient {
49579
49739
  return fetch(`${this.apiUrl}${path}`, {
49580
49740
  ...options,
49581
49741
  redirect: "error",
49742
+ credentials: "omit",
49582
49743
  signal: options?.signal ?? AbortSignal.timeout(15000),
49583
49744
  headers: {
49584
49745
  Authorization: `Bearer ${this.apiKey}`,
@@ -49685,6 +49846,65 @@ class RemoteSkillsClient {
49685
49846
  async getIdentity() {
49686
49847
  return (await this.requestNewRoute("/api/auth/whoami")).json();
49687
49848
  }
49849
+ async listAccountWorkspaces(expectedUserId) {
49850
+ const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
49851
+ const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
49852
+ let identity2;
49853
+ if (expected !== undefined) {
49854
+ const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
49855
+ if (!value || typeof value !== "object" || value.authMethod !== "jwt")
49856
+ throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces", "INTERACTIVE_SESSION_REQUIRED");
49857
+ identity2 = parseWorkspaceIdentity(value, expected);
49858
+ }
49859
+ const result2 = parseAccountWorkspaces(await connection.requestWorkspaceSelection("/api/v1/account/workspaces"));
49860
+ const current = result2.workspaces.find((workspace) => workspace.current);
49861
+ if (identity2 && (current.membershipId !== identity2.user.membershipId || current.organization.id !== identity2.organization.id))
49862
+ throw new WorkspaceIdentityMismatchError;
49863
+ return result2;
49864
+ }
49865
+ async switchWorkspace(context) {
49866
+ const target = workspaceContext(context);
49867
+ const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
49868
+ const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
49869
+ if (!value || typeof value !== "object" || value.authMethod !== "jwt")
49870
+ throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces/switch", "INTERACTIVE_SESSION_REQUIRED");
49871
+ parseWorkspaceIdentity(value, target.userId);
49872
+ const selected = parseWorkspaceSession(await connection.requestWorkspaceSelection("/api/v1/account/workspaces/switch", {
49873
+ method: "POST",
49874
+ body: JSON.stringify({ membershipId: target.membershipId })
49875
+ }), target);
49876
+ const verified = await new RemoteSkillsClient(selected.token, connection.apiUrl).requestWorkspaceSelection("/api/auth/whoami");
49877
+ if (!verified || typeof verified !== "object" || verified.authMethod !== "jwt")
49878
+ throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces/switch", "INTERACTIVE_SESSION_REQUIRED");
49879
+ const identity2 = parseWorkspaceIdentity(verified, target.userId);
49880
+ if (identity2.user.membershipId !== target.membershipId || identity2.organization.id !== selected.organization.id)
49881
+ throw new WorkspaceIdentityMismatchError;
49882
+ return { token: selected.token, ...identity2 };
49883
+ }
49884
+ async requestWorkspaceSelection(path, options) {
49885
+ let response;
49886
+ try {
49887
+ response = await this.request(path, { ...options, credentials: "omit" });
49888
+ } catch {
49889
+ throw new Error("Unable to reach the Skills workspace API.");
49890
+ }
49891
+ let value;
49892
+ try {
49893
+ value = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, response.ok ? 1024 * 1024 : 4096)));
49894
+ } catch {
49895
+ if (response.ok)
49896
+ throw new Error(invalidWorkspaceResult);
49897
+ }
49898
+ if (!response.ok) {
49899
+ const code = workspaceSelectionFailure(value, response.status);
49900
+ if (code)
49901
+ throw new RemoteWorkspaceSelectionError(path, code);
49902
+ if (response.status === 404 || response.status === 405)
49903
+ throw new RemoteRouteUnsupportedError(path, response.status, this.apiUrl);
49904
+ throw new RemoteRequestError(path, response.status);
49905
+ }
49906
+ return value;
49907
+ }
49688
49908
  async updateProfile(input) {
49689
49909
  const body = customerNamePatch(input, "displayName");
49690
49910
  return parseUpdatedProfile(await (await this.requestNewRoute("/api/v1/account/profile", { method: "PATCH", body: JSON.stringify(body) })).json());
@@ -49737,6 +49957,30 @@ class RemoteSkillsClient {
49737
49957
  }
49738
49958
  return value;
49739
49959
  }
49960
+ async leaveWorkspace(context, input) {
49961
+ const captured = workspaceLeaveInput(context, input);
49962
+ const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
49963
+ const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
49964
+ if (!value || typeof value !== "object" || value.authMethod !== "jwt")
49965
+ throw new RemoteWorkspaceLeaveError("INTERACTIVE_SESSION_REQUIRED");
49966
+ const identity2 = parseWorkspaceIdentity(value, captured.context.userId);
49967
+ if (identity2.user.membershipId !== captured.context.membershipId)
49968
+ throw new WorkspaceIdentityMismatchError;
49969
+ let response, body;
49970
+ try {
49971
+ response = await connection.request("/api/v1/account/workspaces/leave", { method: "POST", body: JSON.stringify(captured.body) });
49972
+ body = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, 4096)));
49973
+ } catch {
49974
+ throw new RemoteWorkspaceLeaveUnconfirmedError;
49975
+ }
49976
+ if (!response.ok) {
49977
+ const code = workspaceLeaveFailure(body, response.status);
49978
+ if (code)
49979
+ throw new RemoteWorkspaceLeaveError(code);
49980
+ throw new RemoteWorkspaceLeaveUnconfirmedError;
49981
+ }
49982
+ return parseWorkspaceLeaveResult(body, captured.context.membershipId, identity2.organization.id);
49983
+ }
49740
49984
  async listApiKeys() {
49741
49985
  return this.arrayResponse("/api/auth/keys");
49742
49986
  }
@@ -49987,13 +50231,13 @@ class RemoteSkillsClient {
49987
50231
  return normalizeUpdatedSincePage(await response.json());
49988
50232
  }
49989
50233
  }
49990
- function requireOptionalString(record2, field) {
49991
- if (record2[field] === undefined)
50234
+ function requireOptionalString(record3, field) {
50235
+ if (record3[field] === undefined)
49992
50236
  return;
49993
- if (typeof record2[field] !== "string") {
50237
+ if (typeof record3[field] !== "string") {
49994
50238
  throw new Error(`Remote payload did not match the expected contract (${field} must be a string when present)`);
49995
50239
  }
49996
- return record2[field];
50240
+ return record3[field];
49997
50241
  }
49998
50242
  function isVersionRecord(value) {
49999
50243
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -50015,19 +50259,19 @@ function normalizePin(entry) {
50015
50259
  if (!entry || typeof entry !== "object") {
50016
50260
  throw new Error("Remote pin payload did not match the expected contract (expected an object)");
50017
50261
  }
50018
- const record2 = entry;
50019
- const slug = typeof record2.slug === "string" && record2.slug.trim() ? record2.slug.trim() : undefined;
50262
+ const record3 = entry;
50263
+ const slug = typeof record3.slug === "string" && record3.slug.trim() ? record3.slug.trim() : undefined;
50020
50264
  if (!slug) {
50021
50265
  throw new Error("Remote pin payload did not match the expected contract (missing slug)");
50022
50266
  }
50023
50267
  let metadata;
50024
- if (record2.metadata !== undefined) {
50025
- if (!record2.metadata || typeof record2.metadata !== "object" || Array.isArray(record2.metadata)) {
50268
+ if (record3.metadata !== undefined) {
50269
+ if (!record3.metadata || typeof record3.metadata !== "object" || Array.isArray(record3.metadata)) {
50026
50270
  throw new Error("Remote pin payload did not match the expected contract (metadata must be a JSON object when present)");
50027
50271
  }
50028
- metadata = record2.metadata;
50272
+ metadata = record3.metadata;
50029
50273
  }
50030
- const pinnedAt = requireOptionalString(record2, "pinnedAt");
50274
+ const pinnedAt = requireOptionalString(record3, "pinnedAt");
50031
50275
  return {
50032
50276
  slug,
50033
50277
  ...pinnedAt !== undefined ? { pinnedAt } : {},
@@ -50044,16 +50288,16 @@ function normalizeSkillSummary(entry) {
50044
50288
  if (!entry || typeof entry !== "object") {
50045
50289
  throw new Error("Remote skill payload did not match the expected contract (expected an object)");
50046
50290
  }
50047
- const record2 = entry;
50048
- const slug = typeof record2.slug === "string" && record2.slug.trim() ? record2.slug.trim() : undefined;
50291
+ const record3 = entry;
50292
+ const slug = typeof record3.slug === "string" && record3.slug.trim() ? record3.slug.trim() : undefined;
50049
50293
  if (!slug) {
50050
50294
  throw new Error("Remote skill payload did not match the expected contract (missing slug)");
50051
50295
  }
50052
50296
  return {
50053
50297
  slug,
50054
- ...requireOptionalString(record2, "name") !== undefined ? { name: requireOptionalString(record2, "name") } : {},
50055
- ...requireOptionalString(record2, "version") !== undefined ? { version: requireOptionalString(record2, "version") } : {},
50056
- ...requireOptionalString(record2, "updatedAt") !== undefined ? { updatedAt: requireOptionalString(record2, "updatedAt") } : {}
50298
+ ...requireOptionalString(record3, "name") !== undefined ? { name: requireOptionalString(record3, "name") } : {},
50299
+ ...requireOptionalString(record3, "version") !== undefined ? { version: requireOptionalString(record3, "version") } : {},
50300
+ ...requireOptionalString(record3, "updatedAt") !== undefined ? { updatedAt: requireOptionalString(record3, "updatedAt") } : {}
50057
50301
  };
50058
50302
  }
50059
50303
  function normalizeSkillSummaryList(payload) {
@@ -50106,12 +50350,12 @@ function normalizeUpdatedSincePage(payload) {
50106
50350
  if (!payload || typeof payload !== "object") {
50107
50351
  throw new Error("Updated-since payload did not match the expected contract (expected an object)");
50108
50352
  }
50109
- const record2 = payload;
50110
- if (!Array.isArray(record2.skills)) {
50353
+ const record3 = payload;
50354
+ if (!Array.isArray(record3.skills)) {
50111
50355
  throw new Error("Updated-since payload did not match the expected contract (missing skills array)");
50112
50356
  }
50113
- const skills = record2.skills.map(normalizeSkillSummary);
50114
- const nextCursor = record2.nextCursor === undefined || record2.nextCursor === null ? null : record2.nextCursor;
50357
+ const skills = record3.skills.map(normalizeSkillSummary);
50358
+ const nextCursor = record3.nextCursor === undefined || record3.nextCursor === null ? null : record3.nextCursor;
50115
50359
  if (nextCursor !== null && typeof nextCursor !== "string") {
50116
50360
  throw new Error("Updated-since payload did not match the expected contract (nextCursor must be a string or absent)");
50117
50361
  }
@@ -50124,8 +50368,10 @@ async function createRemoteSkillsClient(env3 = process.env) {
50124
50368
  function createRemoteSkillsClientReadOnly(env3 = process.env) {
50125
50369
  return createRemoteSkillsClient(env3);
50126
50370
  }
50127
- var RemoteRouteUnsupportedError, RemoteRequestError, RemoteWorkspaceMemberError, RemoteCapabilityUnavailableError, INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
50371
+ var RemoteRouteUnsupportedError, RemoteRequestError, RemoteWorkspaceMemberError, RemoteWorkspaceSelectionError, RemoteCapabilityUnavailableError, INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
50128
50372
  var init_remote_client = __esm(() => {
50373
+ init_remote_workspace_leave();
50374
+ init_remote_workspace_selection();
50129
50375
  init_remote_workspace();
50130
50376
  init_remote_workspace();
50131
50377
  init_auth_store();
@@ -50163,6 +50409,15 @@ var init_remote_client = __esm(() => {
50163
50409
  this.message = workspaceMemberFailures[code][1];
50164
50410
  }
50165
50411
  };
50412
+ RemoteWorkspaceSelectionError = class RemoteWorkspaceSelectionError extends RemoteRequestError {
50413
+ code;
50414
+ constructor(path, code) {
50415
+ super(path, workspaceSelectionFailures[code][0]);
50416
+ this.code = code;
50417
+ this.name = "RemoteWorkspaceSelectionError";
50418
+ this.message = workspaceSelectionFailures[code][1];
50419
+ }
50420
+ };
50166
50421
  RemoteCapabilityUnavailableError = class RemoteCapabilityUnavailableError extends RemoteRequestError {
50167
50422
  code = "SUBSCRIPTION_CHECKOUT_UNAVAILABLE";
50168
50423
  constructor() {
@@ -50891,16 +51146,16 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
50891
51146
  }
50892
51147
  return { path: target, created };
50893
51148
  }
50894
- function writePullMarker(dir, record2) {
51149
+ function writePullMarker(dir, record3) {
50895
51150
  const marker = {
50896
51151
  managedBy: "@hasna/skills",
50897
- skill: record2.skill,
50898
- source: record2.source ?? "pull",
50899
- ...record2.version ? { version: record2.version } : {},
50900
- ...record2.contentHash ? { contentHash: record2.contentHash } : {},
50901
- ...record2.sourceCommit ? { sourceCommit: record2.sourceCommit } : {},
50902
- ...record2.signature ? { signature: record2.signature } : {},
50903
- ...record2.revisionId ? { revisionId: record2.revisionId } : {},
51152
+ skill: record3.skill,
51153
+ source: record3.source ?? "pull",
51154
+ ...record3.version ? { version: record3.version } : {},
51155
+ ...record3.contentHash ? { contentHash: record3.contentHash } : {},
51156
+ ...record3.sourceCommit ? { sourceCommit: record3.sourceCommit } : {},
51157
+ ...record3.signature ? { signature: record3.signature } : {},
51158
+ ...record3.revisionId ? { revisionId: record3.revisionId } : {},
50904
51159
  syncedAt: new Date().toISOString()
50905
51160
  };
50906
51161
  writeFileSync8(join20(dir, PULL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
@@ -50915,19 +51170,19 @@ async function safeMeta(client, slug) {
50915
51170
  }
50916
51171
  if (!raw || typeof raw !== "object")
50917
51172
  return null;
50918
- const record2 = raw;
50919
- const kind = record2.kind === "instruction" || record2.kind === "executable" ? record2.kind : undefined;
50920
- const tags = Array.isArray(record2.tags) ? record2.tags.filter((tag) => typeof tag === "string" && tag.trim().length > 0) : undefined;
51173
+ const record3 = raw;
51174
+ const kind = record3.kind === "instruction" || record3.kind === "executable" ? record3.kind : undefined;
51175
+ const tags = Array.isArray(record3.tags) ? record3.tags.filter((tag) => typeof tag === "string" && tag.trim().length > 0) : undefined;
50921
51176
  return {
50922
- ...str(record2.displayName) ? { displayName: str(record2.displayName) } : {},
50923
- ...str(record2.description) ? { description: str(record2.description) } : {},
50924
- ...str(record2.category) ? { category: str(record2.category) } : {},
51177
+ ...str(record3.displayName) ? { displayName: str(record3.displayName) } : {},
51178
+ ...str(record3.description) ? { description: str(record3.description) } : {},
51179
+ ...str(record3.category) ? { category: str(record3.category) } : {},
50925
51180
  ...tags && tags.length ? { tags } : {},
50926
- ...str(record2.version) ? { version: str(record2.version) } : {},
51181
+ ...str(record3.version) ? { version: str(record3.version) } : {},
50927
51182
  ...kind ? { kind } : {},
50928
- ...REVISION_ID_PATTERN.test(str(record2.revisionId) ?? "") ? { revisionId: str(record2.revisionId) } : {},
50929
- ...typeof record2.skillMd === "string" && record2.skillMd.length > 0 ? { skillMd: record2.skillMd } : {},
50930
- ...str(record2.publishedSource) ? { publishedSource: str(record2.publishedSource) } : {}
51183
+ ...REVISION_ID_PATTERN.test(str(record3.revisionId) ?? "") ? { revisionId: str(record3.revisionId) } : {},
51184
+ ...typeof record3.skillMd === "string" && record3.skillMd.length > 0 ? { skillMd: record3.skillMd } : {},
51185
+ ...str(record3.publishedSource) ? { publishedSource: str(record3.publishedSource) } : {}
50931
51186
  };
50932
51187
  }
50933
51188
  function pickCorpusOptions(options) {
@@ -50936,8 +51191,8 @@ function pickCorpusOptions(options) {
50936
51191
  function extractSlug(entry) {
50937
51192
  if (!entry || typeof entry !== "object")
50938
51193
  return;
50939
- const record2 = entry;
50940
- return str(record2.slug) ?? str(record2.name);
51194
+ const record3 = entry;
51195
+ return str(record3.slug) ?? str(record3.name);
50941
51196
  }
50942
51197
  function dedupe(values2) {
50943
51198
  return [...new Set(values2)];
@@ -51210,17 +51465,17 @@ var init_install = __esm(() => {
51210
51465
 
51211
51466
  // src/lib/compact-output.ts
51212
51467
  function truncateText(value, maxChars = 96) {
51213
- const text = String(value ?? "").replace(/\s+/g, " ").trim();
51214
- if (text.length <= maxChars)
51215
- return text;
51216
- return `${text.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
51468
+ const text2 = String(value ?? "").replace(/\s+/g, " ").trim();
51469
+ if (text2.length <= maxChars)
51470
+ return text2;
51471
+ return `${text2.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
51217
51472
  }
51218
51473
  function previewText(value, maxChars = DEFAULT_PREVIEW_CHARS) {
51219
- const text = String(value ?? "");
51474
+ const text2 = String(value ?? "");
51220
51475
  return {
51221
- text: text.length > maxChars ? `${text.slice(0, Math.max(0, maxChars - 3))}...` : text,
51222
- length: text.length,
51223
- truncated: text.length > maxChars
51476
+ text: text2.length > maxChars ? `${text2.slice(0, Math.max(0, maxChars - 3))}...` : text2,
51477
+ length: text2.length,
51478
+ truncated: text2.length > maxChars
51224
51479
  };
51225
51480
  }
51226
51481
  function parsePageLimit(value, fallback, options = {}) {
@@ -51339,10 +51594,10 @@ function handleBrowseError(error) {
51339
51594
  process.exitCode = 1;
51340
51595
  }
51341
51596
  async function writeJson(value, space) {
51342
- const text = `${JSON.stringify(value, null, space)}
51597
+ const text2 = `${JSON.stringify(value, null, space)}
51343
51598
  `;
51344
51599
  await new Promise((resolve3, reject2) => {
51345
- process.stdout.write(text, (error) => {
51600
+ process.stdout.write(text2, (error) => {
51346
51601
  if (error)
51347
51602
  reject2(error);
51348
51603
  else
@@ -52049,12 +52304,12 @@ function generateSkillMd(name) {
52049
52304
  `) + `
52050
52305
  `;
52051
52306
  }
52052
- function extractEnvVars(text) {
52307
+ function extractEnvVars(text2) {
52053
52308
  const envVars = new Set;
52054
52309
  for (const pattern of [ENV_VAR_PATTERN, GENERIC_ENV_PATTERN]) {
52055
52310
  pattern.lastIndex = 0;
52056
52311
  let match;
52057
- while ((match = pattern.exec(text)) !== null) {
52312
+ while ((match = pattern.exec(text2)) !== null) {
52058
52313
  envVars.add(match[1]);
52059
52314
  }
52060
52315
  }
@@ -53260,8 +53515,8 @@ function writeRollbackRecord(mode, entries, appDir = getDataDir()) {
53260
53515
  const dir = join24(appDir, ROLLBACK_DIRNAME);
53261
53516
  mkdirSync9(dir, { recursive: true });
53262
53517
  const file = join24(dir, `${mode}-${Date.now()}.json`);
53263
- const record2 = { version: 1, mode, timestamp: new Date().toISOString(), entries };
53264
- writeFileSync10(file, `${JSON.stringify(record2, null, 2)}
53518
+ const record3 = { version: 1, mode, timestamp: new Date().toISOString(), entries };
53519
+ writeFileSync10(file, `${JSON.stringify(record3, null, 2)}
53265
53520
  `);
53266
53521
  return file;
53267
53522
  }
@@ -53568,10 +53823,10 @@ function setEnvAssignment(path, assignment) {
53568
53823
  }
53569
53824
  let prepared = initial2;
53570
53825
  if (previous) {
53571
- const original = readFileSync20(descriptor), text = original.toString("utf8");
53572
- if (!Buffer.from(text, "utf8").equals(original))
53826
+ const original = readFileSync20(descriptor), text2 = original.toString("utf8");
53827
+ if (!Buffer.from(text2, "utf8").equals(original))
53573
53828
  throw new EnvAssignmentError(INVALID_LAYOUT);
53574
- prepared = prepareEnvAssignment(assignment, text);
53829
+ prepared = prepareEnvAssignment(assignment, text2);
53575
53830
  }
53576
53831
  const bytes = Buffer.from(prepared.content, "utf8");
53577
53832
  let written = 0;
@@ -54139,7 +54394,7 @@ function createSkillRun(params, targetDir = process.cwd()) {
54139
54394
  mkdirSync10(logsDir, { recursive: true });
54140
54395
  mkdirSync10(exportDir, { recursive: true });
54141
54396
  mkdirSync10(join27(root, "tmp"), { recursive: true });
54142
- const record2 = {
54397
+ const record3 = {
54143
54398
  id,
54144
54399
  skill: skillName,
54145
54400
  status: params.status ?? "running",
@@ -54157,10 +54412,10 @@ function createSkillRun(params, targetDir = process.cwd()) {
54157
54412
  logsDir: toProjectRelative(targetDir, logsDir)
54158
54413
  }
54159
54414
  };
54160
- const context = { targetDir, runDir, exportDir, logsDir, record: record2 };
54415
+ const context = { targetDir, runDir, exportDir, logsDir, record: record3 };
54161
54416
  writeRunRecord(context);
54162
54417
  writeArtifactsManifest(context, []);
54163
- appendRunEvent(context, "created", { status: record2.status });
54418
+ appendRunEvent(context, "created", { status: record3.status });
54164
54419
  return context;
54165
54420
  }
54166
54421
  function completeSkillRun(context, patch) {
@@ -54210,9 +54465,9 @@ function listSkillRuns(targetDir = process.cwd(), limit = 50) {
54210
54465
  if (!statSync14(dayDir).isDirectory())
54211
54466
  continue;
54212
54467
  for (const runId of readdirSync13(dayDir).sort().reverse()) {
54213
- const record2 = readRunRecord(join27(dayDir, runId));
54214
- if (record2)
54215
- records.push(record2);
54468
+ const record3 = readRunRecord(join27(dayDir, runId));
54469
+ if (record3)
54470
+ records.push(record3);
54216
54471
  if (records.length >= limit)
54217
54472
  return records;
54218
54473
  }
@@ -54224,9 +54479,9 @@ function findSkillRun(runId, targetDir = process.cwd()) {
54224
54479
  if (!existsSync25(runsRoot))
54225
54480
  return null;
54226
54481
  for (const day of readdirSync13(runsRoot)) {
54227
- const record2 = readRunRecord(join27(runsRoot, day, runId));
54228
- if (record2)
54229
- return record2;
54482
+ const record3 = readRunRecord(join27(runsRoot, day, runId));
54483
+ if (record3)
54484
+ return record3;
54230
54485
  }
54231
54486
  return null;
54232
54487
  }
@@ -55062,7 +55317,7 @@ function datetime(args) {
55062
55317
  const timeRegex2 = `${time2}(?:${opts.join("|")})`;
55063
55318
  return new RegExp(`^${dateSource}T(?:${timeRegex2})$`);
55064
55319
  }
55065
- var cuid, cuid2, ulid, xid, ksuid, nanoid, duration, guid, uuid2 = (version) => {
55320
+ var cuid, cuid2, ulid, xid, ksuid, nanoid, duration, guid, uuid3 = (version) => {
55066
55321
  if (!version)
55067
55322
  return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000)$/;
55068
55323
  return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`);
@@ -55856,9 +56111,9 @@ var init_schemas = __esm(() => {
55856
56111
  const v = versionMap[def.version];
55857
56112
  if (v === undefined)
55858
56113
  throw new Error(`Invalid UUID version: "${def.version}"`);
55859
- def.pattern ?? (def.pattern = uuid2(v));
56114
+ def.pattern ?? (def.pattern = uuid3(v));
55860
56115
  } else
55861
- def.pattern ?? (def.pattern = uuid2());
56116
+ def.pattern ?? (def.pattern = uuid3());
55862
56117
  $ZodStringFormat.init(inst, def);
55863
56118
  });
55864
56119
  $ZodEmail = /* @__PURE__ */ $constructor("$ZodEmail", (inst, def) => {
@@ -58338,7 +58593,7 @@ function intersection2(left, right) {
58338
58593
  right
58339
58594
  });
58340
58595
  }
58341
- function record2(keyType, valueType, params) {
58596
+ function record3(keyType, valueType, params) {
58342
58597
  return new ZodRecord2({
58343
58598
  type: "record",
58344
58599
  keyType,
@@ -59039,7 +59294,7 @@ var init_types2 = __esm(() => {
59039
59294
  });
59040
59295
  FormElicitationCapabilitySchema = intersection2(object2({
59041
59296
  applyDefaults: boolean2().optional()
59042
- }), record2(string3(), unknown()));
59297
+ }), record3(string3(), unknown()));
59043
59298
  ElicitationCapabilitySchema = preprocess((value) => {
59044
59299
  if (value && typeof value === "object" && !Array.isArray(value)) {
59045
59300
  if (Object.keys(value).length === 0) {
@@ -59050,7 +59305,7 @@ var init_types2 = __esm(() => {
59050
59305
  }, intersection2(object2({
59051
59306
  form: FormElicitationCapabilitySchema.optional(),
59052
59307
  url: AssertObjectSchema.optional()
59053
- }), record2(string3(), unknown()).optional()));
59308
+ }), record3(string3(), unknown()).optional()));
59054
59309
  ClientTasksCapabilitySchema = looseObject({
59055
59310
  list: AssertObjectSchema.optional(),
59056
59311
  cancel: AssertObjectSchema.optional(),
@@ -59073,7 +59328,7 @@ var init_types2 = __esm(() => {
59073
59328
  }).optional()
59074
59329
  });
59075
59330
  ClientCapabilitiesSchema = object2({
59076
- experimental: record2(string3(), AssertObjectSchema).optional(),
59331
+ experimental: record3(string3(), AssertObjectSchema).optional(),
59077
59332
  sampling: object2({
59078
59333
  context: AssertObjectSchema.optional(),
59079
59334
  tools: AssertObjectSchema.optional()
@@ -59083,7 +59338,7 @@ var init_types2 = __esm(() => {
59083
59338
  listChanged: boolean2().optional()
59084
59339
  }).optional(),
59085
59340
  tasks: ClientTasksCapabilitySchema.optional(),
59086
- extensions: record2(string3(), AssertObjectSchema).optional()
59341
+ extensions: record3(string3(), AssertObjectSchema).optional()
59087
59342
  });
59088
59343
  InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({
59089
59344
  protocolVersion: string3(),
@@ -59095,7 +59350,7 @@ var init_types2 = __esm(() => {
59095
59350
  params: InitializeRequestParamsSchema
59096
59351
  });
59097
59352
  ServerCapabilitiesSchema = object2({
59098
- experimental: record2(string3(), AssertObjectSchema).optional(),
59353
+ experimental: record3(string3(), AssertObjectSchema).optional(),
59099
59354
  logging: AssertObjectSchema.optional(),
59100
59355
  completions: AssertObjectSchema.optional(),
59101
59356
  prompts: object2({
@@ -59109,7 +59364,7 @@ var init_types2 = __esm(() => {
59109
59364
  listChanged: boolean2().optional()
59110
59365
  }).optional(),
59111
59366
  tasks: ServerTasksCapabilitySchema.optional(),
59112
- extensions: record2(string3(), AssertObjectSchema).optional()
59367
+ extensions: record3(string3(), AssertObjectSchema).optional()
59113
59368
  });
59114
59369
  InitializeResultSchema = ResultSchema.extend({
59115
59370
  protocolVersion: string3(),
@@ -59196,7 +59451,7 @@ var init_types2 = __esm(() => {
59196
59451
  ResourceContentsSchema = object2({
59197
59452
  uri: string3(),
59198
59453
  mimeType: optional(string3()),
59199
- _meta: record2(string3(), unknown()).optional()
59454
+ _meta: record3(string3(), unknown()).optional()
59200
59455
  });
59201
59456
  TextResourceContentsSchema = ResourceContentsSchema.extend({
59202
59457
  text: string3()
@@ -59301,7 +59556,7 @@ var init_types2 = __esm(() => {
59301
59556
  });
59302
59557
  GetPromptRequestParamsSchema = BaseRequestParamsSchema.extend({
59303
59558
  name: string3(),
59304
- arguments: record2(string3(), string3()).optional()
59559
+ arguments: record3(string3(), string3()).optional()
59305
59560
  });
59306
59561
  GetPromptRequestSchema = RequestSchema.extend({
59307
59562
  method: literal("prompts/get"),
@@ -59311,34 +59566,34 @@ var init_types2 = __esm(() => {
59311
59566
  type: literal("text"),
59312
59567
  text: string3(),
59313
59568
  annotations: AnnotationsSchema.optional(),
59314
- _meta: record2(string3(), unknown()).optional()
59569
+ _meta: record3(string3(), unknown()).optional()
59315
59570
  });
59316
59571
  ImageContentSchema = object2({
59317
59572
  type: literal("image"),
59318
59573
  data: Base64Schema,
59319
59574
  mimeType: string3(),
59320
59575
  annotations: AnnotationsSchema.optional(),
59321
- _meta: record2(string3(), unknown()).optional()
59576
+ _meta: record3(string3(), unknown()).optional()
59322
59577
  });
59323
59578
  AudioContentSchema = object2({
59324
59579
  type: literal("audio"),
59325
59580
  data: Base64Schema,
59326
59581
  mimeType: string3(),
59327
59582
  annotations: AnnotationsSchema.optional(),
59328
- _meta: record2(string3(), unknown()).optional()
59583
+ _meta: record3(string3(), unknown()).optional()
59329
59584
  });
59330
59585
  ToolUseContentSchema = object2({
59331
59586
  type: literal("tool_use"),
59332
59587
  name: string3(),
59333
59588
  id: string3(),
59334
- input: record2(string3(), unknown()),
59335
- _meta: record2(string3(), unknown()).optional()
59589
+ input: record3(string3(), unknown()),
59590
+ _meta: record3(string3(), unknown()).optional()
59336
59591
  });
59337
59592
  EmbeddedResourceSchema = object2({
59338
59593
  type: literal("resource"),
59339
59594
  resource: union2([TextResourceContentsSchema, BlobResourceContentsSchema]),
59340
59595
  annotations: AnnotationsSchema.optional(),
59341
- _meta: record2(string3(), unknown()).optional()
59596
+ _meta: record3(string3(), unknown()).optional()
59342
59597
  });
59343
59598
  ResourceLinkSchema = ResourceSchema.extend({
59344
59599
  type: literal("resource_link")
@@ -59378,17 +59633,17 @@ var init_types2 = __esm(() => {
59378
59633
  description: string3().optional(),
59379
59634
  inputSchema: object2({
59380
59635
  type: literal("object"),
59381
- properties: record2(string3(), AssertObjectSchema).optional(),
59636
+ properties: record3(string3(), AssertObjectSchema).optional(),
59382
59637
  required: array(string3()).optional()
59383
59638
  }).catchall(unknown()),
59384
59639
  outputSchema: object2({
59385
59640
  type: literal("object"),
59386
- properties: record2(string3(), AssertObjectSchema).optional(),
59641
+ properties: record3(string3(), AssertObjectSchema).optional(),
59387
59642
  required: array(string3()).optional()
59388
59643
  }).catchall(unknown()).optional(),
59389
59644
  annotations: ToolAnnotationsSchema.optional(),
59390
59645
  execution: ToolExecutionSchema.optional(),
59391
- _meta: record2(string3(), unknown()).optional()
59646
+ _meta: record3(string3(), unknown()).optional()
59392
59647
  });
59393
59648
  ListToolsRequestSchema = PaginatedRequestSchema.extend({
59394
59649
  method: literal("tools/list")
@@ -59398,7 +59653,7 @@ var init_types2 = __esm(() => {
59398
59653
  });
59399
59654
  CallToolResultSchema = ResultSchema.extend({
59400
59655
  content: array(ContentBlockSchema).default([]),
59401
- structuredContent: record2(string3(), unknown()).optional(),
59656
+ structuredContent: record3(string3(), unknown()).optional(),
59402
59657
  isError: boolean2().optional()
59403
59658
  });
59404
59659
  CompatibilityCallToolResultSchema = CallToolResultSchema.or(ResultSchema.extend({
@@ -59406,7 +59661,7 @@ var init_types2 = __esm(() => {
59406
59661
  }));
59407
59662
  CallToolRequestParamsSchema = TaskAugmentedRequestParamsSchema.extend({
59408
59663
  name: string3(),
59409
- arguments: record2(string3(), unknown()).optional()
59664
+ arguments: record3(string3(), unknown()).optional()
59410
59665
  });
59411
59666
  CallToolRequestSchema = RequestSchema.extend({
59412
59667
  method: literal("tools/call"),
@@ -59455,7 +59710,7 @@ var init_types2 = __esm(() => {
59455
59710
  content: array(ContentBlockSchema).default([]),
59456
59711
  structuredContent: object2({}).loose().optional(),
59457
59712
  isError: boolean2().optional(),
59458
- _meta: record2(string3(), unknown()).optional()
59713
+ _meta: record3(string3(), unknown()).optional()
59459
59714
  });
59460
59715
  SamplingContentSchema = discriminatedUnion("type", [TextContentSchema, ImageContentSchema, AudioContentSchema]);
59461
59716
  SamplingMessageContentBlockSchema = discriminatedUnion("type", [
@@ -59468,7 +59723,7 @@ var init_types2 = __esm(() => {
59468
59723
  SamplingMessageSchema = object2({
59469
59724
  role: RoleSchema,
59470
59725
  content: union2([SamplingMessageContentBlockSchema, array(SamplingMessageContentBlockSchema)]),
59471
- _meta: record2(string3(), unknown()).optional()
59726
+ _meta: record3(string3(), unknown()).optional()
59472
59727
  });
59473
59728
  CreateMessageRequestParamsSchema = TaskAugmentedRequestParamsSchema.extend({
59474
59729
  messages: array(SamplingMessageSchema),
@@ -59581,7 +59836,7 @@ var init_types2 = __esm(() => {
59581
59836
  message: string3(),
59582
59837
  requestedSchema: object2({
59583
59838
  type: literal("object"),
59584
- properties: record2(string3(), PrimitiveSchemaDefinitionSchema),
59839
+ properties: record3(string3(), PrimitiveSchemaDefinitionSchema),
59585
59840
  required: array(string3()).optional()
59586
59841
  })
59587
59842
  });
@@ -59605,7 +59860,7 @@ var init_types2 = __esm(() => {
59605
59860
  });
59606
59861
  ElicitResultSchema = ResultSchema.extend({
59607
59862
  action: _enum(["accept", "decline", "cancel"]),
59608
- content: preprocess((val) => val === null ? undefined : val, record2(string3(), union2([string3(), number2(), boolean2(), array(string3())])).optional())
59863
+ content: preprocess((val) => val === null ? undefined : val, record3(string3(), union2([string3(), number2(), boolean2(), array(string3())])).optional())
59609
59864
  });
59610
59865
  ResourceTemplateReferenceSchema = object2({
59611
59866
  type: literal("ref/resource"),
@@ -59622,7 +59877,7 @@ var init_types2 = __esm(() => {
59622
59877
  value: string3()
59623
59878
  }),
59624
59879
  context: object2({
59625
- arguments: record2(string3(), string3()).optional()
59880
+ arguments: record3(string3(), string3()).optional()
59626
59881
  }).optional()
59627
59882
  });
59628
59883
  CompleteRequestSchema = RequestSchema.extend({
@@ -59639,7 +59894,7 @@ var init_types2 = __esm(() => {
59639
59894
  RootSchema = object2({
59640
59895
  uri: string3().startsWith("file://"),
59641
59896
  name: string3().optional(),
59642
- _meta: record2(string3(), unknown()).optional()
59897
+ _meta: record3(string3(), unknown()).optional()
59643
59898
  });
59644
59899
  ListRootsRequestSchema = RequestSchema.extend({
59645
59900
  method: literal("roots/list"),
@@ -66892,7 +67147,7 @@ var require_core = __commonJS((exports) => {
66892
67147
  errorsText(errors4 = this.errors, { separator = ", ", dataVar = "data" } = {}) {
66893
67148
  if (!errors4 || errors4.length === 0)
66894
67149
  return "No errors";
66895
- return errors4.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text, msg) => text + separator + msg);
67150
+ return errors4.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text2, msg) => text2 + separator + msg);
66896
67151
  }
66897
67152
  $dataMetaSchema(metaSchema, keywordsJsonPointers) {
66898
67153
  const rules = this.RULES.all;
@@ -67303,8 +67558,8 @@ var require_multipleOf = __commonJS((exports) => {
67303
67558
  const { gen, data, schemaCode, it } = cxt;
67304
67559
  const prec = it.opts.multipleOfPrecision;
67305
67560
  const res = gen.let("res");
67306
- const invalid = prec ? (0, codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` : (0, codegen_1._)`${res} !== parseInt(${res})`;
67307
- cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid}))`);
67561
+ const invalid2 = prec ? (0, codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` : (0, codegen_1._)`${res} !== parseInt(${res})`;
67562
+ cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid2}))`);
67308
67563
  }
67309
67564
  };
67310
67565
  exports.default = def;
@@ -73400,13 +73655,13 @@ async function requestAuthApi(instance, path, options) {
73400
73655
  apiUrl: safeUrl
73401
73656
  });
73402
73657
  }
73403
- const text = await res.text();
73404
- const body = text ? parseJsonBody(text) : {};
73658
+ const text2 = await res.text();
73659
+ const body = text2 ? parseJsonBody(text2) : {};
73405
73660
  if (!res.ok) {
73406
- const record4 = isRecord5(body) ? body : {};
73407
- const detail = typeof record4.detail === "string" ? record4.detail : undefined;
73408
- const error2 = typeof record4.error === "string" ? record4.error : undefined;
73409
- const code = typeof record4.code === "string" ? record4.code : undefined;
73661
+ const record5 = isRecord5(body) ? body : {};
73662
+ const detail = typeof record5.detail === "string" ? record5.detail : undefined;
73663
+ const error2 = typeof record5.error === "string" ? record5.error : undefined;
73664
+ const code = typeof record5.code === "string" ? record5.code : undefined;
73410
73665
  throw new HostedApiError(detail || error2 || `${res.status} ${res.statusText}`, {
73411
73666
  status: res.status,
73412
73667
  code,
@@ -73417,15 +73672,15 @@ async function requestAuthApi(instance, path, options) {
73417
73672
  }
73418
73673
  return body;
73419
73674
  }
73420
- function parseJsonBody(text) {
73675
+ function parseJsonBody(text2) {
73421
73676
  try {
73422
- return JSON.parse(text);
73677
+ return JSON.parse(text2);
73423
73678
  } catch {
73424
- return { detail: condenseErrorBody(text) };
73679
+ return { detail: condenseErrorBody(text2) };
73425
73680
  }
73426
73681
  }
73427
- function condenseErrorBody(text) {
73428
- const stripped = /<[a-z!/]/i.test(text) ? text.replace(/<(script|style)[\s\S]*?<\/\1>/gi, " ").replace(/<[^>]*>/g, " ") : text;
73682
+ function condenseErrorBody(text2) {
73683
+ const stripped = /<[a-z!/]/i.test(text2) ? text2.replace(/<(script|style)[\s\S]*?<\/\1>/gi, " ").replace(/<[^>]*>/g, " ") : text2;
73429
73684
  const collapsed = stripped.replace(/\s+/g, " ").trim();
73430
73685
  if (collapsed.length <= MAX_ERROR_DETAIL_LENGTH)
73431
73686
  return collapsed;
@@ -73452,7 +73707,12 @@ class RemoteSkillsAuthClient {
73452
73707
  pollDevice(deviceCode) {
73453
73708
  return this.request("/api/auth/device/token", { method: "POST", body: JSON.stringify({ deviceCode }) });
73454
73709
  }
73455
- async sessionClient(email2, code) {
73710
+ async sessionClient(email2, code, context) {
73711
+ if (context !== undefined) {
73712
+ const target = workspaceContext(context), apiOrigin2 = this.apiOrigin;
73713
+ const session = await this.switchWorkspace(email2, code, target);
73714
+ return new RemoteSkillsClient(session.token, apiOrigin2);
73715
+ }
73456
73716
  const apiOrigin = this.apiOrigin;
73457
73717
  if (!email2.includes("@") || !/^\d{6}$/.test(code))
73458
73718
  throw new Error("Fresh email and six-digit verification code are required to manage this account");
@@ -73461,34 +73721,80 @@ class RemoteSkillsAuthClient {
73461
73721
  throw new Error("The server did not return an authorized account session");
73462
73722
  return new RemoteSkillsClient(login.token, apiOrigin);
73463
73723
  }
73464
- async createApiKey(email2, code, name, scopes) {
73465
- return (await this.sessionClient(email2, code)).createApiKey(name, scopes);
73724
+ async listAccountWorkspaces(email2, code, expectedUserId) {
73725
+ const login = await this.workspaceLogin(email2, code, expectedUserId);
73726
+ const result2 = await new RemoteSkillsClient(login.token, login.apiOrigin).listAccountWorkspaces(login.userId);
73727
+ return { userId: login.userId, ...result2 };
73728
+ }
73729
+ async switchWorkspace(email2, code, context) {
73730
+ const target = workspaceContext(context);
73731
+ const login = await this.workspaceLogin(email2, code, target.userId);
73732
+ return new RemoteSkillsClient(login.token, login.apiOrigin).switchWorkspace(target);
73733
+ }
73734
+ async workspaceLogin(email2, code, expectedUserId) {
73735
+ const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
73736
+ const apiOrigin = this.apiOrigin;
73737
+ if (typeof email2 !== "string" || !email2.includes("@") || typeof code !== "string" || !/^\d{6}$/.test(code))
73738
+ throw new Error("Fresh email and six-digit verification code are required to manage this account");
73739
+ let response;
73740
+ try {
73741
+ response = await fetch(`${apiOrigin}/api/auth/verify`, {
73742
+ method: "POST",
73743
+ redirect: "error",
73744
+ credentials: "omit",
73745
+ signal: AbortSignal.timeout(15000),
73746
+ headers: { "Content-Type": "application/json" },
73747
+ body: JSON.stringify({ email: email2, code })
73748
+ });
73749
+ } catch {
73750
+ throw new HostedApiError("Unable to verify the Skills account.");
73751
+ }
73752
+ if (!response.ok) {
73753
+ response.body?.cancel().catch(() => {});
73754
+ throw new HostedApiError("Unable to verify the Skills account.", { status: response.status });
73755
+ }
73756
+ let value;
73757
+ try {
73758
+ value = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, 64 * 1024)));
73759
+ } catch {
73760
+ throw new HostedApiError("The server returned an invalid account verification result.");
73761
+ }
73762
+ return { ...parseWorkspaceLogin(value, expected), apiOrigin };
73466
73763
  }
73467
- async listApiKeys(email2, code) {
73468
- return (await this.sessionClient(email2, code)).listApiKeys();
73764
+ async createApiKey(email2, code, name, scopes, context) {
73765
+ const capturedScopes = scopes === undefined ? undefined : [...scopes];
73766
+ return (await this.sessionClient(email2, code, context)).createApiKey(name, capturedScopes);
73469
73767
  }
73470
- async revokeApiKey(email2, code, keyId) {
73471
- return (await this.sessionClient(email2, code)).revokeApiKey(keyId);
73768
+ async listApiKeys(email2, code, context) {
73769
+ return (await this.sessionClient(email2, code, context)).listApiKeys();
73472
73770
  }
73473
- async updateProfile(email2, code, input) {
73474
- customerNamePatch(input, "displayName");
73475
- return (await this.sessionClient(email2, code)).updateProfile(input);
73771
+ async revokeApiKey(email2, code, keyId, context) {
73772
+ return (await this.sessionClient(email2, code, context)).revokeApiKey(keyId);
73476
73773
  }
73477
- async updateCurrentWorkspace(email2, code, input) {
73478
- customerNamePatch(input, "name");
73479
- return (await this.sessionClient(email2, code)).updateCurrentWorkspace(input);
73774
+ async updateProfile(email2, code, input, context) {
73775
+ const body = customerNamePatch(input, "displayName");
73776
+ return (await this.sessionClient(email2, code, context)).updateProfile({ displayName: body.displayName });
73480
73777
  }
73481
- async listWorkspaceMembers(email2, code, options = {}) {
73778
+ async updateCurrentWorkspace(email2, code, input, context) {
73779
+ const body = customerNamePatch(input, "name");
73780
+ return (await this.sessionClient(email2, code, context)).updateCurrentWorkspace({ name: body.name });
73781
+ }
73782
+ async listWorkspaceMembers(email2, code, options = {}, context) {
73482
73783
  workspaceMembersQuery(options);
73483
- return (await this.sessionClient(email2, code)).listWorkspaceMembers(options);
73784
+ const captured = { ...options };
73785
+ return (await this.sessionClient(email2, code, context)).listWorkspaceMembers(captured);
73484
73786
  }
73485
- async setWorkspaceMemberRole(email2, code, membershipId, input) {
73787
+ async setWorkspaceMemberRole(email2, code, membershipId, input, context) {
73486
73788
  const captured = workspaceMemberRoleInput(membershipId, input);
73487
- return (await this.sessionClient(email2, code)).setWorkspaceMemberRole(captured.membershipId, captured.body);
73789
+ return (await this.sessionClient(email2, code, context)).setWorkspaceMemberRole(captured.membershipId, captured.body);
73790
+ }
73791
+ async leaveWorkspace(email2, code, context, input) {
73792
+ const captured = workspaceLeaveInput(context, input);
73793
+ return (await this.sessionClient(email2, code, captured.context)).leaveWorkspace(captured.context, captured.input);
73488
73794
  }
73489
- async removeWorkspaceMember(email2, code, membershipId, input) {
73795
+ async removeWorkspaceMember(email2, code, membershipId, input, context) {
73490
73796
  const captured = workspaceMemberRemovalInput(membershipId, input);
73491
- return (await this.sessionClient(email2, code)).removeWorkspaceMember(captured.membershipId, captured.body);
73797
+ return (await this.sessionClient(email2, code, context)).removeWorkspaceMember(captured.membershipId, captured.body);
73492
73798
  }
73493
73799
  request(path, options) {
73494
73800
  if (!["/api/auth/login", "/api/auth/verify", "/api/auth/device/start", "/api/auth/device/token", "/api/auth/keys", "/api/auth/whoami"].includes(path))
@@ -73498,6 +73804,9 @@ class RemoteSkillsAuthClient {
73498
73804
  }
73499
73805
  var MAX_ERROR_DETAIL_LENGTH = 200, HostedApiError;
73500
73806
  var init_remote_auth = __esm(() => {
73807
+ init_remote_workspace_leave();
73808
+ init_remote_workspace_selection();
73809
+ init_remote_files();
73501
73810
  init_remote_workspace();
73502
73811
  init_remote_workspace();
73503
73812
  init_remote_client();
@@ -73520,6 +73829,224 @@ var init_remote_auth = __esm(() => {
73520
73829
  };
73521
73830
  });
73522
73831
 
73832
+ // src/lib/workspace-profile.ts
73833
+ import { constants as constants4, closeSync as closeSync4, fstatSync as fstatSync4, lstatSync as lstatSync6, mkdirSync as mkdirSync14, mkdtempSync as mkdtempSync4, openSync as openSync4, readFileSync as readFileSync25, renameSync as renameSync6, rmSync as rmSync7, writeFileSync as writeFileSync14 } from "fs";
73834
+ import { dirname as dirname10, join as join32 } from "path";
73835
+ function stat(path) {
73836
+ try {
73837
+ return lstatSync6(path);
73838
+ } catch (error2) {
73839
+ if (error2.code === "ENOENT")
73840
+ return null;
73841
+ throw error2;
73842
+ }
73843
+ }
73844
+ function safeText(file) {
73845
+ if (stat(file) === null)
73846
+ return null;
73847
+ const fd = openSync4(file, constants4.O_RDONLY | constants4.O_NOFOLLOW | constants4.O_NONBLOCK);
73848
+ try {
73849
+ const s = fstatSync4(fd);
73850
+ if (!s.isFile() || s.size > 65536 || ![256, 384].includes(s.mode & 4095) || process.getuid && s.uid !== process.getuid())
73851
+ return fail2("The selected profile must use bounded owner-only regular files.");
73852
+ return readFileSync25(fd, "utf8");
73853
+ } finally {
73854
+ closeSync4(fd);
73855
+ }
73856
+ }
73857
+ function checkIdentityMetadata(file, identity2) {
73858
+ const text2 = safeText(file);
73859
+ if (text2 === null)
73860
+ return;
73861
+ let value;
73862
+ try {
73863
+ value = JSON.parse(text2);
73864
+ } catch {
73865
+ return fail2("The profile identity metadata is invalid. Sign in again before managing this workspace.");
73866
+ }
73867
+ if (!value || typeof value !== "object" || Array.isArray(value))
73868
+ return fail2("The profile identity metadata is invalid.");
73869
+ for (const [key, expected] of Object.entries({ userId: identity2.user.id, orgId: identity2.organization.id })) {
73870
+ if (value[key] !== undefined && value[key] !== expected)
73871
+ return fail2("The profile identity metadata does not match its authenticated key. Sign in again before managing this workspace.");
73872
+ }
73873
+ }
73874
+ async function keyIdentity(key, origin) {
73875
+ const value = await new RemoteSkillsClient(key, origin).getIdentity();
73876
+ if (value.authMethod !== "api_key")
73877
+ return fail2("The selected credential is not a workspace API key.");
73878
+ const user = value.user;
73879
+ return parseWorkspaceIdentity(value, workspaceExpectedUserId(user?.id));
73880
+ }
73881
+ function prepareProfileWorkspace(action, source = process.env) {
73882
+ const env3 = { ...source }, origin = getApiUrl(action, env3), profile = selectedSkillsProfile(env3);
73883
+ const unchanged2 = captureSkillsCredentialFiles(skillsProfileCredentialFiles(env3));
73884
+ return { origin, async resolve() {
73885
+ unchanged2();
73886
+ if (!profile)
73887
+ return { origin, context: undefined, unchanged: unchanged2 };
73888
+ const connection = await resolveSkillsConnection(env3);
73889
+ if (!connection || connection.apiOrigin !== origin)
73890
+ return fail2("The selected profile has no usable credential for this server.");
73891
+ const identity2 = await keyIdentity(connection.apiKey, origin);
73892
+ checkIdentityMetadata(getIdentityFilePath(env3), identity2);
73893
+ unchanged2();
73894
+ const context = { userId: identity2.user.id, membershipId: identity2.user.membershipId };
73895
+ return { origin, context, unchanged: unchanged2 };
73896
+ } };
73897
+ }
73898
+ async function captureProfileWorkspace(action, source = process.env) {
73899
+ return prepareProfileWorkspace(action, source).resolve();
73900
+ }
73901
+ async function prepareWorkspaceEnrollment(membershipId, source = process.env) {
73902
+ workspaceExpectedUserId(membershipId);
73903
+ const env3 = { ...source }, profile = selectedSkillsProfile(env3);
73904
+ if (!profile)
73905
+ return fail2("Workspace login requires an explicit HASNA_PROFILE name.");
73906
+ if (["HASNA_SKILLS_API_KEY_OVERRIDE", "HASNA_SKILLS_API_KEY_REF", "HASNA_SKILLS_API_KEY", "SKILLS_API_KEY"].some((name) => env3[name]?.trim()))
73907
+ return fail2("Clear injected API keys before enrolling a named workspace profile.");
73908
+ const origin = getApiUrl("Sign in to a workspace", env3);
73909
+ const file = getAuthFilePath(env3), identityFile = getIdentityFilePath(env3);
73910
+ const paths = [...new Set([...skillsProfileCredentialFiles(env3), file, identityFile])];
73911
+ const unchangedFiles = captureSkillsCredentialFiles(paths);
73912
+ const old = safeText(file), oldIdentity = safeText(identityFile);
73913
+ const managed = new Set(["HASNA_SKILLS_API_KEY", "SKILLS_API_KEY", "HASNA_SKILLS_API_URL", "SKILLS_API_URL", "HASNA_SKILLS_BOUND_API_URL"]);
73914
+ const lines = (old ?? "").split(/\r?\n/).filter((line) => !managed.has(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/.exec(line)?.[1] ?? ""));
73915
+ const credentialBody = (key) => [...lines.filter(Boolean), `HASNA_SKILLS_API_KEY=${key}`, `HASNA_SKILLS_API_URL=${origin}`, `HASNA_SKILLS_BOUND_API_URL=${origin}`, ""].join(`
73916
+ `);
73917
+ if (Buffer.byteLength(credentialBody("x".repeat(8192)), "utf8") > 65536)
73918
+ return fail2("The selected profile has insufficient space for a bounded credential. Reduce its unrelated configuration before signing in.");
73919
+ const parents = [];
73920
+ for (let path = dirname10(file);; path = dirname10(path)) {
73921
+ const before2 = stat(path);
73922
+ if (before2 && (!before2.isDirectory() || before2.isSymbolicLink()))
73923
+ return fail2("The profile directory must not be a symbolic link.");
73924
+ parents.push([path, before2]);
73925
+ if (path === dirname10(path))
73926
+ break;
73927
+ }
73928
+ const unchanged2 = () => {
73929
+ unchangedFiles();
73930
+ for (const [path, before2] of parents) {
73931
+ const now3 = stat(path);
73932
+ if (!before2 ? now3 !== null : !now3 || !now3.isDirectory() || before2.dev !== now3.dev || before2.ino !== now3.ino)
73933
+ changed();
73934
+ }
73935
+ };
73936
+ let expected;
73937
+ try {
73938
+ const connection = await resolveSkillsConnection(env3);
73939
+ if (connection) {
73940
+ if (connection.apiKeyTier !== "profile" || connection.apiOrigin !== origin)
73941
+ return fail2("Workspace login must resolve only the explicitly named profile.");
73942
+ expected = await keyIdentity(connection.apiKey, origin);
73943
+ checkIdentityMetadata(identityFile, expected);
73944
+ }
73945
+ } catch (error2) {
73946
+ if (!(error2 instanceof SkillsFleetCredentialError && error2.code === "MISSING_API_CREDENTIAL" && (old === null || !/^\s*(?:export\s+)?(?:HASNA_SKILLS_API_KEY|SKILLS_API_KEY)\s*=/m.test(old))))
73947
+ throw error2;
73948
+ }
73949
+ unchanged2();
73950
+ return {
73951
+ profile,
73952
+ origin,
73953
+ async complete(email2, code) {
73954
+ unchanged2();
73955
+ if (!email2.includes("@") || !/^\d{6}$/.test(code))
73956
+ return fail2("A fresh email and six-digit verification code are required.");
73957
+ if (expected && expected.user.email.toLowerCase() !== email2.toLowerCase())
73958
+ return fail2("This profile belongs to another account. Use a different profile or ordinary replacement login.");
73959
+ let issued = false;
73960
+ try {
73961
+ const result2 = await new RemoteSkillsAuthClient(origin).verifyCode(email2, code);
73962
+ const login = parseWorkspaceLogin(result2, expected?.user.id);
73963
+ if (result2.firstLogin === true)
73964
+ return fail2("This sign-in created a new account using the server signup policy. Finish ordinary account login before enrolling a workspace profile.");
73965
+ const session = await new RemoteSkillsClient(login.token, origin).switchWorkspace({ userId: login.userId, membershipId });
73966
+ if (session.user.email.toLowerCase() !== email2.toLowerCase())
73967
+ return fail2("The verified account does not match the requested email.");
73968
+ if (session.user.role === "viewer")
73969
+ return fail2("Viewer memberships cannot enroll API keys.");
73970
+ unchanged2();
73971
+ issued = true;
73972
+ const created = await new RemoteSkillsClient(session.token, origin).createApiKey("cli");
73973
+ const verified = await keyIdentity(created.key, origin);
73974
+ if (verified.user.id !== session.user.id || verified.user.membershipId !== membershipId || verified.organization.id !== session.organization.id)
73975
+ return fail2("The issued key does not match the selected workspace.");
73976
+ unchanged2();
73977
+ if (!created.key.trim() || /[^\x21-\x7e]/.test(created.key) || created.key.length > 8192)
73978
+ return fail2("The server returned an invalid API key.");
73979
+ const body = credentialBody(created.key);
73980
+ if (Buffer.byteLength(body, "utf8") > 65536)
73981
+ return fail2("The new profile exceeds the credential reader size limit.");
73982
+ const identity2 = JSON.stringify({ email: verified.user.email, userId: verified.user.id, orgId: verified.organization.id, orgSlug: verified.organization.slug, apiUrl: origin }, null, 2) + `
73983
+ `;
73984
+ mkdirSync14(dirname10(file), { recursive: true, mode: 448 });
73985
+ const temp = mkdtempSync4(join32(dirname10(file), ".workspace-login-"));
73986
+ let identityInstalled = false;
73987
+ try {
73988
+ writeFileSync14(join32(temp, "credentials"), body, { mode: 384, flag: "wx" });
73989
+ writeFileSync14(join32(temp, "identity"), identity2, { mode: 384, flag: "wx" });
73990
+ unchangedFiles();
73991
+ for (const [path, before2] of parents) {
73992
+ const now3 = stat(path);
73993
+ if (!now3 || !now3.isDirectory() || now3.isSymbolicLink() || before2 && (before2.dev !== now3.dev || before2.ino !== now3.ino))
73994
+ changed();
73995
+ if (!before2 && now3 && ((now3.mode & 63) !== 0 || process.getuid && now3.uid !== process.getuid()))
73996
+ changed();
73997
+ }
73998
+ renameSync6(join32(temp, "identity"), identityFile);
73999
+ identityInstalled = true;
74000
+ renameSync6(join32(temp, "credentials"), file);
74001
+ } catch (error2) {
74002
+ if (identityInstalled) {
74003
+ if (oldIdentity === null)
74004
+ rmSync7(identityFile, { force: true });
74005
+ else {
74006
+ writeFileSync14(join32(temp, "restore"), oldIdentity, { mode: 384, flag: "wx" });
74007
+ renameSync6(join32(temp, "restore"), identityFile);
74008
+ }
74009
+ }
74010
+ throw error2;
74011
+ } finally {
74012
+ rmSync7(temp, { recursive: true, force: true });
74013
+ }
74014
+ return {
74015
+ status: "authenticated",
74016
+ profile,
74017
+ apiUrl: origin,
74018
+ userId: verified.user.id,
74019
+ email: verified.user.email,
74020
+ membershipId,
74021
+ organization: verified.organization.slug,
74022
+ organizationId: verified.organization.id,
74023
+ role: verified.user.role,
74024
+ keyCreated: true
74025
+ };
74026
+ } catch (error2) {
74027
+ if (issued)
74028
+ return fail2("Workspace key issuance was attempted, but enrollment could not be confirmed. Inspect the selected profile and workspace keys before retrying; do not retry automatically.");
74029
+ if (error2 instanceof WorkspaceProfileError)
74030
+ throw error2;
74031
+ return fail2("Unable to verify and select the workspace. Check the account, membership, selected server and fresh code. No enrollment key was requested.");
74032
+ }
74033
+ }
74034
+ };
74035
+ }
74036
+ var WorkspaceProfileError, fail2 = (message) => {
74037
+ throw new WorkspaceProfileError(message);
74038
+ }, changed = () => fail2("The selected profile changed during sign-in. No credential was saved; retry with a stable profile.");
74039
+ var init_workspace_profile = __esm(() => {
74040
+ init_auth_store();
74041
+ init_fleet_credentials();
74042
+ init_instance_credentials();
74043
+ init_remote_client();
74044
+ init_remote_auth();
74045
+ init_remote_workspace_selection();
74046
+ WorkspaceProfileError = class WorkspaceProfileError extends Error {
74047
+ };
74048
+ });
74049
+
73523
74050
  // src/mcp/remote-customer-tools.ts
73524
74051
  function registerRemoteCustomerTools(server) {
73525
74052
  const memberRole = exports_external.enum(["owner", "admin", "member", "viewer"]);
@@ -73529,13 +74056,25 @@ function registerRemoteCustomerTools(server) {
73529
74056
  email: exports_external.string().email(),
73530
74057
  code: exports_external.string().regex(/^\d{6}$/)
73531
74058
  };
74059
+ server.registerTool("leave_workspace", {
74060
+ title: "Leave Current Workspace",
74061
+ 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.",
74062
+ annotations: { destructiveHint: true, idempotentHint: false, readOnlyHint: false },
74063
+ inputSchema: exports_external.object({ ...memberInput, userId: memberInput.membershipId, confirm: exports_external.literal(true) }).strict()
74064
+ }, async ({ membershipId, userId, expectedRole, email: email2, code, confirm }) => {
74065
+ try {
74066
+ return mcpJson(await freshAccount("Leave workspace", (client, context) => client.leaveWorkspace(email2, code, workspaceLeaveProfileContext(membershipId, userId, context), { expectedRole, confirm })));
74067
+ } catch (error2) {
74068
+ 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.");
74069
+ }
74070
+ });
73532
74071
  server.registerTool("set_workspace_member_role", {
73533
74072
  title: "Set Current Workspace Member Role",
73534
74073
  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.",
73535
74074
  inputSchema: exports_external.object({ ...memberInput, role: memberRole }).strict()
73536
- }, async ({ membershipId, role, expectedRole, email: email2, code }) => {
74075
+ }, async ({ membershipId, role: role2, expectedRole, email: email2, code }) => {
73537
74076
  try {
73538
- return mcpJson(await new RemoteSkillsAuthClient(getApiUrl("Set workspace member role")).setWorkspaceMemberRole(email2, code, membershipId, { role, expectedRole }));
74077
+ return mcpJson(await freshAccount("Set workspace member role", (client, context) => client.setWorkspaceMemberRole(email2, code, membershipId, { role: role2, expectedRole }, context)));
73539
74078
  } catch (error2) {
73540
74079
  return memberError(error2);
73541
74080
  }
@@ -73546,7 +74085,7 @@ function registerRemoteCustomerTools(server) {
73546
74085
  inputSchema: exports_external.object(memberInput).strict()
73547
74086
  }, async ({ membershipId, expectedRole, email: email2, code }) => {
73548
74087
  try {
73549
- return mcpJson(await new RemoteSkillsAuthClient(getApiUrl("Remove workspace member")).removeWorkspaceMember(email2, code, membershipId, { expectedRole }));
74088
+ return mcpJson(await freshAccount("Remove workspace member", (client, context) => client.removeWorkspaceMember(email2, code, membershipId, { expectedRole }, context)));
73550
74089
  } catch (error2) {
73551
74090
  return memberError(error2);
73552
74091
  }
@@ -73562,7 +74101,7 @@ function registerRemoteCustomerTools(server) {
73562
74101
  }).strict()
73563
74102
  }, async ({ email: email2, code, limit, cursor: cursor2 }) => {
73564
74103
  try {
73565
- return mcpJson(await new RemoteSkillsAuthClient(getApiUrl("List workspace members")).listWorkspaceMembers(email2, code, { limit, cursor: cursor2 }));
74104
+ return mcpJson(await freshAccount("List workspace members", (client, context) => client.listWorkspaceMembers(email2, code, { limit, cursor: cursor2 }, context)));
73566
74105
  } catch {
73567
74106
  return mcpError("WORKSPACE_MEMBERS_FAILED", "Unable to list workspace members. Check the selected server, owner/admin permissions, pagination and fresh verification code.");
73568
74107
  }
@@ -73574,8 +74113,7 @@ function registerRemoteCustomerTools(server) {
73574
74113
  inputSchema: exports_external.object({ name: exports_external.string().min(1), email: exports_external.string().email(), code: exports_external.string().regex(/^\d{6}$/) }).strict()
73575
74114
  }, async ({ name, email: email2, code }) => {
73576
74115
  try {
73577
- const client = new RemoteSkillsAuthClient(getApiUrl("Update customer name"));
73578
- return mcpJson(kind === "profile" ? await client.updateProfile(email2, code, { displayName: name }) : await client.updateCurrentWorkspace(email2, code, { name }));
74116
+ 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)));
73579
74117
  } catch {
73580
74118
  return mcpError("NAME_UPDATE_FAILED", "Unable to update the name. Check the selected server, name, permissions and fresh verification code.");
73581
74119
  }
@@ -73597,9 +74135,9 @@ function registerRemoteCustomerTools(server) {
73597
74135
  inputSchema: { email: exports_external.string().email(), code: exports_external.string().regex(/^\d{6}$/) }
73598
74136
  }, async ({ email: email2, code }) => {
73599
74137
  try {
73600
- return mcpJson(await new RemoteSkillsAuthClient(getApiUrl("List API keys")).listApiKeys(email2, code));
73601
- } catch (error2) {
73602
- return mcpError("KEY_LIST_FAILED", error2.message);
74138
+ return mcpJson(await freshAccount("List API keys", (client, context) => client.listApiKeys(email2, code, context)));
74139
+ } catch {
74140
+ return mcpError("KEY_LIST_FAILED", "Unable to list API keys. Check the selected profile, server, account and fresh verification code.");
73603
74141
  }
73604
74142
  });
73605
74143
  server.registerTool("revoke_api_key", {
@@ -73608,9 +74146,9 @@ function registerRemoteCustomerTools(server) {
73608
74146
  inputSchema: { key_id: exports_external.string().min(1), email: exports_external.string().email(), code: exports_external.string().regex(/^\d{6}$/) }
73609
74147
  }, async ({ key_id, email: email2, code }) => {
73610
74148
  try {
73611
- return mcpJson(await new RemoteSkillsAuthClient(getApiUrl("Revoke API key")).revokeApiKey(email2, code, key_id));
73612
- } catch (error2) {
73613
- return mcpError("KEY_REVOKE_FAILED", error2.message);
74149
+ return mcpJson(await freshAccount("Revoke API key", (client, context) => client.revokeApiKey(email2, code, key_id, context)));
74150
+ } catch {
74151
+ return mcpError("KEY_REVOKE_FAILED", "Unable to revoke this API key. Check the selected profile, key, account and fresh verification code.");
73614
74152
  }
73615
74153
  });
73616
74154
  server.registerTool("create_api_key", {
@@ -73618,10 +74156,11 @@ function registerRemoteCustomerTools(server) {
73618
74156
  description: "Create an API key using fresh email OTP reauthentication; returns its secret once. A stored API key cannot grant this authority.",
73619
74157
  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() }
73620
74158
  }, async ({ name, email: email2, code, scopes }) => {
74159
+ const capturedScopes = scopes === undefined ? undefined : [...scopes];
73621
74160
  try {
73622
- return mcpJson(await new RemoteSkillsAuthClient(getApiUrl("Create API key")).createApiKey(email2, code, name, scopes));
73623
- } catch (error2) {
73624
- return mcpError("KEY_CREATION_FAILED", error2.message);
74161
+ return mcpJson(await freshAccount("Create API key", (client, context) => client.createApiKey(email2, code, name, capturedScopes, context)));
74162
+ } catch {
74163
+ 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.");
73625
74164
  }
73626
74165
  });
73627
74166
  server.registerTool("quote_skill", {
@@ -73655,12 +74194,18 @@ async function callRemote(action) {
73655
74194
  return mcpError("REMOTE_REQUEST_FAILED", error2 instanceof Error ? error2.message : "Skills server request failed");
73656
74195
  }
73657
74196
  }
74197
+ async function freshAccount(action, operation) {
74198
+ const target = await captureProfileWorkspace(action);
74199
+ target.unchanged();
74200
+ return operation(new RemoteSkillsAuthClient(target.origin), target.context);
74201
+ }
73658
74202
  var init_remote_customer_tools = __esm(() => {
73659
74203
  init_zod();
73660
74204
  init_remote_auth();
73661
- init_auth_store();
74205
+ init_workspace_profile();
73662
74206
  init_remote_customer_operations();
73663
74207
  init_remote_client();
74208
+ init_remote_workspace_leave();
73664
74209
  init_helpers();
73665
74210
  });
73666
74211
 
@@ -74106,9 +74651,9 @@ var init_mcp2 = __esm(() => {
74106
74651
  });
74107
74652
 
74108
74653
  // src/cli/commands/runtime-mcp.ts
74109
- import { existsSync as existsSync30, mkdirSync as mkdirSync14, readFileSync as readFileSync25, writeFileSync as writeFileSync14 } from "fs";
74654
+ import { existsSync as existsSync30, mkdirSync as mkdirSync15, readFileSync as readFileSync26, writeFileSync as writeFileSync15 } from "fs";
74110
74655
  import { homedir as homedir7 } from "os";
74111
- import { dirname as dirname10, join as join32 } from "path";
74656
+ import { dirname as dirname11, join as join33 } from "path";
74112
74657
  async function handleMcp(options) {
74113
74658
  if (options.register) {
74114
74659
  let agents;
@@ -74151,24 +74696,24 @@ async function registerMcpForAgent(agent, command) {
74151
74696
  case "codex":
74152
74697
  return registerCodexMcp(command);
74153
74698
  case "gemini":
74154
- return registerJsonMcpServer(agent, join32(homedir7(), ".gemini", "settings.json"), "mcpServers", {
74699
+ return registerJsonMcpServer(agent, join33(homedir7(), ".gemini", "settings.json"), "mcpServers", {
74155
74700
  command,
74156
74701
  args: []
74157
74702
  });
74158
74703
  case "pi":
74159
- return registerJsonMcpServer(agent, join32(homedir7(), ".pi", "agent", "mcp.json"), "mcpServers", {
74704
+ return registerJsonMcpServer(agent, join33(homedir7(), ".pi", "agent", "mcp.json"), "mcpServers", {
74160
74705
  command,
74161
74706
  args: []
74162
74707
  });
74163
74708
  case "opencode":
74164
74709
  return registerOpenCodeMcp(command);
74165
74710
  case "cursor":
74166
- return registerJsonMcpServer(agent, join32(homedir7(), ".cursor", "mcp.json"), "mcpServers", {
74711
+ return registerJsonMcpServer(agent, join33(homedir7(), ".cursor", "mcp.json"), "mcpServers", {
74167
74712
  command,
74168
74713
  args: []
74169
74714
  });
74170
74715
  case "windsurf":
74171
- return registerJsonMcpServer(agent, join32(homedir7(), ".windsurf", "mcp.json"), "mcpServers", {
74716
+ return registerJsonMcpServer(agent, join33(homedir7(), ".windsurf", "mcp.json"), "mcpServers", {
74172
74717
  command,
74173
74718
  args: []
74174
74719
  });
@@ -74191,7 +74736,7 @@ async function registerClaudeMcp(command) {
74191
74736
  if (exitCode === 0) {
74192
74737
  return { agent: "claude", success: true, command: cliCommand };
74193
74738
  }
74194
- const fallback = registerJsonMcpServer("claude", join32(homedir7(), ".claude", ".mcp.json"), "mcpServers", {
74739
+ const fallback = registerJsonMcpServer("claude", join33(homedir7(), ".claude", ".mcp.json"), "mcpServers", {
74195
74740
  command,
74196
74741
  args: []
74197
74742
  });
@@ -74201,7 +74746,7 @@ async function registerClaudeMcp(command) {
74201
74746
  error: fallback.success ? undefined : `claude exited with ${exitCode}: ${(stderr || stdout).trim()}`
74202
74747
  };
74203
74748
  } catch (err) {
74204
- const fallback = registerJsonMcpServer("claude", join32(homedir7(), ".claude", ".mcp.json"), "mcpServers", {
74749
+ const fallback = registerJsonMcpServer("claude", join33(homedir7(), ".claude", ".mcp.json"), "mcpServers", {
74205
74750
  command,
74206
74751
  args: []
74207
74752
  });
@@ -74213,11 +74758,11 @@ async function registerClaudeMcp(command) {
74213
74758
  }
74214
74759
  }
74215
74760
  function registerCodexMcp(command) {
74216
- const path = join32(homedir7(), ".codex", "config.toml");
74761
+ const path = join33(homedir7(), ".codex", "config.toml");
74217
74762
  const config2 = `[mcp_servers.${MCP_SERVER_NAME}]
74218
74763
  command = ${JSON.stringify(command)}`;
74219
74764
  try {
74220
- const current = existsSync30(path) ? readFileSync25(path, "utf-8") : "";
74765
+ const current = existsSync30(path) ? readFileSync26(path, "utf-8") : "";
74221
74766
  writeTextFile(path, upsertTomlSection(current, `[mcp_servers.${MCP_SERVER_NAME}]`, `command = ${JSON.stringify(command)}`));
74222
74767
  return { agent: "codex", success: true, path, config: config2 };
74223
74768
  } catch (err) {
@@ -74225,7 +74770,7 @@ command = ${JSON.stringify(command)}`;
74225
74770
  }
74226
74771
  }
74227
74772
  function registerOpenCodeMcp(command) {
74228
- const path = join32(homedir7(), ".config", "opencode", "opencode.json");
74773
+ const path = join33(homedir7(), ".config", "opencode", "opencode.json");
74229
74774
  const config2 = JSON.stringify({
74230
74775
  $schema: "https://opencode.ai/config.json",
74231
74776
  mcp: {
@@ -74269,7 +74814,7 @@ function registerJsonMcpServer(agent, path, containerKey, server2) {
74269
74814
  function readJsonObject2(path) {
74270
74815
  if (!existsSync30(path))
74271
74816
  return {};
74272
- const raw = readFileSync25(path, "utf-8").trim();
74817
+ const raw = readFileSync26(path, "utf-8").trim();
74273
74818
  if (!raw)
74274
74819
  return {};
74275
74820
  const parsed = JSON.parse(raw);
@@ -74282,8 +74827,8 @@ function writeJsonObject(path, data) {
74282
74827
  `);
74283
74828
  }
74284
74829
  function writeTextFile(path, content) {
74285
- mkdirSync14(dirname10(path), { recursive: true });
74286
- writeFileSync14(path, content.endsWith(`
74830
+ mkdirSync15(dirname11(path), { recursive: true });
74831
+ writeFileSync15(path, content.endsWith(`
74287
74832
  `) ? content : `${content}
74288
74833
  `);
74289
74834
  }
@@ -74313,7 +74858,7 @@ function findCommandOnPath(command) {
74313
74858
  for (const dir of pathValue.split(":")) {
74314
74859
  if (!dir)
74315
74860
  continue;
74316
- const candidate = join32(dir, command);
74861
+ const candidate = join33(dir, command);
74317
74862
  if (existsSync30(candidate))
74318
74863
  return candidate;
74319
74864
  }
@@ -74375,8 +74920,8 @@ var exports_runtime = {};
74375
74920
  __export(exports_runtime, {
74376
74921
  registerRuntime: () => registerRuntime
74377
74922
  });
74378
- import { lstatSync as lstatSync6, mkdirSync as mkdirSync15, readFileSync as readFileSync26, realpathSync as realpathSync2, writeFileSync as writeFileSync15 } from "fs";
74379
- import { basename as basename5, isAbsolute as isAbsolute4, join as join33 } from "path";
74923
+ import { lstatSync as lstatSync7, mkdirSync as mkdirSync16, readFileSync as readFileSync27, realpathSync as realpathSync2, writeFileSync as writeFileSync16 } from "fs";
74924
+ import { basename as basename5, isAbsolute as isAbsolute4, join as join34 } from "path";
74380
74925
  import { createInterface } from "readline";
74381
74926
  function registerRuntime(parent) {
74382
74927
  parent.command("run").argument("<skill>", "Skill name").argument("[args...]", "Arguments to pass to the skill").allowUnknownOption(true).passThroughOptions(true).option("--json", "Output result as JSON", false).option("--remote", "Run on the configured server, using its catalog and quote", false).option("--yes", "Approve the server's quoted credit cost for this run", false).option("--idempotency-key <key>", "Reuse this key when retrying the same remote submission").option("--file <path>", "Attach a local input file to a remote run (repeatable)", (value, prior) => [...prior, value], []).option("--wait", "Poll remote runs until a terminal status", false).option("--poll-interval-ms <ms>", "Remote polling interval in milliseconds", "1000").option("--poll-timeout-ms <ms>", "Maximum time to wait for a remote run", "300000").description("Run a skill directly").action(async (name, args2, options) => handleRun(name, args2, options));
@@ -74650,10 +75195,10 @@ async function handleRun(name, args2, options) {
74650
75195
  try {
74651
75196
  parsePollingOptions(options);
74652
75197
  inputFiles = (options.file ?? []).map((path) => {
74653
- const info = lstatSync6(path);
75198
+ const info = lstatSync7(path);
74654
75199
  if (!info.isFile() || info.size > 20 * 1024 * 1024)
74655
75200
  throw new Error("Input must be a regular file no larger than 20 MiB");
74656
- return { name: basename5(path), bytes: new Uint8Array(readFileSync26(path)) };
75201
+ return { name: basename5(path), bytes: new Uint8Array(readFileSync27(path)) };
74657
75202
  });
74658
75203
  describeRemoteFiles(inputFiles);
74659
75204
  client = new RemoteSkillsClient(routing.apiKey, routing.apiOrigin);
@@ -75024,7 +75569,7 @@ async function handleExportsDownload(runId, options) {
75024
75569
  const canonicalSkill = typeof remoteRun.skill === "string" ? remoteRun.skill : "remote";
75025
75570
  const requestedSkill = typeof remoteRun.requestedSlug === "string" && remoteRun.requestedSlug.trim() ? remoteRun.requestedSlug : canonicalSkill;
75026
75571
  const exportDir = getRunExportDir(runId, requestedSkill);
75027
- mkdirSync15(exportDir, { recursive: true });
75572
+ mkdirSync16(exportDir, { recursive: true });
75028
75573
  const downloaded = [];
75029
75574
  for (const artifact of artifacts) {
75030
75575
  const artifactId = String(artifact.id || "");
@@ -75032,9 +75577,9 @@ async function handleExportsDownload(runId, options) {
75032
75577
  continue;
75033
75578
  const verified = await client.getVerifiedRunArtifact(runId, artifactId);
75034
75579
  const relativePath = safeArtifactRelativePath(typeof artifact.relativePath === "string" ? artifact.relativePath : artifact.fileName, String(artifact.fileName || artifactId));
75035
- const outputPath = join33(exportDir, relativePath);
75580
+ const outputPath = join34(exportDir, relativePath);
75036
75581
  ensureSafeExportParent(exportDir, relativePath);
75037
- writeFileSync15(outputPath, verified.bytes, { flag: "wx", mode: 384 });
75582
+ writeFileSync16(outputPath, verified.bytes, { flag: "wx", mode: 384 });
75038
75583
  downloaded.push({ id: artifactId, path: outputPath, byteSize: verified.byteSize });
75039
75584
  }
75040
75585
  const payload = {
@@ -75071,14 +75616,14 @@ function ensureSafeExportParent(root, relativePath) {
75071
75616
  let parent = root;
75072
75617
  for (const part of ["", ...parts]) {
75073
75618
  if (part)
75074
- parent = join33(parent, part);
75619
+ parent = join34(parent, part);
75075
75620
  try {
75076
- if (!lstatSync6(parent).isDirectory() || lstatSync6(parent).isSymbolicLink())
75621
+ if (!lstatSync7(parent).isDirectory() || lstatSync7(parent).isSymbolicLink())
75077
75622
  throw new Error("Unsafe artifact directory");
75078
75623
  } catch (error2) {
75079
75624
  if (error2.code !== "ENOENT")
75080
75625
  throw error2;
75081
- mkdirSync15(parent, { mode: 448 });
75626
+ mkdirSync16(parent, { mode: 448 });
75082
75627
  }
75083
75628
  }
75084
75629
  }
@@ -75416,7 +75961,7 @@ var init_completion = __esm(() => {
75416
75961
  // src/lib/portable-snapshot-filter.ts
75417
75962
  import { readdirSync as readdirSync16, statSync as statSync17 } from "fs";
75418
75963
  import { homedir as homedir8 } from "os";
75419
- import { join as join34, sep as sep3 } from "path";
75964
+ import { join as join35, sep as sep3 } from "path";
75420
75965
  function isExcludedSkillFileName(fileName) {
75421
75966
  if (EXCLUDE_FILE_NAMES.has(fileName)) {
75422
75967
  return true;
@@ -75439,16 +75984,16 @@ function isPortableWithinSkill(relativeParts) {
75439
75984
  function homePathFor(definition, homesRoot) {
75440
75985
  const home = homesRoot ?? homedir8();
75441
75986
  if (definition.subClass === "skills" || definition.subClass === "custom") {
75442
- return join34(skillsDataRootForHome(home), definition.name);
75987
+ return join35(skillsDataRootForHome(home), definition.name);
75443
75988
  }
75444
75989
  if (definition.agent === "opencode") {
75445
- return join34(home, ".config", "opencode", "skills");
75990
+ return join35(home, ".config", "opencode", "skills");
75446
75991
  }
75447
- return join34(home, `.${definition.agent}`, "skills");
75992
+ return join35(home, `.${definition.agent}`, "skills");
75448
75993
  }
75449
75994
  function destinationFor(definition, stationId, relativePath) {
75450
- const category = definition.subClass === "agent-homes" ? join34("agent-homes", definition.agent ?? "") : definition.name;
75451
- return join34("resources", stationId, "skills", category, ...relativePath.split(sep3));
75995
+ const category = definition.subClass === "agent-homes" ? join35("agent-homes", definition.agent ?? "") : definition.name;
75996
+ return join35("resources", stationId, "skills", category, ...relativePath.split(sep3));
75452
75997
  }
75453
75998
  function walkEntries(absoluteRoot) {
75454
75999
  let entries;
@@ -75459,7 +76004,7 @@ function walkEntries(absoluteRoot) {
75459
76004
  }
75460
76005
  const output = [];
75461
76006
  for (const entry of entries) {
75462
- const childFull = join34(absoluteRoot, entry.name);
76007
+ const childFull = join35(absoluteRoot, entry.name);
75463
76008
  if (entry.isSymbolicLink()) {
75464
76009
  output.push({ kind: "symlink", relativePath: entry.name, fullPath: childFull });
75465
76010
  continue;
@@ -75470,7 +76015,7 @@ function walkEntries(absoluteRoot) {
75470
76015
  }
75471
76016
  const nested = walkEntries(childFull);
75472
76017
  for (const item of nested) {
75473
- output.push({ ...item, relativePath: join34(entry.name, item.relativePath) });
76018
+ output.push({ ...item, relativePath: join35(entry.name, item.relativePath) });
75474
76019
  }
75475
76020
  continue;
75476
76021
  }
@@ -75566,19 +76111,19 @@ var init_portable_snapshot_filter = __esm(() => {
75566
76111
  import { createHash as createHash7 } from "crypto";
75567
76112
  import {
75568
76113
  copyFileSync as copyFileSync2,
75569
- mkdirSync as mkdirSync16,
75570
- readFileSync as readFileSync27,
76114
+ mkdirSync as mkdirSync17,
76115
+ readFileSync as readFileSync28,
75571
76116
  statSync as statSync18,
75572
- writeFileSync as writeFileSync16
76117
+ writeFileSync as writeFileSync17
75573
76118
  } from "fs";
75574
- import { dirname as dirname12, isAbsolute as isAbsolute5, relative as relative5, resolve as resolve3, sep as sep4 } from "path";
76119
+ import { dirname as dirname13, isAbsolute as isAbsolute5, relative as relative5, resolve as resolve3, sep as sep4 } from "path";
75575
76120
  function validateStationId(stationId) {
75576
76121
  if (!/^[a-z0-9-]+$/.test(stationId)) {
75577
76122
  throw new StationSnapshotError("INVALID_STATION", `station id must be a slug, got: ${stationId}`);
75578
76123
  }
75579
76124
  }
75580
76125
  function sha256File(filePath) {
75581
- return createHash7("sha256").update(readFileSync27(filePath)).digest("hex");
76126
+ return createHash7("sha256").update(readFileSync28(filePath)).digest("hex");
75582
76127
  }
75583
76128
  function scanHome(definition, homesRoot) {
75584
76129
  const homePath = homePathFor(definition, homesRoot);
@@ -75699,7 +76244,7 @@ function writeStationSnapshot(options) {
75699
76244
  let written = 0;
75700
76245
  for (const plan of untouched) {
75701
76246
  const destination = resolve3(repoRoot, plan.destination);
75702
- mkdirSync16(dirname12(destination), { recursive: true });
76247
+ mkdirSync17(dirname13(destination), { recursive: true });
75703
76248
  copyFileSync2(plan.source.fullPath, destination);
75704
76249
  written += 1;
75705
76250
  }
@@ -75718,8 +76263,8 @@ function writeStationSnapshot(options) {
75718
76263
  files: manifestFiles
75719
76264
  };
75720
76265
  const manifestPath = resolve3(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
75721
- mkdirSync16(dirname12(manifestPath), { recursive: true });
75722
- writeFileSync16(manifestPath, `${JSON.stringify(manifest, null, 2)}
76266
+ mkdirSync17(dirname13(manifestPath), { recursive: true });
76267
+ writeFileSync17(manifestPath, `${JSON.stringify(manifest, null, 2)}
75723
76268
  `);
75724
76269
  return {
75725
76270
  ...base2,
@@ -75751,7 +76296,7 @@ __export(exports_create_sync_config, {
75751
76296
  registerCreateSync: () => registerCreateSync
75752
76297
  });
75753
76298
  import { existsSync as existsSync31 } from "fs";
75754
- import { join as join35 } from "path";
76299
+ import { join as join36 } from "path";
75755
76300
  function registerCreateSync(parent) {
75756
76301
  const configCmd = parent.command("config").description("Manage skills configuration");
75757
76302
  configCmd.command("show", { isDefault: true }).option("--json", "Output as JSON", false).description("Show current merged configuration").action((options) => {
@@ -75857,7 +76402,7 @@ function handleCreate(name, options) {
75857
76402
  console.log(source_default.green(`\u2713 Created custom skill '${result2.name}' at ${result2.path}`));
75858
76403
  console.log(source_default.dim(` Category: ${result2.manifest.category}`));
75859
76404
  console.log(source_default.dim(` Tags: ${result2.manifest.tags?.join(", ")}`));
75860
- console.log(` ${source_default.cyan("Edit:")} ${join35(result2.path, "src", "index.ts")}`);
76405
+ console.log(` ${source_default.cyan("Edit:")} ${join36(result2.path, "src", "index.ts")}`);
75861
76406
  console.log(` ${source_default.cyan("Run:")} skills run ${result2.name} --help`);
75862
76407
  }
75863
76408
  } catch (error2) {
@@ -76136,27 +76681,27 @@ var init_create_sync_config = __esm(() => {
76136
76681
  import { createHash as createHash8 } from "crypto";
76137
76682
  import {
76138
76683
  copyFileSync as copyFileSync3,
76139
- mkdirSync as mkdirSync17,
76684
+ mkdirSync as mkdirSync18,
76140
76685
  readdirSync as readdirSync17,
76141
- readFileSync as readFileSync28,
76686
+ readFileSync as readFileSync29,
76142
76687
  statSync as statSync19,
76143
- writeFileSync as writeFileSync17
76688
+ writeFileSync as writeFileSync18
76144
76689
  } from "fs";
76145
- import { dirname as dirname13, join as join36, resolve as resolve4, sep as sep5 } from "path";
76146
- function fail2(code, message, detail = []) {
76690
+ import { dirname as dirname14, join as join37, resolve as resolve4, sep as sep5 } from "path";
76691
+ function fail3(code, message, detail = []) {
76147
76692
  throw new StationSnapshotError(code, message, detail);
76148
76693
  }
76149
76694
  function snapshotRootFor(repoRoot, stationId) {
76150
- return join36(repoRoot, "resources", stationId, "skills");
76695
+ return join37(repoRoot, "resources", stationId, "skills");
76151
76696
  }
76152
76697
  function readSnapshotManifest(repoRoot, stationId) {
76153
76698
  const snapshotRoot = snapshotRootFor(repoRoot, stationId);
76154
- const manifestPath = join36(snapshotRoot, "sync-manifest.json");
76699
+ const manifestPath = join37(snapshotRoot, "sync-manifest.json");
76155
76700
  let manifest;
76156
76701
  try {
76157
- manifest = JSON.parse(readFileSync28(manifestPath, "utf8"));
76702
+ manifest = JSON.parse(readFileSync29(manifestPath, "utf8"));
76158
76703
  } catch (error2) {
76159
- fail2("MANIFEST_UNREADABLE", `cannot read snapshot manifest: ${manifestPath}: ${error2.message}`);
76704
+ fail3("MANIFEST_UNREADABLE", `cannot read snapshot manifest: ${manifestPath}: ${error2.message}`);
76160
76705
  }
76161
76706
  const sourceSnapshotSha = sha256File(manifestPath);
76162
76707
  return { manifest, manifestPath, sourceSnapshotSha };
@@ -76178,7 +76723,7 @@ function planStationHydration(stationId, repoRoot) {
76178
76723
  const hashMismatches = [];
76179
76724
  const skippedByRule = [];
76180
76725
  for (const agent of SYNC_AGENTS) {
76181
- const agentRoot = join36(snapshotRoot, "agent-homes", agent);
76726
+ const agentRoot = join37(snapshotRoot, "agent-homes", agent);
76182
76727
  let identEntries;
76183
76728
  try {
76184
76729
  identEntries = readdirSync17(agentRoot, { withFileTypes: true });
@@ -76189,7 +76734,7 @@ function planStationHydration(stationId, repoRoot) {
76189
76734
  if (!identEntry.isDirectory() || identEntry.name.startsWith(".")) {
76190
76735
  continue;
76191
76736
  }
76192
- const identRoot = join36(agentRoot, identEntry.name);
76737
+ const identRoot = join37(agentRoot, identEntry.name);
76193
76738
  const entries = walkEntries(identRoot);
76194
76739
  for (const entry of entries) {
76195
76740
  const relativeParts = [identEntry.name, ...entry.relativePath.split(sep5)];
@@ -76263,10 +76808,10 @@ function planStationHydration(stationId, repoRoot) {
76263
76808
  }
76264
76809
  }
76265
76810
  if (symlinks.length > 0) {
76266
- fail2("SYMLINKS_REFUSED", `${symlinks.length} symlink(s) inside the snapshot; symlinks are refused (fail closed)`);
76811
+ fail3("SYMLINKS_REFUSED", `${symlinks.length} symlink(s) inside the snapshot; symlinks are refused (fail closed)`);
76267
76812
  }
76268
76813
  if (hashMismatches.length > 0) {
76269
- fail2("MANIFEST_HASH_MISMATCH", `${hashMismatches.length} snapshot file(s) no longer match their sync-manifest sha256; ` + "stale or tampered content is refused (fail closed), nothing written \u2014 re-run sync to refresh the manifest", hashMismatches.map((mismatch) => `agent-homes/${mismatch.agent}/${mismatch.ident}/${mismatch.relativePath}`));
76814
+ fail3("MANIFEST_HASH_MISMATCH", `${hashMismatches.length} snapshot file(s) no longer match their sync-manifest sha256; ` + "stale or tampered content is refused (fail closed), nothing written \u2014 re-run sync to refresh the manifest", hashMismatches.map((mismatch) => `agent-homes/${mismatch.agent}/${mismatch.ident}/${mismatch.relativePath}`));
76270
76815
  }
76271
76816
  const byIdent = new Map;
76272
76817
  for (const candidate of candidates) {
@@ -76290,7 +76835,7 @@ function planStationHydration(stationId, repoRoot) {
76290
76835
  for (const copy of copies) {
76291
76836
  let isStub = false;
76292
76837
  try {
76293
- isStub = isPointerSkillMd(readFileSync28(copy.fullPath, "utf8"));
76838
+ isStub = isPointerSkillMd(readFileSync29(copy.fullPath, "utf8"));
76294
76839
  } catch {
76295
76840
  isStub = false;
76296
76841
  }
@@ -76374,7 +76919,7 @@ function writeStationHydration(options) {
76374
76919
  const toWrite = [];
76375
76920
  for (const skill of plan.winners) {
76376
76921
  for (const file of skill.files) {
76377
- const destination = join36(cacheRoot, skill.ident, file.withinIdent);
76922
+ const destination = join37(cacheRoot, skill.ident, file.withinIdent);
76378
76923
  const digest = sha256File(file.winner.fullPath);
76379
76924
  let existingDigest = null;
76380
76925
  try {
@@ -76391,11 +76936,11 @@ function writeStationHydration(options) {
76391
76936
  }
76392
76937
  }
76393
76938
  if (conflicts.length > 0) {
76394
- fail2("CONFLICT", `${conflicts.length} conflict(s); terminal non-acceptance, nothing written`, conflicts);
76939
+ fail3("CONFLICT", `${conflicts.length} conflict(s); terminal non-acceptance, nothing written`, conflicts);
76395
76940
  }
76396
76941
  let written = 0;
76397
76942
  for (const entry of toWrite) {
76398
- mkdirSync17(dirname13(entry.destination), { recursive: true });
76943
+ mkdirSync18(dirname14(entry.destination), { recursive: true });
76399
76944
  copyFileSync3(entry.fullPath, entry.destination);
76400
76945
  written += 1;
76401
76946
  }
@@ -76416,9 +76961,9 @@ function writeStationHydration(options) {
76416
76961
  },
76417
76962
  skills: resultSkills
76418
76963
  };
76419
- const hydrationManifestPath = join36(dirname13(cacheRoot), `hydration-${options.stationId}.json`);
76420
- mkdirSync17(dirname13(hydrationManifestPath), { recursive: true });
76421
- writeFileSync17(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
76964
+ const hydrationManifestPath = join37(dirname14(cacheRoot), `hydration-${options.stationId}.json`);
76965
+ mkdirSync18(dirname14(hydrationManifestPath), { recursive: true });
76966
+ writeFileSync18(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
76422
76967
  `);
76423
76968
  return {
76424
76969
  ...base2,
@@ -76846,8 +77391,8 @@ var init_schedule = __esm(() => {
76846
77391
  });
76847
77392
 
76848
77393
  // src/lib/registry-sync.ts
76849
- import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync18 } from "fs";
76850
- import { dirname as dirname14, relative as relative6 } from "path";
77394
+ import { mkdirSync as mkdirSync19, writeFileSync as writeFileSync19 } from "fs";
77395
+ import { dirname as dirname15, relative as relative6 } from "path";
76851
77396
  function createRegistrySyncArtifact(options = {}) {
76852
77397
  const profile = options.profile ?? "all";
76853
77398
  const includeDocs = options.includeDocs ?? true;
@@ -76904,8 +77449,8 @@ function createRegistrySyncArtifact(options = {}) {
76904
77449
  };
76905
77450
  }
76906
77451
  function writeRegistrySyncArtifact(path, artifact) {
76907
- mkdirSync18(dirname14(path), { recursive: true });
76908
- writeFileSync18(path, `${JSON.stringify(artifact, null, 2)}
77452
+ mkdirSync19(dirname15(path), { recursive: true });
77453
+ writeFileSync19(path, `${JSON.stringify(artifact, null, 2)}
76909
77454
  `);
76910
77455
  }
76911
77456
  function buildDocs(name) {
@@ -76935,10 +77480,10 @@ function registerRegistry(parent) {
76935
77480
  registry2.command("sync").description("Generate a deterministic registry sync artifact").option("--profile <profile>", "Registry profile: basic or all", "all").option("--output <path>", "Write artifact to a JSON file").option("--no-docs", "Exclude skill documentation content").option("--no-requirements", "Exclude extracted skill requirements").option("--no-validation", "Exclude validation results").option("--json", "Print artifact JSON to stdout", false).action((options) => handleRegistrySync(options));
76936
77481
  }
76937
77482
  async function writeJson2(value, space) {
76938
- const text = `${JSON.stringify(value, null, space)}
77483
+ const text2 = `${JSON.stringify(value, null, space)}
76939
77484
  `;
76940
77485
  await new Promise((resolve5, reject2) => {
76941
- process.stdout.write(text, (error2) => {
77486
+ process.stdout.write(text2, (error2) => {
76942
77487
  if (error2)
76943
77488
  reject2(error2);
76944
77489
  else
@@ -76970,10 +77515,10 @@ async function handleRegistrySync(options) {
76970
77515
  await writeJson2(artifact, 2);
76971
77516
  return;
76972
77517
  }
76973
- const invalid = artifact.summary.invalidSkillCount ?? "not checked";
77518
+ const invalid2 = artifact.summary.invalidSkillCount ?? "not checked";
76974
77519
  console.log(source_default.green(`Registry sync artifact written to ${options.output}`));
76975
77520
  console.log(source_default.dim(` Skills: ${artifact.summary.skillCount}`));
76976
- console.log(source_default.dim(` Invalid: ${invalid}`));
77521
+ console.log(source_default.dim(` Invalid: ${invalid2}`));
76977
77522
  }
76978
77523
  function registerPull(parent) {
76979
77524
  parent.command("pull").argument("[names...]", "Skills to pull from the configured instance (name or name@version)").option("--all", "Pull every skill the instance serves", false).option("--for-machine", "Prepare this machine with the instance's full catalog (implies --all)", false).option("--json", "Output results as JSON", false).description("Fetch skills from the configured Skills instance into this machine's corpus").action(async (names, options) => {
@@ -77078,9 +77623,9 @@ __export(exports_publish, {
77078
77623
  PushSkillError: () => PushSkillError
77079
77624
  });
77080
77625
  import { execFileSync } from "child_process";
77081
- import { existsSync as existsSync32, readFileSync as readFileSync29 } from "fs";
77626
+ import { existsSync as existsSync32, readFileSync as readFileSync30 } from "fs";
77082
77627
  import { hostname as hostname2 } from "os";
77083
- import { join as join37 } from "path";
77628
+ import { join as join38 } from "path";
77084
77629
  function registerPublish(parent) {
77085
77630
  parent.command("push").argument("<name>", "Name of a skill in the local corpus (~/.hasna/skills/installed or the migrated ~/.hasna/skills/skills)").option("--version <version>", "Override the version recorded on the instance").option("--force-new-version", "If name@version already exists with different content, publish as the next patch version", false).option("--dry-run", "Pack and validate without uploading", false).option("--json", "Output result as JSON", false).description("Publish a local skill to the configured skills instance").action(async (name, options) => {
77086
77631
  try {
@@ -77127,8 +77672,8 @@ async function pushSkill(name, options = {}) {
77127
77672
  }
77128
77673
  const packed = packSkillBundle(skill.path, { maxUnpackedBytes: MAX_UNPACKED_BYTES });
77129
77674
  const versionManifest = buildVersionManifest(skill.path, packed);
77130
- const skillMdPath = join37(skill.path, "SKILL.md");
77131
- const skillMd = existsSync32(skillMdPath) ? readFileSync29(skillMdPath, "utf-8") : undefined;
77675
+ const skillMdPath = join38(skill.path, "SKILL.md");
77676
+ const skillMd = existsSync32(skillMdPath) ? readFileSync30(skillMdPath, "utf-8") : undefined;
77132
77677
  const base2 = {
77133
77678
  slug: skill.name,
77134
77679
  path: skill.path,
@@ -77193,21 +77738,21 @@ async function readPublishRevision(client, slug) {
77193
77738
  throw new PushSkillError("Publishing was refused because the current skill revision could not be verified.", ["Check the configured instance and connection, then retry the push. No upload was attempted."]);
77194
77739
  }
77195
77740
  const body = lookup.body;
77196
- const record4 = body !== null && typeof body === "object" && !Array.isArray(body) ? body : undefined;
77197
- const nestedError = record4?.error;
77198
- const code = typeof record4?.code === "string" ? record4.code : nestedError !== null && typeof nestedError === "object" && !Array.isArray(nestedError) ? nestedError.code : undefined;
77741
+ const record5 = body !== null && typeof body === "object" && !Array.isArray(body) ? body : undefined;
77742
+ const nestedError = record5?.error;
77743
+ const code = typeof record5?.code === "string" ? record5.code : nestedError !== null && typeof nestedError === "object" && !Array.isArray(nestedError) ? nestedError.code : undefined;
77199
77744
  if (lookup.status === 404 && code === "SKILL_NOT_FOUND")
77200
77745
  return;
77201
77746
  if (lookup.status < 200 || lookup.status >= 300) {
77202
77747
  throw new PushSkillError(`Publishing was refused because the current skill lookup failed: HTTP ${lookup.status}.`, ["Only an explicit SKILL_NOT_FOUND response establishes an initial publish. Check access and server compatibility before retrying."]);
77203
77748
  }
77204
- const revision = record4?.revisionId;
77205
- if (record4?.publicationState === "catalogue-only") {
77206
- if (record4.name === slug && (record4.slug === undefined || record4.slug === slug) && revision === null)
77749
+ const revision = record5?.revisionId;
77750
+ if (record5?.publicationState === "catalogue-only") {
77751
+ if (record5.name === slug && (record5.slug === undefined || record5.slug === slug) && revision === null)
77207
77752
  return;
77208
77753
  throw new PushSkillError("Publishing was refused because the catalogue-only response had contradictory identity or revision state.", ["Check the configured instance and server compatibility. No upload was attempted."]);
77209
77754
  }
77210
- if (record4?.slug !== slug || typeof revision !== "string" || revision.length === 0 || revision !== revision.trim() || !/^[\x21-\x7e]+$/.test(revision)) {
77755
+ if (record5?.slug !== slug || typeof revision !== "string" || revision.length === 0 || revision !== revision.trim() || !/^[\x21-\x7e]+$/.test(revision)) {
77211
77756
  throw new PushSkillError("Publishing was refused because the current skill response did not contain a matching slug and valid revision.", ["Check the configured instance and server compatibility. No upload was attempted."]);
77212
77757
  }
77213
77758
  return revision;
@@ -77310,13 +77855,13 @@ Dry run: '${result2.slug}' would be published
77310
77855
  console.log("");
77311
77856
  }
77312
77857
  async function readBody(response) {
77313
- const text = await response.text();
77314
- if (!text.trim())
77858
+ const text2 = await response.text();
77859
+ if (!text2.trim())
77315
77860
  return null;
77316
77861
  try {
77317
- return JSON.parse(text);
77862
+ return JSON.parse(text2);
77318
77863
  } catch {
77319
- return text;
77864
+ return text2;
77320
77865
  }
77321
77866
  }
77322
77867
  function describeError(payload) {
@@ -77349,6 +77894,152 @@ var init_publish = __esm(() => {
77349
77894
  };
77350
77895
  });
77351
77896
 
77897
+ // src/cli/commands/customer-verification.ts
77898
+ import { emitKeypressEvents } from "readline";
77899
+ async function readCode() {
77900
+ if (process.stdin.isTTY)
77901
+ throw new NameInputError("Pipe a fresh six-digit verification code when using --code-stdin.");
77902
+ let text2 = "";
77903
+ for await (const chunk2 of process.stdin) {
77904
+ text2 += chunk2.toString();
77905
+ if (text2.length > 32)
77906
+ throw new NameInputError("Supply only a six-digit verification code on stdin.");
77907
+ }
77908
+ const code = text2.trim();
77909
+ if (!/^\d{6}$/.test(code))
77910
+ throw new NameInputError("Supply only a six-digit verification code on stdin.");
77911
+ return code;
77912
+ }
77913
+ function promptCode() {
77914
+ const { stdin, stderr: output } = process;
77915
+ const { isRaw: wasRaw, readableFlowing: wasFlowing } = stdin;
77916
+ return new Promise((resolve5) => {
77917
+ let value = "", settled = false;
77918
+ const finish = (answer) => {
77919
+ if (settled)
77920
+ return;
77921
+ settled = true;
77922
+ clearTimeout(timer);
77923
+ stdin.off("keypress", keypress);
77924
+ stdin.off("end", cancel);
77925
+ process.off("SIGINT", cancel);
77926
+ stdin.setRawMode(wasRaw);
77927
+ if (wasFlowing !== true)
77928
+ stdin.pause();
77929
+ output.write(`
77930
+ `);
77931
+ if (answer === null)
77932
+ process.exitCode = 130;
77933
+ resolve5(answer);
77934
+ };
77935
+ const cancel = () => finish(null);
77936
+ const keypress = (text2, key) => {
77937
+ if (key.ctrl && ["c", "d"].includes(key.name ?? "") || key.name === "escape")
77938
+ return cancel();
77939
+ if (key.name === "return" || key.name === "enter") {
77940
+ if (value.length === 6)
77941
+ return finish(value);
77942
+ output.write(`
77943
+ Enter all six digits: `);
77944
+ value = "";
77945
+ return;
77946
+ }
77947
+ if (key.name === "backspace") {
77948
+ if (value) {
77949
+ value = value.slice(0, -1);
77950
+ output.write("\b \b");
77951
+ }
77952
+ } else if (/^[0-9]$/.test(text2) && value.length < 6) {
77953
+ value += text2;
77954
+ output.write("*");
77955
+ }
77956
+ };
77957
+ const timer = setTimeout(cancel, 5 * 60 * 1000);
77958
+ emitKeypressEvents(stdin);
77959
+ stdin.setRawMode(true);
77960
+ stdin.on("keypress", keypress);
77961
+ stdin.once("end", cancel);
77962
+ process.once("SIGINT", cancel);
77963
+ output.write("Enter the six-digit code sent to your email: ");
77964
+ stdin.resume();
77965
+ });
77966
+ }
77967
+ var NameInputError;
77968
+ var init_customer_verification = __esm(() => {
77969
+ NameInputError = class NameInputError extends Error {
77970
+ };
77971
+ });
77972
+
77973
+ // src/cli/commands/workspace-selection.ts
77974
+ async function codeFor(client, options) {
77975
+ if (!options.email?.includes("@"))
77976
+ throw new NameInputError("Provide the account email with --email.");
77977
+ if (options.codeStdin)
77978
+ return readCode();
77979
+ if (options.json || !process.stdin.isTTY || !process.stderr.isTTY)
77980
+ throw new NameInputError("Use --code-stdin with a fresh verification code for noninteractive requests.");
77981
+ await client.requestCode(options.email);
77982
+ return promptCode();
77983
+ }
77984
+ function errorResult(error2, json) {
77985
+ const message = error2 instanceof NameInputError || error2 instanceof WorkspaceProfileError ? error2.message : "Unable to complete workspace sign-in. Check the selected server, profile, account and fresh verification code.";
77986
+ if (json)
77987
+ console.log(JSON.stringify({ error: message }));
77988
+ else
77989
+ console.error(message);
77990
+ process.exitCode = 1;
77991
+ }
77992
+ function registerWorkspaceListCommand(workspace) {
77993
+ workspace.command("list").allowExcessArguments(false).description("Discover eligible workspaces with fresh sign-in; no credentials are saved").requiredOption("--email <email>", "Account email for fresh verification").option("--code-stdin", "Read a previously requested six-digit verification code from stdin").option("--json", "Output safe workspace identities as JSON").action(async (options) => {
77994
+ try {
77995
+ const origin = getApiUrl("Discover workspaces", { ...process.env });
77996
+ const client = new RemoteSkillsAuthClient(origin);
77997
+ const code = await codeFor(client, options);
77998
+ if (code === null)
77999
+ return;
78000
+ const result2 = await client.listAccountWorkspaces(options.email, code);
78001
+ if (options.json)
78002
+ console.log(JSON.stringify({ apiUrl: origin, ...result2 }));
78003
+ else {
78004
+ console.log(`Account: ${result2.userId}
78005
+ API: ${origin}`);
78006
+ for (const entry of result2.workspaces)
78007
+ console.log(`${entry.current ? "*" : " "} ${entry.organization.name} ${entry.role} ${entry.membershipId}`);
78008
+ console.log("* Initial sign-in workspace. Enroll a named profile with auth login --membership-id <id>.");
78009
+ }
78010
+ } catch (error2) {
78011
+ errorResult(error2, options.json);
78012
+ }
78013
+ });
78014
+ }
78015
+ async function loginWorkspace(options) {
78016
+ try {
78017
+ const enrollment = await prepareWorkspaceEnrollment(options.membershipId);
78018
+ const code = await codeFor(new RemoteSkillsAuthClient(enrollment.origin), options);
78019
+ if (code === null)
78020
+ return;
78021
+ const result2 = await enrollment.complete(options.email, code);
78022
+ if (options.json)
78023
+ console.log(JSON.stringify(result2));
78024
+ else
78025
+ console.log(`Signed in as ${result2.email}
78026
+ Profile: ${result2.profile}
78027
+ API: ${result2.apiUrl}
78028
+ Workspace: ${result2.organization} (${result2.organizationId})
78029
+ Membership: ${result2.membershipId}
78030
+ Role: ${result2.role}
78031
+ One workspace key saved. Use HASNA_PROFILE=${result2.profile} for subsequent commands.`);
78032
+ } catch (error2) {
78033
+ errorResult(error2, options.json);
78034
+ }
78035
+ }
78036
+ var init_workspace_selection = __esm(() => {
78037
+ init_auth_store();
78038
+ init_remote_auth();
78039
+ init_workspace_profile();
78040
+ init_customer_verification();
78041
+ });
78042
+
77352
78043
  // src/cli/commands/auth.ts
77353
78044
  var exports_auth = {};
77354
78045
  __export(exports_auth, {
@@ -77434,13 +78125,13 @@ function authIdentityPayload(authSource, live, cached2, offline = false) {
77434
78125
  const root = recordField(live) ?? {};
77435
78126
  const data = recordField(root.data);
77436
78127
  const user = recordField(root.user) ?? recordField(data?.user);
77437
- const organization = recordField(root.organization) ?? recordField(root.org) ?? recordField(data?.organization);
78128
+ const organization2 = recordField(root.organization) ?? recordField(root.org) ?? recordField(data?.organization);
77438
78129
  const email2 = stringField2(user?.email) ?? cached2?.email;
77439
- const orgSlug = stringField2(organization?.slug) ?? cached2?.orgSlug;
77440
- const orgName = stringField2(organization?.name);
78130
+ const orgSlug = stringField2(organization2?.slug) ?? cached2?.orgSlug;
78131
+ const orgName = stringField2(organization2?.name);
77441
78132
  const userId = stringField2(user?.id) ?? cached2?.userId;
77442
- const orgId = stringField2(organization?.id) ?? cached2?.orgId;
77443
- const role = stringField2(user?.role);
78133
+ const orgId = stringField2(organization2?.id) ?? cached2?.orgId;
78134
+ const role2 = stringField2(user?.role);
77444
78135
  return {
77445
78136
  status: "authenticated",
77446
78137
  authSource,
@@ -77450,7 +78141,7 @@ function authIdentityPayload(authSource, live, cached2, offline = false) {
77450
78141
  ...orgName ? { organizationName: orgName } : {},
77451
78142
  ...userId ? { userId } : {},
77452
78143
  ...orgId ? { orgId } : {},
77453
- ...role ? { role } : {}
78144
+ ...role2 ? { role: role2 } : {}
77454
78145
  };
77455
78146
  }
77456
78147
  function printWhoami(payload) {
@@ -77748,15 +78439,19 @@ function registerAuth(parent) {
77748
78439
  const keys2 = auth.command("keys").description("Manage API keys on the configured instance");
77749
78440
  keys2.command("list").option("--json", "Output as JSON", false).requiredOption("--email <email>", "Account email for fresh reauthentication").requiredOption("--code <code>", "Fresh OTP requested through auth signup/login").action(async (options) => {
77750
78441
  try {
77751
- console.log(JSON.stringify(await new RemoteSkillsAuthClient(getApiUrl("List API keys")).listApiKeys(options.email, options.code), null, 2));
78442
+ const target = await captureProfileWorkspace("List API keys");
78443
+ target.unchanged();
78444
+ console.log(JSON.stringify(await new RemoteSkillsAuthClient(target.origin).listApiKeys(options.email, options.code, target.context), null, 2));
77752
78445
  } catch (error2) {
77753
78446
  writeCommandError(error2, "Failed to list API keys", options.json);
77754
78447
  }
77755
78448
  });
77756
78449
  keys2.command("create").argument("<name>").option("--scope <scope>", "Limit key scope (repeatable)", (value, all) => [...all, value], []).option("--json", "Output the newly created key as JSON", false).requiredOption("--email <email>", "Account email for fresh reauthentication").requiredOption("--code <code>", "Fresh OTP requested through auth signup/login").description("Create a key; the returned secret is shown once and must be stored securely").action(async (name, options) => {
77757
78450
  try {
77758
- const client = new RemoteSkillsAuthClient(getApiUrl("Create API key"));
77759
- const created = await client.createApiKey(options.email, options.code, name, options.scope.length ? options.scope : undefined);
78451
+ const target = await captureProfileWorkspace("Create API key");
78452
+ const client = new RemoteSkillsAuthClient(target.origin);
78453
+ target.unchanged();
78454
+ const created = await client.createApiKey(options.email, options.code, name, options.scope.length ? options.scope : undefined, target.context);
77760
78455
  console.log(JSON.stringify(created, null, 2));
77761
78456
  } catch (error2) {
77762
78457
  writeCommandError(error2, "Failed to create API key", options.json);
@@ -77764,12 +78459,26 @@ function registerAuth(parent) {
77764
78459
  });
77765
78460
  keys2.command("revoke").argument("<key-id>").option("--json", "Output as JSON", false).requiredOption("--email <email>", "Account email for fresh reauthentication").requiredOption("--code <code>", "Fresh OTP requested through auth signup/login").action(async (id, options) => {
77766
78461
  try {
77767
- console.log(JSON.stringify(await new RemoteSkillsAuthClient(getApiUrl("Revoke API key")).revokeApiKey(options.email, options.code, id), null, 2));
78462
+ const target = await captureProfileWorkspace("Revoke API key");
78463
+ target.unchanged();
78464
+ console.log(JSON.stringify(await new RemoteSkillsAuthClient(target.origin).revokeApiKey(options.email, options.code, id, target.context), null, 2));
77768
78465
  } catch (error2) {
77769
78466
  writeCommandError(error2, "Failed to revoke API key", options.json);
77770
78467
  }
77771
78468
  });
77772
- auth.command("login").description("Sign in with browser/device code or email code").option("--email <email>", "Email address (non-interactive)").option("--code <code>", "Verification code (non-interactive)").option("--api-key <key>", "Verify and store an API key").option("--device", "Use browser/device-code login", false).option("--no-open", "Do not open a browser for device-code login").option("--poll", "Poll until browser authentication completes in non-interactive mode", false).option("--poll-timeout-ms <ms>", "Maximum time to wait for device-code login").option("--json", "Output result as JSON", false).action(async (options) => {
78469
+ auth.command("login").description("Sign in with browser/device code or email code").option("--email <email>", "Email address (non-interactive)").option("--code <code>", "Verification code (non-interactive)").option("--membership-id <id>", "Enroll an exact workspace membership into an explicit HASNA_PROFILE").option("--code-stdin", "Read a fresh six-digit code for workspace enrollment from stdin").option("--api-key <key>", "Verify and store an API key").option("--device", "Use browser/device-code login", false).option("--no-open", "Do not open a browser for device-code login").option("--poll", "Poll until browser authentication completes in non-interactive mode", false).option("--poll-timeout-ms <ms>", "Maximum time to wait for device-code login").option("--json", "Output result as JSON", false).action(async (options) => {
78470
+ if (options.membershipId !== undefined) {
78471
+ if (options.apiKey || options.device || options.code || options.poll) {
78472
+ writeCommandError(new Error("Workspace login uses email and --code-stdin; do not combine it with device, API key or --code login."), "Invalid login options", options.json);
78473
+ return;
78474
+ }
78475
+ await loginWorkspace({ ...options, membershipId: options.membershipId });
78476
+ return;
78477
+ }
78478
+ if (options.codeStdin) {
78479
+ writeCommandError(new Error("--code-stdin requires --membership-id for this login flow."), "Invalid login options", options.json);
78480
+ return;
78481
+ }
77773
78482
  if (options.apiKey) {
77774
78483
  await doApiKeyLogin(options.apiKey, options.json);
77775
78484
  return;
@@ -77877,6 +78586,8 @@ function registerAuth(parent) {
77877
78586
  }
77878
78587
  var isTTY, DEFAULT_DEVICE_POLL_TIMEOUT_MS, CONFIG_HINT_STATUSES;
77879
78588
  var init_auth = __esm(() => {
78589
+ init_workspace_selection();
78590
+ init_workspace_profile();
77880
78591
  init_source();
77881
78592
  init_auth_store();
77882
78593
  init_fleet_credentials();
@@ -77886,80 +78597,51 @@ var init_auth = __esm(() => {
77886
78597
  CONFIG_HINT_STATUSES = new Set([401, 403, 404, 405, 501]);
77887
78598
  });
77888
78599
 
77889
- // src/cli/commands/customer-verification.ts
77890
- import { emitKeypressEvents } from "readline";
77891
- async function readCode() {
77892
- if (process.stdin.isTTY)
77893
- throw new NameInputError("Pipe a fresh six-digit verification code when using --code-stdin.");
77894
- let text = "";
77895
- for await (const chunk2 of process.stdin) {
77896
- text += chunk2.toString();
77897
- if (text.length > 32)
77898
- throw new NameInputError("Supply only a six-digit verification code on stdin.");
77899
- }
77900
- const code = text.trim();
77901
- if (!/^\d{6}$/.test(code))
77902
- throw new NameInputError("Supply only a six-digit verification code on stdin.");
77903
- return code;
77904
- }
77905
- function promptCode() {
77906
- const { stdin, stderr: output } = process;
77907
- const { isRaw: wasRaw, readableFlowing: wasFlowing } = stdin;
77908
- return new Promise((resolve5) => {
77909
- let value = "", settled = false;
77910
- const finish = (answer) => {
77911
- if (settled)
77912
- return;
77913
- settled = true;
77914
- clearTimeout(timer);
77915
- stdin.off("keypress", keypress);
77916
- stdin.off("end", cancel);
77917
- process.off("SIGINT", cancel);
77918
- stdin.setRawMode(wasRaw);
77919
- if (wasFlowing !== true)
77920
- stdin.pause();
77921
- output.write(`
77922
- `);
77923
- if (answer === null)
77924
- process.exitCode = 130;
77925
- resolve5(answer);
77926
- };
77927
- const cancel = () => finish(null);
77928
- const keypress = (text, key) => {
77929
- if (key.ctrl && ["c", "d"].includes(key.name ?? "") || key.name === "escape")
77930
- return cancel();
77931
- if (key.name === "return" || key.name === "enter") {
77932
- if (value.length === 6)
77933
- return finish(value);
77934
- output.write(`
77935
- Enter all six digits: `);
77936
- value = "";
77937
- return;
77938
- }
77939
- if (key.name === "backspace") {
77940
- if (value) {
77941
- value = value.slice(0, -1);
77942
- output.write("\b \b");
77943
- }
77944
- } else if (/^[0-9]$/.test(text) && value.length < 6) {
77945
- value += text;
77946
- output.write("*");
78600
+ // src/cli/commands/workspace-leave.ts
78601
+ function registerWorkspaceLeaveCommand(workspace) {
78602
+ workspace.command("leave <membership-id>").allowExcessArguments(false).description("Leave exactly this membership after fresh verification; saved profiles stay unchanged").requiredOption("--expected-role <role>", "Observed role: owner, admin, member or viewer").requiredOption("--email <email>", "Account email for fresh verification").requiredOption("--confirm", "Confirm losing access through this membership and signing in again").option("--user-id <id>", "Observed user ID; required without a named workspace profile").option("--code-stdin", "Read a previously requested six-digit verification code from stdin").option("--json", "Output the confirmed result as JSON").action(async (membershipId, options) => {
78603
+ try {
78604
+ if (options.confirm !== true)
78605
+ throw new WorkspaceLeaveInputError;
78606
+ const pending = prepareProfileWorkspace("Leave workspace");
78607
+ const target = await pending.resolve();
78608
+ const captured = workspaceLeaveInput(workspaceLeaveProfileContext(membershipId, options.userId, target.context), { expectedRole: options.expectedRole, confirm: true });
78609
+ if (!options.email.includes("@"))
78610
+ throw new NameInputError("Provide the verified account email.");
78611
+ if (!options.codeStdin && (options.json || !process.stdin.isTTY || !process.stderr.isTTY))
78612
+ throw new NameInputError("Use --code-stdin with a fresh verification code for noninteractive leave actions.");
78613
+ const client = new RemoteSkillsAuthClient(target.origin);
78614
+ let code;
78615
+ if (options.codeStdin)
78616
+ code = await readCode();
78617
+ else {
78618
+ await client.requestCode(options.email);
78619
+ code = await promptCode();
77947
78620
  }
77948
- };
77949
- const timer = setTimeout(cancel, 5 * 60 * 1000);
77950
- emitKeypressEvents(stdin);
77951
- stdin.setRawMode(true);
77952
- stdin.on("keypress", keypress);
77953
- stdin.once("end", cancel);
77954
- process.once("SIGINT", cancel);
77955
- output.write("Enter the six-digit code sent to your email: ");
77956
- stdin.resume();
78621
+ if (code === null)
78622
+ return;
78623
+ target.unchanged();
78624
+ const result2 = await client.leaveWorkspace(options.email, code, captured.context, captured.input);
78625
+ if (options.json)
78626
+ console.log(JSON.stringify(result2));
78627
+ else
78628
+ console.log("Workspace membership left. Sign in again to an available workspace. Saved credentials are unchanged; this membership's credentials no longer grant access.");
78629
+ } catch (error2) {
78630
+ const known = error2 instanceof RemoteWorkspaceLeaveError || error2 instanceof RemoteWorkspaceLeaveUnconfirmedError;
78631
+ const message = known || error2 instanceof WorkspaceLeaveInputError || error2 instanceof NameInputError ? error2.message : "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.";
78632
+ if (options.json)
78633
+ console.log(JSON.stringify({ error: message, ...known ? { code: error2.code } : {} }));
78634
+ else
78635
+ console.error(message);
78636
+ process.exitCode = 1;
78637
+ }
77957
78638
  });
77958
78639
  }
77959
- var NameInputError;
77960
- var init_customer_verification = __esm(() => {
77961
- NameInputError = class NameInputError extends Error {
77962
- };
78640
+ var init_workspace_leave = __esm(() => {
78641
+ init_workspace_profile();
78642
+ init_remote_auth();
78643
+ init_remote_workspace_leave();
78644
+ init_customer_verification();
77963
78645
  });
77964
78646
 
77965
78647
  // src/cli/commands/workspace-members.ts
@@ -77970,7 +78652,8 @@ function registerWorkspaceMembersCommand(workspace) {
77970
78652
  throw new NameInputError("Use a roster limit from 1 to 100.");
77971
78653
  const page = { limit: options.limit === undefined ? undefined : Number(options.limit), cursor: options.cursor };
77972
78654
  workspaceMembersQuery(page);
77973
- const client = new RemoteSkillsAuthClient(getApiUrl("List workspace members"));
78655
+ const pending = prepareProfileWorkspace("List workspace members");
78656
+ const client = new RemoteSkillsAuthClient(pending.origin);
77974
78657
  if (!options.codeStdin && (options.json || !process.stdin.isTTY || !process.stderr.isTTY))
77975
78658
  throw new NameInputError("Use --code-stdin with a fresh verification code for JSON or noninteractive roster requests.");
77976
78659
  let code;
@@ -77982,16 +78665,18 @@ function registerWorkspaceMembersCommand(workspace) {
77982
78665
  }
77983
78666
  if (code === null)
77984
78667
  return;
77985
- const result2 = await client.listWorkspaceMembers(options.email, code, page);
78668
+ const target = await pending.resolve();
78669
+ target.unchanged();
78670
+ const result2 = await client.listWorkspaceMembers(options.email, code, page, target.context);
77986
78671
  if (options.json)
77987
78672
  console.log(JSON.stringify(result2));
77988
78673
  else {
77989
- const text = (value) => value.replace(/[\p{Cc}\p{Cs}\u2028\u2029]/gu, " ");
78674
+ const text2 = (value) => value.replace(/[\p{Cc}\p{Cs}\u2028\u2029]/gu, " ");
77990
78675
  console.log(`Workspace: ${result2.organizationId}`);
77991
78676
  if (!result2.members.length)
77992
78677
  console.log("No members in this page.");
77993
78678
  for (const member of result2.members)
77994
- console.log(`${text(member.email)} ${member.role} ${text(member.displayName ?? "")} ${member.membershipId} ${member.createdAt}`);
78679
+ console.log(`${text2(member.email)} ${member.role} ${text2(member.displayName ?? "")} ${member.membershipId} ${member.createdAt}`);
77995
78680
  if (result2.nextCursor !== null)
77996
78681
  console.log(`Next cursor: ${result2.nextCursor}`);
77997
78682
  }
@@ -78007,7 +78692,7 @@ function registerWorkspaceMembersCommand(workspace) {
78007
78692
  });
78008
78693
  }
78009
78694
  var init_workspace_members = __esm(() => {
78010
- init_auth_store();
78695
+ init_workspace_profile();
78011
78696
  init_remote_auth();
78012
78697
  init_remote_workspace();
78013
78698
  init_customer_verification();
@@ -78023,7 +78708,8 @@ function registerWorkspaceMemberMutationCommands(workspace) {
78023
78708
  command.action(async (membershipId, options) => {
78024
78709
  try {
78025
78710
  const captured = action === "role" ? { kind: "role", ...workspaceMemberRoleInput(membershipId, { role: options.role, expectedRole: options.expectedRole }) } : { kind: "remove", ...workspaceMemberRemovalInput(membershipId, { expectedRole: options.expectedRole }) };
78026
- const client = new RemoteSkillsAuthClient(getApiUrl("Manage workspace member"));
78711
+ const pending = prepareProfileWorkspace("Manage workspace member");
78712
+ const client = new RemoteSkillsAuthClient(pending.origin);
78027
78713
  if (!options.codeStdin && (options.json || !process.stdin.isTTY || !process.stderr.isTTY))
78028
78714
  throw new NameInputError("Use --code-stdin with a fresh verification code for JSON or noninteractive member actions.");
78029
78715
  let code;
@@ -78035,14 +78721,16 @@ function registerWorkspaceMemberMutationCommands(workspace) {
78035
78721
  }
78036
78722
  if (code === null)
78037
78723
  return;
78724
+ const target = await pending.resolve();
78725
+ target.unchanged();
78038
78726
  if (captured.kind === "role") {
78039
- const result2 = await client.setWorkspaceMemberRole(options.email, code, captured.membershipId, captured.body);
78727
+ const result2 = await client.setWorkspaceMemberRole(options.email, code, captured.membershipId, captured.body, target.context);
78040
78728
  if (options.json)
78041
78729
  console.log(JSON.stringify(result2));
78042
78730
  else
78043
78731
  console.log(result2.changed ? `Member role changed to ${result2.member.role}.` : `Member already has role ${result2.member.role}.`);
78044
78732
  } else {
78045
- const result2 = await client.removeWorkspaceMember(options.email, code, captured.membershipId, captured.body);
78733
+ const result2 = await client.removeWorkspaceMember(options.email, code, captured.membershipId, captured.body, target.context);
78046
78734
  if (options.json)
78047
78735
  console.log(JSON.stringify(result2));
78048
78736
  else
@@ -78062,7 +78750,7 @@ function registerWorkspaceMemberMutationCommands(workspace) {
78062
78750
  }
78063
78751
  }
78064
78752
  var init_workspace_member_mutations = __esm(() => {
78065
- init_auth_store();
78753
+ init_workspace_profile();
78066
78754
  init_remote_auth();
78067
78755
  init_remote_client();
78068
78756
  init_remote_workspace();
@@ -78077,8 +78765,10 @@ __export(exports_customer_profile, {
78077
78765
  function registerCustomerProfileCommands(program2) {
78078
78766
  const account = program2.command("account").description("Manage your account on the selected Skills server");
78079
78767
  const workspace = program2.command("workspace").description("Manage the current workspace on the selected Skills server");
78768
+ registerWorkspaceListCommand(workspace);
78080
78769
  registerWorkspaceMembersCommand(workspace);
78081
78770
  registerWorkspaceMemberMutationCommands(workspace);
78771
+ registerWorkspaceLeaveCommand(workspace);
78082
78772
  const commands = [
78083
78773
  { kind: "account", command: account.command("update") },
78084
78774
  { kind: "workspace", command: workspace.command("update") }
@@ -78086,7 +78776,8 @@ function registerCustomerProfileCommands(program2) {
78086
78776
  for (const { kind, command } of commands) {
78087
78777
  command.allowExcessArguments(false).description(kind === "account" ? "Update your display name with fresh email verification" : "Update the current workspace name as an owner or admin").requiredOption(kind === "account" ? "--display-name <name>" : "--name <name>", "New name (1\u2013100 characters)").requiredOption("--email <email>", "Account email for fresh verification").option("--code-stdin", "Read a previously requested six-digit verification code from stdin").option("--json", "Output JSON").action(async (options) => {
78088
78778
  try {
78089
- const client = new RemoteSkillsAuthClient(getApiUrl(`Update ${kind} name`));
78779
+ const pending = prepareProfileWorkspace(`Update ${kind} name`);
78780
+ const client = new RemoteSkillsAuthClient(pending.origin);
78090
78781
  customerNamePatch(kind === "account" ? { displayName: options.displayName } : { name: options.name }, kind === "account" ? "displayName" : "name");
78091
78782
  if (!options.codeStdin && (options.json || !process.stdin.isTTY || !process.stderr.isTTY)) {
78092
78783
  throw new NameInputError("Use --code-stdin with a fresh verification code for JSON or noninteractive updates.");
@@ -78100,7 +78791,9 @@ function registerCustomerProfileCommands(program2) {
78100
78791
  }
78101
78792
  if (code === null)
78102
78793
  return;
78103
- const result2 = kind === "account" ? await client.updateProfile(options.email, code, { displayName: options.displayName }) : await client.updateCurrentWorkspace(options.email, code, { name: options.name });
78794
+ const target = await pending.resolve();
78795
+ target.unchanged();
78796
+ const result2 = kind === "account" ? await client.updateProfile(options.email, code, { displayName: options.displayName }, target.context) : await client.updateCurrentWorkspace(options.email, code, { name: options.name }, target.context);
78104
78797
  if (options.json)
78105
78798
  console.log(JSON.stringify(result2));
78106
78799
  else
@@ -78118,9 +78811,11 @@ function registerCustomerProfileCommands(program2) {
78118
78811
  }
78119
78812
  }
78120
78813
  var init_customer_profile = __esm(() => {
78121
- init_auth_store();
78814
+ init_workspace_profile();
78815
+ init_workspace_selection();
78122
78816
  init_remote_auth();
78123
78817
  init_customer_verification();
78818
+ init_workspace_leave();
78124
78819
  init_workspace_members();
78125
78820
  init_workspace_member_mutations();
78126
78821
  });
@@ -78265,8 +78960,8 @@ var init_storage = __esm(() => {
78265
78960
  });
78266
78961
 
78267
78962
  // src/lib/registry-reconcile.ts
78268
- import { existsSync as existsSync33, readFileSync as readFileSync30, statSync as statSync20, writeFileSync as writeFileSync19 } from "fs";
78269
- import { join as join38 } from "path";
78963
+ import { existsSync as existsSync33, readFileSync as readFileSync31, statSync as statSync20, writeFileSync as writeFileSync20 } from "fs";
78964
+ import { join as join39 } from "path";
78270
78965
  function isDirectory2(path) {
78271
78966
  try {
78272
78967
  return statSync20(path).isDirectory();
@@ -78277,15 +78972,15 @@ function isDirectory2(path) {
78277
78972
  function migrationNeeded(options) {
78278
78973
  if (options.rootDir)
78279
78974
  return false;
78280
- const appDir = options.homeDir ? join38(options.homeDir, ".hasna", "skills") : getDataDir();
78281
- return !(isOwnerLayoutMigrated(appDir) && isDirectory2(join38(appDir, SKILLS_CACHE_DIRNAME)));
78975
+ const appDir = options.homeDir ? join39(options.homeDir, ".hasna", "skills") : getDataDir();
78976
+ return !(isOwnerLayoutMigrated(appDir) && isDirectory2(join39(appDir, SKILLS_CACHE_DIRNAME)));
78282
78977
  }
78283
78978
  function readBaseline(skillDir) {
78284
- const markerPath = join38(skillDir, PULL_MARKER_FILE);
78979
+ const markerPath = join39(skillDir, PULL_MARKER_FILE);
78285
78980
  if (!existsSync33(markerPath))
78286
78981
  return;
78287
78982
  try {
78288
- const marker = JSON.parse(readFileSync30(markerPath, "utf-8"));
78983
+ const marker = JSON.parse(readFileSync31(markerPath, "utf-8"));
78289
78984
  if (!isSkillsOwnershipMarker(marker))
78290
78985
  return;
78291
78986
  return {
@@ -78297,11 +78992,11 @@ function readBaseline(skillDir) {
78297
78992
  }
78298
78993
  }
78299
78994
  function readCursor(root) {
78300
- const path = join38(root, SYNC_CURSOR_FILE);
78995
+ const path = join39(root, SYNC_CURSOR_FILE);
78301
78996
  if (!existsSync33(path))
78302
78997
  return { runCount: 0 };
78303
78998
  try {
78304
- const cursor2 = JSON.parse(readFileSync30(path, "utf-8"));
78999
+ const cursor2 = JSON.parse(readFileSync31(path, "utf-8"));
78305
79000
  return { runCount: typeof cursor2.runCount === "number" ? cursor2.runCount : 0 };
78306
79001
  } catch {
78307
79002
  return { runCount: 0 };
@@ -78310,21 +79005,21 @@ function readCursor(root) {
78310
79005
  function resolveCorpusRootReadOnly(options) {
78311
79006
  if (options.rootDir)
78312
79007
  return { root: options.rootDir, migrationPending: false };
78313
- const appDir = options.homeDir ? join38(options.homeDir, ".hasna", "skills") : getDataDirReadOnly();
78314
- const cache3 = join38(appDir, SKILLS_CACHE_DIRNAME);
79008
+ const appDir = options.homeDir ? join39(options.homeDir, ".hasna", "skills") : getDataDirReadOnly();
79009
+ const cache3 = join39(appDir, SKILLS_CACHE_DIRNAME);
78315
79010
  if (isOwnerLayoutMigrated(appDir) && isDirectory2(cache3)) {
78316
79011
  return { root: cache3, migrationPending: false };
78317
79012
  }
78318
- return { root: join38(appDir, INSTALLED_SKILLS_DIRNAME), migrationPending: true };
79013
+ return { root: join39(appDir, INSTALLED_SKILLS_DIRNAME), migrationPending: true };
78319
79014
  }
78320
- function remoteRowToSkill(record4) {
78321
- const slug = typeof record4.slug === "string" ? record4.slug : typeof record4.name === "string" ? record4.name : undefined;
79015
+ function remoteRowToSkill(record5) {
79016
+ const slug = typeof record5.slug === "string" ? record5.slug : typeof record5.name === "string" ? record5.name : undefined;
78322
79017
  if (!slug)
78323
79018
  return;
78324
79019
  return {
78325
79020
  slug,
78326
- version: typeof record4.version === "string" ? record4.version : undefined,
78327
- sha256: typeof record4.bundleSha256 === "string" && record4.bundleSha256 ? record4.bundleSha256 : undefined
79021
+ version: typeof record5.version === "string" ? record5.version : undefined,
79022
+ sha256: typeof record5.bundleSha256 === "string" && record5.bundleSha256 ? record5.bundleSha256 : undefined
78328
79023
  };
78329
79024
  }
78330
79025
  function recheckLocalSide(plannedLocal, localDir, ops = {
@@ -78449,7 +79144,7 @@ async function reconcileRegistry(options = {}) {
78449
79144
  for (const slug of allSlugs) {
78450
79145
  const local = locals.get(slug);
78451
79146
  const remote = remotes.get(slug);
78452
- const baseline = local ? readBaseline(join38(root, slug)) : undefined;
79147
+ const baseline = local ? readBaseline(join39(root, slug)) : undefined;
78453
79148
  const { state, reason } = classifySkill(local, remote, baseline);
78454
79149
  let { action, reason: actionReason } = resolveAction(state, direction, conflict);
78455
79150
  if (state === "remote-only" && isDigestless(remote)) {
@@ -78508,7 +79203,7 @@ async function reconcileRegistry(options = {}) {
78508
79203
  try {
78509
79204
  await pushSkill(slug, { rootDir: root, client });
78510
79205
  const pushed = locals.get(slug);
78511
- writePullMarker(join38(root, slug), {
79206
+ writePullMarker(join39(root, slug), {
78512
79207
  skill: slug,
78513
79208
  ...pushed?.version ? { version: pushed.version } : {},
78514
79209
  ...pushed?.sha256 ? { contentHash: pushed.sha256 } : {},
@@ -78580,7 +79275,7 @@ async function reconcileRegistry(options = {}) {
78580
79275
  runCount: readCursor(root).runCount + 1,
78581
79276
  summary
78582
79277
  };
78583
- writeFileSync19(join38(root, SYNC_CURSOR_FILE), `${JSON.stringify(cursor2, null, 2)}
79278
+ writeFileSync20(join39(root, SYNC_CURSOR_FILE), `${JSON.stringify(cursor2, null, 2)}
78584
79279
  `);
78585
79280
  return {
78586
79281
  corpusRoot: root,