@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/README.md +169 -1
- package/bin/index.js +1182 -623
- package/bin/mcp.js +28155 -26920
- package/bin/migrate.js +172 -1
- package/bin/server.js +187 -1
- package/bin/worker.js +225 -40
- package/dist/cli/commands/invitation-recovery.d.ts +2 -0
- package/dist/cli/commands/invitation-verification.d.ts +8 -0
- package/dist/cli/commands/workspace-invitations.d.ts +2 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +987 -165
- package/dist/lib/invitation-customer-action.d.ts +14 -0
- package/dist/lib/invitation-recovery-target.d.ts +5 -0
- package/dist/lib/remote-auth.d.ts +12 -0
- package/dist/lib/remote-client.d.ts +12 -0
- package/dist/lib/remote-invitation-recovery.d.ts +62 -0
- package/dist/lib/remote-invitations.d.ts +128 -0
- package/dist/lib/skill-bundle.d.ts +54 -1
- package/dist/lib/skill-entry-path.d.ts +6 -0
- package/dist/lib/skill-hash.d.ts +33 -0
- package/dist/mcp/index.d.ts +0 -1
- package/dist/mcp/invitation-recovery.d.ts +2 -0
- package/dist/mcp/remote-invitation-tools.d.ts +2 -0
- package/dist/sdk/index.d.ts +3 -0
- package/dist/sdk/index.js +932 -105
- package/dist/sdk/registry.d.ts +6 -0
- package/package.json +1 -1
package/dist/sdk/index.js
CHANGED
|
@@ -25462,7 +25462,7 @@ class MissingSkillsFleetError extends Error {
|
|
|
25462
25462
|
// package.json
|
|
25463
25463
|
var package_default = {
|
|
25464
25464
|
name: "@hasna/skills",
|
|
25465
|
-
version: "0.5.
|
|
25465
|
+
version: "0.5.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 (
|
|
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
|
-
|
|
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
|
-
|
|
38760
|
-
return;
|
|
39020
|
+
return { rel, content: new TextEncoder().encode(normalized) };
|
|
38761
39021
|
}
|
|
38762
|
-
|
|
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
|
-
|
|
38767
|
-
|
|
38768
|
-
|
|
38769
|
-
|
|
38770
|
-
|
|
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
|
-
|
|
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-
|
|
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 (!
|
|
52824
|
+
if (!uuid2(value))
|
|
52189
52825
|
throw new WorkspaceContextInputError;
|
|
52190
52826
|
return value;
|
|
52191
52827
|
}
|
|
52192
52828
|
function workspaceContext(value) {
|
|
52193
|
-
if (!
|
|
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
|
|
52833
|
+
function invalid2() {
|
|
52198
52834
|
throw new Error(invalidWorkspaceResult);
|
|
52199
52835
|
}
|
|
52200
52836
|
function organization(v2) {
|
|
52201
|
-
if (!
|
|
52202
|
-
return
|
|
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 (!
|
|
52207
|
-
return
|
|
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 (!
|
|
52210
|
-
return
|
|
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
|
|
52850
|
+
return invalid2();
|
|
52215
52851
|
return { workspaces };
|
|
52216
52852
|
}
|
|
52217
52853
|
function parseWorkspaceIdentity(value, expectedUserId) {
|
|
52218
|
-
if (!
|
|
52219
|
-
return
|
|
52854
|
+
if (!record2(value))
|
|
52855
|
+
return invalid2();
|
|
52220
52856
|
const user = value.user;
|
|
52221
|
-
if (!
|
|
52222
|
-
return
|
|
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
|
|
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 =
|
|
52240
|
-
if (!
|
|
52241
|
-
return
|
|
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 (!
|
|
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
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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" || !
|
|
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,
|
|
52305
|
-
if (!isRole(expectedRole) || roleChange && !isRole(
|
|
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:
|
|
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,
|
|
52954
|
+
function parseWorkspaceMemberRoleResult(value, membershipId, role3) {
|
|
52319
52955
|
const fail = () => {
|
|
52320
52956
|
throw new Error(invalidMemberResult);
|
|
52321
52957
|
};
|
|
52322
|
-
if (!
|
|
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 !==
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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(
|
|
53152
|
-
if (
|
|
53834
|
+
function requireOptionalString(record4, field) {
|
|
53835
|
+
if (record4[field] === undefined)
|
|
53153
53836
|
return;
|
|
53154
|
-
if (typeof
|
|
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
|
|
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
|
|
53181
|
-
const slug = typeof
|
|
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 (
|
|
53187
|
-
if (!
|
|
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 =
|
|
53873
|
+
metadata = record4.metadata;
|
|
53191
53874
|
}
|
|
53192
|
-
const pinnedAt = requireOptionalString(
|
|
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
|
|
53210
|
-
const slug = typeof
|
|
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(
|
|
53217
|
-
...requireOptionalString(
|
|
53218
|
-
...requireOptionalString(
|
|
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
|
|
53272
|
-
if (!Array.isArray(
|
|
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 =
|
|
53276
|
-
const 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
|
|
53327
|
-
const detail = typeof
|
|
53328
|
-
const error = typeof
|
|
53329
|
-
const code = typeof
|
|
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
|
-
|
|
53364
|
-
return this.
|
|
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(
|
|
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(
|
|
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(
|
|
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 (!
|
|
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(
|
|
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(
|
|
53390
|
-
const login = await this.workspaceLogin(
|
|
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(
|
|
54178
|
+
async switchWorkspace(email2, code, context) {
|
|
53395
54179
|
const target = workspaceContext(context);
|
|
53396
|
-
const login = await this.workspaceLogin(
|
|
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(
|
|
54183
|
+
async workspaceLogin(email2, code, expectedUserId) {
|
|
53400
54184
|
const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
|
|
53401
54185
|
const apiOrigin = this.apiOrigin;
|
|
53402
|
-
if (typeof
|
|
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
|
|
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(
|
|
54242
|
+
return (await this.sessionClient(email2, code, context)).createApiKey(name, capturedScopes);
|
|
53432
54243
|
}
|
|
53433
|
-
async listApiKeys(
|
|
53434
|
-
return (await this.sessionClient(
|
|
54244
|
+
async listApiKeys(email2, code, context) {
|
|
54245
|
+
return (await this.sessionClient(email2, code, context)).listApiKeys();
|
|
53435
54246
|
}
|
|
53436
|
-
async revokeApiKey(
|
|
53437
|
-
return (await this.sessionClient(
|
|
54247
|
+
async revokeApiKey(email2, code, keyId, context) {
|
|
54248
|
+
return (await this.sessionClient(email2, code, context)).revokeApiKey(keyId);
|
|
53438
54249
|
}
|
|
53439
|
-
async updateProfile(
|
|
54250
|
+
async updateProfile(email2, code, input, context) {
|
|
53440
54251
|
const body = customerNamePatch(input, "displayName");
|
|
53441
|
-
return (await this.sessionClient(
|
|
54252
|
+
return (await this.sessionClient(email2, code, context)).updateProfile({ displayName: body.displayName });
|
|
53442
54253
|
}
|
|
53443
|
-
async updateCurrentWorkspace(
|
|
54254
|
+
async updateCurrentWorkspace(email2, code, input, context) {
|
|
53444
54255
|
const body = customerNamePatch(input, "name");
|
|
53445
|
-
return (await this.sessionClient(
|
|
54256
|
+
return (await this.sessionClient(email2, code, context)).updateCurrentWorkspace({ name: body.name });
|
|
53446
54257
|
}
|
|
53447
|
-
async listWorkspaceMembers(
|
|
54258
|
+
async listWorkspaceMembers(email2, code, options = {}, context) {
|
|
53448
54259
|
workspaceMembersQuery(options);
|
|
53449
54260
|
const captured = { ...options };
|
|
53450
|
-
return (await this.sessionClient(
|
|
54261
|
+
return (await this.sessionClient(email2, code, context)).listWorkspaceMembers(captured);
|
|
53451
54262
|
}
|
|
53452
|
-
async setWorkspaceMemberRole(
|
|
54263
|
+
async setWorkspaceMemberRole(email2, code, membershipId, input, context) {
|
|
53453
54264
|
const captured = workspaceMemberRoleInput(membershipId, input);
|
|
53454
|
-
return (await this.sessionClient(
|
|
54265
|
+
return (await this.sessionClient(email2, code, context)).setWorkspaceMemberRole(captured.membershipId, captured.body);
|
|
53455
54266
|
}
|
|
53456
|
-
async leaveWorkspace(
|
|
54267
|
+
async leaveWorkspace(email2, code, context, input) {
|
|
53457
54268
|
const captured = workspaceLeaveInput(context, input);
|
|
53458
|
-
return (await this.sessionClient(
|
|
54269
|
+
return (await this.sessionClient(email2, code, captured.context)).leaveWorkspace(captured.context, captured.input);
|
|
53459
54270
|
}
|
|
53460
|
-
async removeWorkspaceMember(
|
|
54271
|
+
async removeWorkspaceMember(email2, code, membershipId, input, context) {
|
|
53461
54272
|
const captured = workspaceMemberRemovalInput(membershipId, input);
|
|
53462
|
-
return (await this.sessionClient(
|
|
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
|
};
|