@hasna/skills 0.5.2 → 0.5.3

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/dist/sdk/index.js CHANGED
@@ -25462,7 +25462,7 @@ class MissingSkillsFleetError extends Error {
25462
25462
  // package.json
25463
25463
  var package_default = {
25464
25464
  name: "@hasna/skills",
25465
- version: "0.5.2",
25465
+ version: "0.5.3",
25466
25466
  description: "Skills library for AI coding agents",
25467
25467
  type: "module",
25468
25468
  bin: {
@@ -25635,6 +25635,41 @@ import { createHmac, timingSafeEqual } from "crypto";
25635
25635
  import { createHash } from "crypto";
25636
25636
  import { readFileSync as readFileSync2, readdirSync, statSync } from "fs";
25637
25637
  import { join as join2, relative } from "path";
25638
+ import { createGunzip } from "zlib";
25639
+
25640
+ // src/lib/skill-entry-path.ts
25641
+ class SkillEntryPaths {
25642
+ files = new Set;
25643
+ directories = new Set;
25644
+ add(path, maxBytes, invalid, limit) {
25645
+ if (path.length > maxBytes)
25646
+ limit();
25647
+ const encoded = new TextEncoder().encode(path);
25648
+ if (encoded.byteLength > maxBytes)
25649
+ limit();
25650
+ if (new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(encoded) !== path)
25651
+ invalid("Invalid UTF-8 entry path");
25652
+ if (!path || /[\\:\x00-\x1f\x7f]/u.test(path))
25653
+ invalid("Unsafe entry path");
25654
+ if (path.split("/").some((segment) => !segment || segment === "." || segment === ".."))
25655
+ invalid("Unsafe entry path segment");
25656
+ const key = path.normalize("NFC").toLowerCase().normalize("NFC");
25657
+ if (this.files.has(key) || this.directories.has(key))
25658
+ invalid("Duplicate or conflicting entry path");
25659
+ const parents = key.split("/");
25660
+ parents.pop();
25661
+ while (parents.length) {
25662
+ const parent = parents.join("/");
25663
+ if (this.files.has(parent))
25664
+ invalid("Conflicting entry file ancestor");
25665
+ this.directories.add(parent);
25666
+ parents.pop();
25667
+ }
25668
+ this.files.add(key);
25669
+ }
25670
+ }
25671
+
25672
+ // src/lib/skill-bundle.ts
25638
25673
  var BLOCK = 512;
25639
25674
  var ANY_SEGMENT_EXCLUDES = new Set([
25640
25675
  ".git",
@@ -25957,6 +25992,227 @@ function concat(chunks) {
25957
25992
  }
25958
25993
  return merged;
25959
25994
  }
25995
+ var SKILL_BUNDLE_INSPECTION_LIMITS = Object.freeze({
25996
+ compressedBytes: 16 * 1024 * 1024,
25997
+ decompressedBytes: 64 * 1024 * 1024,
25998
+ entries: 1024,
25999
+ fileBytes: 16 * 1024 * 1024,
26000
+ pathBytes: 100,
26001
+ timeoutMs: 5000
26002
+ });
26003
+
26004
+ class SkillBundleInspectionError extends Error {
26005
+ code;
26006
+ constructor(code, message) {
26007
+ super(message);
26008
+ this.code = code;
26009
+ this.name = "SkillBundleInspectionError";
26010
+ }
26011
+ }
26012
+ function invalidBundle(message) {
26013
+ throw new SkillBundleInspectionError("BUNDLE_INVALID", message);
26014
+ }
26015
+ function inspectionLimits(options) {
26016
+ const limits = { ...SKILL_BUNDLE_INSPECTION_LIMITS };
26017
+ for (const key of Object.keys(options.limits ?? {})) {
26018
+ if (!Object.hasOwn(limits, key))
26019
+ throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Unknown bundle limit");
26020
+ const field = key;
26021
+ const value = options.limits[field];
26022
+ if (!Number.isSafeInteger(value) || value <= 0 || value > limits[field]) {
26023
+ throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Bundle limits must be positive integers within the hard ceilings");
26024
+ }
26025
+ limits[field] = value;
26026
+ }
26027
+ return limits;
26028
+ }
26029
+ async function inspectSkillBundle(bundle, options = {}) {
26030
+ const signal = options.signal;
26031
+ const limits = inspectionLimits(options);
26032
+ const deadline = performance.now() + limits.timeoutMs;
26033
+ const check = () => {
26034
+ if (signal?.aborted)
26035
+ throw new SkillBundleInspectionError("BUNDLE_ABORTED", "Bundle inspection aborted");
26036
+ if (performance.now() >= deadline)
26037
+ throw new SkillBundleInspectionError("BUNDLE_TIMEOUT", "Bundle inspection deadline exceeded");
26038
+ };
26039
+ check();
26040
+ if (bundle.byteLength > limits.compressedBytes)
26041
+ throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Compressed bundle exceeds byte limit");
26042
+ const snapshot = ownBytes(bundle);
26043
+ check();
26044
+ const sha256 = sha256Hex(snapshot);
26045
+ check();
26046
+ const parser = new BoundedTarReader(limits, check);
26047
+ const streamOptions = { chunkSize: 16 * 1024, highWaterMark: 16 * 1024 };
26048
+ const decoder = createGunzip(streamOptions);
26049
+ let terminalError;
26050
+ const stop = (code) => {
26051
+ terminalError ??= new SkillBundleInspectionError(code, code === "BUNDLE_ABORTED" ? "Bundle inspection aborted" : "Bundle inspection deadline exceeded");
26052
+ decoder.destroy(terminalError);
26053
+ };
26054
+ const onAbort = () => stop("BUNDLE_ABORTED");
26055
+ const timer = setTimeout(() => stop("BUNDLE_TIMEOUT"), Math.max(1, deadline - performance.now()));
26056
+ signal?.addEventListener("abort", onAbort, { once: true });
26057
+ let decompressedByteSize = 0;
26058
+ let bytesSinceYield = 0;
26059
+ try {
26060
+ check();
26061
+ decoder.end(snapshot);
26062
+ for await (const chunk of decoder) {
26063
+ check();
26064
+ decompressedByteSize += chunk.byteLength;
26065
+ if (decompressedByteSize > limits.decompressedBytes)
26066
+ throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Decompressed bundle exceeds byte limit");
26067
+ parser.push(chunk);
26068
+ bytesSinceYield += chunk.byteLength;
26069
+ if (bytesSinceYield >= 256 * 1024) {
26070
+ await new Promise((resolve) => setTimeout(resolve, 0));
26071
+ bytesSinceYield = 0;
26072
+ check();
26073
+ }
26074
+ }
26075
+ check();
26076
+ const entries = parser.finish();
26077
+ return {
26078
+ entries,
26079
+ sha256,
26080
+ compressedByteSize: snapshot.byteLength,
26081
+ decompressedByteSize,
26082
+ unpackedByteSize: parser.fileBytes,
26083
+ fileCount: entries.length
26084
+ };
26085
+ } catch (error) {
26086
+ if (terminalError)
26087
+ throw terminalError;
26088
+ if (error instanceof SkillBundleInspectionError)
26089
+ throw error;
26090
+ throw new SkillBundleInspectionError("BUNDLE_INVALID", "Invalid or truncated gzip bundle");
26091
+ } finally {
26092
+ clearTimeout(timer);
26093
+ signal?.removeEventListener("abort", onAbort);
26094
+ decoder.destroy();
26095
+ }
26096
+ }
26097
+
26098
+ class BoundedTarReader {
26099
+ limits;
26100
+ check;
26101
+ header = new Uint8Array(BLOCK);
26102
+ headerOffset = 0;
26103
+ pending;
26104
+ bodyOffset = 0;
26105
+ padding = 0;
26106
+ zeroBlocks = 0;
26107
+ entries = [];
26108
+ paths = new SkillEntryPaths;
26109
+ fileBytes = 0;
26110
+ constructor(limits, check) {
26111
+ this.limits = limits;
26112
+ this.check = check;
26113
+ }
26114
+ push(chunk) {
26115
+ let offset = 0;
26116
+ while (offset < chunk.byteLength) {
26117
+ this.check();
26118
+ if (this.pending) {
26119
+ const count = Math.min(this.pending.bytes.byteLength - this.bodyOffset, chunk.byteLength - offset);
26120
+ this.pending.bytes.set(chunk.subarray(offset, offset + count), this.bodyOffset);
26121
+ offset += count;
26122
+ this.bodyOffset += count;
26123
+ if (this.bodyOffset === this.pending.bytes.byteLength) {
26124
+ this.entries.push(this.pending);
26125
+ this.pending = undefined;
26126
+ }
26127
+ } else if (this.padding) {
26128
+ const count = Math.min(this.padding, chunk.byteLength - offset);
26129
+ if (chunk.subarray(offset, offset + count).some((byte) => byte !== 0))
26130
+ invalidBundle("Nonzero tar body padding");
26131
+ offset += count;
26132
+ this.padding -= count;
26133
+ } else {
26134
+ const count = Math.min(BLOCK - this.headerOffset, chunk.byteLength - offset);
26135
+ this.header.set(chunk.subarray(offset, offset + count), this.headerOffset);
26136
+ offset += count;
26137
+ this.headerOffset += count;
26138
+ if (this.headerOffset === BLOCK) {
26139
+ this.readHeader();
26140
+ this.headerOffset = 0;
26141
+ }
26142
+ }
26143
+ }
26144
+ }
26145
+ finish() {
26146
+ this.check();
26147
+ if (this.pending || this.padding || this.headerOffset || this.zeroBlocks < 2)
26148
+ invalidBundle("Truncated tar bundle");
26149
+ return this.entries;
26150
+ }
26151
+ readHeader() {
26152
+ this.check();
26153
+ const h = this.header;
26154
+ if (h.every((byte) => byte === 0)) {
26155
+ this.zeroBlocks++;
26156
+ return;
26157
+ }
26158
+ if (this.zeroBlocks)
26159
+ invalidBundle("Nonzero tar data after terminator");
26160
+ if (this.entries.length >= this.limits.entries)
26161
+ throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Bundle entry limit exceeded");
26162
+ let checksum = 0;
26163
+ for (let i = 0;i < BLOCK; i++)
26164
+ checksum += i >= 148 && i < 156 ? 32 : h[i];
26165
+ if (tarOctal(h.subarray(148, 156)) !== checksum)
26166
+ invalidBundle("Invalid tar header checksum");
26167
+ if (new TextDecoder().decode(h.subarray(257, 265)) !== "ustar\x00" + "00")
26168
+ invalidBundle("Unsupported tar format");
26169
+ if (h[156] !== 48 && h[156] !== 0 || h.subarray(157, 257).some((b) => b !== 0) || h.subarray(345).some((b) => b !== 0))
26170
+ invalidBundle("Unsupported tar entry or path prefix");
26171
+ const mode = tarOctal(h.subarray(100, 108));
26172
+ if (mode > 511)
26173
+ invalidBundle("Unsupported tar permission bits");
26174
+ tarOctal(h.subarray(108, 116));
26175
+ tarOctal(h.subarray(116, 124));
26176
+ tarOctal(h.subarray(136, 148));
26177
+ const size = tarOctal(h.subarray(124, 136));
26178
+ if (size > this.limits.fileBytes || this.fileBytes + size > this.limits.decompressedBytes) {
26179
+ throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Bundle file byte limit exceeded");
26180
+ }
26181
+ const name = h.subarray(0, 100);
26182
+ const end = name.indexOf(0);
26183
+ if (end !== -1 && name.subarray(end).some((b) => b !== 0))
26184
+ invalidBundle("Invalid tar path padding");
26185
+ const raw = end === -1 ? name : name.subarray(0, end);
26186
+ if (raw.byteLength > this.limits.pathBytes)
26187
+ throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Bundle path byte limit exceeded");
26188
+ let path;
26189
+ try {
26190
+ path = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(raw);
26191
+ } catch {
26192
+ return invalidBundle("Invalid UTF-8 bundle path");
26193
+ }
26194
+ this.paths.add(path, this.limits.pathBytes, invalidBundle, () => {
26195
+ throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Bundle path byte limit exceeded");
26196
+ });
26197
+ this.fileBytes += size;
26198
+ this.pending = { path, mode, bytes: new Uint8Array(new ArrayBuffer(size)) };
26199
+ this.bodyOffset = 0;
26200
+ this.padding = (BLOCK - size % BLOCK) % BLOCK;
26201
+ if (!size) {
26202
+ this.entries.push(this.pending);
26203
+ this.pending = undefined;
26204
+ }
26205
+ }
26206
+ }
26207
+ function tarOctal(field) {
26208
+ const text = new TextDecoder().decode(field);
26209
+ if (!/^[0-7]+[\0 ]*$/.test(text))
26210
+ invalidBundle("Invalid tar octal field");
26211
+ const value = Number.parseInt(text, 8);
26212
+ if (!Number.isSafeInteger(value))
26213
+ invalidBundle("Tar integer is out of range");
26214
+ return value;
26215
+ }
25960
26216
 
25961
26217
  // src/lib/skill-bundles.ts
25962
26218
  var SKILLS_SIGNING_KEY_ENV = "SKILLS_SIGNING_KEY";
@@ -38659,6 +38915,9 @@ import { existsSync as existsSync6, readdirSync as readdirSync6, readFileSync as
38659
38915
  import { join as join11, sep } from "path";
38660
38916
  var CONTENT_HASH_ALGORITHM = "sha256";
38661
38917
  var HASH_EXCLUDE_DIRS = new Set([".git", "node_modules", "dist", "build", ".turbo"]);
38918
+ function excludedHashEntry(name, directory) {
38919
+ return name.startsWith(".") || directory && HASH_EXCLUDE_DIRS.has(name);
38920
+ }
38662
38921
  var HASH_COVERAGE = [
38663
38922
  "SKILL.md",
38664
38923
  "skill.json",
@@ -38740,7 +38999,7 @@ function collectDirectory(files, dir, rel) {
38740
38999
  if (stats.isSymbolicLink())
38741
39000
  continue;
38742
39001
  if (stats.isDirectory()) {
38743
- if (HASH_EXCLUDE_DIRS.has(entry))
39002
+ if (excludedHashEntry(entry, true))
38744
39003
  continue;
38745
39004
  collectDirectory(files, absolute, childRel);
38746
39005
  } else if (stats.isFile()) {
@@ -38750,28 +39009,242 @@ function collectDirectory(files, dir, rel) {
38750
39009
  }
38751
39010
  function collectFile(files, absolute, rel) {
38752
39011
  const buffer = readFileSync8(absolute);
39012
+ files.push(normalizeBundleFile(rel.split(sep).join("/"), buffer));
39013
+ }
39014
+ function normalizeBundleFile(rel, buffer) {
38753
39015
  if (rel === "skill.json") {
38754
- files.push({ rel: rel.split(sep).join("/"), content: new TextEncoder().encode(canonicalizeManifest(new TextDecoder().decode(buffer))) });
38755
- return;
39016
+ return { rel, content: new TextEncoder().encode(canonicalizeManifest(new TextDecoder().decode(buffer))) };
38756
39017
  }
38757
39018
  if (looksLikeText(buffer)) {
38758
39019
  const normalized = normalizeLineEndings(new TextDecoder().decode(buffer));
38759
- files.push({ rel: rel.split(sep).join("/"), content: new TextEncoder().encode(normalized) });
38760
- return;
39020
+ return { rel, content: new TextEncoder().encode(normalized) };
38761
39021
  }
38762
- files.push({ rel: rel.split(sep).join("/"), content: buffer });
39022
+ return { rel, content: buffer };
38763
39023
  }
38764
39024
  function computeContentHash(skillPath) {
39025
+ return hashBundleFiles(collectBundleFiles(skillPath));
39026
+ }
39027
+ function* bundleHashParts(files) {
39028
+ for (const file of files) {
39029
+ yield new TextEncoder().encode(file.rel);
39030
+ yield new TextEncoder().encode(`\x00${file.content.length}\x00`);
39031
+ yield file.content;
39032
+ yield new TextEncoder().encode("\x00");
39033
+ }
39034
+ yield new TextEncoder().encode("\x00");
39035
+ }
39036
+ function hashBundleFiles(files) {
39037
+ const hash = createHash6(CONTENT_HASH_ALGORITHM);
39038
+ for (const part of bundleHashParts(files))
39039
+ hash.update(part);
39040
+ return hash.digest("hex");
39041
+ }
39042
+ async function hashBundleFilesCooperatively(files, check) {
38765
39043
  const hash = createHash6(CONTENT_HASH_ALGORITHM);
38766
- for (const file of collectBundleFiles(skillPath)) {
38767
- hash.update(new TextEncoder().encode(file.rel));
38768
- hash.update(new TextEncoder().encode(`\x00${file.content.length}\x00`));
38769
- hash.update(file.content);
38770
- hash.update(new TextEncoder().encode("\x00"));
39044
+ let bytesSinceYield = 0;
39045
+ for (const part of bundleHashParts(files)) {
39046
+ for (let offset = 0;offset < part.byteLength; offset += 64 * 1024) {
39047
+ check();
39048
+ const chunk = part.subarray(offset, offset + 64 * 1024);
39049
+ hash.update(chunk);
39050
+ bytesSinceYield += chunk.byteLength;
39051
+ if (bytesSinceYield >= 256 * 1024) {
39052
+ await new Promise((resolve2) => setImmediate(resolve2));
39053
+ bytesSinceYield = 0;
39054
+ }
39055
+ }
38771
39056
  }
38772
- hash.update(new TextEncoder().encode("\x00"));
39057
+ check();
38773
39058
  return hash.digest("hex");
38774
39059
  }
39060
+ var CONTENT_HASH_LIMITS = Object.freeze({
39061
+ entries: 1024,
39062
+ rawBytes: 64 * 1024 * 1024,
39063
+ normalizedBytes: 64 * 1024 * 1024,
39064
+ fileBytes: 16 * 1024 * 1024,
39065
+ normalizedFileBytes: 16 * 1024 * 1024,
39066
+ pathBytes: 100,
39067
+ manifestBytes: 16 * 1024,
39068
+ manifestDepth: 64,
39069
+ timeoutMs: 5000
39070
+ });
39071
+
39072
+ class ContentHashInputError extends Error {
39073
+ code;
39074
+ constructor(code, message) {
39075
+ super(message);
39076
+ this.code = code;
39077
+ this.name = "ContentHashInputError";
39078
+ }
39079
+ }
39080
+ function invalidContent(message = "Invalid content hash input") {
39081
+ throw new ContentHashInputError("CONTENT_HASH_INVALID", message);
39082
+ }
39083
+ function contentLimit(message) {
39084
+ throw new ContentHashInputError("CONTENT_HASH_LIMIT", message);
39085
+ }
39086
+ function contentRecord(value, allowed) {
39087
+ if (!value || typeof value !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(value)))
39088
+ invalidContent();
39089
+ const result = Object.create(null);
39090
+ for (const key of Reflect.ownKeys(value)) {
39091
+ if (typeof key !== "string" || !allowed.includes(key))
39092
+ invalidContent();
39093
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
39094
+ if (!descriptor || !("value" in descriptor))
39095
+ invalidContent("Accessor content hash input is unsupported");
39096
+ result[key] = descriptor.value;
39097
+ }
39098
+ return result;
39099
+ }
39100
+ function contentOptions(options) {
39101
+ const record = contentRecord(options, ["limits", "signal"]);
39102
+ const limits = { ...CONTENT_HASH_LIMITS };
39103
+ if (record.limits !== undefined) {
39104
+ const supplied = contentRecord(record.limits, Object.keys(limits));
39105
+ for (const key of Object.keys(supplied)) {
39106
+ const value = supplied[key];
39107
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0 || value > limits[key])
39108
+ contentLimit("Invalid content hash limit");
39109
+ limits[key] = value;
39110
+ }
39111
+ }
39112
+ if (record.signal !== undefined && !(record.signal instanceof AbortSignal))
39113
+ invalidContent("Invalid content hash signal");
39114
+ return { limits, signal: record.signal };
39115
+ }
39116
+ var typedArrayPrototype = Object.getPrototypeOf(Uint8Array.prototype);
39117
+ var byteLengthOf = Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteLength").get;
39118
+ var bufferOf = Object.getOwnPropertyDescriptor(typedArrayPrototype, "buffer").get;
39119
+ function snapshotContentEntries(entries, limits, check) {
39120
+ if (!Array.isArray(entries))
39121
+ invalidContent("Content hash entries must be an array");
39122
+ if (entries.length > limits.entries)
39123
+ contentLimit("Content hash entry limit exceeded");
39124
+ if (Reflect.ownKeys(entries).length !== entries.length + 1)
39125
+ invalidContent("Invalid content hash entry array");
39126
+ const snapshot = [];
39127
+ const paths = new SkillEntryPaths;
39128
+ let rawBytes = 0;
39129
+ for (let index = 0;index < entries.length; index++) {
39130
+ check();
39131
+ const descriptor = Object.getOwnPropertyDescriptor(entries, String(index));
39132
+ if (!descriptor || !("value" in descriptor))
39133
+ invalidContent("Invalid content hash entry array");
39134
+ const entry = contentRecord(descriptor.value, ["path", "bytes", "mode"]);
39135
+ if (typeof entry.path !== "string" || typeof entry.mode !== "number" || !Number.isInteger(entry.mode) || entry.mode < 0 || entry.mode > 511)
39136
+ invalidContent("Invalid regular-file content hash entry");
39137
+ paths.add(entry.path, limits.pathBytes, invalidContent, () => contentLimit("Content hash path limit exceeded"));
39138
+ if (!(entry.bytes instanceof Uint8Array) || !ArrayBuffer.isView(entry.bytes))
39139
+ invalidContent("Content hash entry requires bytes");
39140
+ const size = byteLengthOf.call(entry.bytes);
39141
+ if (!(bufferOf.call(entry.bytes) instanceof ArrayBuffer))
39142
+ invalidContent("Shared content hash bytes are unsupported");
39143
+ if (size > limits.fileBytes || rawBytes + size > limits.rawBytes)
39144
+ contentLimit("Content hash raw byte limit exceeded");
39145
+ if (entry.path === "skill.json" && size > limits.manifestBytes)
39146
+ contentLimit("Content hash manifest byte limit exceeded");
39147
+ rawBytes += size;
39148
+ const bytes = new Uint8Array(new ArrayBuffer(size));
39149
+ bytes.set(entry.bytes);
39150
+ snapshot.push({ path: entry.path, bytes, mode: entry.mode });
39151
+ }
39152
+ check();
39153
+ return snapshot;
39154
+ }
39155
+ function coveredContentPath(path) {
39156
+ const segments = path.split("/");
39157
+ if (!HASH_COVERAGE.includes(segments[0]))
39158
+ return false;
39159
+ return !segments.slice(1).some((segment, index) => excludedHashEntry(segment, index < segments.length - 2));
39160
+ }
39161
+ function boundedManifest(raw, maxDepth) {
39162
+ let parsed;
39163
+ try {
39164
+ parsed = JSON.parse(raw);
39165
+ } catch {
39166
+ return;
39167
+ }
39168
+ const pending = [{ value: parsed, depth: 1 }];
39169
+ while (pending.length) {
39170
+ const { value, depth } = pending.pop();
39171
+ if (!value || typeof value !== "object")
39172
+ continue;
39173
+ if (depth > maxDepth)
39174
+ contentLimit("Content hash manifest depth limit exceeded");
39175
+ for (const child of Object.values(value))
39176
+ pending.push({ value: child, depth: depth + 1 });
39177
+ }
39178
+ return parsed;
39179
+ }
39180
+ async function hashContentEntries(entries, options) {
39181
+ const { limits, signal } = contentOptions(options);
39182
+ const deadline = performance.now() + limits.timeoutMs;
39183
+ let terminal;
39184
+ const abort = () => {
39185
+ terminal ??= new ContentHashInputError("CONTENT_HASH_ABORTED", "Content hashing aborted");
39186
+ };
39187
+ const timer = setTimeout(() => {
39188
+ terminal ??= new ContentHashInputError("CONTENT_HASH_TIMEOUT", "Content hashing deadline exceeded");
39189
+ }, limits.timeoutMs);
39190
+ const check = () => {
39191
+ if (signal?.aborted)
39192
+ abort();
39193
+ if (terminal)
39194
+ throw terminal;
39195
+ if (performance.now() >= deadline)
39196
+ throw new ContentHashInputError("CONTENT_HASH_TIMEOUT", "Content hashing deadline exceeded");
39197
+ };
39198
+ try {
39199
+ signal?.addEventListener("abort", abort, { once: true });
39200
+ check();
39201
+ const snapshot = snapshotContentEntries(entries, limits, check);
39202
+ const normalized = [];
39203
+ let normalizedBytes = 0;
39204
+ let manifest;
39205
+ await new Promise((resolve2) => setImmediate(resolve2));
39206
+ for (const entry of snapshot) {
39207
+ check();
39208
+ if (!coveredContentPath(entry.path))
39209
+ continue;
39210
+ if (entry.path === "skill.json")
39211
+ manifest = boundedManifest(new TextDecoder().decode(entry.bytes), limits.manifestDepth);
39212
+ const file = normalizeBundleFile(entry.path, entry.bytes);
39213
+ check();
39214
+ if (file.content.byteLength > limits.normalizedFileBytes || normalizedBytes + file.content.byteLength > limits.normalizedBytes)
39215
+ contentLimit("Content hash normalized byte limit exceeded");
39216
+ normalizedBytes += file.content.byteLength;
39217
+ normalized.push(file);
39218
+ await new Promise((resolve2) => setImmediate(resolve2));
39219
+ }
39220
+ normalized.sort((a3, b3) => a3.rel < b3.rel ? -1 : a3.rel > b3.rel ? 1 : 0);
39221
+ check();
39222
+ return { hash: await hashBundleFilesCooperatively(normalized, check), manifest };
39223
+ } catch (error) {
39224
+ if (error instanceof ContentHashInputError)
39225
+ throw error;
39226
+ throw new ContentHashInputError("CONTENT_HASH_INVALID", "Invalid content hash input");
39227
+ } finally {
39228
+ clearTimeout(timer);
39229
+ signal?.removeEventListener("abort", abort);
39230
+ }
39231
+ }
39232
+ async function computeContentHashFromEntries(entries, options = {}) {
39233
+ return (await hashContentEntries(entries, options)).hash;
39234
+ }
39235
+ async function verifyContentHashFromEntries(entries, options = {}) {
39236
+ const { hash, manifest } = await hashContentEntries(entries, options);
39237
+ const provenance = manifest && typeof manifest === "object" && !Array.isArray(manifest) ? manifest.provenance : undefined;
39238
+ const value = provenance && typeof provenance === "object" && !Array.isArray(provenance) ? provenance.content_hash : undefined;
39239
+ if (value !== undefined && typeof value !== "string")
39240
+ invalidContent("Invalid content hash declaration");
39241
+ const declaredHash = value?.trim() || undefined;
39242
+ if (!declaredHash)
39243
+ return { declared: false, valid: false };
39244
+ if (!/^[a-f0-9]{64}$/.test(declaredHash))
39245
+ return { declared: true, valid: false, declaredHash };
39246
+ return { declared: true, valid: hash === declaredHash, declaredHash, computedHash: hash };
39247
+ }
38775
39248
  function verifyContentHash(skillPath, manifest) {
38776
39249
  const declaredHash = manifest?.provenance?.content_hash?.trim() || undefined;
38777
39250
  if (!declaredHash)
@@ -52164,11 +52637,174 @@ function createOfflineGate(options) {
52164
52637
  }
52165
52638
  };
52166
52639
  }
52167
- // src/lib/remote-workspace-selection.ts
52640
+ // src/lib/remote-invitations.ts
52641
+ class WorkspaceInvitationInputError extends Error {
52642
+ code = "INVITATION_INPUT_INVALID";
52643
+ constructor() {
52644
+ super("Provide only the documented invitation fields, exact lowercase IDs, expected generation and explicit confirmation. Issue and resend require your stable idempotency key.");
52645
+ this.name = "WorkspaceInvitationInputError";
52646
+ }
52647
+ }
52648
+ var invitationFailures = {
52649
+ INVALID_REQUEST: [400, "Invitation parameters were refused."],
52650
+ ACCOUNT_UNAVAILABLE: [403, "Account is unavailable."],
52651
+ INTERACTIVE_SESSION_REQUIRED: [403, "Fresh interactive sign-in is required."],
52652
+ WORKSPACE_ADMIN_REQUIRED: [403, "A current workspace owner or admin is required."],
52653
+ INVITATION_FORBIDDEN: [403, "Your current role cannot manage this invitation."],
52654
+ INVITATION_UNAVAILABLE: [404, "Invitation is unavailable for this account."],
52655
+ INVITATION_CHANGED: [409, "Invitation changed. Read its current generation before another action."],
52656
+ INVITATION_EXISTS: [409, "A pending invitation already exists. Read current invitations."],
52657
+ ALREADY_MEMBER: [409, "An active membership already exists. An invitation cannot change its role."],
52658
+ IDEMPOTENCY_CONFLICT: [409, "This request key was used for different invitation parameters. Reconcile the original request."],
52659
+ INVITATION_LIMIT: [429, "Invitation limit reached. Wait before issuing or resending."],
52660
+ INVITATION_BUSY: [503, "Invitation is busy. Read its state before another action."],
52661
+ INVITATION_DELIVERY_UNAVAILABLE: [503, "Invitation email delivery is unavailable."]
52662
+ };
52663
+
52664
+ class RemoteWorkspaceInvitationError extends Error {
52665
+ code;
52666
+ status;
52667
+ constructor(code) {
52668
+ super(invitationFailures[code][1]);
52669
+ this.code = code;
52670
+ this.name = "RemoteWorkspaceInvitationError";
52671
+ this.status = invitationFailures[code][0];
52672
+ }
52673
+ }
52674
+
52675
+ class RemoteWorkspaceInvitationUnconfirmedError extends Error {
52676
+ code = "INVITATION_UNCONFIRMED";
52677
+ constructor() {
52678
+ super("The invitation outcome is unconfirmed. Read current invitations or memberships. Reconcile issue/resend only with the same request key, parameters, server and membership; never generate a new key or retry automatically. Saved credentials are unchanged.");
52679
+ this.name = "RemoteWorkspaceInvitationUnconfirmedError";
52680
+ }
52681
+ }
52682
+
52683
+ class RemoteWorkspaceInvitationReadError extends Error {
52684
+ code = "INVITATION_READ_FAILED";
52685
+ constructor() {
52686
+ super("Unable to read a valid invitation result. Check the selected server, account, current membership and permissions.");
52687
+ this.name = "RemoteWorkspaceInvitationReadError";
52688
+ }
52689
+ }
52690
+ function invitationFailure(value, status) {
52691
+ if (!record(value) || typeof value.code !== "string" || !Object.hasOwn(invitationFailures, value.code))
52692
+ return null;
52693
+ const code = value.code;
52694
+ return invitationFailures[code][0] === status ? code : null;
52695
+ }
52168
52696
  var record = (v2) => !!v2 && typeof v2 === "object" && !Array.isArray(v2);
52169
52697
  var uuid = (v2) => typeof v2 === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v2);
52170
- var text = (v2, max = 1024) => typeof v2 === "string" && !!v2.trim() && v2.length <= max && !/[\p{Cc}\p{Cs}\u2028\u2029]/u.test(v2);
52171
52698
  var role = (v2) => typeof v2 === "string" && ["owner", "admin", "member", "viewer"].includes(v2);
52699
+ var email = (v2) => typeof v2 === "string" && v2.length <= 254 && !/[\p{Cc}\p{Cs}\u2028\u2029\s]/u.test(v2) && /^[^@]+@[^@]+\.[^@]+$/.test(v2);
52700
+ var timestamp = (v2) => typeof v2 === "string" && v2.length <= 40 && /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d{1,6})?(?:Z|[+-]\d\d:\d\d)$/.test(v2) && Number.isFinite(Date.parse(v2));
52701
+ var inputFailure = () => {
52702
+ throw new WorkspaceInvitationInputError;
52703
+ };
52704
+ function invitationInput(action, input) {
52705
+ if (!record(input))
52706
+ return inputFailure();
52707
+ const keys = {
52708
+ list: ["after"],
52709
+ get: ["invitationId"],
52710
+ issue: ["email", "role", "idempotencyKey", "confirm"],
52711
+ resend: ["invitationId", "expectedGeneration", "idempotencyKey", "confirm"],
52712
+ revoke: ["invitationId", "expectedGeneration", "confirm"],
52713
+ accept: ["invitationId", "token", "confirm"]
52714
+ };
52715
+ if (Object.keys(input).some((key) => !keys[action].includes(key)) || action !== "list" && keys[action].some((key) => !Object.hasOwn(input, key)))
52716
+ return inputFailure();
52717
+ const value = { ...input };
52718
+ if (action === "list") {
52719
+ if (value.after !== undefined && !uuid(value.after))
52720
+ return inputFailure();
52721
+ }
52722
+ if (["get", "resend", "revoke", "accept"].includes(action) && !uuid(value.invitationId))
52723
+ return inputFailure();
52724
+ if (!["list", "get"].includes(action) && value.confirm !== true)
52725
+ return inputFailure();
52726
+ if (["issue", "resend"].includes(action) && !uuid(value.idempotencyKey))
52727
+ return inputFailure();
52728
+ if (action === "issue") {
52729
+ if (typeof value.email !== "string")
52730
+ return inputFailure();
52731
+ value.email = value.email.trim().toLowerCase();
52732
+ if (!email(value.email) || !role(value.role))
52733
+ return inputFailure();
52734
+ }
52735
+ if (["resend", "revoke"].includes(action) && (!Number.isInteger(value.expectedGeneration) || Number(value.expectedGeneration) < 1 || Number(value.expectedGeneration) > 10))
52736
+ return inputFailure();
52737
+ if (action === "accept" && (typeof value.token !== "string" || !/^[A-Za-z0-9_-]{43}$/.test(value.token)))
52738
+ return inputFailure();
52739
+ return value;
52740
+ }
52741
+ function invitationRequest(action, input) {
52742
+ const base = "/api/v1/workspace/invitations", value = input;
52743
+ const { confirm: _confirm, invitationId: id, ...body } = value;
52744
+ if (action === "list")
52745
+ return { path: base + (value.after ? `?after=${value.after}` : ""), method: "GET" };
52746
+ if (action === "get")
52747
+ return { path: `${base}/${id}`, method: "GET" };
52748
+ if (action === "accept")
52749
+ return { path: "/api/v1/account/invitations/accept", method: "POST", body: JSON.stringify({ invitationId: id, token: value.token }) };
52750
+ return { path: action === "issue" ? base : `${base}/${id}${action === "resend" ? "/resend" : ""}`, method: action === "revoke" ? "DELETE" : "POST", body: JSON.stringify(body) };
52751
+ }
52752
+ function invalid() {
52753
+ throw new RemoteWorkspaceInvitationReadError;
52754
+ }
52755
+ function projection(v2, organizationId) {
52756
+ if (!record(v2) || !uuid(v2.id) || v2.organizationId !== organizationId || !email(v2.email) || v2.email !== v2.email.trim().toLowerCase() || !role(v2.role) || !Number.isInteger(v2.generation) || Number(v2.generation) < 1 || Number(v2.generation) > 10 || typeof v2.status !== "string" || !["pending", "expired", "accepted", "revoked"].includes(v2.status) || !timestamp(v2.expiresAt) || !timestamp(v2.createdAt) || !record(v2.delivery) || typeof v2.delivery.state !== "string" || !["queued", "sending", "uncertain", "provider_accepted", "failed", "cancelled"].includes(v2.delivery.state) || !Number.isInteger(v2.delivery.attempts) || Number(v2.delivery.attempts) < 0 || Number(v2.delivery.attempts) > 5)
52757
+ return invalid();
52758
+ return {
52759
+ id: v2.id,
52760
+ organizationId,
52761
+ email: v2.email,
52762
+ role: v2.role,
52763
+ generation: Number(v2.generation),
52764
+ status: v2.status,
52765
+ expiresAt: v2.expiresAt,
52766
+ createdAt: v2.createdAt,
52767
+ delivery: { state: v2.delivery.state, attempts: Number(v2.delivery.attempts) }
52768
+ };
52769
+ }
52770
+ function parseInvitationResult(action, value, input, organizationId) {
52771
+ if (!record(value))
52772
+ return invalid();
52773
+ const request = input;
52774
+ if (action === "accept") {
52775
+ if (!uuid(value.organizationId) || !uuid(value.membershipId) || value.accepted !== true || typeof value.changed !== "boolean")
52776
+ return invalid();
52777
+ return { organizationId: value.organizationId, membershipId: value.membershipId, accepted: true, changed: value.changed };
52778
+ }
52779
+ if (action === "list") {
52780
+ if (value.organizationId !== organizationId || !Array.isArray(value.invitations) || value.invitations.length > 50 || value.nextCursor !== null && !uuid(value.nextCursor))
52781
+ return invalid();
52782
+ const invitations = value.invitations.map((v2) => projection(v2, organizationId));
52783
+ if (invitations.some((v2, n2) => v2.id <= String(n2 ? invitations[n2 - 1].id : request.after ?? "")) || value.nextCursor !== null && (invitations.length !== 50 || value.nextCursor !== invitations.at(-1)?.id))
52784
+ return invalid();
52785
+ return { organizationId, invitations, nextCursor: value.nextCursor };
52786
+ }
52787
+ const invitation = projection(value.invitation, organizationId);
52788
+ if (action !== "issue" && invitation.id !== request.invitationId)
52789
+ return invalid();
52790
+ if (action === "get")
52791
+ return { invitation };
52792
+ if (typeof value.changed !== "boolean")
52793
+ return invalid();
52794
+ if (action === "issue" && (invitation.email !== request.email || invitation.role !== request.role || value.changed && invitation.generation !== 1))
52795
+ return invalid();
52796
+ if (action === "resend" && (value.changed ? invitation.generation !== Number(request.expectedGeneration) + 1 : invitation.generation <= Number(request.expectedGeneration)))
52797
+ return invalid();
52798
+ if (action === "revoke" && (invitation.status !== "revoked" || invitation.generation < Number(request.expectedGeneration) || value.changed && invitation.generation !== request.expectedGeneration))
52799
+ return invalid();
52800
+ return { invitation, changed: value.changed };
52801
+ }
52802
+
52803
+ // src/lib/remote-workspace-selection.ts
52804
+ var record2 = (v2) => !!v2 && typeof v2 === "object" && !Array.isArray(v2);
52805
+ var uuid2 = (v2) => typeof v2 === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v2);
52806
+ var text = (v2, max = 1024) => typeof v2 === "string" && !!v2.trim() && v2.length <= max && !/[\p{Cc}\p{Cs}\u2028\u2029]/u.test(v2);
52807
+ var role2 = (v2) => typeof v2 === "string" && ["owner", "admin", "member", "viewer"].includes(v2);
52172
52808
  var invalidWorkspaceResult = "The server returned an invalid workspace selection result.";
52173
52809
 
52174
52810
  class WorkspaceContextInputError extends Error {
@@ -52185,48 +52821,48 @@ class WorkspaceIdentityMismatchError extends Error {
52185
52821
  }
52186
52822
  }
52187
52823
  function workspaceExpectedUserId(value) {
52188
- if (!uuid(value))
52824
+ if (!uuid2(value))
52189
52825
  throw new WorkspaceContextInputError;
52190
52826
  return value;
52191
52827
  }
52192
52828
  function workspaceContext(value) {
52193
- if (!record(value) || Object.keys(value).sort().join(",") !== "membershipId,userId" || !uuid(value.userId) || !uuid(value.membershipId))
52829
+ if (!record2(value) || Object.keys(value).sort().join(",") !== "membershipId,userId" || !uuid2(value.userId) || !uuid2(value.membershipId))
52194
52830
  throw new WorkspaceContextInputError;
52195
52831
  return { userId: value.userId, membershipId: value.membershipId };
52196
52832
  }
52197
- function invalid() {
52833
+ function invalid2() {
52198
52834
  throw new Error(invalidWorkspaceResult);
52199
52835
  }
52200
52836
  function organization(v2) {
52201
- if (!record(v2) || !uuid(v2.id) || !text(v2.slug) || !text(v2.name))
52202
- return invalid();
52837
+ if (!record2(v2) || !uuid2(v2.id) || !text(v2.slug) || !text(v2.name))
52838
+ return invalid2();
52203
52839
  return { id: v2.id, slug: v2.slug, name: v2.name };
52204
52840
  }
52205
52841
  function parseAccountWorkspaces(value) {
52206
- if (!record(value) || !Array.isArray(value.workspaces) || !value.workspaces.length || value.workspaces.length > 1000)
52207
- return invalid();
52842
+ if (!record2(value) || !Array.isArray(value.workspaces) || !value.workspaces.length || value.workspaces.length > 1000)
52843
+ return invalid2();
52208
52844
  const workspaces = value.workspaces.map((v2) => {
52209
- if (!record(v2) || !uuid(v2.membershipId) || !role(v2.role) || typeof v2.current !== "boolean")
52210
- return invalid();
52845
+ if (!record2(v2) || !uuid2(v2.membershipId) || !role2(v2.role) || typeof v2.current !== "boolean")
52846
+ return invalid2();
52211
52847
  return { membershipId: v2.membershipId, organization: organization(v2.organization), role: v2.role, current: v2.current };
52212
52848
  });
52213
52849
  if (workspaces.filter((w2) => w2.current).length !== 1 || new Set(workspaces.map((w2) => w2.membershipId)).size !== workspaces.length || new Set(workspaces.map((w2) => w2.organization.id)).size !== workspaces.length)
52214
- return invalid();
52850
+ return invalid2();
52215
52851
  return { workspaces };
52216
52852
  }
52217
52853
  function parseWorkspaceIdentity(value, expectedUserId) {
52218
- if (!record(value))
52219
- return invalid();
52854
+ if (!record2(value))
52855
+ return invalid2();
52220
52856
  const user = value.user;
52221
- if (!record(user) || !uuid(user.id) || !uuid(user.membershipId) || !text(user.email, 320) || !(user.displayName === null || text(user.displayName)) || !role(user.role))
52222
- return invalid();
52857
+ if (!record2(user) || !uuid2(user.id) || !uuid2(user.membershipId) || !text(user.email, 320) || !(user.displayName === null || text(user.displayName)) || !role2(user.role))
52858
+ return invalid2();
52223
52859
  if (user.id !== expectedUserId)
52224
52860
  throw new WorkspaceIdentityMismatchError;
52225
52861
  return { user: { id: user.id, membershipId: user.membershipId, email: user.email, displayName: user.displayName, role: user.role }, organization: organization(value.organization) };
52226
52862
  }
52227
52863
  function sessionToken(value) {
52228
52864
  if (typeof value !== "string" || !value || value.length > 8192 || /[^\x21-\x7e]/.test(value) || value.startsWith("sk_"))
52229
- return invalid();
52865
+ return invalid2();
52230
52866
  return value;
52231
52867
  }
52232
52868
  function parseWorkspaceSession(value, expected) {
@@ -52236,9 +52872,9 @@ function parseWorkspaceSession(value, expected) {
52236
52872
  return { token: sessionToken(value.token), ...identity };
52237
52873
  }
52238
52874
  function parseWorkspaceLogin(value, expectedUserId) {
52239
- const user = record(value) && value.user;
52240
- if (!record(value) || !record(user) || !uuid(user.id))
52241
- return invalid();
52875
+ const user = record2(value) && value.user;
52876
+ if (!record2(value) || !record2(user) || !uuid2(user.id))
52877
+ return invalid2();
52242
52878
  if (expectedUserId !== undefined && user.id !== expectedUserId)
52243
52879
  throw new WorkspaceIdentityMismatchError;
52244
52880
  return { token: sessionToken(value.token), userId: user.id };
@@ -52252,18 +52888,18 @@ var workspaceSelectionFailures = {
52252
52888
  WORKSPACE_BUSY: [503, "Workspace is busy. Refresh before retrying."]
52253
52889
  };
52254
52890
  function workspaceSelectionFailure(value, status) {
52255
- if (!record(value) || typeof value.code !== "string" || !Object.hasOwn(workspaceSelectionFailures, value.code))
52891
+ if (!record2(value) || typeof value.code !== "string" || !Object.hasOwn(workspaceSelectionFailures, value.code))
52256
52892
  return null;
52257
52893
  const code = value.code;
52258
52894
  return workspaceSelectionFailures[code][0] === status ? code : null;
52259
52895
  }
52260
52896
 
52261
52897
  // src/lib/remote-workspace.ts
52262
- var record2 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
52898
+ var record3 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
52263
52899
  var cursor = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,512}$/.test(value);
52264
- var uuid2 = (value) => typeof value === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(value);
52900
+ var uuid3 = (value) => typeof value === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(value);
52265
52901
  function workspaceMembersQuery(options = {}) {
52266
- if (!record2(options) || Object.keys(options).some((key) => key !== "limit" && key !== "cursor") || options.limit !== undefined && (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > 100) || options.cursor !== undefined && !cursor(options.cursor))
52902
+ if (!record3(options) || Object.keys(options).some((key) => key !== "limit" && key !== "cursor") || options.limit !== undefined && (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > 100) || options.cursor !== undefined && !cursor(options.cursor))
52267
52903
  throw new Error("Use a roster limit from 1 to 100 and an unchanged continuation cursor.");
52268
52904
  const query = new URLSearchParams;
52269
52905
  if (options.limit !== undefined)
@@ -52272,14 +52908,14 @@ function workspaceMembersQuery(options = {}) {
52272
52908
  query.set("cursor", options.cursor);
52273
52909
  return query.size ? `?${query}` : "";
52274
52910
  }
52275
- function timestamp(value) {
52911
+ function timestamp2(value) {
52276
52912
  if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}Z$/.test(value))
52277
52913
  return false;
52278
52914
  const time = Date.parse(value);
52279
52915
  return Number.isFinite(time) && new Date(time).toISOString().slice(0, 23) === value.slice(0, 23);
52280
52916
  }
52281
52917
  function parseMember(row, fail) {
52282
- if (!record2(row) || !uuid2(row.membershipId) || !uuid2(row.userId) || typeof row.email !== "string" || !row.email || !(row.displayName === null || typeof row.displayName === "string") || !isRole(row.role) || !timestamp(row.createdAt))
52918
+ if (!record3(row) || !uuid3(row.membershipId) || !uuid3(row.userId) || typeof row.email !== "string" || !row.email || !(row.displayName === null || typeof row.displayName === "string") || !isRole(row.role) || !timestamp2(row.createdAt))
52283
52919
  return fail();
52284
52920
  return {
52285
52921
  membershipId: row.membershipId,
@@ -52299,12 +52935,12 @@ class WorkspaceMemberInputError extends Error {
52299
52935
  }
52300
52936
  }
52301
52937
  function mutationInput(membershipId, input, roleChange) {
52302
- if (typeof membershipId !== "string" || !uuid2(membershipId) || membershipId !== membershipId.toLowerCase() || !record2(input) || Object.keys(input).sort().join(",") !== (roleChange ? "expectedRole,role" : "expectedRole"))
52938
+ if (typeof membershipId !== "string" || !uuid3(membershipId) || membershipId !== membershipId.toLowerCase() || !record3(input) || Object.keys(input).sort().join(",") !== (roleChange ? "expectedRole,role" : "expectedRole"))
52303
52939
  throw new WorkspaceMemberInputError;
52304
- const expectedRole = input.expectedRole, role2 = roleChange ? input.role : undefined;
52305
- if (!isRole(expectedRole) || roleChange && !isRole(role2))
52940
+ const expectedRole = input.expectedRole, role3 = roleChange ? input.role : undefined;
52941
+ if (!isRole(expectedRole) || roleChange && !isRole(role3))
52306
52942
  throw new WorkspaceMemberInputError;
52307
- return { membershipId, role: role2, expectedRole };
52943
+ return { membershipId, role: role3, expectedRole };
52308
52944
  }
52309
52945
  function workspaceMemberRoleInput(membershipId, input) {
52310
52946
  const value = mutationInput(membershipId, input, true);
@@ -52315,19 +52951,19 @@ function workspaceMemberRemovalInput(membershipId, input) {
52315
52951
  return { membershipId: value.membershipId, body: { expectedRole: value.expectedRole } };
52316
52952
  }
52317
52953
  var invalidMemberResult = "The server returned an invalid workspace member result. Refresh the roster before another action.";
52318
- function parseWorkspaceMemberRoleResult(value, membershipId, role2) {
52954
+ function parseWorkspaceMemberRoleResult(value, membershipId, role3) {
52319
52955
  const fail = () => {
52320
52956
  throw new Error(invalidMemberResult);
52321
52957
  };
52322
- if (!record2(value) || !uuid2(value.organizationId) || typeof value.changed !== "boolean")
52958
+ if (!record3(value) || !uuid3(value.organizationId) || typeof value.changed !== "boolean")
52323
52959
  return fail();
52324
52960
  const member = parseMember(value.member, fail);
52325
- if (member.membershipId !== membershipId || member.role !== role2)
52961
+ if (member.membershipId !== membershipId || member.role !== role3)
52326
52962
  return fail();
52327
52963
  return { organizationId: value.organizationId, member, changed: value.changed };
52328
52964
  }
52329
52965
  function parseWorkspaceMemberRemovalResult(value, membershipId) {
52330
- if (!record2(value) || !uuid2(value.organizationId) || value.membershipId !== membershipId || value.removed !== true || typeof value.alreadyRemoved !== "boolean")
52966
+ if (!record3(value) || !uuid3(value.organizationId) || value.membershipId !== membershipId || value.removed !== true || typeof value.alreadyRemoved !== "boolean")
52331
52967
  throw new Error(invalidMemberResult);
52332
52968
  return { organizationId: value.organizationId, membershipId, removed: true, alreadyRemoved: value.alreadyRemoved };
52333
52969
  }
@@ -52344,7 +52980,7 @@ var workspaceMemberFailures = {
52344
52980
  MEMBERSHIP_BUSY: [503, "Membership is busy. Refresh the roster before another action."]
52345
52981
  };
52346
52982
  function workspaceMemberFailure(value, status) {
52347
- if (!record2(value) || typeof value.code !== "string" || !Object.hasOwn(workspaceMemberFailures, value.code))
52983
+ if (!record3(value) || typeof value.code !== "string" || !Object.hasOwn(workspaceMemberFailures, value.code))
52348
52984
  return null;
52349
52985
  const code = value.code;
52350
52986
  return workspaceMemberFailures[code][0] === status ? code : null;
@@ -52353,7 +52989,7 @@ function parseWorkspaceMembersPage(value) {
52353
52989
  const fail = () => {
52354
52990
  throw new Error("The server returned an invalid workspace roster.");
52355
52991
  };
52356
- if (!record2(value) || !uuid2(value.organizationId) || !Array.isArray(value.members) || value.members.length > 100 || !(value.nextCursor === null || cursor(value.nextCursor)) || !value.members.length && value.nextCursor !== null)
52992
+ if (!record3(value) || !uuid3(value.organizationId) || !Array.isArray(value.members) || value.members.length > 100 || !(value.nextCursor === null || cursor(value.nextCursor)) || !value.members.length && value.nextCursor !== null)
52357
52993
  return fail();
52358
52994
  const members = value.members.map((row) => parseMember(row, fail));
52359
52995
  if (new Set(members.map((row) => row.membershipId)).size !== members.length)
@@ -52898,6 +53534,53 @@ class RemoteSkillsClient {
52898
53534
  }
52899
53535
  return parseWorkspaceLeaveResult(body, captured.context.membershipId, identity.organization.id);
52900
53536
  }
53537
+ listWorkspaceInvitations(context, options = {}) {
53538
+ return this.requestWorkspaceInvitation(context, "list", options);
53539
+ }
53540
+ getWorkspaceInvitation(context, invitationId) {
53541
+ return this.requestWorkspaceInvitation(context, "get", { invitationId });
53542
+ }
53543
+ issueWorkspaceInvitation(context, input) {
53544
+ return this.requestWorkspaceInvitation(context, "issue", input);
53545
+ }
53546
+ resendWorkspaceInvitation(context, invitationId, input) {
53547
+ return this.requestWorkspaceInvitation(context, "resend", { ...input, invitationId });
53548
+ }
53549
+ revokeWorkspaceInvitation(context, invitationId, input) {
53550
+ return this.requestWorkspaceInvitation(context, "revoke", { ...input, invitationId });
53551
+ }
53552
+ acceptWorkspaceInvitation(context, invitationId, input) {
53553
+ return this.requestWorkspaceInvitation(context, "accept", { ...input, invitationId });
53554
+ }
53555
+ async requestWorkspaceInvitation(context, action, input) {
53556
+ const target = workspaceContext(context), captured = invitationInput(action, input);
53557
+ const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
53558
+ const identityValue = await connection.requestWorkspaceSelection("/api/auth/whoami");
53559
+ if (!identityValue || typeof identityValue !== "object" || identityValue.authMethod !== "jwt")
53560
+ throw new RemoteWorkspaceInvitationError("INTERACTIVE_SESSION_REQUIRED");
53561
+ const identity = parseWorkspaceIdentity(identityValue, target.userId);
53562
+ if (identity.user.membershipId !== target.membershipId)
53563
+ throw new WorkspaceIdentityMismatchError;
53564
+ const request = invitationRequest(action, captured), read = action === "list" || action === "get";
53565
+ let response, value;
53566
+ try {
53567
+ response = await connection.request(request.path, { method: request.method, ...request.body ? { body: request.body } : {}, credentials: "omit" });
53568
+ value = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, response.ok ? 64 * 1024 : 4096)));
53569
+ } catch {
53570
+ throw read ? new RemoteWorkspaceInvitationReadError : new RemoteWorkspaceInvitationUnconfirmedError;
53571
+ }
53572
+ if (!response.ok) {
53573
+ const code = invitationFailure(value, response.status);
53574
+ if (code)
53575
+ throw new RemoteWorkspaceInvitationError(code);
53576
+ throw read ? new RemoteWorkspaceInvitationReadError : new RemoteWorkspaceInvitationUnconfirmedError;
53577
+ }
53578
+ try {
53579
+ return parseInvitationResult(action, value, captured, identity.organization.id);
53580
+ } catch {
53581
+ throw read ? new RemoteWorkspaceInvitationReadError : new RemoteWorkspaceInvitationUnconfirmedError;
53582
+ }
53583
+ }
52901
53584
  async listApiKeys() {
52902
53585
  return this.arrayResponse("/api/auth/keys");
52903
53586
  }
@@ -53148,13 +53831,13 @@ class RemoteSkillsClient {
53148
53831
  return normalizeUpdatedSincePage(await response.json());
53149
53832
  }
53150
53833
  }
53151
- function requireOptionalString(record3, field) {
53152
- if (record3[field] === undefined)
53834
+ function requireOptionalString(record4, field) {
53835
+ if (record4[field] === undefined)
53153
53836
  return;
53154
- if (typeof record3[field] !== "string") {
53837
+ if (typeof record4[field] !== "string") {
53155
53838
  throw new Error(`Remote payload did not match the expected contract (${field} must be a string when present)`);
53156
53839
  }
53157
- return record3[field];
53840
+ return record4[field];
53158
53841
  }
53159
53842
  var INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
53160
53843
  function isVersionRecord(value) {
@@ -53177,19 +53860,19 @@ function normalizePin(entry) {
53177
53860
  if (!entry || typeof entry !== "object") {
53178
53861
  throw new Error("Remote pin payload did not match the expected contract (expected an object)");
53179
53862
  }
53180
- const record3 = entry;
53181
- const slug = typeof record3.slug === "string" && record3.slug.trim() ? record3.slug.trim() : undefined;
53863
+ const record4 = entry;
53864
+ const slug = typeof record4.slug === "string" && record4.slug.trim() ? record4.slug.trim() : undefined;
53182
53865
  if (!slug) {
53183
53866
  throw new Error("Remote pin payload did not match the expected contract (missing slug)");
53184
53867
  }
53185
53868
  let metadata;
53186
- if (record3.metadata !== undefined) {
53187
- if (!record3.metadata || typeof record3.metadata !== "object" || Array.isArray(record3.metadata)) {
53869
+ if (record4.metadata !== undefined) {
53870
+ if (!record4.metadata || typeof record4.metadata !== "object" || Array.isArray(record4.metadata)) {
53188
53871
  throw new Error("Remote pin payload did not match the expected contract (metadata must be a JSON object when present)");
53189
53872
  }
53190
- metadata = record3.metadata;
53873
+ metadata = record4.metadata;
53191
53874
  }
53192
- const pinnedAt = requireOptionalString(record3, "pinnedAt");
53875
+ const pinnedAt = requireOptionalString(record4, "pinnedAt");
53193
53876
  return {
53194
53877
  slug,
53195
53878
  ...pinnedAt !== undefined ? { pinnedAt } : {},
@@ -53206,16 +53889,16 @@ function normalizeSkillSummary(entry) {
53206
53889
  if (!entry || typeof entry !== "object") {
53207
53890
  throw new Error("Remote skill payload did not match the expected contract (expected an object)");
53208
53891
  }
53209
- const record3 = entry;
53210
- const slug = typeof record3.slug === "string" && record3.slug.trim() ? record3.slug.trim() : undefined;
53892
+ const record4 = entry;
53893
+ const slug = typeof record4.slug === "string" && record4.slug.trim() ? record4.slug.trim() : undefined;
53211
53894
  if (!slug) {
53212
53895
  throw new Error("Remote skill payload did not match the expected contract (missing slug)");
53213
53896
  }
53214
53897
  return {
53215
53898
  slug,
53216
- ...requireOptionalString(record3, "name") !== undefined ? { name: requireOptionalString(record3, "name") } : {},
53217
- ...requireOptionalString(record3, "version") !== undefined ? { version: requireOptionalString(record3, "version") } : {},
53218
- ...requireOptionalString(record3, "updatedAt") !== undefined ? { updatedAt: requireOptionalString(record3, "updatedAt") } : {}
53899
+ ...requireOptionalString(record4, "name") !== undefined ? { name: requireOptionalString(record4, "name") } : {},
53900
+ ...requireOptionalString(record4, "version") !== undefined ? { version: requireOptionalString(record4, "version") } : {},
53901
+ ...requireOptionalString(record4, "updatedAt") !== undefined ? { updatedAt: requireOptionalString(record4, "updatedAt") } : {}
53219
53902
  };
53220
53903
  }
53221
53904
  function normalizeSkillSummaryList(payload) {
@@ -53268,12 +53951,12 @@ function normalizeUpdatedSincePage(payload) {
53268
53951
  if (!payload || typeof payload !== "object") {
53269
53952
  throw new Error("Updated-since payload did not match the expected contract (expected an object)");
53270
53953
  }
53271
- const record3 = payload;
53272
- if (!Array.isArray(record3.skills)) {
53954
+ const record4 = payload;
53955
+ if (!Array.isArray(record4.skills)) {
53273
53956
  throw new Error("Updated-since payload did not match the expected contract (missing skills array)");
53274
53957
  }
53275
- const skills = record3.skills.map(normalizeSkillSummary);
53276
- const nextCursor = record3.nextCursor === undefined || record3.nextCursor === null ? null : record3.nextCursor;
53958
+ const skills = record4.skills.map(normalizeSkillSummary);
53959
+ const nextCursor = record4.nextCursor === undefined || record4.nextCursor === null ? null : record4.nextCursor;
53277
53960
  if (nextCursor !== null && typeof nextCursor !== "string") {
53278
53961
  throw new Error("Updated-since payload did not match the expected contract (nextCursor must be a string or absent)");
53279
53962
  }
@@ -53283,6 +53966,101 @@ async function createRemoteSkillsClient(env = process.env) {
53283
53966
  const connection = await resolveSkillsConnection(env);
53284
53967
  return connection ? new RemoteSkillsClient(connection.apiKey, connection.apiOrigin) : null;
53285
53968
  }
53969
+ // src/lib/remote-invitation-recovery.ts
53970
+ class InvitationEmailInputError extends Error {
53971
+ code = "INVITATION_EMAIL_INPUT_INVALID";
53972
+ constructor() {
53973
+ super("Use an explicit Skills API URL, exact invitation and retained challenge IDs, secret input, and deliberate confirmation.");
53974
+ this.name = "InvitationEmailInputError";
53975
+ }
53976
+ }
53977
+ var refusals = {
53978
+ INVALID_REQUEST: [400, "Invitation recovery parameters were refused."],
53979
+ ORIGIN_REQUIRED: [403, "Invitation recovery requires the configured site origin."],
53980
+ INVITATION_PROOF_UNAVAILABLE: [401, "Invitation proof is unavailable. Sign in or explicitly request another recovery code."],
53981
+ RATE_LIMITED: [429, "Invitation verification is rate limited. Wait before another deliberate action."],
53982
+ INVITATION_BUSY: [503, "Invitation is busy. Sign in to check membership before another deliberate action."],
53983
+ INVITATION_DELIVERY_UNAVAILABLE: [503, "Invitation recovery is unavailable on this service."]
53984
+ };
53985
+
53986
+ class RemoteInvitationEmailError extends Error {
53987
+ code;
53988
+ status;
53989
+ constructor(code) {
53990
+ super(refusals[code][1]);
53991
+ this.code = code;
53992
+ this.name = "RemoteInvitationEmailError";
53993
+ this.status = refusals[code][0];
53994
+ }
53995
+ }
53996
+
53997
+ class RemoteInvitationEmailUnconfirmedError extends Error {
53998
+ action;
53999
+ code = "INVITATION_EMAIL_UNCONFIRMED";
54000
+ constructor(action) {
54001
+ super(action === "challenge" ? "The recovery challenge outcome is unconfirmed. Retain the same invitation, challenge ID and server. Check your inbox; never rotate the challenge or retry automatically. No delivery is confirmed." : "Invitation acceptance is unconfirmed. Use fresh ordinary sign-in to inspect available memberships. Do not retry acceptance automatically; explicitly request another recovery code only if needed. Saved credentials are unchanged.");
54002
+ this.action = action;
54003
+ this.name = "RemoteInvitationEmailUnconfirmedError";
54004
+ }
54005
+ }
54006
+ var record4 = (v2) => !!v2 && typeof v2 === "object" && !Array.isArray(v2);
54007
+ var uuid4 = (v2) => typeof v2 === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v2);
54008
+ function invitationEmailIds(invitationId, challengeId) {
54009
+ if (!uuid4(invitationId) || !uuid4(challengeId))
54010
+ throw new InvitationEmailInputError;
54011
+ return { invitationId, challengeId };
54012
+ }
54013
+ function invitationEmailInput(action, input) {
54014
+ const keys = ["invitationId", "token", "challengeId", "confirm", ...action === "accept" ? ["code"] : []];
54015
+ if (!record4(input) || Object.keys(input).length !== keys.length || keys.some((key) => !Object.hasOwn(input, key)))
54016
+ throw new InvitationEmailInputError;
54017
+ const value = { ...input };
54018
+ const ids = invitationEmailIds(value.invitationId, value.challengeId);
54019
+ if (value.confirm !== true || typeof value.token !== "string" || !/^[A-Za-z0-9_-]{43}$/.test(value.token) || action === "accept" && (typeof value.code !== "string" || !/^\d{6}$/.test(value.code)))
54020
+ throw new InvitationEmailInputError;
54021
+ return { ...ids, token: value.token, confirm: true, ...action === "accept" ? { code: value.code } : {} };
54022
+ }
54023
+ async function requestInvitationEmail(origin, action, input) {
54024
+ const value = invitationEmailInput(action, input);
54025
+ let target;
54026
+ try {
54027
+ target = normalizeSkillsApiOrigin(origin);
54028
+ } catch {
54029
+ throw new InvitationEmailInputError;
54030
+ }
54031
+ const body = JSON.stringify({ invitationId: value.invitationId, token: value.token, challengeId: value.challengeId, ...action === "accept" ? { code: value.code } : {} });
54032
+ try {
54033
+ const response = await fetch(`${target}/api/v1/account/invitations/email-${action}`, {
54034
+ method: "POST",
54035
+ headers: { "Content-Type": "application/json" },
54036
+ body,
54037
+ credentials: "omit",
54038
+ redirect: "error",
54039
+ cache: "no-store",
54040
+ referrerPolicy: "no-referrer",
54041
+ signal: AbortSignal.timeout(15000)
54042
+ });
54043
+ const parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await readBoundedResponse(response, 4096)));
54044
+ if (!response.ok && record4(parsed) && typeof parsed.code === "string" && Object.hasOwn(refusals, parsed.code)) {
54045
+ const code = parsed.code;
54046
+ if (response.status === refusals[code][0])
54047
+ throw new RemoteInvitationEmailError(code);
54048
+ }
54049
+ if (!record4(parsed))
54050
+ throw new RemoteInvitationEmailUnconfirmedError(action);
54051
+ if (action === "challenge" && response.status === 202 && parsed.challengeId === value.challengeId && parsed.expiresIn === 600 && typeof parsed.message === "string" && parsed.message.length <= 256) {
54052
+ return { challengeId: value.challengeId, message: "If this invitation is eligible, a verification code will arrive. Delivery is not confirmed.", expiresIn: 600 };
54053
+ }
54054
+ if (action === "accept" && response.status === 200 && uuid4(parsed.organizationId) && uuid4(parsed.membershipId) && parsed.accepted === true && parsed.changed === true && parsed.signInRequired === true) {
54055
+ return { organizationId: parsed.organizationId, membershipId: parsed.membershipId, accepted: true, changed: true, signInRequired: true };
54056
+ }
54057
+ } catch (error) {
54058
+ if (error instanceof RemoteInvitationEmailError)
54059
+ throw error;
54060
+ }
54061
+ throw new RemoteInvitationEmailUnconfirmedError(action);
54062
+ }
54063
+
53286
54064
  // src/lib/remote-auth.ts
53287
54065
  var MAX_ERROR_DETAIL_LENGTH = 200;
53288
54066
 
@@ -53323,10 +54101,10 @@ async function requestAuthApi(instance, path, options) {
53323
54101
  const text2 = await res.text();
53324
54102
  const body = text2 ? parseJsonBody(text2) : {};
53325
54103
  if (!res.ok) {
53326
- const record3 = isRecord6(body) ? body : {};
53327
- const detail = typeof record3.detail === "string" ? record3.detail : undefined;
53328
- const error = typeof record3.error === "string" ? record3.error : undefined;
53329
- const code = typeof record3.code === "string" ? record3.code : undefined;
54104
+ const record5 = isRecord6(body) ? body : {};
54105
+ const detail = typeof record5.detail === "string" ? record5.detail : undefined;
54106
+ const error = typeof record5.error === "string" ? record5.error : undefined;
54107
+ const code = typeof record5.code === "string" ? record5.code : undefined;
53330
54108
  throw new HostedApiError(detail || error || `${res.status} ${res.statusText}`, {
53331
54109
  status: res.status,
53332
54110
  code,
@@ -53360,11 +54138,17 @@ class RemoteSkillsAuthClient {
53360
54138
  constructor(apiUrl) {
53361
54139
  this.apiOrigin = normalizeSkillsApiOrigin(apiUrl);
53362
54140
  }
53363
- requestCode(email) {
53364
- return this.request("/api/auth/login", { method: "POST", body: JSON.stringify({ email }) });
54141
+ requestInvitationEmailChallenge(input) {
54142
+ return requestInvitationEmail(this.apiOrigin, "challenge", input);
54143
+ }
54144
+ acceptInvitationEmailChallenge(input) {
54145
+ return requestInvitationEmail(this.apiOrigin, "accept", input);
54146
+ }
54147
+ requestCode(email2) {
54148
+ return this.request("/api/auth/login", { method: "POST", body: JSON.stringify({ email: email2 }) });
53365
54149
  }
53366
- verifyCode(email, code) {
53367
- return this.request("/api/auth/verify", { method: "POST", body: JSON.stringify({ email, code }) });
54150
+ verifyCode(email2, code) {
54151
+ return this.request("/api/auth/verify", { method: "POST", body: JSON.stringify({ email: email2, code }) });
53368
54152
  }
53369
54153
  startDevice() {
53370
54154
  return this.request("/api/auth/device/start", { method: "POST", body: JSON.stringify({ client: "skills-sdk" }) });
@@ -53372,34 +54156,34 @@ class RemoteSkillsAuthClient {
53372
54156
  pollDevice(deviceCode) {
53373
54157
  return this.request("/api/auth/device/token", { method: "POST", body: JSON.stringify({ deviceCode }) });
53374
54158
  }
53375
- async sessionClient(email, code, context) {
54159
+ async sessionClient(email2, code, context) {
53376
54160
  if (context !== undefined) {
53377
54161
  const target = workspaceContext(context), apiOrigin2 = this.apiOrigin;
53378
- const session = await this.switchWorkspace(email, code, target);
54162
+ const session = await this.switchWorkspace(email2, code, target);
53379
54163
  return new RemoteSkillsClient(session.token, apiOrigin2);
53380
54164
  }
53381
54165
  const apiOrigin = this.apiOrigin;
53382
- if (!email.includes("@") || !/^\d{6}$/.test(code))
54166
+ if (!email2.includes("@") || !/^\d{6}$/.test(code))
53383
54167
  throw new Error("Fresh email and six-digit verification code are required to manage this account");
53384
- const login = await this.verifyCode(email, code);
54168
+ const login = await this.verifyCode(email2, code);
53385
54169
  if (!login || typeof login.token !== "string" || !login.token)
53386
54170
  throw new Error("The server did not return an authorized account session");
53387
54171
  return new RemoteSkillsClient(login.token, apiOrigin);
53388
54172
  }
53389
- async listAccountWorkspaces(email, code, expectedUserId) {
53390
- const login = await this.workspaceLogin(email, code, expectedUserId);
54173
+ async listAccountWorkspaces(email2, code, expectedUserId) {
54174
+ const login = await this.workspaceLogin(email2, code, expectedUserId);
53391
54175
  const result = await new RemoteSkillsClient(login.token, login.apiOrigin).listAccountWorkspaces(login.userId);
53392
54176
  return { userId: login.userId, ...result };
53393
54177
  }
53394
- async switchWorkspace(email, code, context) {
54178
+ async switchWorkspace(email2, code, context) {
53395
54179
  const target = workspaceContext(context);
53396
- const login = await this.workspaceLogin(email, code, target.userId);
54180
+ const login = await this.workspaceLogin(email2, code, target.userId);
53397
54181
  return new RemoteSkillsClient(login.token, login.apiOrigin).switchWorkspace(target);
53398
54182
  }
53399
- async workspaceLogin(email, code, expectedUserId) {
54183
+ async workspaceLogin(email2, code, expectedUserId) {
53400
54184
  const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
53401
54185
  const apiOrigin = this.apiOrigin;
53402
- if (typeof email !== "string" || !email.includes("@") || typeof code !== "string" || !/^\d{6}$/.test(code))
54186
+ if (typeof email2 !== "string" || !email2.includes("@") || typeof code !== "string" || !/^\d{6}$/.test(code))
53403
54187
  throw new Error("Fresh email and six-digit verification code are required to manage this account");
53404
54188
  let response;
53405
54189
  try {
@@ -53409,7 +54193,7 @@ class RemoteSkillsAuthClient {
53409
54193
  credentials: "omit",
53410
54194
  signal: AbortSignal.timeout(15000),
53411
54195
  headers: { "Content-Type": "application/json" },
53412
- body: JSON.stringify({ email, code })
54196
+ body: JSON.stringify({ email: email2, code })
53413
54197
  });
53414
54198
  } catch {
53415
54199
  throw new HostedApiError("Unable to verify the Skills account.");
@@ -53426,40 +54210,67 @@ class RemoteSkillsAuthClient {
53426
54210
  }
53427
54211
  return { ...parseWorkspaceLogin(value, expected), apiOrigin };
53428
54212
  }
53429
- async createApiKey(email, code, name, scopes, context) {
54213
+ async listWorkspaceInvitations(email2, code, context, options = {}) {
54214
+ const target = workspaceContext(context), captured = invitationInput("list", options);
54215
+ return (await this.sessionClient(email2, code, target)).listWorkspaceInvitations(target, captured);
54216
+ }
54217
+ async getWorkspaceInvitation(email2, code, context, invitationId) {
54218
+ const target = workspaceContext(context), captured = invitationInput("get", { invitationId });
54219
+ return (await this.sessionClient(email2, code, target)).getWorkspaceInvitation(target, captured.invitationId);
54220
+ }
54221
+ async issueWorkspaceInvitation(email2, code, context, input) {
54222
+ const target = workspaceContext(context), captured = invitationInput("issue", input);
54223
+ return (await this.sessionClient(email2, code, target)).issueWorkspaceInvitation(target, captured);
54224
+ }
54225
+ async resendWorkspaceInvitation(email2, code, context, invitationId, input) {
54226
+ const target = workspaceContext(context), captured = invitationInput("resend", { ...input, invitationId });
54227
+ const { invitationId: id, ...options } = captured;
54228
+ return (await this.sessionClient(email2, code, target)).resendWorkspaceInvitation(target, id, options);
54229
+ }
54230
+ async revokeWorkspaceInvitation(email2, code, context, invitationId, input) {
54231
+ const target = workspaceContext(context), captured = invitationInput("revoke", { ...input, invitationId });
54232
+ const { invitationId: id, ...options } = captured;
54233
+ return (await this.sessionClient(email2, code, target)).revokeWorkspaceInvitation(target, id, options);
54234
+ }
54235
+ async acceptWorkspaceInvitation(email2, code, context, invitationId, input) {
54236
+ const target = workspaceContext(context), captured = invitationInput("accept", { ...input, invitationId });
54237
+ const { invitationId: id, ...options } = captured;
54238
+ return (await this.sessionClient(email2, code, target)).acceptWorkspaceInvitation(target, id, options);
54239
+ }
54240
+ async createApiKey(email2, code, name, scopes, context) {
53430
54241
  const capturedScopes = scopes === undefined ? undefined : [...scopes];
53431
- return (await this.sessionClient(email, code, context)).createApiKey(name, capturedScopes);
54242
+ return (await this.sessionClient(email2, code, context)).createApiKey(name, capturedScopes);
53432
54243
  }
53433
- async listApiKeys(email, code, context) {
53434
- return (await this.sessionClient(email, code, context)).listApiKeys();
54244
+ async listApiKeys(email2, code, context) {
54245
+ return (await this.sessionClient(email2, code, context)).listApiKeys();
53435
54246
  }
53436
- async revokeApiKey(email, code, keyId, context) {
53437
- return (await this.sessionClient(email, code, context)).revokeApiKey(keyId);
54247
+ async revokeApiKey(email2, code, keyId, context) {
54248
+ return (await this.sessionClient(email2, code, context)).revokeApiKey(keyId);
53438
54249
  }
53439
- async updateProfile(email, code, input, context) {
54250
+ async updateProfile(email2, code, input, context) {
53440
54251
  const body = customerNamePatch(input, "displayName");
53441
- return (await this.sessionClient(email, code, context)).updateProfile({ displayName: body.displayName });
54252
+ return (await this.sessionClient(email2, code, context)).updateProfile({ displayName: body.displayName });
53442
54253
  }
53443
- async updateCurrentWorkspace(email, code, input, context) {
54254
+ async updateCurrentWorkspace(email2, code, input, context) {
53444
54255
  const body = customerNamePatch(input, "name");
53445
- return (await this.sessionClient(email, code, context)).updateCurrentWorkspace({ name: body.name });
54256
+ return (await this.sessionClient(email2, code, context)).updateCurrentWorkspace({ name: body.name });
53446
54257
  }
53447
- async listWorkspaceMembers(email, code, options = {}, context) {
54258
+ async listWorkspaceMembers(email2, code, options = {}, context) {
53448
54259
  workspaceMembersQuery(options);
53449
54260
  const captured = { ...options };
53450
- return (await this.sessionClient(email, code, context)).listWorkspaceMembers(captured);
54261
+ return (await this.sessionClient(email2, code, context)).listWorkspaceMembers(captured);
53451
54262
  }
53452
- async setWorkspaceMemberRole(email, code, membershipId, input, context) {
54263
+ async setWorkspaceMemberRole(email2, code, membershipId, input, context) {
53453
54264
  const captured = workspaceMemberRoleInput(membershipId, input);
53454
- return (await this.sessionClient(email, code, context)).setWorkspaceMemberRole(captured.membershipId, captured.body);
54265
+ return (await this.sessionClient(email2, code, context)).setWorkspaceMemberRole(captured.membershipId, captured.body);
53455
54266
  }
53456
- async leaveWorkspace(email, code, context, input) {
54267
+ async leaveWorkspace(email2, code, context, input) {
53457
54268
  const captured = workspaceLeaveInput(context, input);
53458
- return (await this.sessionClient(email, code, captured.context)).leaveWorkspace(captured.context, captured.input);
54269
+ return (await this.sessionClient(email2, code, captured.context)).leaveWorkspace(captured.context, captured.input);
53459
54270
  }
53460
- async removeWorkspaceMember(email, code, membershipId, input, context) {
54271
+ async removeWorkspaceMember(email2, code, membershipId, input, context) {
53461
54272
  const captured = workspaceMemberRemovalInput(membershipId, input);
53462
- return (await this.sessionClient(email, code, context)).removeWorkspaceMember(captured.membershipId, captured.body);
54273
+ return (await this.sessionClient(email2, code, context)).removeWorkspaceMember(captured.membershipId, captured.body);
53463
54274
  }
53464
54275
  request(path, options) {
53465
54276
  if (!["/api/auth/login", "/api/auth/verify", "/api/auth/device/start", "/api/auth/device/token", "/api/auth/keys", "/api/auth/whoami"].includes(path))
@@ -53468,6 +54279,7 @@ class RemoteSkillsAuthClient {
53468
54279
  }
53469
54280
  }
53470
54281
  export {
54282
+ verifyContentHashFromEntries,
53471
54283
  validateRunLifecycleEvent,
53472
54284
  startedByFor,
53473
54285
  startSkillsServer,
@@ -53482,6 +54294,7 @@ export {
53482
54294
  runPointersOf,
53483
54295
  runLeaseSchema,
53484
54296
  runAdmissionSchema,
54297
+ revisionIdOf,
53485
54298
  resolveSkillsFleet,
53486
54299
  resolveSkillsApiOrigin,
53487
54300
  resolveServerConfig,
@@ -53495,6 +54308,7 @@ export {
53495
54308
  redactRunOutput,
53496
54309
  receiptId,
53497
54310
  protocolStateOf,
54311
+ packSkillBundle,
53498
54312
  noticeLocalSkillsMode,
53499
54313
  normalizeSkillsApiOrigin,
53500
54314
  normalizeRemoteSkillRunContract,
@@ -53506,6 +54320,7 @@ export {
53506
54320
  isTerminalStatus,
53507
54321
  isSkillsLocalOptIn,
53508
54322
  isActiveStatus,
54323
+ inspectSkillBundle,
53509
54324
  getServerSkillMd,
53510
54325
  getServerSkill,
53511
54326
  expiresAtFor,
@@ -53531,6 +54346,7 @@ export {
53531
54346
  createCancelService,
53532
54347
  createAwsEcsClient,
53533
54348
  configuredSkillsApiUrl,
54349
+ computeContentHashFromEntries,
53534
54350
  clientTokenFor,
53535
54351
  canonicalSystemDepsKey,
53536
54352
  canonicalJson,
@@ -53541,12 +54357,15 @@ export {
53541
54357
  assertDurableStore,
53542
54358
  artifactStorageSeam,
53543
54359
  WorkspaceLeaveInputError,
54360
+ WorkspaceInvitationInputError,
53544
54361
  WorkspaceIdentityMismatchError,
53545
54362
  WorkspaceContextInputError,
53546
54363
  SqliteSkillsStore,
53547
54364
  SqliteRunExecutionStore,
53548
54365
  SqliteGovernanceStore,
53549
54366
  SkillsFleetCredentialError,
54367
+ SkillBundleInspectionError,
54368
+ SKILL_BUNDLE_INSPECTION_LIMITS,
53550
54369
  SKILLS_LOCAL_OPT_IN_ENV_KEYS,
53551
54370
  SKILLS_APP,
53552
54371
  SKILLS_API_URL_ENV,
@@ -53555,10 +54374,15 @@ export {
53555
54374
  RemoteWorkspaceMemberError,
53556
54375
  RemoteWorkspaceLeaveUnconfirmedError,
53557
54376
  RemoteWorkspaceLeaveError,
54377
+ RemoteWorkspaceInvitationUnconfirmedError,
54378
+ RemoteWorkspaceInvitationReadError,
54379
+ RemoteWorkspaceInvitationError,
53558
54380
  RemoteSkillsClient,
53559
54381
  RemoteSkillsAuthClient,
53560
54382
  RemoteRouteUnsupportedError,
53561
54383
  RemoteRequestError,
54384
+ RemoteInvitationEmailUnconfirmedError,
54385
+ RemoteInvitationEmailError,
53562
54386
  RemoteCreditApprovalError,
53563
54387
  RemoteCapabilityUnavailableError,
53564
54388
  RUN_PROTOCOL_VERSION,
@@ -53573,6 +54397,7 @@ export {
53573
54397
  MemoryRunExecutionStore,
53574
54398
  MemoryGovernanceStore,
53575
54399
  LEGAL_TRANSITIONS,
54400
+ InvitationEmailInputError,
53576
54401
  ImageProfileResolutionError,
53577
54402
  HostedApiError,
53578
54403
  GovernanceError,
@@ -53588,6 +54413,8 @@ export {
53588
54413
  DEFAULT_RUN_LIMITS,
53589
54414
  DEFAULT_OUTPUT_GOVERNANCE,
53590
54415
  DEFAULT_IMAGE_PROFILES,
54416
+ ContentHashInputError,
54417
+ CONTENT_HASH_LIMITS,
53591
54418
  CLIENT_TOKEN_BYTES,
53592
54419
  ArtifactStorage
53593
54420
  };