@hasna/skills 0.5.1 → 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 +196 -1
- package/bin/index.js +1339 -619
- package/bin/mcp.js +28222 -26875
- 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/cli/commands/workspace-leave.d.ts +2 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +1075 -163
- 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 +15 -0
- package/dist/lib/remote-client.d.ts +15 -0
- package/dist/lib/remote-invitation-recovery.d.ts +62 -0
- package/dist/lib/remote-invitations.d.ts +128 -0
- package/dist/lib/remote-workspace-leave.d.ts +52 -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 +5 -0
- package/dist/sdk/index.js +1020 -103
- 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) {
|
|
38765
39037
|
const hash = createHash6(CONTENT_HASH_ALGORITHM);
|
|
38766
|
-
for (const
|
|
38767
|
-
hash.update(
|
|
38768
|
-
|
|
38769
|
-
|
|
38770
|
-
|
|
39038
|
+
for (const part of bundleHashParts(files))
|
|
39039
|
+
hash.update(part);
|
|
39040
|
+
return hash.digest("hex");
|
|
39041
|
+
}
|
|
39042
|
+
async function hashBundleFilesCooperatively(files, check) {
|
|
39043
|
+
const hash = createHash6(CONTENT_HASH_ALGORITHM);
|
|
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,13 +52989,72 @@ 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)
|
|
52360
52996
|
return fail();
|
|
52361
52997
|
return { organizationId: value.organizationId, members, nextCursor: value.nextCursor };
|
|
52362
52998
|
}
|
|
52999
|
+
|
|
53000
|
+
// src/lib/remote-workspace-leave.ts
|
|
53001
|
+
class WorkspaceLeaveInputError extends Error {
|
|
53002
|
+
constructor() {
|
|
53003
|
+
super("Confirm leaving the exact observed user and membership with its expected role.");
|
|
53004
|
+
this.name = "WorkspaceLeaveInputError";
|
|
53005
|
+
}
|
|
53006
|
+
}
|
|
53007
|
+
function workspaceLeaveInput(context, input) {
|
|
53008
|
+
const target = workspaceContext(context);
|
|
53009
|
+
if (!input || typeof input !== "object" || Array.isArray(input) || Object.keys(input).sort().join(",") !== "confirm,expectedRole" || input.confirm !== true)
|
|
53010
|
+
throw new WorkspaceLeaveInputError;
|
|
53011
|
+
const captured = workspaceMemberRemovalInput(target.membershipId, { expectedRole: input.expectedRole });
|
|
53012
|
+
return {
|
|
53013
|
+
context: target,
|
|
53014
|
+
input: { expectedRole: captured.body.expectedRole, confirm: true },
|
|
53015
|
+
body: { membershipId: target.membershipId, expectedRole: captured.body.expectedRole }
|
|
53016
|
+
};
|
|
53017
|
+
}
|
|
53018
|
+
var workspaceLeaveFailures = {
|
|
53019
|
+
INVALID_REQUEST: [400, "Provide the exact current membership and expected role."],
|
|
53020
|
+
ACCOUNT_UNAVAILABLE: [403, "Account is unavailable."],
|
|
53021
|
+
INTERACTIVE_SESSION_REQUIRED: [403, "Fresh interactive sign-in is required to leave a workspace."],
|
|
53022
|
+
MEMBERSHIP_ROLE_CHANGED: [409, "Your role changed. Sign in and inspect the workspace before leaving."],
|
|
53023
|
+
LAST_OWNER_REQUIRED: [409, "The workspace must retain another active owner."],
|
|
53024
|
+
LAST_WORKSPACE_REQUIRED: [409, "Another available workspace is required before leaving."],
|
|
53025
|
+
MEMBERSHIP_BUSY: [503, "Membership is busy. Inspect the workspace before another leave action."]
|
|
53026
|
+
};
|
|
53027
|
+
|
|
53028
|
+
class RemoteWorkspaceLeaveError extends Error {
|
|
53029
|
+
code;
|
|
53030
|
+
status;
|
|
53031
|
+
constructor(code) {
|
|
53032
|
+
super(workspaceLeaveFailures[code][1]);
|
|
53033
|
+
this.code = code;
|
|
53034
|
+
this.name = "RemoteWorkspaceLeaveError";
|
|
53035
|
+
this.status = workspaceLeaveFailures[code][0];
|
|
53036
|
+
}
|
|
53037
|
+
}
|
|
53038
|
+
|
|
53039
|
+
class RemoteWorkspaceLeaveUnconfirmedError extends Error {
|
|
53040
|
+
code = "WORKSPACE_LEAVE_UNCONFIRMED";
|
|
53041
|
+
constructor() {
|
|
53042
|
+
super("The leave outcome is unconfirmed. Sign in again and inspect available memberships before another action. Do not retry automatically; saved credentials are unchanged.");
|
|
53043
|
+
this.name = "RemoteWorkspaceLeaveUnconfirmedError";
|
|
53044
|
+
}
|
|
53045
|
+
}
|
|
53046
|
+
function workspaceLeaveFailure(value, status) {
|
|
53047
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
53048
|
+
return null;
|
|
53049
|
+
const code = value.code;
|
|
53050
|
+
return typeof code === "string" && Object.hasOwn(workspaceLeaveFailures, code) && workspaceLeaveFailures[code][0] === status ? code : null;
|
|
53051
|
+
}
|
|
53052
|
+
function parseWorkspaceLeaveResult(value, membershipId, organizationId) {
|
|
53053
|
+
const row = value;
|
|
53054
|
+
if (!row || typeof row !== "object" || Array.isArray(row) || row.membershipId !== membershipId || row.organizationId !== organizationId || row.removed !== true || row.signInRequired !== true)
|
|
53055
|
+
throw new RemoteWorkspaceLeaveUnconfirmedError;
|
|
53056
|
+
return { membershipId, organizationId, removed: true, signInRequired: true };
|
|
53057
|
+
}
|
|
52363
53058
|
// src/lib/auth-store.ts
|
|
52364
53059
|
function getApiUrl(action, env = process.env, options = {}) {
|
|
52365
53060
|
return requireSkillsApiOrigin(action, env, options);
|
|
@@ -52815,6 +53510,77 @@ class RemoteSkillsClient {
|
|
|
52815
53510
|
}
|
|
52816
53511
|
return value;
|
|
52817
53512
|
}
|
|
53513
|
+
async leaveWorkspace(context, input) {
|
|
53514
|
+
const captured = workspaceLeaveInput(context, input);
|
|
53515
|
+
const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
|
|
53516
|
+
const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
|
|
53517
|
+
if (!value || typeof value !== "object" || value.authMethod !== "jwt")
|
|
53518
|
+
throw new RemoteWorkspaceLeaveError("INTERACTIVE_SESSION_REQUIRED");
|
|
53519
|
+
const identity = parseWorkspaceIdentity(value, captured.context.userId);
|
|
53520
|
+
if (identity.user.membershipId !== captured.context.membershipId)
|
|
53521
|
+
throw new WorkspaceIdentityMismatchError;
|
|
53522
|
+
let response, body;
|
|
53523
|
+
try {
|
|
53524
|
+
response = await connection.request("/api/v1/account/workspaces/leave", { method: "POST", body: JSON.stringify(captured.body) });
|
|
53525
|
+
body = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, 4096)));
|
|
53526
|
+
} catch {
|
|
53527
|
+
throw new RemoteWorkspaceLeaveUnconfirmedError;
|
|
53528
|
+
}
|
|
53529
|
+
if (!response.ok) {
|
|
53530
|
+
const code = workspaceLeaveFailure(body, response.status);
|
|
53531
|
+
if (code)
|
|
53532
|
+
throw new RemoteWorkspaceLeaveError(code);
|
|
53533
|
+
throw new RemoteWorkspaceLeaveUnconfirmedError;
|
|
53534
|
+
}
|
|
53535
|
+
return parseWorkspaceLeaveResult(body, captured.context.membershipId, identity.organization.id);
|
|
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
|
+
}
|
|
52818
53584
|
async listApiKeys() {
|
|
52819
53585
|
return this.arrayResponse("/api/auth/keys");
|
|
52820
53586
|
}
|
|
@@ -53065,13 +53831,13 @@ class RemoteSkillsClient {
|
|
|
53065
53831
|
return normalizeUpdatedSincePage(await response.json());
|
|
53066
53832
|
}
|
|
53067
53833
|
}
|
|
53068
|
-
function requireOptionalString(
|
|
53069
|
-
if (
|
|
53834
|
+
function requireOptionalString(record4, field) {
|
|
53835
|
+
if (record4[field] === undefined)
|
|
53070
53836
|
return;
|
|
53071
|
-
if (typeof
|
|
53837
|
+
if (typeof record4[field] !== "string") {
|
|
53072
53838
|
throw new Error(`Remote payload did not match the expected contract (${field} must be a string when present)`);
|
|
53073
53839
|
}
|
|
53074
|
-
return
|
|
53840
|
+
return record4[field];
|
|
53075
53841
|
}
|
|
53076
53842
|
var INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
|
|
53077
53843
|
function isVersionRecord(value) {
|
|
@@ -53094,19 +53860,19 @@ function normalizePin(entry) {
|
|
|
53094
53860
|
if (!entry || typeof entry !== "object") {
|
|
53095
53861
|
throw new Error("Remote pin payload did not match the expected contract (expected an object)");
|
|
53096
53862
|
}
|
|
53097
|
-
const
|
|
53098
|
-
const slug = typeof
|
|
53863
|
+
const record4 = entry;
|
|
53864
|
+
const slug = typeof record4.slug === "string" && record4.slug.trim() ? record4.slug.trim() : undefined;
|
|
53099
53865
|
if (!slug) {
|
|
53100
53866
|
throw new Error("Remote pin payload did not match the expected contract (missing slug)");
|
|
53101
53867
|
}
|
|
53102
53868
|
let metadata;
|
|
53103
|
-
if (
|
|
53104
|
-
if (!
|
|
53869
|
+
if (record4.metadata !== undefined) {
|
|
53870
|
+
if (!record4.metadata || typeof record4.metadata !== "object" || Array.isArray(record4.metadata)) {
|
|
53105
53871
|
throw new Error("Remote pin payload did not match the expected contract (metadata must be a JSON object when present)");
|
|
53106
53872
|
}
|
|
53107
|
-
metadata =
|
|
53873
|
+
metadata = record4.metadata;
|
|
53108
53874
|
}
|
|
53109
|
-
const pinnedAt = requireOptionalString(
|
|
53875
|
+
const pinnedAt = requireOptionalString(record4, "pinnedAt");
|
|
53110
53876
|
return {
|
|
53111
53877
|
slug,
|
|
53112
53878
|
...pinnedAt !== undefined ? { pinnedAt } : {},
|
|
@@ -53123,16 +53889,16 @@ function normalizeSkillSummary(entry) {
|
|
|
53123
53889
|
if (!entry || typeof entry !== "object") {
|
|
53124
53890
|
throw new Error("Remote skill payload did not match the expected contract (expected an object)");
|
|
53125
53891
|
}
|
|
53126
|
-
const
|
|
53127
|
-
const slug = typeof
|
|
53892
|
+
const record4 = entry;
|
|
53893
|
+
const slug = typeof record4.slug === "string" && record4.slug.trim() ? record4.slug.trim() : undefined;
|
|
53128
53894
|
if (!slug) {
|
|
53129
53895
|
throw new Error("Remote skill payload did not match the expected contract (missing slug)");
|
|
53130
53896
|
}
|
|
53131
53897
|
return {
|
|
53132
53898
|
slug,
|
|
53133
|
-
...requireOptionalString(
|
|
53134
|
-
...requireOptionalString(
|
|
53135
|
-
...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") } : {}
|
|
53136
53902
|
};
|
|
53137
53903
|
}
|
|
53138
53904
|
function normalizeSkillSummaryList(payload) {
|
|
@@ -53185,12 +53951,12 @@ function normalizeUpdatedSincePage(payload) {
|
|
|
53185
53951
|
if (!payload || typeof payload !== "object") {
|
|
53186
53952
|
throw new Error("Updated-since payload did not match the expected contract (expected an object)");
|
|
53187
53953
|
}
|
|
53188
|
-
const
|
|
53189
|
-
if (!Array.isArray(
|
|
53954
|
+
const record4 = payload;
|
|
53955
|
+
if (!Array.isArray(record4.skills)) {
|
|
53190
53956
|
throw new Error("Updated-since payload did not match the expected contract (missing skills array)");
|
|
53191
53957
|
}
|
|
53192
|
-
const skills =
|
|
53193
|
-
const nextCursor =
|
|
53958
|
+
const skills = record4.skills.map(normalizeSkillSummary);
|
|
53959
|
+
const nextCursor = record4.nextCursor === undefined || record4.nextCursor === null ? null : record4.nextCursor;
|
|
53194
53960
|
if (nextCursor !== null && typeof nextCursor !== "string") {
|
|
53195
53961
|
throw new Error("Updated-since payload did not match the expected contract (nextCursor must be a string or absent)");
|
|
53196
53962
|
}
|
|
@@ -53200,6 +53966,101 @@ async function createRemoteSkillsClient(env = process.env) {
|
|
|
53200
53966
|
const connection = await resolveSkillsConnection(env);
|
|
53201
53967
|
return connection ? new RemoteSkillsClient(connection.apiKey, connection.apiOrigin) : null;
|
|
53202
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
|
+
|
|
53203
54064
|
// src/lib/remote-auth.ts
|
|
53204
54065
|
var MAX_ERROR_DETAIL_LENGTH = 200;
|
|
53205
54066
|
|
|
@@ -53240,10 +54101,10 @@ async function requestAuthApi(instance, path, options) {
|
|
|
53240
54101
|
const text2 = await res.text();
|
|
53241
54102
|
const body = text2 ? parseJsonBody(text2) : {};
|
|
53242
54103
|
if (!res.ok) {
|
|
53243
|
-
const
|
|
53244
|
-
const detail = typeof
|
|
53245
|
-
const error = typeof
|
|
53246
|
-
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;
|
|
53247
54108
|
throw new HostedApiError(detail || error || `${res.status} ${res.statusText}`, {
|
|
53248
54109
|
status: res.status,
|
|
53249
54110
|
code,
|
|
@@ -53277,11 +54138,17 @@ class RemoteSkillsAuthClient {
|
|
|
53277
54138
|
constructor(apiUrl) {
|
|
53278
54139
|
this.apiOrigin = normalizeSkillsApiOrigin(apiUrl);
|
|
53279
54140
|
}
|
|
53280
|
-
|
|
53281
|
-
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 }) });
|
|
53282
54149
|
}
|
|
53283
|
-
verifyCode(
|
|
53284
|
-
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 }) });
|
|
53285
54152
|
}
|
|
53286
54153
|
startDevice() {
|
|
53287
54154
|
return this.request("/api/auth/device/start", { method: "POST", body: JSON.stringify({ client: "skills-sdk" }) });
|
|
@@ -53289,34 +54156,34 @@ class RemoteSkillsAuthClient {
|
|
|
53289
54156
|
pollDevice(deviceCode) {
|
|
53290
54157
|
return this.request("/api/auth/device/token", { method: "POST", body: JSON.stringify({ deviceCode }) });
|
|
53291
54158
|
}
|
|
53292
|
-
async sessionClient(
|
|
54159
|
+
async sessionClient(email2, code, context) {
|
|
53293
54160
|
if (context !== undefined) {
|
|
53294
54161
|
const target = workspaceContext(context), apiOrigin2 = this.apiOrigin;
|
|
53295
|
-
const session = await this.switchWorkspace(
|
|
54162
|
+
const session = await this.switchWorkspace(email2, code, target);
|
|
53296
54163
|
return new RemoteSkillsClient(session.token, apiOrigin2);
|
|
53297
54164
|
}
|
|
53298
54165
|
const apiOrigin = this.apiOrigin;
|
|
53299
|
-
if (!
|
|
54166
|
+
if (!email2.includes("@") || !/^\d{6}$/.test(code))
|
|
53300
54167
|
throw new Error("Fresh email and six-digit verification code are required to manage this account");
|
|
53301
|
-
const login = await this.verifyCode(
|
|
54168
|
+
const login = await this.verifyCode(email2, code);
|
|
53302
54169
|
if (!login || typeof login.token !== "string" || !login.token)
|
|
53303
54170
|
throw new Error("The server did not return an authorized account session");
|
|
53304
54171
|
return new RemoteSkillsClient(login.token, apiOrigin);
|
|
53305
54172
|
}
|
|
53306
|
-
async listAccountWorkspaces(
|
|
53307
|
-
const login = await this.workspaceLogin(
|
|
54173
|
+
async listAccountWorkspaces(email2, code, expectedUserId) {
|
|
54174
|
+
const login = await this.workspaceLogin(email2, code, expectedUserId);
|
|
53308
54175
|
const result = await new RemoteSkillsClient(login.token, login.apiOrigin).listAccountWorkspaces(login.userId);
|
|
53309
54176
|
return { userId: login.userId, ...result };
|
|
53310
54177
|
}
|
|
53311
|
-
async switchWorkspace(
|
|
54178
|
+
async switchWorkspace(email2, code, context) {
|
|
53312
54179
|
const target = workspaceContext(context);
|
|
53313
|
-
const login = await this.workspaceLogin(
|
|
54180
|
+
const login = await this.workspaceLogin(email2, code, target.userId);
|
|
53314
54181
|
return new RemoteSkillsClient(login.token, login.apiOrigin).switchWorkspace(target);
|
|
53315
54182
|
}
|
|
53316
|
-
async workspaceLogin(
|
|
54183
|
+
async workspaceLogin(email2, code, expectedUserId) {
|
|
53317
54184
|
const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
|
|
53318
54185
|
const apiOrigin = this.apiOrigin;
|
|
53319
|
-
if (typeof
|
|
54186
|
+
if (typeof email2 !== "string" || !email2.includes("@") || typeof code !== "string" || !/^\d{6}$/.test(code))
|
|
53320
54187
|
throw new Error("Fresh email and six-digit verification code are required to manage this account");
|
|
53321
54188
|
let response;
|
|
53322
54189
|
try {
|
|
@@ -53326,7 +54193,7 @@ class RemoteSkillsAuthClient {
|
|
|
53326
54193
|
credentials: "omit",
|
|
53327
54194
|
signal: AbortSignal.timeout(15000),
|
|
53328
54195
|
headers: { "Content-Type": "application/json" },
|
|
53329
|
-
body: JSON.stringify({ email, code })
|
|
54196
|
+
body: JSON.stringify({ email: email2, code })
|
|
53330
54197
|
});
|
|
53331
54198
|
} catch {
|
|
53332
54199
|
throw new HostedApiError("Unable to verify the Skills account.");
|
|
@@ -53343,36 +54210,67 @@ class RemoteSkillsAuthClient {
|
|
|
53343
54210
|
}
|
|
53344
54211
|
return { ...parseWorkspaceLogin(value, expected), apiOrigin };
|
|
53345
54212
|
}
|
|
53346
|
-
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) {
|
|
53347
54241
|
const capturedScopes = scopes === undefined ? undefined : [...scopes];
|
|
53348
|
-
return (await this.sessionClient(
|
|
54242
|
+
return (await this.sessionClient(email2, code, context)).createApiKey(name, capturedScopes);
|
|
53349
54243
|
}
|
|
53350
|
-
async listApiKeys(
|
|
53351
|
-
return (await this.sessionClient(
|
|
54244
|
+
async listApiKeys(email2, code, context) {
|
|
54245
|
+
return (await this.sessionClient(email2, code, context)).listApiKeys();
|
|
53352
54246
|
}
|
|
53353
|
-
async revokeApiKey(
|
|
53354
|
-
return (await this.sessionClient(
|
|
54247
|
+
async revokeApiKey(email2, code, keyId, context) {
|
|
54248
|
+
return (await this.sessionClient(email2, code, context)).revokeApiKey(keyId);
|
|
53355
54249
|
}
|
|
53356
|
-
async updateProfile(
|
|
54250
|
+
async updateProfile(email2, code, input, context) {
|
|
53357
54251
|
const body = customerNamePatch(input, "displayName");
|
|
53358
|
-
return (await this.sessionClient(
|
|
54252
|
+
return (await this.sessionClient(email2, code, context)).updateProfile({ displayName: body.displayName });
|
|
53359
54253
|
}
|
|
53360
|
-
async updateCurrentWorkspace(
|
|
54254
|
+
async updateCurrentWorkspace(email2, code, input, context) {
|
|
53361
54255
|
const body = customerNamePatch(input, "name");
|
|
53362
|
-
return (await this.sessionClient(
|
|
54256
|
+
return (await this.sessionClient(email2, code, context)).updateCurrentWorkspace({ name: body.name });
|
|
53363
54257
|
}
|
|
53364
|
-
async listWorkspaceMembers(
|
|
54258
|
+
async listWorkspaceMembers(email2, code, options = {}, context) {
|
|
53365
54259
|
workspaceMembersQuery(options);
|
|
53366
54260
|
const captured = { ...options };
|
|
53367
|
-
return (await this.sessionClient(
|
|
54261
|
+
return (await this.sessionClient(email2, code, context)).listWorkspaceMembers(captured);
|
|
53368
54262
|
}
|
|
53369
|
-
async setWorkspaceMemberRole(
|
|
54263
|
+
async setWorkspaceMemberRole(email2, code, membershipId, input, context) {
|
|
53370
54264
|
const captured = workspaceMemberRoleInput(membershipId, input);
|
|
53371
|
-
return (await this.sessionClient(
|
|
54265
|
+
return (await this.sessionClient(email2, code, context)).setWorkspaceMemberRole(captured.membershipId, captured.body);
|
|
54266
|
+
}
|
|
54267
|
+
async leaveWorkspace(email2, code, context, input) {
|
|
54268
|
+
const captured = workspaceLeaveInput(context, input);
|
|
54269
|
+
return (await this.sessionClient(email2, code, captured.context)).leaveWorkspace(captured.context, captured.input);
|
|
53372
54270
|
}
|
|
53373
|
-
async removeWorkspaceMember(
|
|
54271
|
+
async removeWorkspaceMember(email2, code, membershipId, input, context) {
|
|
53374
54272
|
const captured = workspaceMemberRemovalInput(membershipId, input);
|
|
53375
|
-
return (await this.sessionClient(
|
|
54273
|
+
return (await this.sessionClient(email2, code, context)).removeWorkspaceMember(captured.membershipId, captured.body);
|
|
53376
54274
|
}
|
|
53377
54275
|
request(path, options) {
|
|
53378
54276
|
if (!["/api/auth/login", "/api/auth/verify", "/api/auth/device/start", "/api/auth/device/token", "/api/auth/keys", "/api/auth/whoami"].includes(path))
|
|
@@ -53381,6 +54279,7 @@ class RemoteSkillsAuthClient {
|
|
|
53381
54279
|
}
|
|
53382
54280
|
}
|
|
53383
54281
|
export {
|
|
54282
|
+
verifyContentHashFromEntries,
|
|
53384
54283
|
validateRunLifecycleEvent,
|
|
53385
54284
|
startedByFor,
|
|
53386
54285
|
startSkillsServer,
|
|
@@ -53395,6 +54294,7 @@ export {
|
|
|
53395
54294
|
runPointersOf,
|
|
53396
54295
|
runLeaseSchema,
|
|
53397
54296
|
runAdmissionSchema,
|
|
54297
|
+
revisionIdOf,
|
|
53398
54298
|
resolveSkillsFleet,
|
|
53399
54299
|
resolveSkillsApiOrigin,
|
|
53400
54300
|
resolveServerConfig,
|
|
@@ -53408,6 +54308,7 @@ export {
|
|
|
53408
54308
|
redactRunOutput,
|
|
53409
54309
|
receiptId,
|
|
53410
54310
|
protocolStateOf,
|
|
54311
|
+
packSkillBundle,
|
|
53411
54312
|
noticeLocalSkillsMode,
|
|
53412
54313
|
normalizeSkillsApiOrigin,
|
|
53413
54314
|
normalizeRemoteSkillRunContract,
|
|
@@ -53419,6 +54320,7 @@ export {
|
|
|
53419
54320
|
isTerminalStatus,
|
|
53420
54321
|
isSkillsLocalOptIn,
|
|
53421
54322
|
isActiveStatus,
|
|
54323
|
+
inspectSkillBundle,
|
|
53422
54324
|
getServerSkillMd,
|
|
53423
54325
|
getServerSkill,
|
|
53424
54326
|
expiresAtFor,
|
|
@@ -53444,6 +54346,7 @@ export {
|
|
|
53444
54346
|
createCancelService,
|
|
53445
54347
|
createAwsEcsClient,
|
|
53446
54348
|
configuredSkillsApiUrl,
|
|
54349
|
+
computeContentHashFromEntries,
|
|
53447
54350
|
clientTokenFor,
|
|
53448
54351
|
canonicalSystemDepsKey,
|
|
53449
54352
|
canonicalJson,
|
|
@@ -53453,22 +54356,33 @@ export {
|
|
|
53453
54356
|
assertDurableTarget,
|
|
53454
54357
|
assertDurableStore,
|
|
53455
54358
|
artifactStorageSeam,
|
|
54359
|
+
WorkspaceLeaveInputError,
|
|
54360
|
+
WorkspaceInvitationInputError,
|
|
53456
54361
|
WorkspaceIdentityMismatchError,
|
|
53457
54362
|
WorkspaceContextInputError,
|
|
53458
54363
|
SqliteSkillsStore,
|
|
53459
54364
|
SqliteRunExecutionStore,
|
|
53460
54365
|
SqliteGovernanceStore,
|
|
53461
54366
|
SkillsFleetCredentialError,
|
|
54367
|
+
SkillBundleInspectionError,
|
|
54368
|
+
SKILL_BUNDLE_INSPECTION_LIMITS,
|
|
53462
54369
|
SKILLS_LOCAL_OPT_IN_ENV_KEYS,
|
|
53463
54370
|
SKILLS_APP,
|
|
53464
54371
|
SKILLS_API_URL_ENV,
|
|
53465
54372
|
SKILLS_API_KEY_ENV,
|
|
53466
54373
|
RemoteWorkspaceSelectionError,
|
|
53467
54374
|
RemoteWorkspaceMemberError,
|
|
54375
|
+
RemoteWorkspaceLeaveUnconfirmedError,
|
|
54376
|
+
RemoteWorkspaceLeaveError,
|
|
54377
|
+
RemoteWorkspaceInvitationUnconfirmedError,
|
|
54378
|
+
RemoteWorkspaceInvitationReadError,
|
|
54379
|
+
RemoteWorkspaceInvitationError,
|
|
53468
54380
|
RemoteSkillsClient,
|
|
53469
54381
|
RemoteSkillsAuthClient,
|
|
53470
54382
|
RemoteRouteUnsupportedError,
|
|
53471
54383
|
RemoteRequestError,
|
|
54384
|
+
RemoteInvitationEmailUnconfirmedError,
|
|
54385
|
+
RemoteInvitationEmailError,
|
|
53472
54386
|
RemoteCreditApprovalError,
|
|
53473
54387
|
RemoteCapabilityUnavailableError,
|
|
53474
54388
|
RUN_PROTOCOL_VERSION,
|
|
@@ -53483,6 +54397,7 @@ export {
|
|
|
53483
54397
|
MemoryRunExecutionStore,
|
|
53484
54398
|
MemoryGovernanceStore,
|
|
53485
54399
|
LEGAL_TRANSITIONS,
|
|
54400
|
+
InvitationEmailInputError,
|
|
53486
54401
|
ImageProfileResolutionError,
|
|
53487
54402
|
HostedApiError,
|
|
53488
54403
|
GovernanceError,
|
|
@@ -53498,6 +54413,8 @@ export {
|
|
|
53498
54413
|
DEFAULT_RUN_LIMITS,
|
|
53499
54414
|
DEFAULT_OUTPUT_GOVERNANCE,
|
|
53500
54415
|
DEFAULT_IMAGE_PROFILES,
|
|
54416
|
+
ContentHashInputError,
|
|
54417
|
+
CONTENT_HASH_LIMITS,
|
|
53501
54418
|
CLIENT_TOKEN_BYTES,
|
|
53502
54419
|
ArtifactStorage
|
|
53503
54420
|
};
|