@wayai/cli 0.3.105 → 0.3.106
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1012 -990
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -441,11 +441,11 @@ function captureException2(error, context) {
|
|
|
441
441
|
Sentry.captureException(error);
|
|
442
442
|
});
|
|
443
443
|
}
|
|
444
|
-
function addApiBreadcrumb(method,
|
|
444
|
+
function addApiBreadcrumb(method, path31) {
|
|
445
445
|
if (!initialized) return;
|
|
446
446
|
Sentry.addBreadcrumb({
|
|
447
447
|
category: "http",
|
|
448
|
-
message: `${method} ${
|
|
448
|
+
message: `${method} ${path31}`,
|
|
449
449
|
level: "info"
|
|
450
450
|
});
|
|
451
451
|
}
|
|
@@ -507,9 +507,9 @@ var init_api_client = __esm({
|
|
|
507
507
|
ApiError = class extends Error {
|
|
508
508
|
status;
|
|
509
509
|
body;
|
|
510
|
-
constructor(method,
|
|
510
|
+
constructor(method, path31, status, body) {
|
|
511
511
|
const safeBody = maskSecretsInMessage(body);
|
|
512
|
-
super(`API request failed: ${method} ${
|
|
512
|
+
super(`API request failed: ${method} ${path31} (${status}): ${safeBody}`);
|
|
513
513
|
this.name = "ApiError";
|
|
514
514
|
this.status = status;
|
|
515
515
|
this.body = safeBody;
|
|
@@ -657,8 +657,8 @@ var init_api_client = __esm({
|
|
|
657
657
|
...organizationId && { organization_id: organizationId }
|
|
658
658
|
});
|
|
659
659
|
}
|
|
660
|
-
async lookup(
|
|
661
|
-
const params = new URLSearchParams({ path:
|
|
660
|
+
async lookup(path31, opts) {
|
|
661
|
+
const params = new URLSearchParams({ path: path31 });
|
|
662
662
|
if (opts?.organizationId) params.set("organization_id", opts.organizationId);
|
|
663
663
|
return this.request("GET", `/api/ci/lookup?${params.toString()}`);
|
|
664
664
|
}
|
|
@@ -1058,9 +1058,9 @@ var init_api_client = __esm({
|
|
|
1058
1058
|
* sandbox for this conversation, or the blob was purged).
|
|
1059
1059
|
*/
|
|
1060
1060
|
async downloadArchiveSandboxFs(hubId, conversationId) {
|
|
1061
|
-
const
|
|
1062
|
-
addApiBreadcrumb("GET",
|
|
1063
|
-
const url = `${this.apiUrl}${
|
|
1061
|
+
const path31 = `/api/archive/${encodeURIComponent(hubId)}/conversations/${encodeURIComponent(conversationId)}/sandbox`;
|
|
1062
|
+
addApiBreadcrumb("GET", path31);
|
|
1063
|
+
const url = `${this.apiUrl}${path31}`;
|
|
1064
1064
|
let response = await this.send(url, "GET");
|
|
1065
1065
|
if (response.status === 401 && this.onUnauthorized) {
|
|
1066
1066
|
let refreshed;
|
|
@@ -1074,7 +1074,7 @@ var init_api_client = __esm({
|
|
|
1074
1074
|
}
|
|
1075
1075
|
}
|
|
1076
1076
|
if (!response.ok) {
|
|
1077
|
-
throw new ApiError("GET",
|
|
1077
|
+
throw new ApiError("GET", path31, response.status, await response.text());
|
|
1078
1078
|
}
|
|
1079
1079
|
return new Uint8Array(await response.arrayBuffer());
|
|
1080
1080
|
}
|
|
@@ -1114,9 +1114,9 @@ var init_api_client = __esm({
|
|
|
1114
1114
|
`/api/admin/data-explorer/debug/observability/${encodeURIComponent(hubId)}/conversations/${encodeURIComponent(conversationId)}${qs}`
|
|
1115
1115
|
);
|
|
1116
1116
|
}
|
|
1117
|
-
async request(method,
|
|
1118
|
-
addApiBreadcrumb(method,
|
|
1119
|
-
const url = `${this.apiUrl}${
|
|
1117
|
+
async request(method, path31, body) {
|
|
1118
|
+
addApiBreadcrumb(method, path31);
|
|
1119
|
+
const url = `${this.apiUrl}${path31}`;
|
|
1120
1120
|
let refreshedOn401 = false;
|
|
1121
1121
|
for (let retry = 0; ; retry++) {
|
|
1122
1122
|
let response = await this.send(url, method, body);
|
|
@@ -1140,7 +1140,7 @@ var init_api_client = __esm({
|
|
|
1140
1140
|
await delay(RETRYABLE_BACKOFF_MS[retry]);
|
|
1141
1141
|
continue;
|
|
1142
1142
|
}
|
|
1143
|
-
throw new ApiError(method,
|
|
1143
|
+
throw new ApiError(method, path31, response.status, errorBody);
|
|
1144
1144
|
}
|
|
1145
1145
|
}
|
|
1146
1146
|
/** Issue a single authenticated request with the client's current token. */
|
|
@@ -1575,8 +1575,8 @@ var init_parseUtil = __esm({
|
|
|
1575
1575
|
init_errors();
|
|
1576
1576
|
init_en();
|
|
1577
1577
|
makeIssue = (params) => {
|
|
1578
|
-
const { data, path:
|
|
1579
|
-
const fullPath = [...
|
|
1578
|
+
const { data, path: path31, errorMaps, issueData } = params;
|
|
1579
|
+
const fullPath = [...path31, ...issueData.path || []];
|
|
1580
1580
|
const fullIssue = {
|
|
1581
1581
|
...issueData,
|
|
1582
1582
|
path: fullPath
|
|
@@ -1887,11 +1887,11 @@ var init_types = __esm({
|
|
|
1887
1887
|
init_parseUtil();
|
|
1888
1888
|
init_util();
|
|
1889
1889
|
ParseInputLazyPath = class {
|
|
1890
|
-
constructor(parent, value,
|
|
1890
|
+
constructor(parent, value, path31, key) {
|
|
1891
1891
|
this._cachedPath = [];
|
|
1892
1892
|
this.parent = parent;
|
|
1893
1893
|
this.data = value;
|
|
1894
|
-
this._path =
|
|
1894
|
+
this._path = path31;
|
|
1895
1895
|
this._key = key;
|
|
1896
1896
|
}
|
|
1897
1897
|
get path() {
|
|
@@ -5375,12 +5375,12 @@ function typeMatches(typeField, allowed) {
|
|
|
5375
5375
|
if (Array.isArray(typeField)) return typeField.every((t) => typeof t === "string" && allowed.has(t));
|
|
5376
5376
|
return false;
|
|
5377
5377
|
}
|
|
5378
|
-
function validateSchema(schema,
|
|
5378
|
+
function validateSchema(schema, path31, errors, opts = {}) {
|
|
5379
5379
|
if (typeof schema === "boolean") return;
|
|
5380
5380
|
const depth = opts.depth ?? 0;
|
|
5381
5381
|
if (depth > MAX_SCHEMA_DEPTH) {
|
|
5382
5382
|
errors.push({
|
|
5383
|
-
path:
|
|
5383
|
+
path: path31 || "<root>",
|
|
5384
5384
|
message: `schema nesting exceeds ${MAX_SCHEMA_DEPTH} levels`,
|
|
5385
5385
|
suggestion: "Flatten the schema; LLM providers reject deeply nested tool input_schemas."
|
|
5386
5386
|
});
|
|
@@ -5388,7 +5388,7 @@ function validateSchema(schema, path30, errors, opts = {}) {
|
|
|
5388
5388
|
}
|
|
5389
5389
|
if (!isRecord(schema)) {
|
|
5390
5390
|
errors.push({
|
|
5391
|
-
path:
|
|
5391
|
+
path: path31,
|
|
5392
5392
|
message: `expected object, got ${schema === null ? "null" : typeof schema}`
|
|
5393
5393
|
});
|
|
5394
5394
|
return;
|
|
@@ -5396,14 +5396,14 @@ function validateSchema(schema, path30, errors, opts = {}) {
|
|
|
5396
5396
|
if (opts.isRoot) {
|
|
5397
5397
|
if ("type" in schema && schema.type !== "object") {
|
|
5398
5398
|
errors.push({
|
|
5399
|
-
path:
|
|
5399
|
+
path: path31 ? `${path31}.type` : "type",
|
|
5400
5400
|
message: `root type must be 'object', got ${JSON.stringify(schema.type)}`,
|
|
5401
5401
|
suggestion: "Tool parameters at the root must be an object: `{ type: 'object', properties: { ... } }`."
|
|
5402
5402
|
});
|
|
5403
5403
|
}
|
|
5404
5404
|
} else if ("type" in schema && !typeMatches(schema.type, ALLOWED_TYPES)) {
|
|
5405
5405
|
errors.push({
|
|
5406
|
-
path: `${
|
|
5406
|
+
path: `${path31}.type`,
|
|
5407
5407
|
message: `type must be one of ${[...ALLOWED_TYPES].join("/")} or an array of those, got ${JSON.stringify(schema.type)}`
|
|
5408
5408
|
});
|
|
5409
5409
|
}
|
|
@@ -5412,25 +5412,25 @@ function validateSchema(schema, path30, errors, opts = {}) {
|
|
|
5412
5412
|
if (typeof e === "string") {
|
|
5413
5413
|
const isPlaceholder = PLACEHOLDER_TOKENS.includes(e);
|
|
5414
5414
|
errors.push({
|
|
5415
|
-
path: `${
|
|
5415
|
+
path: `${path31}.enum`,
|
|
5416
5416
|
message: `enum must be a non-empty array of primitives, got string ${JSON.stringify(e)}`,
|
|
5417
5417
|
suggestion: isPlaceholder ? "Set `operation: 'update_kanban_status'` on the tool and ensure at least one hub.kanban_status has `allowsAgentUpdate: true` so the platform can render the placeholder into a valid array." : 'Wrap the value in an array: `enum: ["value"]`.'
|
|
5418
5418
|
});
|
|
5419
5419
|
} else if (!Array.isArray(e)) {
|
|
5420
5420
|
errors.push({
|
|
5421
|
-
path: `${
|
|
5421
|
+
path: `${path31}.enum`,
|
|
5422
5422
|
message: `enum must be a non-empty array of primitives, got ${typeof e}`
|
|
5423
5423
|
});
|
|
5424
5424
|
} else if (e.length === 0) {
|
|
5425
5425
|
errors.push({
|
|
5426
|
-
path: `${
|
|
5426
|
+
path: `${path31}.enum`,
|
|
5427
5427
|
message: "enum must not be empty"
|
|
5428
5428
|
});
|
|
5429
5429
|
} else {
|
|
5430
5430
|
for (let i = 0; i < e.length; i++) {
|
|
5431
5431
|
if (!isPrimitive(e[i])) {
|
|
5432
5432
|
errors.push({
|
|
5433
|
-
path: `${
|
|
5433
|
+
path: `${path31}.enum[${i}]`,
|
|
5434
5434
|
message: `enum entry must be a primitive (string/number/boolean/null), got ${typeof e[i]}`
|
|
5435
5435
|
});
|
|
5436
5436
|
}
|
|
@@ -5440,7 +5440,7 @@ function validateSchema(schema, path30, errors, opts = {}) {
|
|
|
5440
5440
|
for (const key of ["exclusiveMinimum", "exclusiveMaximum"]) {
|
|
5441
5441
|
if (key in schema && typeof schema[key] === "boolean") {
|
|
5442
5442
|
errors.push({
|
|
5443
|
-
path: `${
|
|
5443
|
+
path: `${path31}.${key}`,
|
|
5444
5444
|
message: `${key} as boolean is draft-04 syntax; draft 2020-12 requires a numeric value`,
|
|
5445
5445
|
suggestion: `Replace with the numeric bound, e.g. \`${key}: 0\`.`
|
|
5446
5446
|
});
|
|
@@ -5449,45 +5449,45 @@ function validateSchema(schema, path30, errors, opts = {}) {
|
|
|
5449
5449
|
if ("properties" in schema) {
|
|
5450
5450
|
if (!isRecord(schema.properties)) {
|
|
5451
5451
|
errors.push({
|
|
5452
|
-
path: `${
|
|
5452
|
+
path: `${path31}.properties`,
|
|
5453
5453
|
message: `properties must be an object, got ${Array.isArray(schema.properties) ? "array" : typeof schema.properties}`
|
|
5454
5454
|
});
|
|
5455
5455
|
} else {
|
|
5456
5456
|
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
5457
|
-
validateSchema(propSchema, `${
|
|
5457
|
+
validateSchema(propSchema, `${path31}.properties.${propName}`, errors, { depth: depth + 1 });
|
|
5458
5458
|
}
|
|
5459
5459
|
}
|
|
5460
5460
|
}
|
|
5461
5461
|
if (schemaTypeIncludes(schema, "array") && "items" in schema) {
|
|
5462
5462
|
if (Array.isArray(schema.items)) {
|
|
5463
|
-
schema.items.forEach((sub, i) => validateSchema(sub, `${
|
|
5463
|
+
schema.items.forEach((sub, i) => validateSchema(sub, `${path31}.items[${i}]`, errors, { depth: depth + 1 }));
|
|
5464
5464
|
} else {
|
|
5465
|
-
validateSchema(schema.items, `${
|
|
5465
|
+
validateSchema(schema.items, `${path31}.items`, errors, { depth: depth + 1 });
|
|
5466
5466
|
}
|
|
5467
5467
|
}
|
|
5468
5468
|
for (const key of SUBSCHEMA_OBJECT_KEYWORDS) {
|
|
5469
5469
|
if (key in schema && isRecord(schema[key])) {
|
|
5470
|
-
validateSchema(schema[key], `${
|
|
5470
|
+
validateSchema(schema[key], `${path31}.${key}`, errors, { depth: depth + 1 });
|
|
5471
5471
|
}
|
|
5472
5472
|
}
|
|
5473
5473
|
for (const key of SUBSCHEMA_LIST_KEYWORDS) {
|
|
5474
5474
|
const list = schema[key];
|
|
5475
5475
|
if (Array.isArray(list)) {
|
|
5476
|
-
list.forEach((sub, i) => validateSchema(sub, `${
|
|
5476
|
+
list.forEach((sub, i) => validateSchema(sub, `${path31}.${key}[${i}]`, errors, { depth: depth + 1 }));
|
|
5477
5477
|
}
|
|
5478
5478
|
}
|
|
5479
5479
|
for (const key of SUBSCHEMA_MAP_KEYWORDS) {
|
|
5480
5480
|
const map = schema[key];
|
|
5481
5481
|
if (isRecord(map)) {
|
|
5482
5482
|
for (const [name, sub] of Object.entries(map)) {
|
|
5483
|
-
validateSchema(sub, `${
|
|
5483
|
+
validateSchema(sub, `${path31}.${key}.${name}`, errors, { depth: depth + 1 });
|
|
5484
5484
|
}
|
|
5485
5485
|
}
|
|
5486
5486
|
}
|
|
5487
5487
|
const reportedPaths = new Set(errors.map((e) => e.path));
|
|
5488
5488
|
for (const [k, v] of Object.entries(schema)) {
|
|
5489
5489
|
if (typeof v !== "string") continue;
|
|
5490
|
-
const fieldPath = `${
|
|
5490
|
+
const fieldPath = `${path31}.${k}`;
|
|
5491
5491
|
if (reportedPaths.has(fieldPath)) continue;
|
|
5492
5492
|
for (const token of PLACEHOLDER_TOKENS) {
|
|
5493
5493
|
if (v === token) {
|
|
@@ -5787,10 +5787,10 @@ function refineHubAsCodeEvals(config, ctx) {
|
|
|
5787
5787
|
function refineHubAsCodeEvalAttachments(config, ctx) {
|
|
5788
5788
|
const cfg = config;
|
|
5789
5789
|
const referencedHashes = /* @__PURE__ */ new Set();
|
|
5790
|
-
const addTurnIssue = (
|
|
5790
|
+
const addTurnIssue = (path31, turn, name) => {
|
|
5791
5791
|
const error = evalTurnAttachmentsError(turn);
|
|
5792
5792
|
if (error) {
|
|
5793
|
-
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path:
|
|
5793
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: path31, message: `${name}: ${error}` });
|
|
5794
5794
|
return;
|
|
5795
5795
|
}
|
|
5796
5796
|
for (const hash of collectTurnAttachmentHashes(turn)) referencedHashes.add(hash);
|
|
@@ -5911,10 +5911,10 @@ function refineHubAsCodeResources(config, ctx) {
|
|
|
5911
5911
|
});
|
|
5912
5912
|
}
|
|
5913
5913
|
}
|
|
5914
|
-
function isSafeTemplatePath(
|
|
5915
|
-
if (
|
|
5916
|
-
if (
|
|
5917
|
-
const segments =
|
|
5914
|
+
function isSafeTemplatePath(path31) {
|
|
5915
|
+
if (path31.length === 0 || path31.length > 300) return false;
|
|
5916
|
+
if (path31.startsWith("/") || path31.includes("\\")) return false;
|
|
5917
|
+
const segments = path31.split("/");
|
|
5918
5918
|
return segments.every(
|
|
5919
5919
|
(seg) => seg.length > 0 && seg !== "." && seg !== ".." && /^[A-Za-z0-9._-]+$/.test(seg)
|
|
5920
5920
|
);
|
|
@@ -9614,9 +9614,9 @@ function getVersionCachePath(filename = CLI_CACHE_FILE) {
|
|
|
9614
9614
|
}
|
|
9615
9615
|
function readVersionCache(filename = CLI_CACHE_FILE) {
|
|
9616
9616
|
try {
|
|
9617
|
-
const
|
|
9618
|
-
if (!existsSync4(
|
|
9619
|
-
const parsed = JSON.parse(readFileSync5(
|
|
9617
|
+
const path31 = getVersionCachePath(filename);
|
|
9618
|
+
if (!existsSync4(path31)) return null;
|
|
9619
|
+
const parsed = JSON.parse(readFileSync5(path31, "utf-8"));
|
|
9620
9620
|
if (typeof parsed.lastCheck !== "number") return null;
|
|
9621
9621
|
if (parsed.latest !== null && typeof parsed.latest !== "string") return null;
|
|
9622
9622
|
return parsed;
|
|
@@ -9636,10 +9636,10 @@ function isVersionCacheStale(filename = CLI_CACHE_FILE, maxAgeMs = MAX_AGE_24H_M
|
|
|
9636
9636
|
return Date.now() - cache.lastCheck > maxAgeMs;
|
|
9637
9637
|
}
|
|
9638
9638
|
function writeVersionCache(filename, cache) {
|
|
9639
|
-
const
|
|
9640
|
-
const dir = dirname3(
|
|
9639
|
+
const path31 = getVersionCachePath(filename);
|
|
9640
|
+
const dir = dirname3(path31);
|
|
9641
9641
|
if (!existsSync4(dir)) mkdirSync(dir, { recursive: true });
|
|
9642
|
-
writeFileSync2(
|
|
9642
|
+
writeFileSync2(path31, JSON.stringify(cache));
|
|
9643
9643
|
}
|
|
9644
9644
|
function touchVersionCache(filename) {
|
|
9645
9645
|
writeVersionCache(filename, { lastCheck: Date.now(), latest: readVersionCache(filename)?.latest ?? null });
|
|
@@ -9678,14 +9678,14 @@ function parseFrontmatterVersion(content) {
|
|
|
9678
9678
|
function findInstalledSkills(projectRoot, paths = SKILL_INSTALL_PATHS) {
|
|
9679
9679
|
const found = [];
|
|
9680
9680
|
for (const rel of paths) {
|
|
9681
|
-
const
|
|
9682
|
-
if (!existsSync5(
|
|
9681
|
+
const path31 = join7(projectRoot, rel);
|
|
9682
|
+
if (!existsSync5(path31)) continue;
|
|
9683
9683
|
let version = null;
|
|
9684
9684
|
try {
|
|
9685
|
-
version = parseFrontmatterVersion(readFileSync6(
|
|
9685
|
+
version = parseFrontmatterVersion(readFileSync6(path31, "utf-8"));
|
|
9686
9686
|
} catch {
|
|
9687
9687
|
}
|
|
9688
|
-
found.push({ path:
|
|
9688
|
+
found.push({ path: path31, version });
|
|
9689
9689
|
}
|
|
9690
9690
|
return found;
|
|
9691
9691
|
}
|
|
@@ -11304,12 +11304,12 @@ function typeMatches2(typeField, allowed) {
|
|
|
11304
11304
|
if (Array.isArray(typeField)) return typeField.every((t) => typeof t === "string" && allowed.has(t));
|
|
11305
11305
|
return false;
|
|
11306
11306
|
}
|
|
11307
|
-
function validateSchema2(schema,
|
|
11307
|
+
function validateSchema2(schema, path31, errors, opts = {}) {
|
|
11308
11308
|
if (typeof schema === "boolean") return;
|
|
11309
11309
|
const depth = opts.depth ?? 0;
|
|
11310
11310
|
if (depth > MAX_SCHEMA_DEPTH2) {
|
|
11311
11311
|
errors.push({
|
|
11312
|
-
path:
|
|
11312
|
+
path: path31 || "<root>",
|
|
11313
11313
|
message: `schema nesting exceeds ${MAX_SCHEMA_DEPTH2} levels`,
|
|
11314
11314
|
suggestion: "Flatten the schema; LLM providers reject deeply nested tool input_schemas."
|
|
11315
11315
|
});
|
|
@@ -11317,7 +11317,7 @@ function validateSchema2(schema, path30, errors, opts = {}) {
|
|
|
11317
11317
|
}
|
|
11318
11318
|
if (!isRecord2(schema)) {
|
|
11319
11319
|
errors.push({
|
|
11320
|
-
path:
|
|
11320
|
+
path: path31,
|
|
11321
11321
|
message: `expected object, got ${schema === null ? "null" : typeof schema}`
|
|
11322
11322
|
});
|
|
11323
11323
|
return;
|
|
@@ -11325,14 +11325,14 @@ function validateSchema2(schema, path30, errors, opts = {}) {
|
|
|
11325
11325
|
if (opts.isRoot) {
|
|
11326
11326
|
if ("type" in schema && schema.type !== "object") {
|
|
11327
11327
|
errors.push({
|
|
11328
|
-
path:
|
|
11328
|
+
path: path31 ? `${path31}.type` : "type",
|
|
11329
11329
|
message: `root type must be 'object', got ${JSON.stringify(schema.type)}`,
|
|
11330
11330
|
suggestion: "Tool parameters at the root must be an object: `{ type: 'object', properties: { ... } }`."
|
|
11331
11331
|
});
|
|
11332
11332
|
}
|
|
11333
11333
|
} else if ("type" in schema && !typeMatches2(schema.type, ALLOWED_TYPES2)) {
|
|
11334
11334
|
errors.push({
|
|
11335
|
-
path: `${
|
|
11335
|
+
path: `${path31}.type`,
|
|
11336
11336
|
message: `type must be one of ${[...ALLOWED_TYPES2].join("/")} or an array of those, got ${JSON.stringify(schema.type)}`
|
|
11337
11337
|
});
|
|
11338
11338
|
}
|
|
@@ -11341,25 +11341,25 @@ function validateSchema2(schema, path30, errors, opts = {}) {
|
|
|
11341
11341
|
if (typeof e === "string") {
|
|
11342
11342
|
const isPlaceholder = PLACEHOLDER_TOKENS2.includes(e);
|
|
11343
11343
|
errors.push({
|
|
11344
|
-
path: `${
|
|
11344
|
+
path: `${path31}.enum`,
|
|
11345
11345
|
message: `enum must be a non-empty array of primitives, got string ${JSON.stringify(e)}`,
|
|
11346
11346
|
suggestion: isPlaceholder ? "Set `operation: 'update_kanban_status'` on the tool and ensure at least one hub.kanban_status has `allowsAgentUpdate: true` so the platform can render the placeholder into a valid array." : 'Wrap the value in an array: `enum: ["value"]`.'
|
|
11347
11347
|
});
|
|
11348
11348
|
} else if (!Array.isArray(e)) {
|
|
11349
11349
|
errors.push({
|
|
11350
|
-
path: `${
|
|
11350
|
+
path: `${path31}.enum`,
|
|
11351
11351
|
message: `enum must be a non-empty array of primitives, got ${typeof e}`
|
|
11352
11352
|
});
|
|
11353
11353
|
} else if (e.length === 0) {
|
|
11354
11354
|
errors.push({
|
|
11355
|
-
path: `${
|
|
11355
|
+
path: `${path31}.enum`,
|
|
11356
11356
|
message: "enum must not be empty"
|
|
11357
11357
|
});
|
|
11358
11358
|
} else {
|
|
11359
11359
|
for (let i = 0; i < e.length; i++) {
|
|
11360
11360
|
if (!isPrimitive2(e[i])) {
|
|
11361
11361
|
errors.push({
|
|
11362
|
-
path: `${
|
|
11362
|
+
path: `${path31}.enum[${i}]`,
|
|
11363
11363
|
message: `enum entry must be a primitive (string/number/boolean/null), got ${typeof e[i]}`
|
|
11364
11364
|
});
|
|
11365
11365
|
}
|
|
@@ -11369,7 +11369,7 @@ function validateSchema2(schema, path30, errors, opts = {}) {
|
|
|
11369
11369
|
for (const key of ["exclusiveMinimum", "exclusiveMaximum"]) {
|
|
11370
11370
|
if (key in schema && typeof schema[key] === "boolean") {
|
|
11371
11371
|
errors.push({
|
|
11372
|
-
path: `${
|
|
11372
|
+
path: `${path31}.${key}`,
|
|
11373
11373
|
message: `${key} as boolean is draft-04 syntax; draft 2020-12 requires a numeric value`,
|
|
11374
11374
|
suggestion: `Replace with the numeric bound, e.g. \`${key}: 0\`.`
|
|
11375
11375
|
});
|
|
@@ -11378,45 +11378,45 @@ function validateSchema2(schema, path30, errors, opts = {}) {
|
|
|
11378
11378
|
if ("properties" in schema) {
|
|
11379
11379
|
if (!isRecord2(schema.properties)) {
|
|
11380
11380
|
errors.push({
|
|
11381
|
-
path: `${
|
|
11381
|
+
path: `${path31}.properties`,
|
|
11382
11382
|
message: `properties must be an object, got ${Array.isArray(schema.properties) ? "array" : typeof schema.properties}`
|
|
11383
11383
|
});
|
|
11384
11384
|
} else {
|
|
11385
11385
|
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
11386
|
-
validateSchema2(propSchema, `${
|
|
11386
|
+
validateSchema2(propSchema, `${path31}.properties.${propName}`, errors, { depth: depth + 1 });
|
|
11387
11387
|
}
|
|
11388
11388
|
}
|
|
11389
11389
|
}
|
|
11390
11390
|
if (schemaTypeIncludes2(schema, "array") && "items" in schema) {
|
|
11391
11391
|
if (Array.isArray(schema.items)) {
|
|
11392
|
-
schema.items.forEach((sub, i) => validateSchema2(sub, `${
|
|
11392
|
+
schema.items.forEach((sub, i) => validateSchema2(sub, `${path31}.items[${i}]`, errors, { depth: depth + 1 }));
|
|
11393
11393
|
} else {
|
|
11394
|
-
validateSchema2(schema.items, `${
|
|
11394
|
+
validateSchema2(schema.items, `${path31}.items`, errors, { depth: depth + 1 });
|
|
11395
11395
|
}
|
|
11396
11396
|
}
|
|
11397
11397
|
for (const key of SUBSCHEMA_OBJECT_KEYWORDS2) {
|
|
11398
11398
|
if (key in schema && isRecord2(schema[key])) {
|
|
11399
|
-
validateSchema2(schema[key], `${
|
|
11399
|
+
validateSchema2(schema[key], `${path31}.${key}`, errors, { depth: depth + 1 });
|
|
11400
11400
|
}
|
|
11401
11401
|
}
|
|
11402
11402
|
for (const key of SUBSCHEMA_LIST_KEYWORDS2) {
|
|
11403
11403
|
const list = schema[key];
|
|
11404
11404
|
if (Array.isArray(list)) {
|
|
11405
|
-
list.forEach((sub, i) => validateSchema2(sub, `${
|
|
11405
|
+
list.forEach((sub, i) => validateSchema2(sub, `${path31}.${key}[${i}]`, errors, { depth: depth + 1 }));
|
|
11406
11406
|
}
|
|
11407
11407
|
}
|
|
11408
11408
|
for (const key of SUBSCHEMA_MAP_KEYWORDS2) {
|
|
11409
11409
|
const map = schema[key];
|
|
11410
11410
|
if (isRecord2(map)) {
|
|
11411
11411
|
for (const [name, sub] of Object.entries(map)) {
|
|
11412
|
-
validateSchema2(sub, `${
|
|
11412
|
+
validateSchema2(sub, `${path31}.${key}.${name}`, errors, { depth: depth + 1 });
|
|
11413
11413
|
}
|
|
11414
11414
|
}
|
|
11415
11415
|
}
|
|
11416
11416
|
const reportedPaths = new Set(errors.map((e) => e.path));
|
|
11417
11417
|
for (const [k, v] of Object.entries(schema)) {
|
|
11418
11418
|
if (typeof v !== "string") continue;
|
|
11419
|
-
const fieldPath = `${
|
|
11419
|
+
const fieldPath = `${path31}.${k}`;
|
|
11420
11420
|
if (reportedPaths.has(fieldPath)) continue;
|
|
11421
11421
|
for (const token of PLACEHOLDER_TOKENS2) {
|
|
11422
11422
|
if (v === token) {
|
|
@@ -11690,10 +11690,10 @@ function refineHubAsCodeEvals2(config, ctx) {
|
|
|
11690
11690
|
function refineHubAsCodeEvalAttachments2(config, ctx) {
|
|
11691
11691
|
const cfg = config;
|
|
11692
11692
|
const referencedHashes = /* @__PURE__ */ new Set();
|
|
11693
|
-
const addTurnIssue = (
|
|
11693
|
+
const addTurnIssue = (path31, turn, name) => {
|
|
11694
11694
|
const error = evalTurnAttachmentsError2(turn);
|
|
11695
11695
|
if (error) {
|
|
11696
|
-
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path:
|
|
11696
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: path31, message: `${name}: ${error}` });
|
|
11697
11697
|
return;
|
|
11698
11698
|
}
|
|
11699
11699
|
for (const hash of collectTurnAttachmentHashes2(turn)) referencedHashes.add(hash);
|
|
@@ -11814,10 +11814,10 @@ function refineHubAsCodeResources2(config, ctx) {
|
|
|
11814
11814
|
});
|
|
11815
11815
|
}
|
|
11816
11816
|
}
|
|
11817
|
-
function isSafeTemplatePath2(
|
|
11818
|
-
if (
|
|
11819
|
-
if (
|
|
11820
|
-
const segments =
|
|
11817
|
+
function isSafeTemplatePath2(path31) {
|
|
11818
|
+
if (path31.length === 0 || path31.length > 300) return false;
|
|
11819
|
+
if (path31.startsWith("/") || path31.includes("\\")) return false;
|
|
11820
|
+
const segments = path31.split("/");
|
|
11821
11821
|
return segments.every(
|
|
11822
11822
|
(seg) => seg.length > 0 && seg !== "." && seg !== ".." && /^[A-Za-z0-9._-]+$/.test(seg)
|
|
11823
11823
|
);
|
|
@@ -11845,9 +11845,9 @@ function isPlainObject(value) {
|
|
|
11845
11845
|
const proto = Object.getPrototypeOf(value);
|
|
11846
11846
|
return proto === Object.prototype || proto === null;
|
|
11847
11847
|
}
|
|
11848
|
-
function isSanitizableSliceFile(
|
|
11849
|
-
if (!/\.ya?ml$/i.test(
|
|
11850
|
-
return
|
|
11848
|
+
function isSanitizableSliceFile(path31) {
|
|
11849
|
+
if (!/\.ya?ml$/i.test(path31)) return false;
|
|
11850
|
+
return path31 !== "resources" && !path31.startsWith("resources/");
|
|
11851
11851
|
}
|
|
11852
11852
|
function distinctUsesBackends(uses) {
|
|
11853
11853
|
return [...new Set((uses ?? []).map((u) => u.backend))];
|
|
@@ -14864,897 +14864,951 @@ var init_resource_files = __esm({
|
|
|
14864
14864
|
}
|
|
14865
14865
|
});
|
|
14866
14866
|
|
|
14867
|
-
// src/lib/
|
|
14867
|
+
// src/lib/eval-attachments.ts
|
|
14868
14868
|
import * as fs11 from "fs";
|
|
14869
14869
|
import * as path13 from "path";
|
|
14870
|
-
|
|
14871
|
-
|
|
14872
|
-
|
|
14873
|
-
if (
|
|
14874
|
-
|
|
14875
|
-
|
|
14876
|
-
if (evalEntry.agent_id) out.agent_id = evalEntry.agent_id;
|
|
14877
|
-
if (typeof evalEntry.runs === "number" && evalEntry.runs !== 1) {
|
|
14878
|
-
out.runs = evalEntry.runs;
|
|
14879
|
-
}
|
|
14880
|
-
if (evalEntry.enabled === false) {
|
|
14881
|
-
out.enabled = false;
|
|
14882
|
-
}
|
|
14883
|
-
if (evalEntry.history && evalEntry.history.length > 0) {
|
|
14884
|
-
out.history = evalEntry.history;
|
|
14885
|
-
}
|
|
14886
|
-
if (typeof evalEntry.fixture === "string" && evalEntry.fixture.trim()) {
|
|
14887
|
-
out.fixture = evalEntry.fixture.trim();
|
|
14888
|
-
}
|
|
14889
|
-
if (typeof evalEntry.target_base === "string" && evalEntry.target_base.trim()) {
|
|
14890
|
-
out.target_base = evalEntry.target_base.trim();
|
|
14870
|
+
function resolveTurnAttachments(hubFolder, turn, label, bytesByHash) {
|
|
14871
|
+
const raw = turn.attachments;
|
|
14872
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
14873
|
+
if (!Array.isArray(raw) || raw.length === 0) {
|
|
14874
|
+
if (Array.isArray(raw)) return void 0;
|
|
14875
|
+
throw expected(`${label}: "attachments" must be a list of file paths.`);
|
|
14891
14876
|
}
|
|
14892
|
-
if (
|
|
14893
|
-
|
|
14877
|
+
if (turn.role !== "user") {
|
|
14878
|
+
throw expected(`${label}: attachments are only allowed on user turns (this turn is "${String(turn.role)}").`);
|
|
14894
14879
|
}
|
|
14895
|
-
|
|
14896
|
-
|
|
14897
|
-
|
|
14898
|
-
|
|
14880
|
+
const refs = [];
|
|
14881
|
+
for (const entry of raw) {
|
|
14882
|
+
if (typeof entry !== "string" || !entry.trim()) {
|
|
14883
|
+
throw expected(`${label}: each attachment must be a hub-relative file path string.`);
|
|
14884
|
+
}
|
|
14885
|
+
const abs = path13.resolve(hubFolder, entry);
|
|
14886
|
+
const root = path13.resolve(hubFolder);
|
|
14887
|
+
if (abs !== root && !abs.startsWith(root + path13.sep)) {
|
|
14888
|
+
throw expected(`${label}: attachment "${entry}" escapes the hub folder.`);
|
|
14889
|
+
}
|
|
14890
|
+
let stat;
|
|
14891
|
+
try {
|
|
14892
|
+
stat = fs11.statSync(abs);
|
|
14893
|
+
} catch {
|
|
14894
|
+
}
|
|
14895
|
+
if (!stat?.isFile()) {
|
|
14896
|
+
throw expected(`${label}: attachment "${entry}" not found.`);
|
|
14897
|
+
}
|
|
14898
|
+
if (!realpathContained(root, abs)) {
|
|
14899
|
+
throw expected(`${label}: attachment "${entry}" resolves outside the hub folder (symlinks are not allowed).`);
|
|
14900
|
+
}
|
|
14901
|
+
if (stat.size > MAX_RESOURCE_FILE_SIZE3) {
|
|
14902
|
+
throw expected(
|
|
14903
|
+
`${label}: attachment "${entry}" is ${(stat.size / 1024 / 1024).toFixed(1)}MB, over the 10MB limit.`
|
|
14904
|
+
);
|
|
14905
|
+
}
|
|
14906
|
+
const data = fs11.readFileSync(abs);
|
|
14907
|
+
const hash = computeHash(data);
|
|
14908
|
+
const fileName = path13.basename(abs);
|
|
14909
|
+
const mimeType = guessMimeType(fileName) ?? "application/octet-stream";
|
|
14910
|
+
refs.push({ hash, file_name: fileName, mime_type: mimeType, file_size: stat.size });
|
|
14911
|
+
if (!bytesByHash.has(hash)) {
|
|
14912
|
+
bytesByHash.set(hash, {
|
|
14913
|
+
hash,
|
|
14914
|
+
content_base64: data.toString("base64"),
|
|
14915
|
+
file_name: fileName,
|
|
14916
|
+
mime_type: mimeType,
|
|
14917
|
+
file_size: stat.size
|
|
14918
|
+
});
|
|
14919
|
+
}
|
|
14899
14920
|
}
|
|
14900
|
-
return
|
|
14921
|
+
return refs;
|
|
14901
14922
|
}
|
|
14902
|
-
function
|
|
14903
|
-
const
|
|
14904
|
-
if (
|
|
14905
|
-
|
|
14906
|
-
|
|
14907
|
-
|
|
14908
|
-
|
|
14909
|
-
|
|
14910
|
-
const
|
|
14911
|
-
|
|
14912
|
-
|
|
14913
|
-
|
|
14914
|
-
|
|
14915
|
-
|
|
14916
|
-
writeIfAbsent(path13.join(hubFolder, "CLAUDE.md"), CLAUDE_MD_SHIM);
|
|
14923
|
+
function applyTurnAttachments(hubFolder, turn, label, bytesByHash) {
|
|
14924
|
+
const refs = resolveTurnAttachments(hubFolder, turn, label, bytesByHash);
|
|
14925
|
+
if (refs) turn.attachments = refs;
|
|
14926
|
+
else delete turn.attachments;
|
|
14927
|
+
}
|
|
14928
|
+
function assertAttachmentsAreLocalPaths(turn, label) {
|
|
14929
|
+
const attachments = turn?.attachments;
|
|
14930
|
+
if (!Array.isArray(attachments)) return;
|
|
14931
|
+
for (const entry of attachments) {
|
|
14932
|
+
if (entry !== null && typeof entry === "object" && "hash" in entry) {
|
|
14933
|
+
throw new Error(
|
|
14934
|
+
`wire-shape attachment ref reached YAML serialization \u2014 CLI bug (${label}): the payload was not run through materializeHubFolder before writing.`
|
|
14935
|
+
);
|
|
14936
|
+
}
|
|
14917
14937
|
}
|
|
14918
|
-
|
|
14919
|
-
|
|
14920
|
-
|
|
14938
|
+
}
|
|
14939
|
+
function forEachAttachableTurn(payload, fn) {
|
|
14940
|
+
for (const ev of payload.evals ?? []) {
|
|
14941
|
+
if (ev.input) fn(ev.input);
|
|
14942
|
+
for (const t of ev.history ?? []) fn(t);
|
|
14921
14943
|
}
|
|
14922
|
-
const
|
|
14923
|
-
|
|
14924
|
-
for (const { slug, content } of agentFiles) {
|
|
14925
|
-
mdSlugs.add(slug);
|
|
14926
|
-
fs11.writeFileSync(path13.join(agentsDir, `${slug}.md`), content, "utf-8");
|
|
14944
|
+
for (const jr of payload.journeys ?? []) {
|
|
14945
|
+
for (const t of jr.transcript ?? []) fn(t);
|
|
14927
14946
|
}
|
|
14928
|
-
|
|
14929
|
-
|
|
14930
|
-
|
|
14931
|
-
|
|
14932
|
-
|
|
14933
|
-
|
|
14947
|
+
}
|
|
14948
|
+
function rewriteEvalAttachmentsToLocalPaths(payload) {
|
|
14949
|
+
const urlByHash = new Map((payload.eval_attachment_files ?? []).map((f) => [f.hash, f.download_url]));
|
|
14950
|
+
const safeName = (ref) => {
|
|
14951
|
+
const base = path13.basename(ref.file_name);
|
|
14952
|
+
return base === "" || base === "." || base === ".." ? ref.hash.slice(0, 16) : base;
|
|
14953
|
+
};
|
|
14954
|
+
const hashesByName = /* @__PURE__ */ new Map();
|
|
14955
|
+
const collect = (turn) => {
|
|
14956
|
+
if (!Array.isArray(turn.attachments)) return;
|
|
14957
|
+
for (const ref of turn.attachments) {
|
|
14958
|
+
if (!ref?.hash || !ref.file_name) continue;
|
|
14959
|
+
const name = safeName(ref);
|
|
14960
|
+
let set = hashesByName.get(name);
|
|
14961
|
+
if (!set) {
|
|
14962
|
+
set = /* @__PURE__ */ new Set();
|
|
14963
|
+
hashesByName.set(name, set);
|
|
14934
14964
|
}
|
|
14935
|
-
|
|
14936
|
-
|
|
14937
|
-
|
|
14938
|
-
|
|
14965
|
+
set.add(ref.hash);
|
|
14966
|
+
}
|
|
14967
|
+
};
|
|
14968
|
+
forEachAttachableTurn(payload, collect);
|
|
14969
|
+
const pathByKey = /* @__PURE__ */ new Map();
|
|
14970
|
+
const usedLower = /* @__PURE__ */ new Set();
|
|
14971
|
+
const localPathFor = (ref) => {
|
|
14972
|
+
const name = safeName(ref);
|
|
14973
|
+
const key = `${ref.hash} ${name}`;
|
|
14974
|
+
const existing = pathByKey.get(key);
|
|
14975
|
+
if (existing) return existing;
|
|
14976
|
+
const ext = path13.extname(name);
|
|
14977
|
+
const stem = path13.basename(name, ext);
|
|
14978
|
+
const collides = (hashesByName.get(name)?.size ?? 0) > 1;
|
|
14979
|
+
let rel = collides ? `${ATTACHMENTS_DIR}/${stem}-${ref.hash.slice(0, 8)}${ext}` : `${ATTACHMENTS_DIR}/${name}`;
|
|
14980
|
+
if (usedLower.has(rel.toLowerCase())) {
|
|
14981
|
+
let n = 12;
|
|
14982
|
+
while (usedLower.has(rel.toLowerCase()) && n <= ref.hash.length) {
|
|
14983
|
+
rel = `${ATTACHMENTS_DIR}/${stem}-${ref.hash.slice(0, n)}${ext}`;
|
|
14984
|
+
n += 4;
|
|
14985
|
+
}
|
|
14986
|
+
for (let extra = 2; usedLower.has(rel.toLowerCase()); extra++) {
|
|
14987
|
+
rel = `${ATTACHMENTS_DIR}/${stem}-${ref.hash}-${extra}${ext}`;
|
|
14939
14988
|
}
|
|
14940
14989
|
}
|
|
14941
|
-
|
|
14942
|
-
|
|
14943
|
-
|
|
14944
|
-
writeJourneyYamlFiles(hubFolder, payload.journeys || []);
|
|
14945
|
-
}
|
|
14946
|
-
function setPreviewLabelInHubYaml(hubFolder, label) {
|
|
14947
|
-
const yamlPath = resolveHubYamlPath(hubFolder);
|
|
14948
|
-
if (!yamlPath) return;
|
|
14949
|
-
const obj = yaml4.load(fs11.readFileSync(yamlPath, "utf-8")) ?? {};
|
|
14950
|
-
if (label) obj.preview_label = label;
|
|
14951
|
-
else delete obj.preview_label;
|
|
14952
|
-
fs11.writeFileSync(yamlPath, yaml4.dump(obj, YAML_DUMP_OPTIONS), "utf-8");
|
|
14953
|
-
}
|
|
14954
|
-
function buildYamlPayload(payload) {
|
|
14955
|
-
const result = {
|
|
14956
|
-
version: payload.version,
|
|
14957
|
-
hub_id: payload.hub_id,
|
|
14958
|
-
hub_environment: payload.hub_environment
|
|
14990
|
+
usedLower.add(rel.toLowerCase());
|
|
14991
|
+
pathByKey.set(key, rel);
|
|
14992
|
+
return rel;
|
|
14959
14993
|
};
|
|
14960
|
-
|
|
14961
|
-
|
|
14962
|
-
|
|
14963
|
-
|
|
14964
|
-
|
|
14965
|
-
|
|
14966
|
-
|
|
14967
|
-
return
|
|
14994
|
+
const downloads = /* @__PURE__ */ new Map();
|
|
14995
|
+
forEachAttachableTurn(payload, (turn) => {
|
|
14996
|
+
if (!Array.isArray(turn.attachments)) return;
|
|
14997
|
+
turn.attachments = turn.attachments.map((ref) => {
|
|
14998
|
+
const rel = localPathFor(ref);
|
|
14999
|
+
const url = urlByHash.get(ref.hash);
|
|
15000
|
+
if (url && !downloads.has(rel)) downloads.set(rel, { relPath: rel, url, hash: ref.hash });
|
|
15001
|
+
return rel;
|
|
14968
15002
|
});
|
|
14969
|
-
}
|
|
14970
|
-
|
|
14971
|
-
|
|
14972
|
-
}
|
|
14973
|
-
if (payload.teams && payload.teams.length > 0) {
|
|
14974
|
-
result.teams = payload.teams;
|
|
14975
|
-
}
|
|
14976
|
-
if (payload.connections && payload.connections.length > 0) {
|
|
14977
|
-
result.connections = payload.connections;
|
|
14978
|
-
}
|
|
14979
|
-
if (payload.outbound_contacts && payload.outbound_contacts.length > 0) {
|
|
14980
|
-
result.outbound_contacts = payload.outbound_contacts;
|
|
14981
|
-
}
|
|
14982
|
-
if (payload.outbound_lists && payload.outbound_lists.length > 0) {
|
|
14983
|
-
result.outbound_lists = payload.outbound_lists;
|
|
14984
|
-
}
|
|
14985
|
-
if (payload.outbound_schedules && payload.outbound_schedules.length > 0) {
|
|
14986
|
-
result.outbound_schedules = payload.outbound_schedules;
|
|
14987
|
-
}
|
|
14988
|
-
return result;
|
|
14989
|
-
}
|
|
14990
|
-
function writeAgentYamlFiles(agentsDir, agents) {
|
|
14991
|
-
const slugs = /* @__PURE__ */ new Set();
|
|
14992
|
-
for (const agent of agents) {
|
|
14993
|
-
const slug = slugify(agent.name);
|
|
14994
|
-
slugs.add(slug);
|
|
14995
|
-
const entry = { ...agent };
|
|
14996
|
-
delete entry.instructions;
|
|
14997
|
-
const yamlContent = yaml4.dump(entry, YAML_DUMP_OPTIONS);
|
|
14998
|
-
fs11.writeFileSync(path13.join(agentsDir, `${slug}.yaml`), yamlContent, "utf-8");
|
|
14999
|
-
}
|
|
15000
|
-
return slugs;
|
|
15003
|
+
});
|
|
15004
|
+
delete payload.eval_attachment_files;
|
|
15005
|
+
return [...downloads.values()];
|
|
15001
15006
|
}
|
|
15002
|
-
function
|
|
15003
|
-
const
|
|
15004
|
-
|
|
15005
|
-
|
|
15006
|
-
|
|
15007
|
-
|
|
15008
|
-
content: agent.instructions
|
|
15009
|
-
});
|
|
15010
|
-
}
|
|
15007
|
+
async function downloadEvalAttachments(hubFolder, downloads) {
|
|
15008
|
+
const attachDir = path13.resolve(hubFolder, ATTACHMENTS_DIR);
|
|
15009
|
+
let written = 0;
|
|
15010
|
+
if (!ensureRealSubdirNoSymlink(hubFolder, attachDir, downloads.length > 0)) {
|
|
15011
|
+
console.warn(` Warning: ${ATTACHMENTS_DIR}/ is not reachable inside the hub folder without a symlink; skipping attachment sync.`);
|
|
15012
|
+
return written;
|
|
15011
15013
|
}
|
|
15012
|
-
return
|
|
15013
|
-
|
|
15014
|
-
|
|
15015
|
-
|
|
15016
|
-
|
|
15017
|
-
|
|
15018
|
-
|
|
15019
|
-
|
|
15020
|
-
if (fs11.readdirSync(evalsDir).length === 0) fs11.rmdirSync(evalsDir);
|
|
15021
|
-
} catch {
|
|
15022
|
-
}
|
|
15014
|
+
if (!fs11.existsSync(attachDir)) return written;
|
|
15015
|
+
for (const dl of downloads) {
|
|
15016
|
+
const abs = path13.resolve(hubFolder, dl.relPath);
|
|
15017
|
+
if (abs !== attachDir && !abs.startsWith(attachDir + path13.sep)) continue;
|
|
15018
|
+
let existing;
|
|
15019
|
+
try {
|
|
15020
|
+
existing = fs11.lstatSync(abs);
|
|
15021
|
+
} catch {
|
|
15023
15022
|
}
|
|
15024
|
-
|
|
15025
|
-
}
|
|
15026
|
-
if (!fs11.existsSync(evalsDir)) {
|
|
15027
|
-
fs11.mkdirSync(evalsDir, { recursive: true });
|
|
15028
|
-
}
|
|
15029
|
-
const writtenRelPaths = /* @__PURE__ */ new Set();
|
|
15030
|
-
for (const evalEntry of evals) {
|
|
15031
|
-
const slug = slugify(evalEntry.name);
|
|
15032
|
-
const setName = evalEntry.path && evalEntry.path.trim().length > 0 ? evalEntry.path : null;
|
|
15033
|
-
const relPath = setName ? `${setName}/${slug}.yaml` : `${slug}.yaml`;
|
|
15034
|
-
const targetPath = path13.join(evalsDir, relPath);
|
|
15035
|
-
if (!targetPath.startsWith(evalsDir + path13.sep)) {
|
|
15036
|
-
console.warn(` Warning: skipping eval "${evalEntry.name}" (scenario set "${setName}" escapes evals/ \u2014 possible bad backend data)`);
|
|
15023
|
+
if (existing?.isFile() && !existing.isSymbolicLink() && computeHash(fs11.readFileSync(abs)) === dl.hash) {
|
|
15037
15024
|
continue;
|
|
15038
15025
|
}
|
|
15039
|
-
|
|
15040
|
-
const
|
|
15041
|
-
if (!
|
|
15042
|
-
|
|
15026
|
+
try {
|
|
15027
|
+
const res = await fetch(dl.url);
|
|
15028
|
+
if (!res.ok) {
|
|
15029
|
+
console.warn(` Warning: failed to download ${dl.relPath} (HTTP ${res.status})`);
|
|
15030
|
+
continue;
|
|
15043
15031
|
}
|
|
15032
|
+
if (writeFileNoFollow(hubFolder, abs, Buffer.from(await res.arrayBuffer()))) written++;
|
|
15033
|
+
} catch (err) {
|
|
15034
|
+
console.warn(` Warning: failed to download ${dl.relPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
15044
15035
|
}
|
|
15045
|
-
const yamlContent = yaml4.dump(buildEvalYamlObject(evalEntry, slug), YAML_DUMP_OPTIONS);
|
|
15046
|
-
fs11.writeFileSync(targetPath, yamlContent, "utf-8");
|
|
15047
|
-
writtenRelPaths.add(relPath);
|
|
15048
15036
|
}
|
|
15049
|
-
|
|
15050
|
-
|
|
15051
|
-
|
|
15052
|
-
|
|
15053
|
-
|
|
15054
|
-
|
|
15055
|
-
|
|
15056
|
-
|
|
15057
|
-
|
|
15058
|
-
|
|
15059
|
-
|
|
15037
|
+
const keep = new Set(downloads.map((d) => path13.resolve(hubFolder, d.relPath)));
|
|
15038
|
+
for (const name of fs11.readdirSync(attachDir)) {
|
|
15039
|
+
const abs = path13.join(attachDir, name);
|
|
15040
|
+
const st = fs11.lstatSync(abs);
|
|
15041
|
+
if ((st.isFile() || st.isSymbolicLink()) && !keep.has(abs)) fs11.rmSync(abs);
|
|
15042
|
+
}
|
|
15043
|
+
return written;
|
|
15044
|
+
}
|
|
15045
|
+
var ATTACHMENTS_DIR;
|
|
15046
|
+
var init_eval_attachments = __esm({
|
|
15047
|
+
"src/lib/eval-attachments.ts"() {
|
|
15048
|
+
"use strict";
|
|
15049
|
+
init_resource_files();
|
|
15050
|
+
init_fs_safety();
|
|
15051
|
+
init_parser();
|
|
15052
|
+
ATTACHMENTS_DIR = "attachments";
|
|
15053
|
+
}
|
|
15054
|
+
});
|
|
15055
|
+
|
|
15056
|
+
// src/lib/parser.ts
|
|
15057
|
+
import * as fs12 from "fs";
|
|
15058
|
+
import * as path14 from "path";
|
|
15059
|
+
import * as yaml4 from "js-yaml";
|
|
15060
|
+
function expected(message) {
|
|
15061
|
+
const err = new Error(message);
|
|
15062
|
+
err.isExpected = true;
|
|
15063
|
+
return err;
|
|
15064
|
+
}
|
|
15065
|
+
function parseHubFolder(hubFolder, opts) {
|
|
15066
|
+
const yamlPath = resolveHubYamlPath(hubFolder);
|
|
15067
|
+
if (!yamlPath) {
|
|
15068
|
+
const err = new Error(`hub.yaml not found in ${hubFolder}`);
|
|
15069
|
+
err.isExpected = true;
|
|
15070
|
+
throw err;
|
|
15071
|
+
}
|
|
15072
|
+
const yamlContent = fs12.readFileSync(yamlPath, "utf-8");
|
|
15073
|
+
let config;
|
|
15074
|
+
try {
|
|
15075
|
+
config = yaml4.load(yamlContent);
|
|
15076
|
+
} catch (err) {
|
|
15077
|
+
if (err instanceof yaml4.YAMLException) {
|
|
15078
|
+
const userError = new Error(`Invalid YAML in ${yamlPath}: ${err.message}`);
|
|
15079
|
+
userError.isExpected = true;
|
|
15080
|
+
throw userError;
|
|
15060
15081
|
}
|
|
15061
|
-
|
|
15062
|
-
|
|
15063
|
-
|
|
15064
|
-
|
|
15065
|
-
|
|
15066
|
-
|
|
15067
|
-
|
|
15068
|
-
|
|
15069
|
-
|
|
15070
|
-
|
|
15082
|
+
throw err;
|
|
15083
|
+
}
|
|
15084
|
+
if (!config || typeof config !== "object") {
|
|
15085
|
+
const invalidError = new Error(`Invalid YAML in ${yamlPath}`);
|
|
15086
|
+
invalidError.isExpected = true;
|
|
15087
|
+
throw invalidError;
|
|
15088
|
+
}
|
|
15089
|
+
if (!opts?.allowNewHub) {
|
|
15090
|
+
if (!config.hub_id) {
|
|
15091
|
+
const err = new Error(`Missing hub_id in ${yamlPath}`);
|
|
15092
|
+
err.isExpected = true;
|
|
15093
|
+
throw err;
|
|
15094
|
+
}
|
|
15095
|
+
if (!config.hub_environment) {
|
|
15096
|
+
const err = new Error(`Missing hub_environment in ${yamlPath}`);
|
|
15097
|
+
err.isExpected = true;
|
|
15098
|
+
throw err;
|
|
15099
|
+
}
|
|
15100
|
+
}
|
|
15101
|
+
const agentYamlFiles = scanAgentYamlFiles(hubFolder);
|
|
15102
|
+
const rawAgents = agentYamlFiles.length > 0 ? agentYamlFiles : config.agents;
|
|
15103
|
+
const agents = rawAgents?.map((agent) => {
|
|
15104
|
+
const resolved = { ...agent };
|
|
15105
|
+
if (typeof resolved.instructions === "string" && resolved.instructions.endsWith(".md")) {
|
|
15106
|
+
const instrValue = resolved.instructions;
|
|
15107
|
+
const instructionsPath = instrValue.startsWith("agents/") ? path14.join(hubFolder, instrValue) : path14.join(hubFolder, "agents", instrValue);
|
|
15108
|
+
if (fs12.existsSync(instructionsPath)) {
|
|
15109
|
+
resolved.instructions = fs12.readFileSync(instructionsPath, "utf-8");
|
|
15110
|
+
} else {
|
|
15111
|
+
const err = new Error(
|
|
15112
|
+
`Agent instructions file not found: ${instructionsPath} (referenced by agent "${agent.name}")`
|
|
15113
|
+
);
|
|
15114
|
+
err.isExpected = true;
|
|
15115
|
+
throw err;
|
|
15071
15116
|
}
|
|
15072
|
-
|
|
15073
|
-
|
|
15074
|
-
|
|
15117
|
+
} else if (resolved.instructions === void 0 && typeof agent.name === "string") {
|
|
15118
|
+
const conventionPath = path14.join(hubFolder, "agents", `${slugify(agent.name)}.md`);
|
|
15119
|
+
if (fs12.existsSync(conventionPath)) {
|
|
15120
|
+
resolved.instructions = fs12.readFileSync(conventionPath, "utf-8");
|
|
15075
15121
|
}
|
|
15076
15122
|
}
|
|
15123
|
+
return resolved;
|
|
15124
|
+
});
|
|
15125
|
+
const resources = parseResources(hubFolder, config.resources || []);
|
|
15126
|
+
const payload = {
|
|
15127
|
+
version: config.version || 1,
|
|
15128
|
+
hub_id: config.hub_id || "",
|
|
15129
|
+
hub_environment: config.hub_environment || "preview",
|
|
15130
|
+
hub: config.hub
|
|
15131
|
+
};
|
|
15132
|
+
if (config.preview_label) {
|
|
15133
|
+
payload.preview_label = config.preview_label;
|
|
15077
15134
|
}
|
|
15078
|
-
|
|
15079
|
-
|
|
15080
|
-
const out = {};
|
|
15081
|
-
if (journeyEntry.id) out.id = journeyEntry.id;
|
|
15082
|
-
if (journeyEntry.name !== slug) out.name = journeyEntry.name;
|
|
15083
|
-
out.agent = journeyEntry.agent;
|
|
15084
|
-
if (journeyEntry.agent_id) out.agent_id = journeyEntry.agent_id;
|
|
15085
|
-
if (journeyEntry.enabled === false) {
|
|
15086
|
-
out.enabled = false;
|
|
15135
|
+
if (agents && agents.length > 0) {
|
|
15136
|
+
payload.agents = agents;
|
|
15087
15137
|
}
|
|
15088
|
-
if (
|
|
15089
|
-
|
|
15138
|
+
if (resources.length > 0) {
|
|
15139
|
+
payload.resources = resources;
|
|
15090
15140
|
}
|
|
15091
|
-
if (
|
|
15092
|
-
|
|
15141
|
+
if (config.states && config.states.length > 0) {
|
|
15142
|
+
payload.states = config.states;
|
|
15093
15143
|
}
|
|
15094
|
-
if (
|
|
15095
|
-
|
|
15144
|
+
if (config.teams && config.teams.length > 0) {
|
|
15145
|
+
payload.teams = config.teams;
|
|
15096
15146
|
}
|
|
15097
|
-
if (
|
|
15098
|
-
|
|
15147
|
+
if (Array.isArray(config.connections)) {
|
|
15148
|
+
payload.connections = config.connections;
|
|
15099
15149
|
}
|
|
15100
|
-
if (
|
|
15101
|
-
|
|
15150
|
+
if (config.outbound_contacts && config.outbound_contacts.length > 0) {
|
|
15151
|
+
payload.outbound_contacts = config.outbound_contacts;
|
|
15102
15152
|
}
|
|
15103
|
-
if (
|
|
15104
|
-
|
|
15153
|
+
if (config.outbound_lists && config.outbound_lists.length > 0) {
|
|
15154
|
+
payload.outbound_lists = config.outbound_lists;
|
|
15105
15155
|
}
|
|
15106
|
-
|
|
15107
|
-
|
|
15108
|
-
|
|
15109
|
-
const
|
|
15110
|
-
|
|
15111
|
-
|
|
15112
|
-
|
|
15113
|
-
try {
|
|
15114
|
-
if (fs11.readdirSync(journeysDir).length === 0) fs11.rmdirSync(journeysDir);
|
|
15115
|
-
} catch {
|
|
15116
|
-
}
|
|
15117
|
-
}
|
|
15118
|
-
return;
|
|
15156
|
+
if (config.outbound_schedules && config.outbound_schedules.length > 0) {
|
|
15157
|
+
payload.outbound_schedules = config.outbound_schedules;
|
|
15158
|
+
}
|
|
15159
|
+
const bytesByHash = /* @__PURE__ */ new Map();
|
|
15160
|
+
const evals = scanEvalYamlFiles(hubFolder, bytesByHash);
|
|
15161
|
+
if (evals.length > 0) {
|
|
15162
|
+
payload.evals = evals;
|
|
15119
15163
|
}
|
|
15120
|
-
|
|
15121
|
-
|
|
15164
|
+
const journeys = scanJourneyYamlFiles(hubFolder, bytesByHash);
|
|
15165
|
+
if (journeys.length > 0) {
|
|
15166
|
+
payload.journeys = journeys;
|
|
15122
15167
|
}
|
|
15123
|
-
|
|
15124
|
-
|
|
15125
|
-
for (const journeyEntry of journeys) {
|
|
15126
|
-
const slug = ensureUniqueSlug(slugify(journeyEntry.name), usedSlugs);
|
|
15127
|
-
const fileName = `${slug}.yaml`;
|
|
15128
|
-
const yamlContent = yaml4.dump(buildJourneyYamlObject(journeyEntry, slug), YAML_DUMP_OPTIONS);
|
|
15129
|
-
fs11.writeFileSync(path13.join(journeysDir, fileName), yamlContent, "utf-8");
|
|
15130
|
-
writtenFiles.add(fileName);
|
|
15168
|
+
if (bytesByHash.size > 0) {
|
|
15169
|
+
payload.eval_attachment_files = [...bytesByHash.values()];
|
|
15131
15170
|
}
|
|
15132
|
-
|
|
15171
|
+
return payload;
|
|
15133
15172
|
}
|
|
15134
|
-
function
|
|
15135
|
-
const
|
|
15136
|
-
|
|
15137
|
-
|
|
15138
|
-
|
|
15173
|
+
function scanEvalYamlFiles(hubFolder, bytesByHash) {
|
|
15174
|
+
const evalsDir = path14.join(hubFolder, "evals");
|
|
15175
|
+
if (!fs12.existsSync(evalsDir)) return [];
|
|
15176
|
+
const evals = [];
|
|
15177
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15178
|
+
const topEntries = fs12.readdirSync(evalsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
15179
|
+
for (const entry of topEntries) {
|
|
15180
|
+
if (entry.isFile() && entry.name.endsWith(".yaml")) {
|
|
15181
|
+
collectEval(evals, seen, path14.join(evalsDir, entry.name), `evals/${entry.name}`, null, hubFolder, bytesByHash);
|
|
15182
|
+
} else if (entry.isDirectory()) {
|
|
15183
|
+
const setName = entry.name;
|
|
15184
|
+
const setDir = path14.join(evalsDir, setName);
|
|
15185
|
+
const setEntries = fs12.readdirSync(setDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
15186
|
+
for (const sub of setEntries) {
|
|
15187
|
+
if (sub.isDirectory()) {
|
|
15188
|
+
throw expected(
|
|
15189
|
+
`Nested scenario sets are not supported: ${path14.relative(hubFolder, path14.join(setDir, sub.name))}. Move scenarios up to evals/${setName}/.`
|
|
15190
|
+
);
|
|
15191
|
+
}
|
|
15192
|
+
if (sub.isFile() && sub.name.endsWith(".yaml")) {
|
|
15193
|
+
collectEval(evals, seen, path14.join(setDir, sub.name), `evals/${setName}/${sub.name}`, setName, hubFolder, bytesByHash);
|
|
15194
|
+
}
|
|
15195
|
+
}
|
|
15139
15196
|
}
|
|
15140
15197
|
}
|
|
15198
|
+
return evals;
|
|
15141
15199
|
}
|
|
15142
|
-
function
|
|
15143
|
-
const
|
|
15144
|
-
|
|
15145
|
-
|
|
15146
|
-
|
|
15147
|
-
|
|
15148
|
-
|
|
15149
|
-
|
|
15200
|
+
function collectEval(evals, seen, filePath, relPath, setName, hubFolder, bytesByHash) {
|
|
15201
|
+
const evalEntry = parseEvalYaml(filePath, relPath, setName, hubFolder, bytesByHash);
|
|
15202
|
+
if (!evalEntry) return;
|
|
15203
|
+
const key = `${evalEntry.name}|${setName ?? ""}`;
|
|
15204
|
+
if (seen.has(key)) {
|
|
15205
|
+
const where = setName ? `scenario set "${setName}"` : "root";
|
|
15206
|
+
throw expected(
|
|
15207
|
+
`Duplicate eval at ${where}: "${evalEntry.name}" (in ${path14.basename(filePath)}). Each (name, scenario set) pair must be unique.`
|
|
15208
|
+
);
|
|
15150
15209
|
}
|
|
15151
|
-
|
|
15152
|
-
|
|
15153
|
-
|
|
15154
|
-
|
|
15155
|
-
|
|
15156
|
-
|
|
15210
|
+
seen.add(key);
|
|
15211
|
+
evals.push(evalEntry);
|
|
15212
|
+
}
|
|
15213
|
+
function parseEvalYaml(filePath, relPath, setName, hubFolder, bytesByHash) {
|
|
15214
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
15215
|
+
let raw;
|
|
15216
|
+
try {
|
|
15217
|
+
raw = yaml4.load(content);
|
|
15218
|
+
} catch (err) {
|
|
15219
|
+
if (err instanceof yaml4.YAMLException) {
|
|
15220
|
+
throw expected(`Invalid YAML in ${relPath}: ${err.message}`);
|
|
15157
15221
|
}
|
|
15222
|
+
throw err;
|
|
15158
15223
|
}
|
|
15159
|
-
|
|
15160
|
-
|
|
15161
|
-
|
|
15162
|
-
"src/lib/yaml-writer.ts"() {
|
|
15163
|
-
"use strict";
|
|
15164
|
-
init_dist();
|
|
15165
|
-
init_utils();
|
|
15166
|
-
init_resource_files();
|
|
15167
|
-
init_workspace_files();
|
|
15168
|
-
YAML_DUMP_OPTIONS = {
|
|
15169
|
-
lineWidth: -1,
|
|
15170
|
-
// don't wrap lines
|
|
15171
|
-
noRefs: true,
|
|
15172
|
-
// don't use YAML anchors
|
|
15173
|
-
quotingType: '"',
|
|
15174
|
-
forceQuotes: false,
|
|
15175
|
-
sortKeys: false
|
|
15176
|
-
};
|
|
15224
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
15225
|
+
console.warn(` Warning: skipping ${relPath} (not a YAML object)`);
|
|
15226
|
+
return null;
|
|
15177
15227
|
}
|
|
15178
|
-
|
|
15179
|
-
|
|
15180
|
-
|
|
15181
|
-
|
|
15182
|
-
|
|
15183
|
-
function resolveTurnAttachments(hubFolder, turn, label, bytesByHash) {
|
|
15184
|
-
const raw = turn.attachments;
|
|
15185
|
-
if (raw === void 0 || raw === null) return void 0;
|
|
15186
|
-
if (!Array.isArray(raw) || raw.length === 0) {
|
|
15187
|
-
if (Array.isArray(raw)) return void 0;
|
|
15188
|
-
throw expected(`${label}: "attachments" must be a list of file paths.`);
|
|
15228
|
+
const data = raw;
|
|
15229
|
+
const fileSlug = path14.basename(filePath, ".yaml");
|
|
15230
|
+
const evalName = typeof data.name === "string" && data.name.trim().length > 0 ? data.name : fileSlug;
|
|
15231
|
+
if (typeof data.agent !== "string" || data.agent.trim().length === 0) {
|
|
15232
|
+
throw expected(`Eval "${evalName}" in ${relPath}: missing required field "agent" (string).`);
|
|
15189
15233
|
}
|
|
15190
|
-
if (
|
|
15191
|
-
throw expected(
|
|
15234
|
+
if (!data.input || typeof data.input !== "object" || Array.isArray(data.input)) {
|
|
15235
|
+
throw expected(`Eval "${evalName}" in ${relPath}: missing required field "input" (object).`);
|
|
15192
15236
|
}
|
|
15193
|
-
|
|
15194
|
-
|
|
15195
|
-
|
|
15196
|
-
|
|
15197
|
-
|
|
15198
|
-
|
|
15199
|
-
|
|
15200
|
-
|
|
15201
|
-
|
|
15202
|
-
|
|
15203
|
-
|
|
15204
|
-
|
|
15205
|
-
|
|
15206
|
-
|
|
15207
|
-
|
|
15208
|
-
|
|
15209
|
-
|
|
15210
|
-
|
|
15211
|
-
|
|
15212
|
-
|
|
15213
|
-
|
|
15214
|
-
|
|
15237
|
+
if (!data.expected || typeof data.expected !== "object" || Array.isArray(data.expected)) {
|
|
15238
|
+
throw expected(`Eval "${evalName}" in ${relPath}: missing required field "expected" (object).`);
|
|
15239
|
+
}
|
|
15240
|
+
const input = { ...data.input };
|
|
15241
|
+
applyTurnAttachments(hubFolder, input, `Eval "${evalName}" input`, bytesByHash);
|
|
15242
|
+
const evalEntry = {
|
|
15243
|
+
name: evalName,
|
|
15244
|
+
agent: data.agent,
|
|
15245
|
+
path: setName,
|
|
15246
|
+
input,
|
|
15247
|
+
expected: data.expected
|
|
15248
|
+
};
|
|
15249
|
+
if (typeof data.id === "string") evalEntry.id = data.id;
|
|
15250
|
+
if (typeof data.agent_id === "string") evalEntry.agent_id = data.agent_id;
|
|
15251
|
+
if (typeof data.runs === "number") evalEntry.runs = data.runs;
|
|
15252
|
+
if (data.enabled === false) evalEntry.enabled = false;
|
|
15253
|
+
if (Array.isArray(data.history)) {
|
|
15254
|
+
evalEntry.history = data.history.map((turn, i) => {
|
|
15255
|
+
const t = { ...turn };
|
|
15256
|
+
applyTurnAttachments(hubFolder, t, `Eval "${evalName}" history[${i}]`, bytesByHash);
|
|
15257
|
+
return t;
|
|
15258
|
+
});
|
|
15259
|
+
}
|
|
15260
|
+
if (typeof data.evaluator_instructions === "string") {
|
|
15261
|
+
evalEntry.evaluator_instructions = data.evaluator_instructions;
|
|
15262
|
+
}
|
|
15263
|
+
const variables = parseVariablesMap(data.variables, `Eval "${evalName}" in ${relPath}`);
|
|
15264
|
+
if (variables) evalEntry.variables = variables;
|
|
15265
|
+
const fixture = parseFixtureField(data.fixture, `Eval "${evalName}" in ${relPath}`, "fixture");
|
|
15266
|
+
if (fixture) evalEntry.fixture = fixture;
|
|
15267
|
+
const targetBase = parseFixtureField(data.target_base, `Eval "${evalName}" in ${relPath}`, "target_base");
|
|
15268
|
+
if (targetBase) evalEntry.target_base = targetBase;
|
|
15269
|
+
return evalEntry;
|
|
15270
|
+
}
|
|
15271
|
+
function parseVariablesMap(raw, label) {
|
|
15272
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
15273
|
+
const vars = raw;
|
|
15274
|
+
if (Object.keys(vars).length === 0) return void 0;
|
|
15275
|
+
if (!Object.values(vars).every((v) => Array.isArray(v))) {
|
|
15276
|
+
throw expected(`${label}: "variables" must be a map of arrays (variables.<name>: [ ... ]).`);
|
|
15277
|
+
}
|
|
15278
|
+
return vars;
|
|
15279
|
+
}
|
|
15280
|
+
function parseFixtureField(raw, label, key) {
|
|
15281
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
15282
|
+
if (typeof raw !== "string") {
|
|
15283
|
+
throw expected(`${label}: "${key}" must be a string (the Rekor fixture name).`);
|
|
15284
|
+
}
|
|
15285
|
+
const trimmed = raw.trim();
|
|
15286
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
15287
|
+
}
|
|
15288
|
+
function scanJourneyYamlFiles(hubFolder, bytesByHash) {
|
|
15289
|
+
const journeysDir = path14.join(hubFolder, "journeys");
|
|
15290
|
+
if (!fs12.existsSync(journeysDir)) return [];
|
|
15291
|
+
const journeys = [];
|
|
15292
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15293
|
+
const entries = fs12.readdirSync(journeysDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
15294
|
+
for (const entry of entries) {
|
|
15295
|
+
if (entry.isDirectory()) {
|
|
15215
15296
|
throw expected(
|
|
15216
|
-
|
|
15297
|
+
`Subfolders are not supported under journeys/: ${path14.relative(hubFolder, path14.join(journeysDir, entry.name))}. A journey owns its own managed scenario set \u2014 put each journey in a single journeys/<slug>.yaml file.`
|
|
15217
15298
|
);
|
|
15218
15299
|
}
|
|
15219
|
-
|
|
15220
|
-
const
|
|
15221
|
-
|
|
15222
|
-
|
|
15223
|
-
|
|
15224
|
-
|
|
15225
|
-
|
|
15226
|
-
hash,
|
|
15227
|
-
content_base64: data.toString("base64"),
|
|
15228
|
-
file_name: fileName,
|
|
15229
|
-
mime_type: mimeType,
|
|
15230
|
-
file_size: stat.size
|
|
15231
|
-
});
|
|
15300
|
+
if (!entry.isFile() || !entry.name.endsWith(".yaml")) continue;
|
|
15301
|
+
const journeyEntry = parseJourneyYaml(path14.join(journeysDir, entry.name), `journeys/${entry.name}`, hubFolder, bytesByHash);
|
|
15302
|
+
if (!journeyEntry) continue;
|
|
15303
|
+
if (seen.has(journeyEntry.name)) {
|
|
15304
|
+
throw expected(
|
|
15305
|
+
`Duplicate journey: "${journeyEntry.name}" (in ${entry.name}). Each journey name must be unique.`
|
|
15306
|
+
);
|
|
15232
15307
|
}
|
|
15308
|
+
seen.add(journeyEntry.name);
|
|
15309
|
+
journeys.push(journeyEntry);
|
|
15233
15310
|
}
|
|
15234
|
-
return
|
|
15235
|
-
}
|
|
15236
|
-
function applyTurnAttachments(hubFolder, turn, label, bytesByHash) {
|
|
15237
|
-
const refs = resolveTurnAttachments(hubFolder, turn, label, bytesByHash);
|
|
15238
|
-
if (refs) turn.attachments = refs;
|
|
15239
|
-
else delete turn.attachments;
|
|
15311
|
+
return journeys;
|
|
15240
15312
|
}
|
|
15241
|
-
function
|
|
15242
|
-
|
|
15243
|
-
|
|
15244
|
-
|
|
15313
|
+
function parseJourneyYaml(filePath, relPath, hubFolder, bytesByHash) {
|
|
15314
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
15315
|
+
let raw;
|
|
15316
|
+
try {
|
|
15317
|
+
raw = yaml4.load(content);
|
|
15318
|
+
} catch (err) {
|
|
15319
|
+
if (err instanceof yaml4.YAMLException) {
|
|
15320
|
+
throw expected(`Invalid YAML in ${relPath}: ${err.message}`);
|
|
15321
|
+
}
|
|
15322
|
+
throw err;
|
|
15245
15323
|
}
|
|
15246
|
-
|
|
15247
|
-
|
|
15324
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
15325
|
+
console.warn(` Warning: skipping ${relPath} (not a YAML object)`);
|
|
15326
|
+
return null;
|
|
15248
15327
|
}
|
|
15249
|
-
|
|
15250
|
-
|
|
15251
|
-
const
|
|
15252
|
-
|
|
15253
|
-
|
|
15254
|
-
|
|
15255
|
-
|
|
15256
|
-
|
|
15257
|
-
|
|
15258
|
-
|
|
15259
|
-
|
|
15260
|
-
|
|
15261
|
-
|
|
15262
|
-
|
|
15263
|
-
|
|
15264
|
-
set = /* @__PURE__ */ new Set();
|
|
15265
|
-
hashesByName.set(name, set);
|
|
15266
|
-
}
|
|
15267
|
-
set.add(ref.hash);
|
|
15268
|
-
}
|
|
15269
|
-
};
|
|
15270
|
-
forEachAttachableTurn(payload, collect);
|
|
15271
|
-
const pathByKey = /* @__PURE__ */ new Map();
|
|
15272
|
-
const usedLower = /* @__PURE__ */ new Set();
|
|
15273
|
-
const localPathFor = (ref) => {
|
|
15274
|
-
const name = safeName(ref);
|
|
15275
|
-
const key = `${ref.hash} ${name}`;
|
|
15276
|
-
const existing = pathByKey.get(key);
|
|
15277
|
-
if (existing) return existing;
|
|
15278
|
-
const ext = path14.extname(name);
|
|
15279
|
-
const stem = path14.basename(name, ext);
|
|
15280
|
-
const collides = (hashesByName.get(name)?.size ?? 0) > 1;
|
|
15281
|
-
let rel = collides ? `${ATTACHMENTS_DIR}/${stem}-${ref.hash.slice(0, 8)}${ext}` : `${ATTACHMENTS_DIR}/${name}`;
|
|
15282
|
-
if (usedLower.has(rel.toLowerCase())) {
|
|
15283
|
-
let n = 12;
|
|
15284
|
-
while (usedLower.has(rel.toLowerCase()) && n <= ref.hash.length) {
|
|
15285
|
-
rel = `${ATTACHMENTS_DIR}/${stem}-${ref.hash.slice(0, n)}${ext}`;
|
|
15286
|
-
n += 4;
|
|
15287
|
-
}
|
|
15288
|
-
for (let extra = 2; usedLower.has(rel.toLowerCase()); extra++) {
|
|
15289
|
-
rel = `${ATTACHMENTS_DIR}/${stem}-${ref.hash}-${extra}${ext}`;
|
|
15290
|
-
}
|
|
15291
|
-
}
|
|
15292
|
-
usedLower.add(rel.toLowerCase());
|
|
15293
|
-
pathByKey.set(key, rel);
|
|
15294
|
-
return rel;
|
|
15328
|
+
const data = raw;
|
|
15329
|
+
const fileSlug = path14.basename(filePath, ".yaml");
|
|
15330
|
+
const journeyName = typeof data.name === "string" && data.name.trim().length > 0 ? data.name : fileSlug;
|
|
15331
|
+
if (typeof data.agent !== "string" || data.agent.trim().length === 0) {
|
|
15332
|
+
throw expected(`Journey "${journeyName}" in ${relPath}: missing required field "agent" (string).`);
|
|
15333
|
+
}
|
|
15334
|
+
if (data.transcript !== void 0 && !Array.isArray(data.transcript)) {
|
|
15335
|
+
throw expected(`Journey "${journeyName}" in ${relPath}: "transcript" must be an array.`);
|
|
15336
|
+
}
|
|
15337
|
+
if (data.step_config !== void 0 && (typeof data.step_config !== "object" || Array.isArray(data.step_config) || data.step_config === null)) {
|
|
15338
|
+
throw expected(`Journey "${journeyName}" in ${relPath}: "step_config" must be an object keyed by step_id.`);
|
|
15339
|
+
}
|
|
15340
|
+
const journeyEntry = {
|
|
15341
|
+
name: journeyName,
|
|
15342
|
+
agent: data.agent
|
|
15295
15343
|
};
|
|
15296
|
-
|
|
15297
|
-
|
|
15298
|
-
|
|
15299
|
-
|
|
15300
|
-
|
|
15301
|
-
|
|
15302
|
-
|
|
15303
|
-
|
|
15344
|
+
if (typeof data.id === "string") journeyEntry.id = data.id;
|
|
15345
|
+
if (typeof data.agent_id === "string") journeyEntry.agent_id = data.agent_id;
|
|
15346
|
+
if (data.enabled === false) journeyEntry.enabled = false;
|
|
15347
|
+
if (typeof data.evaluator_instructions === "string") {
|
|
15348
|
+
journeyEntry.evaluator_instructions = data.evaluator_instructions;
|
|
15349
|
+
}
|
|
15350
|
+
if (Array.isArray(data.transcript)) {
|
|
15351
|
+
journeyEntry.transcript = data.transcript.map((turn, i) => {
|
|
15352
|
+
const t = { ...turn };
|
|
15353
|
+
applyTurnAttachments(hubFolder, t, `Journey "${journeyName}" transcript[${i}]`, bytesByHash);
|
|
15354
|
+
return t;
|
|
15304
15355
|
});
|
|
15305
|
-
});
|
|
15306
|
-
delete payload.eval_attachment_files;
|
|
15307
|
-
return [...downloads.values()];
|
|
15308
|
-
}
|
|
15309
|
-
async function downloadEvalAttachments(hubFolder, downloads) {
|
|
15310
|
-
const attachDir = path14.resolve(hubFolder, ATTACHMENTS_DIR);
|
|
15311
|
-
let written = 0;
|
|
15312
|
-
if (!ensureRealSubdirNoSymlink(hubFolder, attachDir, downloads.length > 0)) {
|
|
15313
|
-
console.warn(` Warning: ${ATTACHMENTS_DIR}/ is not reachable inside the hub folder without a symlink; skipping attachment sync.`);
|
|
15314
|
-
return written;
|
|
15315
15356
|
}
|
|
15316
|
-
if (!
|
|
15317
|
-
|
|
15318
|
-
|
|
15319
|
-
|
|
15320
|
-
|
|
15357
|
+
if (data.step_config && typeof data.step_config === "object" && !Array.isArray(data.step_config)) {
|
|
15358
|
+
journeyEntry.step_config = data.step_config;
|
|
15359
|
+
}
|
|
15360
|
+
const variables = parseVariablesMap(data.variables, `Journey "${journeyName}" in ${relPath}`);
|
|
15361
|
+
if (variables) journeyEntry.variables = variables;
|
|
15362
|
+
const fixture = parseFixtureField(data.fixture, `Journey "${journeyName}" in ${relPath}`, "fixture");
|
|
15363
|
+
if (fixture) journeyEntry.fixture = fixture;
|
|
15364
|
+
const targetBase = parseFixtureField(data.target_base, `Journey "${journeyName}" in ${relPath}`, "target_base");
|
|
15365
|
+
if (targetBase) journeyEntry.target_base = targetBase;
|
|
15366
|
+
return journeyEntry;
|
|
15367
|
+
}
|
|
15368
|
+
function scanAgentYamlFiles(hubFolder) {
|
|
15369
|
+
const agentsDir = path14.join(hubFolder, "agents");
|
|
15370
|
+
if (!fs12.existsSync(agentsDir)) return [];
|
|
15371
|
+
const yamlFiles = fs12.readdirSync(agentsDir).filter((f) => f.endsWith(".yaml")).sort();
|
|
15372
|
+
if (yamlFiles.length === 0) return [];
|
|
15373
|
+
const agents = [];
|
|
15374
|
+
for (const file of yamlFiles) {
|
|
15375
|
+
const filePath = path14.join(agentsDir, file);
|
|
15376
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
15377
|
+
let agent;
|
|
15321
15378
|
try {
|
|
15322
|
-
|
|
15323
|
-
} catch {
|
|
15379
|
+
agent = yaml4.load(content);
|
|
15380
|
+
} catch (err) {
|
|
15381
|
+
if (err instanceof yaml4.YAMLException) {
|
|
15382
|
+
const userError = new Error(`Invalid YAML in agents/${file}: ${err.message}`);
|
|
15383
|
+
userError.isExpected = true;
|
|
15384
|
+
throw userError;
|
|
15385
|
+
}
|
|
15386
|
+
throw err;
|
|
15324
15387
|
}
|
|
15325
|
-
if (
|
|
15388
|
+
if (!agent || typeof agent !== "object") continue;
|
|
15389
|
+
if (!agent.name) {
|
|
15390
|
+
console.warn(` Warning: skipping ${file} (missing 'name' field)`);
|
|
15326
15391
|
continue;
|
|
15327
15392
|
}
|
|
15328
|
-
|
|
15329
|
-
|
|
15330
|
-
|
|
15331
|
-
|
|
15332
|
-
|
|
15393
|
+
agents.push(agent);
|
|
15394
|
+
}
|
|
15395
|
+
return agents;
|
|
15396
|
+
}
|
|
15397
|
+
function parseResources(hubFolder, configResources) {
|
|
15398
|
+
const resourcesDir = path14.join(hubFolder, "resources");
|
|
15399
|
+
const results = [];
|
|
15400
|
+
for (const res of configResources) {
|
|
15401
|
+
const resource = {
|
|
15402
|
+
name: res.name
|
|
15403
|
+
};
|
|
15404
|
+
if (res.id) resource.id = res.id;
|
|
15405
|
+
if (res.type === "knowledge" || res.type === "skill") resource.type = res.type;
|
|
15406
|
+
else if (res.type !== void 0) throw new Error(`Invalid resource type for "${resource.name}": expected knowledge or skill`);
|
|
15407
|
+
if (res.description) resource.description = res.description;
|
|
15408
|
+
if (res.enabled === false) resource.enabled = false;
|
|
15409
|
+
if (res.user_browsable) resource.user_browsable = res.user_browsable;
|
|
15410
|
+
if (res.skill_name) resource.skill_name = res.skill_name;
|
|
15411
|
+
const resSlug = slugify(resource.name);
|
|
15412
|
+
const resDir = path14.join(resourcesDir, resSlug);
|
|
15413
|
+
if (fs12.existsSync(resDir)) {
|
|
15414
|
+
const files = scanResourceFiles(resDir, "");
|
|
15415
|
+
if (files.length > 0) {
|
|
15416
|
+
resource.files = files;
|
|
15333
15417
|
}
|
|
15334
|
-
if (writeFileNoFollow(hubFolder, abs, Buffer.from(await res.arrayBuffer()))) written++;
|
|
15335
|
-
} catch (err) {
|
|
15336
|
-
console.warn(` Warning: failed to download ${dl.relPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
15337
15418
|
}
|
|
15419
|
+
results.push(resource);
|
|
15338
15420
|
}
|
|
15339
|
-
|
|
15340
|
-
for (const name of fs12.readdirSync(attachDir)) {
|
|
15341
|
-
const abs = path14.join(attachDir, name);
|
|
15342
|
-
const st = fs12.lstatSync(abs);
|
|
15343
|
-
if ((st.isFile() || st.isSymbolicLink()) && !keep.has(abs)) fs12.rmSync(abs);
|
|
15344
|
-
}
|
|
15345
|
-
return written;
|
|
15421
|
+
return results;
|
|
15346
15422
|
}
|
|
15347
|
-
var
|
|
15348
|
-
|
|
15349
|
-
"src/lib/eval-attachments.ts"() {
|
|
15423
|
+
var init_parser = __esm({
|
|
15424
|
+
"src/lib/parser.ts"() {
|
|
15350
15425
|
"use strict";
|
|
15426
|
+
init_utils();
|
|
15351
15427
|
init_resource_files();
|
|
15352
|
-
|
|
15353
|
-
init_parser();
|
|
15354
|
-
ATTACHMENTS_DIR = "attachments";
|
|
15428
|
+
init_eval_attachments();
|
|
15355
15429
|
}
|
|
15356
15430
|
});
|
|
15357
15431
|
|
|
15358
|
-
// src/lib/
|
|
15359
|
-
|
|
15360
|
-
|
|
15361
|
-
|
|
15362
|
-
|
|
15363
|
-
|
|
15364
|
-
|
|
15365
|
-
|
|
15366
|
-
}
|
|
15367
|
-
|
|
15368
|
-
|
|
15369
|
-
|
|
15370
|
-
|
|
15371
|
-
|
|
15372
|
-
|
|
15373
|
-
|
|
15374
|
-
|
|
15375
|
-
let config;
|
|
15376
|
-
try {
|
|
15377
|
-
config = yaml5.load(yamlContent);
|
|
15378
|
-
} catch (err) {
|
|
15379
|
-
if (err instanceof yaml5.YAMLException) {
|
|
15380
|
-
const userError = new Error(`Invalid YAML in ${yamlPath}: ${err.message}`);
|
|
15381
|
-
userError.isExpected = true;
|
|
15382
|
-
throw userError;
|
|
15432
|
+
// src/lib/diff-display.ts
|
|
15433
|
+
function printDiff(summary) {
|
|
15434
|
+
const lines = summary.split("\n");
|
|
15435
|
+
for (const line of lines) {
|
|
15436
|
+
if (line.startsWith("**") && line.endsWith("**")) {
|
|
15437
|
+
console.log(`
|
|
15438
|
+
\x1B[1m${line.replace(/\*\*/g, "")}\x1B[0m`);
|
|
15439
|
+
} else if (line.trimStart().startsWith("+ ")) {
|
|
15440
|
+
console.log(`\x1B[32m${line}\x1B[0m`);
|
|
15441
|
+
} else if (line.trimStart().startsWith("- ")) {
|
|
15442
|
+
console.log(`\x1B[31m${line}\x1B[0m`);
|
|
15443
|
+
} else if (line.trimStart().startsWith("~ ")) {
|
|
15444
|
+
console.log(`\x1B[33m${line}\x1B[0m`);
|
|
15445
|
+
} else if (line.includes("(no changes)")) {
|
|
15446
|
+
console.log(`\x1B[2m${line}\x1B[0m`);
|
|
15447
|
+
} else {
|
|
15448
|
+
console.log(line);
|
|
15383
15449
|
}
|
|
15384
|
-
throw err;
|
|
15385
|
-
}
|
|
15386
|
-
if (!config || typeof config !== "object") {
|
|
15387
|
-
const invalidError = new Error(`Invalid YAML in ${yamlPath}`);
|
|
15388
|
-
invalidError.isExpected = true;
|
|
15389
|
-
throw invalidError;
|
|
15390
15450
|
}
|
|
15391
|
-
|
|
15392
|
-
|
|
15393
|
-
|
|
15394
|
-
|
|
15395
|
-
|
|
15396
|
-
}
|
|
15397
|
-
if (!config.hub_environment) {
|
|
15398
|
-
const err = new Error(`Missing hub_environment in ${yamlPath}`);
|
|
15399
|
-
err.isExpected = true;
|
|
15400
|
-
throw err;
|
|
15401
|
-
}
|
|
15451
|
+
console.log();
|
|
15452
|
+
}
|
|
15453
|
+
var init_diff_display = __esm({
|
|
15454
|
+
"src/lib/diff-display.ts"() {
|
|
15455
|
+
"use strict";
|
|
15402
15456
|
}
|
|
15403
|
-
|
|
15404
|
-
|
|
15405
|
-
|
|
15406
|
-
|
|
15407
|
-
|
|
15408
|
-
|
|
15409
|
-
|
|
15410
|
-
|
|
15411
|
-
|
|
15412
|
-
|
|
15413
|
-
|
|
15414
|
-
|
|
15415
|
-
|
|
15416
|
-
|
|
15417
|
-
throw err;
|
|
15418
|
-
}
|
|
15419
|
-
} else if (resolved.instructions === void 0 && typeof agent.name === "string") {
|
|
15420
|
-
const conventionPath = path15.join(hubFolder, "agents", `${slugify(agent.name)}.md`);
|
|
15421
|
-
if (fs13.existsSync(conventionPath)) {
|
|
15422
|
-
resolved.instructions = fs13.readFileSync(conventionPath, "utf-8");
|
|
15423
|
-
}
|
|
15424
|
-
}
|
|
15425
|
-
return resolved;
|
|
15426
|
-
});
|
|
15427
|
-
const resources = parseResources(hubFolder, config.resources || []);
|
|
15428
|
-
const payload = {
|
|
15429
|
-
version: config.version || 1,
|
|
15430
|
-
hub_id: config.hub_id || "",
|
|
15431
|
-
hub_environment: config.hub_environment || "preview",
|
|
15432
|
-
hub: config.hub
|
|
15433
|
-
};
|
|
15434
|
-
if (config.preview_label) {
|
|
15435
|
-
payload.preview_label = config.preview_label;
|
|
15457
|
+
});
|
|
15458
|
+
|
|
15459
|
+
// src/lib/yaml-writer.ts
|
|
15460
|
+
import * as fs13 from "fs";
|
|
15461
|
+
import * as path15 from "path";
|
|
15462
|
+
import * as yaml5 from "js-yaml";
|
|
15463
|
+
function buildEvalYamlObject(evalEntry, slug) {
|
|
15464
|
+
const out = {};
|
|
15465
|
+
if (evalEntry.id) out.id = evalEntry.id;
|
|
15466
|
+
if (evalEntry.name !== slug) out.name = evalEntry.name;
|
|
15467
|
+
out.agent = evalEntry.agent;
|
|
15468
|
+
if (evalEntry.agent_id) out.agent_id = evalEntry.agent_id;
|
|
15469
|
+
if (typeof evalEntry.runs === "number" && evalEntry.runs !== 1) {
|
|
15470
|
+
out.runs = evalEntry.runs;
|
|
15436
15471
|
}
|
|
15437
|
-
if (
|
|
15438
|
-
|
|
15472
|
+
if (evalEntry.enabled === false) {
|
|
15473
|
+
out.enabled = false;
|
|
15439
15474
|
}
|
|
15440
|
-
if (
|
|
15441
|
-
|
|
15475
|
+
if (evalEntry.history && evalEntry.history.length > 0) {
|
|
15476
|
+
for (const turn of evalEntry.history) assertAttachmentsAreLocalPaths(turn, `eval "${evalEntry.name}" history`);
|
|
15477
|
+
out.history = evalEntry.history;
|
|
15442
15478
|
}
|
|
15443
|
-
if (
|
|
15444
|
-
|
|
15479
|
+
if (typeof evalEntry.fixture === "string" && evalEntry.fixture.trim()) {
|
|
15480
|
+
out.fixture = evalEntry.fixture.trim();
|
|
15445
15481
|
}
|
|
15446
|
-
if (
|
|
15447
|
-
|
|
15482
|
+
if (typeof evalEntry.target_base === "string" && evalEntry.target_base.trim()) {
|
|
15483
|
+
out.target_base = evalEntry.target_base.trim();
|
|
15448
15484
|
}
|
|
15449
|
-
if (
|
|
15450
|
-
|
|
15485
|
+
if (evalEntry.variables && Object.keys(evalEntry.variables).length > 0) {
|
|
15486
|
+
out.variables = evalEntry.variables;
|
|
15451
15487
|
}
|
|
15452
|
-
|
|
15453
|
-
|
|
15488
|
+
assertAttachmentsAreLocalPaths(evalEntry.input, `eval "${evalEntry.name}" input`);
|
|
15489
|
+
out.input = evalEntry.input;
|
|
15490
|
+
out.expected = evalEntry.expected;
|
|
15491
|
+
if (evalEntry.evaluator_instructions) {
|
|
15492
|
+
out.evaluator_instructions = evalEntry.evaluator_instructions;
|
|
15454
15493
|
}
|
|
15455
|
-
|
|
15456
|
-
|
|
15494
|
+
return out;
|
|
15495
|
+
}
|
|
15496
|
+
function writeHubFolder(hubFolder, payload, options = {}) {
|
|
15497
|
+
const agentsDir = path15.join(hubFolder, "agents");
|
|
15498
|
+
if (!fs13.existsSync(hubFolder)) {
|
|
15499
|
+
fs13.mkdirSync(hubFolder, { recursive: true });
|
|
15457
15500
|
}
|
|
15458
|
-
if (
|
|
15459
|
-
|
|
15501
|
+
if (!fs13.existsSync(agentsDir)) {
|
|
15502
|
+
fs13.mkdirSync(agentsDir, { recursive: true });
|
|
15460
15503
|
}
|
|
15461
|
-
const
|
|
15462
|
-
const
|
|
15463
|
-
|
|
15464
|
-
|
|
15504
|
+
const yamlPayload = buildYamlPayload(payload);
|
|
15505
|
+
const agentFiles = extractAgentFiles(payload);
|
|
15506
|
+
const yamlContent = yaml5.dump(yamlPayload, YAML_DUMP_OPTIONS);
|
|
15507
|
+
fs13.writeFileSync(path15.join(hubFolder, "hub.yaml"), yamlContent, "utf-8");
|
|
15508
|
+
if (options.seedAgentContext ?? true) {
|
|
15509
|
+
writeIfAbsent(path15.join(hubFolder, "AGENTS.md"), perHubAgentsMd(path15.basename(hubFolder)));
|
|
15510
|
+
writeIfAbsent(path15.join(hubFolder, "CLAUDE.md"), CLAUDE_MD_SHIM);
|
|
15465
15511
|
}
|
|
15466
|
-
const
|
|
15467
|
-
if (
|
|
15468
|
-
|
|
15512
|
+
const oldYamlPath = path15.join(hubFolder, "wayai.yaml");
|
|
15513
|
+
if (fs13.existsSync(oldYamlPath)) {
|
|
15514
|
+
fs13.unlinkSync(oldYamlPath);
|
|
15469
15515
|
}
|
|
15470
|
-
|
|
15471
|
-
|
|
15516
|
+
const yamlSlugs = writeAgentYamlFiles(agentsDir, payload.agents || []);
|
|
15517
|
+
const mdSlugs = /* @__PURE__ */ new Set();
|
|
15518
|
+
for (const { slug, content } of agentFiles) {
|
|
15519
|
+
mdSlugs.add(slug);
|
|
15520
|
+
fs13.writeFileSync(path15.join(agentsDir, `${slug}.md`), content, "utf-8");
|
|
15472
15521
|
}
|
|
15473
|
-
|
|
15474
|
-
|
|
15475
|
-
|
|
15476
|
-
|
|
15477
|
-
|
|
15478
|
-
|
|
15479
|
-
|
|
15480
|
-
|
|
15481
|
-
|
|
15482
|
-
|
|
15483
|
-
|
|
15484
|
-
} else if (entry.isDirectory()) {
|
|
15485
|
-
const setName = entry.name;
|
|
15486
|
-
const setDir = path15.join(evalsDir, setName);
|
|
15487
|
-
const setEntries = fs13.readdirSync(setDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
15488
|
-
for (const sub of setEntries) {
|
|
15489
|
-
if (sub.isDirectory()) {
|
|
15490
|
-
throw expected(
|
|
15491
|
-
`Nested scenario sets are not supported: ${path15.relative(hubFolder, path15.join(setDir, sub.name))}. Move scenarios up to evals/${setName}/.`
|
|
15492
|
-
);
|
|
15493
|
-
}
|
|
15494
|
-
if (sub.isFile() && sub.name.endsWith(".yaml")) {
|
|
15495
|
-
collectEval(evals, seen, path15.join(setDir, sub.name), `evals/${setName}/${sub.name}`, setName, hubFolder, bytesByHash);
|
|
15496
|
-
}
|
|
15522
|
+
const existingFiles = fs13.readdirSync(agentsDir);
|
|
15523
|
+
for (const file of existingFiles) {
|
|
15524
|
+
if (file.endsWith(".yaml")) {
|
|
15525
|
+
const slug = file.slice(0, -5);
|
|
15526
|
+
if (!yamlSlugs.has(slug)) {
|
|
15527
|
+
fs13.unlinkSync(path15.join(agentsDir, file));
|
|
15528
|
+
}
|
|
15529
|
+
} else if (file.endsWith(".md")) {
|
|
15530
|
+
const slug = file.slice(0, -3);
|
|
15531
|
+
if (!mdSlugs.has(slug)) {
|
|
15532
|
+
fs13.unlinkSync(path15.join(agentsDir, file));
|
|
15497
15533
|
}
|
|
15498
15534
|
}
|
|
15499
15535
|
}
|
|
15500
|
-
|
|
15536
|
+
writeResourceFiles(hubFolder, payload.resources || []);
|
|
15537
|
+
writeEvalYamlFiles(hubFolder, payload.evals || []);
|
|
15538
|
+
writeJourneyYamlFiles(hubFolder, payload.journeys || []);
|
|
15501
15539
|
}
|
|
15502
|
-
function
|
|
15503
|
-
const
|
|
15504
|
-
if (!
|
|
15505
|
-
const
|
|
15506
|
-
if (
|
|
15507
|
-
|
|
15508
|
-
|
|
15509
|
-
`Duplicate eval at ${where}: "${evalEntry.name}" (in ${path15.basename(filePath)}). Each (name, scenario set) pair must be unique.`
|
|
15510
|
-
);
|
|
15511
|
-
}
|
|
15512
|
-
seen.add(key);
|
|
15513
|
-
evals.push(evalEntry);
|
|
15540
|
+
function setPreviewLabelInHubYaml(hubFolder, label) {
|
|
15541
|
+
const yamlPath = resolveHubYamlPath(hubFolder);
|
|
15542
|
+
if (!yamlPath) return;
|
|
15543
|
+
const obj = yaml5.load(fs13.readFileSync(yamlPath, "utf-8")) ?? {};
|
|
15544
|
+
if (label) obj.preview_label = label;
|
|
15545
|
+
else delete obj.preview_label;
|
|
15546
|
+
fs13.writeFileSync(yamlPath, yaml5.dump(obj, YAML_DUMP_OPTIONS), "utf-8");
|
|
15514
15547
|
}
|
|
15515
|
-
function
|
|
15516
|
-
const
|
|
15517
|
-
|
|
15518
|
-
|
|
15519
|
-
|
|
15520
|
-
}
|
|
15521
|
-
|
|
15522
|
-
|
|
15523
|
-
}
|
|
15524
|
-
throw err;
|
|
15548
|
+
function buildYamlPayload(payload) {
|
|
15549
|
+
const result = {
|
|
15550
|
+
version: payload.version,
|
|
15551
|
+
hub_id: payload.hub_id,
|
|
15552
|
+
hub_environment: payload.hub_environment
|
|
15553
|
+
};
|
|
15554
|
+
if (payload.preview_label) {
|
|
15555
|
+
result.preview_label = payload.preview_label;
|
|
15525
15556
|
}
|
|
15526
|
-
|
|
15527
|
-
|
|
15528
|
-
|
|
15557
|
+
result.hub = payload.hub;
|
|
15558
|
+
if (payload.resources && payload.resources.length > 0) {
|
|
15559
|
+
result.resources = payload.resources.map((r) => {
|
|
15560
|
+
const { files, ...rest } = r;
|
|
15561
|
+
return rest;
|
|
15562
|
+
});
|
|
15529
15563
|
}
|
|
15530
|
-
|
|
15531
|
-
|
|
15532
|
-
const evalName = typeof data.name === "string" && data.name.trim().length > 0 ? data.name : fileSlug;
|
|
15533
|
-
if (typeof data.agent !== "string" || data.agent.trim().length === 0) {
|
|
15534
|
-
throw expected(`Eval "${evalName}" in ${relPath}: missing required field "agent" (string).`);
|
|
15564
|
+
if (payload.states && payload.states.length > 0) {
|
|
15565
|
+
result.states = payload.states;
|
|
15535
15566
|
}
|
|
15536
|
-
if (
|
|
15537
|
-
|
|
15567
|
+
if (payload.teams && payload.teams.length > 0) {
|
|
15568
|
+
result.teams = payload.teams;
|
|
15538
15569
|
}
|
|
15539
|
-
if (
|
|
15540
|
-
|
|
15570
|
+
if (payload.connections && payload.connections.length > 0) {
|
|
15571
|
+
result.connections = payload.connections;
|
|
15541
15572
|
}
|
|
15542
|
-
|
|
15543
|
-
|
|
15544
|
-
const evalEntry = {
|
|
15545
|
-
name: evalName,
|
|
15546
|
-
agent: data.agent,
|
|
15547
|
-
path: setName,
|
|
15548
|
-
input,
|
|
15549
|
-
expected: data.expected
|
|
15550
|
-
};
|
|
15551
|
-
if (typeof data.id === "string") evalEntry.id = data.id;
|
|
15552
|
-
if (typeof data.agent_id === "string") evalEntry.agent_id = data.agent_id;
|
|
15553
|
-
if (typeof data.runs === "number") evalEntry.runs = data.runs;
|
|
15554
|
-
if (data.enabled === false) evalEntry.enabled = false;
|
|
15555
|
-
if (Array.isArray(data.history)) {
|
|
15556
|
-
evalEntry.history = data.history.map((turn, i) => {
|
|
15557
|
-
const t = { ...turn };
|
|
15558
|
-
applyTurnAttachments(hubFolder, t, `Eval "${evalName}" history[${i}]`, bytesByHash);
|
|
15559
|
-
return t;
|
|
15560
|
-
});
|
|
15573
|
+
if (payload.outbound_contacts && payload.outbound_contacts.length > 0) {
|
|
15574
|
+
result.outbound_contacts = payload.outbound_contacts;
|
|
15561
15575
|
}
|
|
15562
|
-
if (
|
|
15563
|
-
|
|
15576
|
+
if (payload.outbound_lists && payload.outbound_lists.length > 0) {
|
|
15577
|
+
result.outbound_lists = payload.outbound_lists;
|
|
15564
15578
|
}
|
|
15565
|
-
|
|
15566
|
-
|
|
15567
|
-
|
|
15568
|
-
|
|
15569
|
-
const targetBase = parseFixtureField(data.target_base, `Eval "${evalName}" in ${relPath}`, "target_base");
|
|
15570
|
-
if (targetBase) evalEntry.target_base = targetBase;
|
|
15571
|
-
return evalEntry;
|
|
15579
|
+
if (payload.outbound_schedules && payload.outbound_schedules.length > 0) {
|
|
15580
|
+
result.outbound_schedules = payload.outbound_schedules;
|
|
15581
|
+
}
|
|
15582
|
+
return result;
|
|
15572
15583
|
}
|
|
15573
|
-
function
|
|
15574
|
-
|
|
15575
|
-
const
|
|
15576
|
-
|
|
15577
|
-
|
|
15578
|
-
|
|
15584
|
+
function writeAgentYamlFiles(agentsDir, agents) {
|
|
15585
|
+
const slugs = /* @__PURE__ */ new Set();
|
|
15586
|
+
for (const agent of agents) {
|
|
15587
|
+
const slug = slugify(agent.name);
|
|
15588
|
+
slugs.add(slug);
|
|
15589
|
+
const entry = { ...agent };
|
|
15590
|
+
delete entry.instructions;
|
|
15591
|
+
const yamlContent = yaml5.dump(entry, YAML_DUMP_OPTIONS);
|
|
15592
|
+
fs13.writeFileSync(path15.join(agentsDir, `${slug}.yaml`), yamlContent, "utf-8");
|
|
15579
15593
|
}
|
|
15580
|
-
return
|
|
15594
|
+
return slugs;
|
|
15581
15595
|
}
|
|
15582
|
-
function
|
|
15583
|
-
|
|
15584
|
-
|
|
15585
|
-
|
|
15596
|
+
function extractAgentFiles(payload) {
|
|
15597
|
+
const files = [];
|
|
15598
|
+
for (const agent of payload.agents || []) {
|
|
15599
|
+
if (agent.instructions) {
|
|
15600
|
+
files.push({
|
|
15601
|
+
slug: slugify(agent.name),
|
|
15602
|
+
content: agent.instructions
|
|
15603
|
+
});
|
|
15604
|
+
}
|
|
15586
15605
|
}
|
|
15587
|
-
|
|
15588
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
15606
|
+
return files;
|
|
15589
15607
|
}
|
|
15590
|
-
function
|
|
15591
|
-
const
|
|
15592
|
-
if (
|
|
15593
|
-
|
|
15594
|
-
|
|
15595
|
-
|
|
15608
|
+
function writeEvalYamlFiles(hubFolder, evals) {
|
|
15609
|
+
const evalsDir = path15.join(hubFolder, "evals");
|
|
15610
|
+
if (evals.length === 0) {
|
|
15611
|
+
if (fs13.existsSync(evalsDir)) {
|
|
15612
|
+
cleanEvalOrphans(evalsDir, /* @__PURE__ */ new Set());
|
|
15613
|
+
try {
|
|
15614
|
+
if (fs13.readdirSync(evalsDir).length === 0) fs13.rmdirSync(evalsDir);
|
|
15615
|
+
} catch {
|
|
15616
|
+
}
|
|
15617
|
+
}
|
|
15618
|
+
return;
|
|
15619
|
+
}
|
|
15620
|
+
if (!fs13.existsSync(evalsDir)) {
|
|
15621
|
+
fs13.mkdirSync(evalsDir, { recursive: true });
|
|
15622
|
+
}
|
|
15623
|
+
const writtenRelPaths = /* @__PURE__ */ new Set();
|
|
15624
|
+
for (const evalEntry of evals) {
|
|
15625
|
+
const slug = slugify(evalEntry.name);
|
|
15626
|
+
const setName = evalEntry.path && evalEntry.path.trim().length > 0 ? evalEntry.path : null;
|
|
15627
|
+
const relPath = setName ? `${setName}/${slug}.yaml` : `${slug}.yaml`;
|
|
15628
|
+
const targetPath = path15.join(evalsDir, relPath);
|
|
15629
|
+
if (!targetPath.startsWith(evalsDir + path15.sep)) {
|
|
15630
|
+
console.warn(` Warning: skipping eval "${evalEntry.name}" (scenario set "${setName}" escapes evals/ \u2014 possible bad backend data)`);
|
|
15631
|
+
continue;
|
|
15632
|
+
}
|
|
15633
|
+
if (setName) {
|
|
15634
|
+
const setDir = path15.join(evalsDir, setName);
|
|
15635
|
+
if (!fs13.existsSync(setDir)) {
|
|
15636
|
+
fs13.mkdirSync(setDir, { recursive: true });
|
|
15637
|
+
}
|
|
15638
|
+
}
|
|
15639
|
+
const yamlContent = yaml5.dump(buildEvalYamlObject(evalEntry, slug), YAML_DUMP_OPTIONS);
|
|
15640
|
+
fs13.writeFileSync(targetPath, yamlContent, "utf-8");
|
|
15641
|
+
writtenRelPaths.add(relPath);
|
|
15642
|
+
}
|
|
15643
|
+
cleanEvalOrphans(evalsDir, writtenRelPaths);
|
|
15644
|
+
}
|
|
15645
|
+
function cleanEvalOrphans(evalsDir, writtenRelPaths) {
|
|
15646
|
+
const entries = fs13.readdirSync(evalsDir, { withFileTypes: true });
|
|
15596
15647
|
for (const entry of entries) {
|
|
15597
|
-
|
|
15598
|
-
|
|
15599
|
-
|
|
15600
|
-
|
|
15648
|
+
const fullPath = path15.join(evalsDir, entry.name);
|
|
15649
|
+
if (entry.isFile()) {
|
|
15650
|
+
if (entry.name.endsWith(".yaml") && !writtenRelPaths.has(entry.name)) {
|
|
15651
|
+
fs13.unlinkSync(fullPath);
|
|
15652
|
+
}
|
|
15653
|
+
continue;
|
|
15601
15654
|
}
|
|
15602
|
-
if (
|
|
15603
|
-
|
|
15604
|
-
|
|
15605
|
-
|
|
15606
|
-
|
|
15607
|
-
|
|
15608
|
-
|
|
15655
|
+
if (entry.isDirectory()) {
|
|
15656
|
+
const setName = entry.name;
|
|
15657
|
+
const subEntries = fs13.readdirSync(fullPath, { withFileTypes: true });
|
|
15658
|
+
for (const sub of subEntries) {
|
|
15659
|
+
if (sub.isFile() && sub.name.endsWith(".yaml")) {
|
|
15660
|
+
const relPath = `${setName}/${sub.name}`;
|
|
15661
|
+
if (!writtenRelPaths.has(relPath)) {
|
|
15662
|
+
fs13.unlinkSync(path15.join(fullPath, sub.name));
|
|
15663
|
+
}
|
|
15664
|
+
}
|
|
15665
|
+
}
|
|
15666
|
+
try {
|
|
15667
|
+
if (fs13.readdirSync(fullPath).length === 0) fs13.rmdirSync(fullPath);
|
|
15668
|
+
} catch {
|
|
15669
|
+
}
|
|
15609
15670
|
}
|
|
15610
|
-
seen.add(journeyEntry.name);
|
|
15611
|
-
journeys.push(journeyEntry);
|
|
15612
15671
|
}
|
|
15613
|
-
return journeys;
|
|
15614
15672
|
}
|
|
15615
|
-
function
|
|
15616
|
-
const
|
|
15617
|
-
|
|
15618
|
-
|
|
15619
|
-
|
|
15620
|
-
|
|
15621
|
-
|
|
15622
|
-
|
|
15623
|
-
}
|
|
15624
|
-
throw err;
|
|
15625
|
-
}
|
|
15626
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
15627
|
-
console.warn(` Warning: skipping ${relPath} (not a YAML object)`);
|
|
15628
|
-
return null;
|
|
15673
|
+
function buildJourneyYamlObject(journeyEntry, slug) {
|
|
15674
|
+
const out = {};
|
|
15675
|
+
if (journeyEntry.id) out.id = journeyEntry.id;
|
|
15676
|
+
if (journeyEntry.name !== slug) out.name = journeyEntry.name;
|
|
15677
|
+
out.agent = journeyEntry.agent;
|
|
15678
|
+
if (journeyEntry.agent_id) out.agent_id = journeyEntry.agent_id;
|
|
15679
|
+
if (journeyEntry.enabled === false) {
|
|
15680
|
+
out.enabled = false;
|
|
15629
15681
|
}
|
|
15630
|
-
|
|
15631
|
-
|
|
15632
|
-
const journeyName = typeof data.name === "string" && data.name.trim().length > 0 ? data.name : fileSlug;
|
|
15633
|
-
if (typeof data.agent !== "string" || data.agent.trim().length === 0) {
|
|
15634
|
-
throw expected(`Journey "${journeyName}" in ${relPath}: missing required field "agent" (string).`);
|
|
15682
|
+
if (journeyEntry.evaluator_instructions) {
|
|
15683
|
+
out.evaluator_instructions = journeyEntry.evaluator_instructions;
|
|
15635
15684
|
}
|
|
15636
|
-
if (
|
|
15637
|
-
|
|
15685
|
+
if (typeof journeyEntry.fixture === "string" && journeyEntry.fixture.trim()) {
|
|
15686
|
+
out.fixture = journeyEntry.fixture.trim();
|
|
15638
15687
|
}
|
|
15639
|
-
if (
|
|
15640
|
-
|
|
15688
|
+
if (typeof journeyEntry.target_base === "string" && journeyEntry.target_base.trim()) {
|
|
15689
|
+
out.target_base = journeyEntry.target_base.trim();
|
|
15641
15690
|
}
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
agent: data.agent
|
|
15645
|
-
};
|
|
15646
|
-
if (typeof data.id === "string") journeyEntry.id = data.id;
|
|
15647
|
-
if (typeof data.agent_id === "string") journeyEntry.agent_id = data.agent_id;
|
|
15648
|
-
if (data.enabled === false) journeyEntry.enabled = false;
|
|
15649
|
-
if (typeof data.evaluator_instructions === "string") {
|
|
15650
|
-
journeyEntry.evaluator_instructions = data.evaluator_instructions;
|
|
15691
|
+
if (journeyEntry.variables && Object.keys(journeyEntry.variables).length > 0) {
|
|
15692
|
+
out.variables = journeyEntry.variables;
|
|
15651
15693
|
}
|
|
15652
|
-
if (
|
|
15653
|
-
journeyEntry.transcript
|
|
15654
|
-
|
|
15655
|
-
applyTurnAttachments(hubFolder, t, `Journey "${journeyName}" transcript[${i}]`, bytesByHash);
|
|
15656
|
-
return t;
|
|
15657
|
-
});
|
|
15694
|
+
if (journeyEntry.transcript && journeyEntry.transcript.length > 0) {
|
|
15695
|
+
for (const turn of journeyEntry.transcript) assertAttachmentsAreLocalPaths(turn, `journey "${journeyEntry.name}" transcript`);
|
|
15696
|
+
out.transcript = journeyEntry.transcript;
|
|
15658
15697
|
}
|
|
15659
|
-
if (
|
|
15660
|
-
|
|
15698
|
+
if (journeyEntry.step_config && Object.keys(journeyEntry.step_config).length > 0) {
|
|
15699
|
+
out.step_config = journeyEntry.step_config;
|
|
15661
15700
|
}
|
|
15662
|
-
|
|
15663
|
-
if (variables) journeyEntry.variables = variables;
|
|
15664
|
-
const fixture = parseFixtureField(data.fixture, `Journey "${journeyName}" in ${relPath}`, "fixture");
|
|
15665
|
-
if (fixture) journeyEntry.fixture = fixture;
|
|
15666
|
-
const targetBase = parseFixtureField(data.target_base, `Journey "${journeyName}" in ${relPath}`, "target_base");
|
|
15667
|
-
if (targetBase) journeyEntry.target_base = targetBase;
|
|
15668
|
-
return journeyEntry;
|
|
15701
|
+
return out;
|
|
15669
15702
|
}
|
|
15670
|
-
function
|
|
15671
|
-
const
|
|
15672
|
-
if (
|
|
15673
|
-
|
|
15674
|
-
|
|
15675
|
-
|
|
15676
|
-
|
|
15677
|
-
|
|
15678
|
-
const content = fs13.readFileSync(filePath, "utf-8");
|
|
15679
|
-
let agent;
|
|
15680
|
-
try {
|
|
15681
|
-
agent = yaml5.load(content);
|
|
15682
|
-
} catch (err) {
|
|
15683
|
-
if (err instanceof yaml5.YAMLException) {
|
|
15684
|
-
const userError = new Error(`Invalid YAML in agents/${file}: ${err.message}`);
|
|
15685
|
-
userError.isExpected = true;
|
|
15686
|
-
throw userError;
|
|
15703
|
+
function writeJourneyYamlFiles(hubFolder, journeys) {
|
|
15704
|
+
const journeysDir = path15.join(hubFolder, "journeys");
|
|
15705
|
+
if (journeys.length === 0) {
|
|
15706
|
+
if (fs13.existsSync(journeysDir)) {
|
|
15707
|
+
cleanJourneyOrphans(journeysDir, /* @__PURE__ */ new Set());
|
|
15708
|
+
try {
|
|
15709
|
+
if (fs13.readdirSync(journeysDir).length === 0) fs13.rmdirSync(journeysDir);
|
|
15710
|
+
} catch {
|
|
15687
15711
|
}
|
|
15688
|
-
throw err;
|
|
15689
15712
|
}
|
|
15690
|
-
|
|
15691
|
-
|
|
15692
|
-
|
|
15693
|
-
|
|
15713
|
+
return;
|
|
15714
|
+
}
|
|
15715
|
+
if (!fs13.existsSync(journeysDir)) {
|
|
15716
|
+
fs13.mkdirSync(journeysDir, { recursive: true });
|
|
15717
|
+
}
|
|
15718
|
+
const writtenFiles = /* @__PURE__ */ new Set();
|
|
15719
|
+
const usedSlugs = /* @__PURE__ */ new Set();
|
|
15720
|
+
for (const journeyEntry of journeys) {
|
|
15721
|
+
const slug = ensureUniqueSlug(slugify(journeyEntry.name), usedSlugs);
|
|
15722
|
+
const fileName = `${slug}.yaml`;
|
|
15723
|
+
const yamlContent = yaml5.dump(buildJourneyYamlObject(journeyEntry, slug), YAML_DUMP_OPTIONS);
|
|
15724
|
+
fs13.writeFileSync(path15.join(journeysDir, fileName), yamlContent, "utf-8");
|
|
15725
|
+
writtenFiles.add(fileName);
|
|
15726
|
+
}
|
|
15727
|
+
cleanJourneyOrphans(journeysDir, writtenFiles);
|
|
15728
|
+
}
|
|
15729
|
+
function cleanJourneyOrphans(journeysDir, writtenFiles) {
|
|
15730
|
+
const entries = fs13.readdirSync(journeysDir, { withFileTypes: true });
|
|
15731
|
+
for (const entry of entries) {
|
|
15732
|
+
if (entry.isFile() && entry.name.endsWith(".yaml") && !writtenFiles.has(entry.name)) {
|
|
15733
|
+
fs13.unlinkSync(path15.join(journeysDir, entry.name));
|
|
15694
15734
|
}
|
|
15695
|
-
agents.push(agent);
|
|
15696
15735
|
}
|
|
15697
|
-
return agents;
|
|
15698
15736
|
}
|
|
15699
|
-
function
|
|
15737
|
+
function writeResourceFiles(hubFolder, resources) {
|
|
15700
15738
|
const resourcesDir = path15.join(hubFolder, "resources");
|
|
15701
|
-
const
|
|
15702
|
-
for (const
|
|
15703
|
-
const resource = {
|
|
15704
|
-
name: res.name
|
|
15705
|
-
};
|
|
15706
|
-
if (res.id) resource.id = res.id;
|
|
15707
|
-
if (res.type === "knowledge" || res.type === "skill") resource.type = res.type;
|
|
15708
|
-
else if (res.type !== void 0) throw new Error(`Invalid resource type for "${resource.name}": expected knowledge or skill`);
|
|
15709
|
-
if (res.description) resource.description = res.description;
|
|
15710
|
-
if (res.enabled === false) resource.enabled = false;
|
|
15711
|
-
if (res.user_browsable) resource.user_browsable = res.user_browsable;
|
|
15712
|
-
if (res.skill_name) resource.skill_name = res.skill_name;
|
|
15739
|
+
const currentSlugs = /* @__PURE__ */ new Set();
|
|
15740
|
+
for (const resource of resources) {
|
|
15713
15741
|
const resSlug = slugify(resource.name);
|
|
15742
|
+
currentSlugs.add(resSlug);
|
|
15714
15743
|
const resDir = path15.join(resourcesDir, resSlug);
|
|
15715
|
-
|
|
15716
|
-
|
|
15717
|
-
|
|
15718
|
-
|
|
15744
|
+
writeResourceFileTree(resDir, resource.files || [], hubFolder);
|
|
15745
|
+
}
|
|
15746
|
+
if (fs13.existsSync(resourcesDir)) {
|
|
15747
|
+
const existingDirs = fs13.readdirSync(resourcesDir, { withFileTypes: true });
|
|
15748
|
+
for (const entry of existingDirs) {
|
|
15749
|
+
if (entry.isDirectory() && !currentSlugs.has(entry.name)) {
|
|
15750
|
+
fs13.rmSync(path15.join(resourcesDir, entry.name), { recursive: true, force: true });
|
|
15719
15751
|
}
|
|
15720
15752
|
}
|
|
15721
|
-
results.push(resource);
|
|
15722
15753
|
}
|
|
15723
|
-
return results;
|
|
15724
15754
|
}
|
|
15725
|
-
var
|
|
15726
|
-
|
|
15755
|
+
var YAML_DUMP_OPTIONS;
|
|
15756
|
+
var init_yaml_writer = __esm({
|
|
15757
|
+
"src/lib/yaml-writer.ts"() {
|
|
15727
15758
|
"use strict";
|
|
15759
|
+
init_dist();
|
|
15728
15760
|
init_utils();
|
|
15729
15761
|
init_resource_files();
|
|
15730
15762
|
init_eval_attachments();
|
|
15763
|
+
init_workspace_files();
|
|
15764
|
+
YAML_DUMP_OPTIONS = {
|
|
15765
|
+
lineWidth: -1,
|
|
15766
|
+
// don't wrap lines
|
|
15767
|
+
noRefs: true,
|
|
15768
|
+
// don't use YAML anchors
|
|
15769
|
+
quotingType: '"',
|
|
15770
|
+
forceQuotes: false,
|
|
15771
|
+
sortKeys: false
|
|
15772
|
+
};
|
|
15731
15773
|
}
|
|
15732
15774
|
});
|
|
15733
15775
|
|
|
15734
|
-
// src/lib/
|
|
15735
|
-
|
|
15736
|
-
|
|
15737
|
-
|
|
15738
|
-
|
|
15739
|
-
|
|
15740
|
-
|
|
15741
|
-
} else if (line.trimStart().startsWith("+ ")) {
|
|
15742
|
-
console.log(`\x1B[32m${line}\x1B[0m`);
|
|
15743
|
-
} else if (line.trimStart().startsWith("- ")) {
|
|
15744
|
-
console.log(`\x1B[31m${line}\x1B[0m`);
|
|
15745
|
-
} else if (line.trimStart().startsWith("~ ")) {
|
|
15746
|
-
console.log(`\x1B[33m${line}\x1B[0m`);
|
|
15747
|
-
} else if (line.includes("(no changes)")) {
|
|
15748
|
-
console.log(`\x1B[2m${line}\x1B[0m`);
|
|
15749
|
-
} else {
|
|
15750
|
-
console.log(line);
|
|
15751
|
-
}
|
|
15776
|
+
// src/lib/hub-materializer.ts
|
|
15777
|
+
import * as path16 from "path";
|
|
15778
|
+
async function materializeHubFolder(hubFolder, payload, options = {}) {
|
|
15779
|
+
if (materialized.has(payload)) {
|
|
15780
|
+
throw new Error(
|
|
15781
|
+
"materializeHubFolder called twice on the same payload object \u2014 CLI bug. The first run rewrote the payload in place; fetch a fresh payload to write again."
|
|
15782
|
+
);
|
|
15752
15783
|
}
|
|
15753
|
-
|
|
15784
|
+
materialized.add(payload);
|
|
15785
|
+
const attachmentDownloads = rewriteEvalAttachmentsToLocalPaths(payload);
|
|
15786
|
+
writeHubFolder(hubFolder, payload, options);
|
|
15787
|
+
await downloadBinaryResourceFiles(hubFolder, payload);
|
|
15788
|
+
const attachmentsWritten = await downloadEvalAttachments(hubFolder, attachmentDownloads);
|
|
15789
|
+
return { attachmentsWritten };
|
|
15754
15790
|
}
|
|
15755
|
-
|
|
15756
|
-
|
|
15791
|
+
async function downloadBinaryResourceFiles(hubFolder, payload) {
|
|
15792
|
+
const resources = payload.resources || [];
|
|
15793
|
+
let downloadCount = 0;
|
|
15794
|
+
for (const resource of resources) {
|
|
15795
|
+
if (!resource.files) continue;
|
|
15796
|
+
const resDir = path16.join(hubFolder, "resources", slugify(resource.name));
|
|
15797
|
+
downloadCount += await downloadBinaryFiles(resDir, resource.files, hubFolder);
|
|
15798
|
+
}
|
|
15799
|
+
if (downloadCount > 0) {
|
|
15800
|
+
console.log(`Downloaded ${downloadCount} binary resource file(s).`);
|
|
15801
|
+
}
|
|
15802
|
+
}
|
|
15803
|
+
var materialized;
|
|
15804
|
+
var init_hub_materializer = __esm({
|
|
15805
|
+
"src/lib/hub-materializer.ts"() {
|
|
15757
15806
|
"use strict";
|
|
15807
|
+
init_yaml_writer();
|
|
15808
|
+
init_eval_attachments();
|
|
15809
|
+
init_resource_files();
|
|
15810
|
+
init_utils();
|
|
15811
|
+
materialized = /* @__PURE__ */ new WeakSet();
|
|
15758
15812
|
}
|
|
15759
15813
|
});
|
|
15760
15814
|
|
|
@@ -15762,13 +15816,12 @@ var init_diff_display = __esm({
|
|
|
15762
15816
|
var pull_exports = {};
|
|
15763
15817
|
__export(pull_exports, {
|
|
15764
15818
|
connectionsEqual: () => connectionsEqual,
|
|
15765
|
-
downloadBinaryResourceFiles: () => downloadBinaryResourceFiles,
|
|
15766
15819
|
pullCommand: () => pullCommand,
|
|
15767
15820
|
resolveHubTarget: () => resolveHubTarget,
|
|
15768
15821
|
writeProductionMirror: () => writeProductionMirror
|
|
15769
15822
|
});
|
|
15770
15823
|
import * as fs14 from "fs";
|
|
15771
|
-
import * as
|
|
15824
|
+
import * as path17 from "path";
|
|
15772
15825
|
function parseArgs4(args2) {
|
|
15773
15826
|
let autoConfirm = false;
|
|
15774
15827
|
let hubSelector;
|
|
@@ -15787,7 +15840,7 @@ function resolveHubTarget(workspaceDir, selector) {
|
|
|
15787
15840
|
const allHubs = scanWorkspaceHubs(workspaceDir);
|
|
15788
15841
|
if (selector) {
|
|
15789
15842
|
const match = allHubs.find(
|
|
15790
|
-
(h) => h.hubId === selector ||
|
|
15843
|
+
(h) => h.hubId === selector || path17.basename(h.hubFolder) === selector
|
|
15791
15844
|
);
|
|
15792
15845
|
if (match) return { hubId: match.hubId, hubFolder: match.hubFolder };
|
|
15793
15846
|
if (UUID_RE2.test(selector)) return { hubId: selector, hubFolder: null };
|
|
@@ -15804,7 +15857,7 @@ function resolveHubTarget(workspaceDir, selector) {
|
|
|
15804
15857
|
}
|
|
15805
15858
|
console.error(`Multiple hubs found in ${wsLabel}/. Pass --hub <uuid|folder-name> to choose, or run from inside a hub folder:`);
|
|
15806
15859
|
for (const h of previewHubs) {
|
|
15807
|
-
console.error(` ${
|
|
15860
|
+
console.error(` ${path17.basename(h.hubFolder)} (${h.hubId})`);
|
|
15808
15861
|
}
|
|
15809
15862
|
process.exit(1);
|
|
15810
15863
|
}
|
|
@@ -15831,7 +15884,6 @@ async function pullCommand(args2) {
|
|
|
15831
15884
|
return;
|
|
15832
15885
|
}
|
|
15833
15886
|
assertHubMatchesBinding(hubId);
|
|
15834
|
-
const attachmentDownloads = rewriteEvalAttachmentsToLocalPaths(payload);
|
|
15835
15887
|
let hubFolder = existingFolder;
|
|
15836
15888
|
let wroteFiles = false;
|
|
15837
15889
|
if (!hubFolder) {
|
|
@@ -15843,31 +15895,20 @@ async function pullCommand(args2) {
|
|
|
15843
15895
|
payload.preview_label,
|
|
15844
15896
|
payload.branch_name
|
|
15845
15897
|
);
|
|
15846
|
-
fs14.mkdirSync(
|
|
15898
|
+
fs14.mkdirSync(path17.dirname(hubFolder), { recursive: true });
|
|
15847
15899
|
console.log("Writing hub configuration...");
|
|
15848
|
-
|
|
15849
|
-
await downloadBinaryResourceFiles(hubFolder, payload);
|
|
15850
|
-
await downloadEvalAttachments(hubFolder, attachmentDownloads);
|
|
15900
|
+
await materializeHubFolder(hubFolder, payload);
|
|
15851
15901
|
const finalFolder = autoRenameHubFolder(hubFolder, payload.hub.name, payload.hub_environment, payload.hub_id, payload.preview_label, payload.branch_name);
|
|
15852
15902
|
console.log(`Hub configuration written to ${finalFolder}`);
|
|
15853
15903
|
wroteFiles = true;
|
|
15854
15904
|
} else {
|
|
15855
|
-
let
|
|
15905
|
+
let action = "overwrite";
|
|
15856
15906
|
try {
|
|
15857
15907
|
const localPayload = parseHubFolder(hubFolder);
|
|
15858
15908
|
const diffResult = await client.diff(hubId, prepareResourcePayload(localPayload, payload), "pull", organizationId);
|
|
15859
15909
|
const connectionsChanged = !connectionsEqual(localPayload.connections, payload.connections);
|
|
15860
15910
|
if (!diffResult.has_changes && !connectionsChanged) {
|
|
15861
|
-
|
|
15862
|
-
await downloadBinaryResourceFiles(hubFolder, payload);
|
|
15863
|
-
await downloadEvalAttachments(hubFolder, attachmentDownloads);
|
|
15864
|
-
const finalFolder = autoRenameHubFolder(hubFolder, payload.hub.name, payload.hub_environment, payload.hub_id, payload.preview_label, payload.branch_name);
|
|
15865
|
-
wroteFiles = true;
|
|
15866
|
-
if (finalFolder === hubFolder) {
|
|
15867
|
-
console.log("Metadata synced \u2014 no config changes.");
|
|
15868
|
-
} else {
|
|
15869
|
-
console.log("Metadata synced; folder renamed.");
|
|
15870
|
-
}
|
|
15911
|
+
action = "sync_metadata";
|
|
15871
15912
|
} else {
|
|
15872
15913
|
console.log("\nChanges to apply locally:");
|
|
15873
15914
|
if (connectionsChanged) {
|
|
@@ -15878,28 +15919,27 @@ async function pullCommand(args2) {
|
|
|
15878
15919
|
const ok = await confirm("Apply these changes?");
|
|
15879
15920
|
if (!ok) {
|
|
15880
15921
|
console.log("Cancelled.");
|
|
15881
|
-
|
|
15922
|
+
return;
|
|
15882
15923
|
}
|
|
15883
15924
|
}
|
|
15884
|
-
|
|
15885
|
-
writeHubFolder(hubFolder, payload);
|
|
15886
|
-
await downloadBinaryResourceFiles(hubFolder, payload);
|
|
15887
|
-
await downloadEvalAttachments(hubFolder, attachmentDownloads);
|
|
15888
|
-
const finalFolder = autoRenameHubFolder(hubFolder, payload.hub.name, payload.hub_environment, payload.hub_id, payload.preview_label, payload.branch_name);
|
|
15889
|
-
console.log(`Hub configuration written to ${finalFolder}`);
|
|
15890
|
-
wroteFiles = true;
|
|
15891
|
-
}
|
|
15925
|
+
action = "apply";
|
|
15892
15926
|
}
|
|
15893
15927
|
} catch {
|
|
15894
|
-
|
|
15895
|
-
|
|
15896
|
-
|
|
15897
|
-
|
|
15898
|
-
|
|
15928
|
+
action = "overwrite";
|
|
15929
|
+
}
|
|
15930
|
+
if (action === "overwrite") console.log("Writing hub configuration...");
|
|
15931
|
+
await materializeHubFolder(hubFolder, payload);
|
|
15932
|
+
const finalFolder = autoRenameHubFolder(hubFolder, payload.hub.name, payload.hub_environment, payload.hub_id, payload.preview_label, payload.branch_name);
|
|
15933
|
+
wroteFiles = true;
|
|
15934
|
+
if (action === "sync_metadata") {
|
|
15935
|
+
if (finalFolder === hubFolder) {
|
|
15936
|
+
console.log("Metadata synced \u2014 no config changes.");
|
|
15937
|
+
} else {
|
|
15938
|
+
console.log("Metadata synced; folder renamed.");
|
|
15939
|
+
}
|
|
15940
|
+
} else {
|
|
15899
15941
|
console.log(`Hub configuration written to ${finalFolder}`);
|
|
15900
|
-
wroteFiles = true;
|
|
15901
15942
|
}
|
|
15902
|
-
if (cancelled) return;
|
|
15903
15943
|
}
|
|
15904
15944
|
if (wroteFiles) autoBindIfUnbound(hubId);
|
|
15905
15945
|
if (payload.production_hub_id) {
|
|
@@ -15908,11 +15948,8 @@ async function pullCommand(args2) {
|
|
|
15908
15948
|
}
|
|
15909
15949
|
async function writeProductionMirror(workspaceDir, prodPayload) {
|
|
15910
15950
|
const folder = resolveHubFolder(workspaceDir, prodPayload.hub_id, prodPayload.hub.name, "production", null, null);
|
|
15911
|
-
fs14.mkdirSync(
|
|
15912
|
-
|
|
15913
|
-
writeHubFolder(folder, prodPayload, { seedAgentContext: false });
|
|
15914
|
-
await downloadBinaryResourceFiles(folder, prodPayload);
|
|
15915
|
-
await downloadEvalAttachments(folder, attachmentDownloads);
|
|
15951
|
+
fs14.mkdirSync(path17.dirname(folder), { recursive: true });
|
|
15952
|
+
await materializeHubFolder(folder, prodPayload, { seedAgentContext: false });
|
|
15916
15953
|
const finalFolder = autoRenameHubFolder(folder, prodPayload.hub.name, "production", prodPayload.hub_id, null, null);
|
|
15917
15954
|
prependMirrorMarker(finalFolder, prodPayload.hub_id);
|
|
15918
15955
|
return finalFolder;
|
|
@@ -15927,7 +15964,7 @@ async function mirrorLinkedProduction(client, workspaceDir, productionHubId, org
|
|
|
15927
15964
|
}
|
|
15928
15965
|
}
|
|
15929
15966
|
function prependMirrorMarker(hubFolder, productionHubId) {
|
|
15930
|
-
const hubYaml =
|
|
15967
|
+
const hubYaml = path17.join(hubFolder, "hub.yaml");
|
|
15931
15968
|
try {
|
|
15932
15969
|
const content = fs14.readFileSync(hubYaml, "utf-8");
|
|
15933
15970
|
if (content.startsWith(MIRROR_MARKER_PREFIX)) return;
|
|
@@ -15937,18 +15974,6 @@ function prependMirrorMarker(hubFolder, productionHubId) {
|
|
|
15937
15974
|
} catch {
|
|
15938
15975
|
}
|
|
15939
15976
|
}
|
|
15940
|
-
async function downloadBinaryResourceFiles(hubFolder, payload) {
|
|
15941
|
-
const resources = payload.resources || [];
|
|
15942
|
-
let downloadCount = 0;
|
|
15943
|
-
for (const resource of resources) {
|
|
15944
|
-
if (!resource.files) continue;
|
|
15945
|
-
const resDir = path16.join(hubFolder, "resources", slugify(resource.name));
|
|
15946
|
-
downloadCount += await downloadBinaryFiles(resDir, resource.files, hubFolder);
|
|
15947
|
-
}
|
|
15948
|
-
if (downloadCount > 0) {
|
|
15949
|
-
console.log(`Downloaded ${downloadCount} binary resource file(s).`);
|
|
15950
|
-
}
|
|
15951
|
-
}
|
|
15952
15977
|
function connectionsEqual(a, b) {
|
|
15953
15978
|
const listA = a || [];
|
|
15954
15979
|
const listB = b || [];
|
|
@@ -15968,12 +15993,11 @@ var init_pull = __esm({
|
|
|
15968
15993
|
"use strict";
|
|
15969
15994
|
init_auth();
|
|
15970
15995
|
init_api_client();
|
|
15971
|
-
init_yaml_writer();
|
|
15972
15996
|
init_parser();
|
|
15973
15997
|
init_diff_display();
|
|
15974
15998
|
init_utils();
|
|
15975
15999
|
init_resource_files();
|
|
15976
|
-
|
|
16000
|
+
init_hub_materializer();
|
|
15977
16001
|
init_workspace();
|
|
15978
16002
|
init_layout();
|
|
15979
16003
|
init_workspace_files();
|
|
@@ -15994,10 +16018,11 @@ __export(push_exports, {
|
|
|
15994
16018
|
pushCommand: () => pushCommand,
|
|
15995
16019
|
reportUnresolvedPushTarget: () => reportUnresolvedPushTarget,
|
|
15996
16020
|
selectExistingHub: () => selectExistingHub,
|
|
15997
|
-
shouldWarnIgnoredPreviewLabel: () => shouldWarnIgnoredPreviewLabel
|
|
16021
|
+
shouldWarnIgnoredPreviewLabel: () => shouldWarnIgnoredPreviewLabel,
|
|
16022
|
+
syncAfterPush: () => syncAfterPush
|
|
15998
16023
|
});
|
|
15999
16024
|
import * as fs15 from "fs";
|
|
16000
|
-
import * as
|
|
16025
|
+
import * as path18 from "path";
|
|
16001
16026
|
import * as yaml6 from "js-yaml";
|
|
16002
16027
|
function parseArgs5(args2) {
|
|
16003
16028
|
let autoConfirm = false;
|
|
@@ -16090,13 +16115,13 @@ function printErrors(errors) {
|
|
|
16090
16115
|
}
|
|
16091
16116
|
}
|
|
16092
16117
|
async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, remoteConfig) {
|
|
16093
|
-
const agentsDir =
|
|
16118
|
+
const agentsDir = path18.join(hubFolder, "agents");
|
|
16094
16119
|
let agentsWithIds = [];
|
|
16095
16120
|
if (fs15.existsSync(agentsDir)) {
|
|
16096
16121
|
const yamlFiles = fs15.readdirSync(agentsDir).filter((f) => f.endsWith(".yaml"));
|
|
16097
16122
|
for (const file of yamlFiles) {
|
|
16098
16123
|
try {
|
|
16099
|
-
const content = fs15.readFileSync(
|
|
16124
|
+
const content = fs15.readFileSync(path18.join(agentsDir, file), "utf-8");
|
|
16100
16125
|
const agent = yaml6.load(content);
|
|
16101
16126
|
if (agent?.id && agent.name) {
|
|
16102
16127
|
agentsWithIds.push({ id: agent.id, name: agent.name });
|
|
@@ -16145,12 +16170,12 @@ async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, re
|
|
|
16145
16170
|
for (const file of fs15.readdirSync(agentsDir)) {
|
|
16146
16171
|
if (file.startsWith("__rename_temp_") && (file.endsWith(".md") || file.endsWith(".yaml"))) {
|
|
16147
16172
|
console.warn(` Warning: removing orphaned temp file agents/${file}`);
|
|
16148
|
-
fs15.unlinkSync(
|
|
16173
|
+
fs15.unlinkSync(path18.join(agentsDir, file));
|
|
16149
16174
|
}
|
|
16150
16175
|
}
|
|
16151
16176
|
const renameFileIfExists = (dir, oldName, newName) => {
|
|
16152
|
-
const oldPath =
|
|
16153
|
-
const newPath =
|
|
16177
|
+
const oldPath = path18.join(dir, oldName);
|
|
16178
|
+
const newPath = path18.join(dir, newName);
|
|
16154
16179
|
if (!fs15.existsSync(oldPath)) return false;
|
|
16155
16180
|
fs15.renameSync(oldPath, newPath);
|
|
16156
16181
|
return true;
|
|
@@ -16185,9 +16210,9 @@ async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, re
|
|
|
16185
16210
|
}
|
|
16186
16211
|
} else {
|
|
16187
16212
|
for (const { oldSlug, newSlug } of renames) {
|
|
16188
|
-
const hasOldFile = extensions.some((ext) => fs15.existsSync(
|
|
16213
|
+
const hasOldFile = extensions.some((ext) => fs15.existsSync(path18.join(agentsDir, `${oldSlug}${ext}`)));
|
|
16189
16214
|
if (!hasOldFile) continue;
|
|
16190
|
-
const hasNewFile = extensions.some((ext) => fs15.existsSync(
|
|
16215
|
+
const hasNewFile = extensions.some((ext) => fs15.existsSync(path18.join(agentsDir, `${newSlug}${ext}`)));
|
|
16191
16216
|
if (hasNewFile) {
|
|
16192
16217
|
console.warn(` Warning: skipping rename agents/${oldSlug}.* \u2192 agents/${newSlug}.* (target already exists)`);
|
|
16193
16218
|
continue;
|
|
@@ -16214,15 +16239,14 @@ async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, re
|
|
|
16214
16239
|
);
|
|
16215
16240
|
if (updatedYaml !== mainYamlContent) {
|
|
16216
16241
|
fs15.writeFileSync(mainYamlPath, updatedYaml, "utf-8");
|
|
16217
|
-
console.log(` Updated instructions paths in ${
|
|
16242
|
+
console.log(` Updated instructions paths in ${path18.basename(mainYamlPath)}`);
|
|
16218
16243
|
}
|
|
16219
16244
|
}
|
|
16220
16245
|
}
|
|
16221
16246
|
async function syncAfterPush(client, hubId, hubFolder, organizationId) {
|
|
16222
16247
|
try {
|
|
16223
16248
|
const remoteConfig = await client.pull(hubId, organizationId);
|
|
16224
|
-
|
|
16225
|
-
await downloadBinaryResourceFiles(hubFolder, remoteConfig);
|
|
16249
|
+
await materializeHubFolder(hubFolder, remoteConfig);
|
|
16226
16250
|
console.log("Local files synced with server.");
|
|
16227
16251
|
const finalFolder = autoRenameHubFolder(hubFolder, remoteConfig.hub.name, remoteConfig.hub_environment, hubId, remoteConfig.preview_label, remoteConfig.branch_name);
|
|
16228
16252
|
if (finalFolder !== hubFolder) {
|
|
@@ -16283,7 +16307,7 @@ async function pushSingleHub(client, hubId, hubFolder, autoConfirm, organization
|
|
|
16283
16307
|
function selectExistingHub(workspaceDir, allHubs, selector, wsLabel) {
|
|
16284
16308
|
if (selector) {
|
|
16285
16309
|
const match = allHubs.find(
|
|
16286
|
-
(h) => h.hubId === selector ||
|
|
16310
|
+
(h) => h.hubId === selector || path18.basename(h.hubFolder) === selector
|
|
16287
16311
|
);
|
|
16288
16312
|
if (!match) {
|
|
16289
16313
|
console.error(`No hub matching --hub ${selector} found in ${wsLabel}/.`);
|
|
@@ -16351,7 +16375,7 @@ function reportUnresolvedPushTarget(reason, existingHubs, newHubs, selector, wsL
|
|
|
16351
16375
|
if (existingHubs.length > 0) {
|
|
16352
16376
|
console.error(`No pushable hub found in ${wsLabel}/ \u2014 only read-only production mirror folder(s). Edit the linked preview hub, pull one, or create a new hub with \`wayai create\`:`);
|
|
16353
16377
|
for (const h of existingHubs) {
|
|
16354
|
-
console.error(` ${
|
|
16378
|
+
console.error(` ${path18.basename(h.hubFolder)} (${h.hubId}, production mirror)`);
|
|
16355
16379
|
}
|
|
16356
16380
|
} else {
|
|
16357
16381
|
console.error(`No hub folders found. Create hub files in ${wsLabel}/<hub>/hub.yaml with \`hub: { name: ... }\` and run \`wayai push\` (or \`wayai create\`).`);
|
|
@@ -16364,10 +16388,10 @@ function reportUnresolvedPushTarget(reason, existingHubs, newHubs, selector, wsL
|
|
|
16364
16388
|
console.error(`Multiple hub folders found in ${wsLabel}/. Pass --hub <uuid|folder-name> to choose, or run from inside a hub folder:`);
|
|
16365
16389
|
}
|
|
16366
16390
|
for (const h of existingHubs) {
|
|
16367
|
-
console.error(` ${
|
|
16391
|
+
console.error(` ${path18.basename(h.hubFolder)} (${h.hubId})`);
|
|
16368
16392
|
}
|
|
16369
16393
|
for (const h of newHubs) {
|
|
16370
|
-
console.error(` ${
|
|
16394
|
+
console.error(` ${path18.basename(h.hubFolder)} (new \u2014 "${h.hubName}")`);
|
|
16371
16395
|
}
|
|
16372
16396
|
process.exit(1);
|
|
16373
16397
|
}
|
|
@@ -16405,8 +16429,8 @@ async function pushCommand(args2) {
|
|
|
16405
16429
|
if (label) {
|
|
16406
16430
|
console.warn("Note: --label is only used when creating a new hub. To change an existing hub's label, use `wayai relabel`.");
|
|
16407
16431
|
}
|
|
16408
|
-
console.log(`Target hub: ${
|
|
16409
|
-
assertHubMatchesBinding(existing.hubId,
|
|
16432
|
+
console.log(`Target hub: ${path18.basename(existing.hubFolder)} (${existing.hubId})`);
|
|
16433
|
+
assertHubMatchesBinding(existing.hubId, path18.basename(existing.hubFolder));
|
|
16410
16434
|
await pushSingleHub(client, existing.hubId, existing.hubFolder, autoConfirm, organizationId);
|
|
16411
16435
|
return;
|
|
16412
16436
|
}
|
|
@@ -16419,8 +16443,7 @@ var init_push = __esm({
|
|
|
16419
16443
|
init_auth();
|
|
16420
16444
|
init_api_client();
|
|
16421
16445
|
init_parser();
|
|
16422
|
-
|
|
16423
|
-
init_pull();
|
|
16446
|
+
init_hub_materializer();
|
|
16424
16447
|
init_resource_files();
|
|
16425
16448
|
init_diff_display();
|
|
16426
16449
|
init_utils();
|
|
@@ -16437,7 +16460,7 @@ var create_exports = {};
|
|
|
16437
16460
|
__export(create_exports, {
|
|
16438
16461
|
createCommand: () => createCommand
|
|
16439
16462
|
});
|
|
16440
|
-
import * as
|
|
16463
|
+
import * as path19 from "path";
|
|
16441
16464
|
import * as fs16 from "fs";
|
|
16442
16465
|
function parseArgs6(args2) {
|
|
16443
16466
|
let autoConfirm = false;
|
|
@@ -16474,7 +16497,7 @@ async function createCommand(args2) {
|
|
|
16474
16497
|
const resolution = resolveNewHubForCreate(workspaceDir, existingHubs, newHubs, folderSelector);
|
|
16475
16498
|
if (!resolution.ok) {
|
|
16476
16499
|
if (resolution.reason === "exists") {
|
|
16477
|
-
const folder =
|
|
16500
|
+
const folder = path19.basename(resolution.existing.hubFolder);
|
|
16478
16501
|
console.error(`Hub "${folder}" already exists (${resolution.existing.hubId}). Use \`wayai push --hub ${folder}\` to update it.`);
|
|
16479
16502
|
process.exit(1);
|
|
16480
16503
|
}
|
|
@@ -16488,7 +16511,7 @@ async function createCommand(args2) {
|
|
|
16488
16511
|
}
|
|
16489
16512
|
console.error(`Multiple new hub folders found in ${wsLabel}/. Pass the folder name (\`wayai create <folder>\`) or run from inside one:`);
|
|
16490
16513
|
for (const h of newHubs) {
|
|
16491
|
-
console.error(` ${
|
|
16514
|
+
console.error(` ${path19.basename(h.hubFolder)} (new \u2014 "${h.hubName}")`);
|
|
16492
16515
|
}
|
|
16493
16516
|
process.exit(1);
|
|
16494
16517
|
}
|
|
@@ -16600,7 +16623,7 @@ __export(replicate_exports, {
|
|
|
16600
16623
|
replicateCommand: () => replicateCommand
|
|
16601
16624
|
});
|
|
16602
16625
|
import * as fs17 from "fs";
|
|
16603
|
-
import * as
|
|
16626
|
+
import * as path20 from "path";
|
|
16604
16627
|
function parseArgs8(args2) {
|
|
16605
16628
|
let label;
|
|
16606
16629
|
let hubSelector;
|
|
@@ -16646,9 +16669,8 @@ async function replicateCommand(args2) {
|
|
|
16646
16669
|
payload.preview_label,
|
|
16647
16670
|
payload.branch_name
|
|
16648
16671
|
);
|
|
16649
|
-
fs17.mkdirSync(
|
|
16650
|
-
|
|
16651
|
-
await downloadBinaryResourceFiles(hubFolder, payload);
|
|
16672
|
+
fs17.mkdirSync(path20.dirname(hubFolder), { recursive: true });
|
|
16673
|
+
await materializeHubFolder(hubFolder, payload);
|
|
16652
16674
|
hubFolder = autoRenameHubFolder(
|
|
16653
16675
|
hubFolder,
|
|
16654
16676
|
payload.hub.name,
|
|
@@ -16658,15 +16680,15 @@ async function replicateCommand(args2) {
|
|
|
16658
16680
|
payload.branch_name
|
|
16659
16681
|
);
|
|
16660
16682
|
autoBindIfUnbound(previewHubId);
|
|
16661
|
-
console.log(`Preview written to ${
|
|
16662
|
-
console.log(` Switch to it with: wayai use ${
|
|
16683
|
+
console.log(`Preview written to ${path20.basename(hubFolder)}`);
|
|
16684
|
+
console.log(` Switch to it with: wayai use ${path20.basename(hubFolder)}`);
|
|
16663
16685
|
}
|
|
16664
16686
|
var init_replicate = __esm({
|
|
16665
16687
|
"src/commands/replicate.ts"() {
|
|
16666
16688
|
"use strict";
|
|
16667
16689
|
init_auth();
|
|
16668
16690
|
init_api_client();
|
|
16669
|
-
|
|
16691
|
+
init_hub_materializer();
|
|
16670
16692
|
init_pull();
|
|
16671
16693
|
init_workspace();
|
|
16672
16694
|
init_layout();
|
|
@@ -16681,7 +16703,7 @@ var relabel_exports = {};
|
|
|
16681
16703
|
__export(relabel_exports, {
|
|
16682
16704
|
relabelCommand: () => relabelCommand
|
|
16683
16705
|
});
|
|
16684
|
-
import * as
|
|
16706
|
+
import * as path21 from "path";
|
|
16685
16707
|
function parseArgs9(args2) {
|
|
16686
16708
|
let label;
|
|
16687
16709
|
let clear = false;
|
|
@@ -16725,7 +16747,7 @@ async function relabelCommand(args2) {
|
|
|
16725
16747
|
} else {
|
|
16726
16748
|
console.error(`Multiple hubs found in ${wsLabel}/. Pass --hub <uuid|folder-name> to choose, or run from inside a hub folder:`);
|
|
16727
16749
|
for (const h of previewHubs) {
|
|
16728
|
-
console.error(` ${
|
|
16750
|
+
console.error(` ${path21.basename(h.hubFolder)} (${h.hubId})`);
|
|
16729
16751
|
}
|
|
16730
16752
|
}
|
|
16731
16753
|
process.exit(1);
|
|
@@ -16734,7 +16756,7 @@ async function relabelCommand(args2) {
|
|
|
16734
16756
|
console.log("This is a read-only production mirror \u2014 production hubs have no preview label. Relabel the linked preview hub instead.");
|
|
16735
16757
|
return;
|
|
16736
16758
|
}
|
|
16737
|
-
assertHubMatchesBinding(target.hubId,
|
|
16759
|
+
assertHubMatchesBinding(target.hubId, path21.basename(target.hubFolder));
|
|
16738
16760
|
console.log(normalizedLabel ? `Setting preview label to "${normalizedLabel}"...` : "Clearing preview label...");
|
|
16739
16761
|
const { data } = await client.relabelPreview(target.hubId, normalizedLabel);
|
|
16740
16762
|
const row = data[0];
|
|
@@ -16750,7 +16772,7 @@ async function relabelCommand(args2) {
|
|
|
16750
16772
|
);
|
|
16751
16773
|
console.log(serverLabel ? `Preview label set to "${serverLabel}".` : "Preview label cleared.");
|
|
16752
16774
|
if (finalFolder !== target.hubFolder) {
|
|
16753
|
-
console.log(`Hub folder is now ${
|
|
16775
|
+
console.log(`Hub folder is now ${path21.basename(finalFolder)}`);
|
|
16754
16776
|
}
|
|
16755
16777
|
}
|
|
16756
16778
|
var init_relabel = __esm({
|
|
@@ -16776,7 +16798,7 @@ __export(publish_exports, {
|
|
|
16776
16798
|
renderHubDiff: () => renderHubDiff,
|
|
16777
16799
|
runPublish: () => runPublish
|
|
16778
16800
|
});
|
|
16779
|
-
import * as
|
|
16801
|
+
import * as path22 from "path";
|
|
16780
16802
|
function parseArgs10(args2) {
|
|
16781
16803
|
let autoConfirm = false;
|
|
16782
16804
|
let hubSelector;
|
|
@@ -16890,7 +16912,7 @@ async function publishCommand(args2) {
|
|
|
16890
16912
|
return;
|
|
16891
16913
|
}
|
|
16892
16914
|
}
|
|
16893
|
-
assertHubMatchesBinding(hubId, hubFolder ?
|
|
16915
|
+
assertHubMatchesBinding(hubId, hubFolder ? path22.basename(hubFolder) : void 0);
|
|
16894
16916
|
await runPublish(client, { hubId, localConfig, organizationId, autoConfirm });
|
|
16895
16917
|
}
|
|
16896
16918
|
var STATUS_STYLE;
|
|
@@ -16923,7 +16945,7 @@ var use_exports = {};
|
|
|
16923
16945
|
__export(use_exports, {
|
|
16924
16946
|
useCommand: () => useCommand
|
|
16925
16947
|
});
|
|
16926
|
-
import * as
|
|
16948
|
+
import * as path23 from "path";
|
|
16927
16949
|
async function useCommand(args2) {
|
|
16928
16950
|
const target = args2[0];
|
|
16929
16951
|
if (!target || target.startsWith("-")) {
|
|
@@ -16945,7 +16967,7 @@ async function useCommand(args2) {
|
|
|
16945
16967
|
const match = findHubByFolderName(workspaceDir, target);
|
|
16946
16968
|
if (!match) {
|
|
16947
16969
|
const newHubs = scanNewHubs(workspaceDir);
|
|
16948
|
-
const pending = newHubs.find((h) =>
|
|
16970
|
+
const pending = newHubs.find((h) => path23.basename(h.hubFolder) === target);
|
|
16949
16971
|
if (pending) {
|
|
16950
16972
|
console.error(
|
|
16951
16973
|
`"${target}" is a new hub that hasn't been created on the platform yet, so it has no id to bind to.
|
|
@@ -16956,10 +16978,10 @@ Create it first \u2014 the worktree binds automatically afterward:
|
|
|
16956
16978
|
}
|
|
16957
16979
|
console.error(`No hub matching "${target}" found in ${wsLabel}/. Pass a UUID or a folder name from:`);
|
|
16958
16980
|
for (const h of scanWorkspaceHubs(workspaceDir)) {
|
|
16959
|
-
console.error(` ${
|
|
16981
|
+
console.error(` ${path23.basename(h.hubFolder)} (${h.hubId})`);
|
|
16960
16982
|
}
|
|
16961
16983
|
for (const h of newHubs) {
|
|
16962
|
-
console.error(` ${
|
|
16984
|
+
console.error(` ${path23.basename(h.hubFolder)} (new \u2014 "${h.hubName}", run \`wayai create ${path23.basename(h.hubFolder)}\`)`);
|
|
16963
16985
|
}
|
|
16964
16986
|
process.exit(1);
|
|
16965
16987
|
}
|
|
@@ -16999,10 +17021,10 @@ __export(migrate_exports, {
|
|
|
16999
17021
|
});
|
|
17000
17022
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
17001
17023
|
import * as fs18 from "fs";
|
|
17002
|
-
import * as
|
|
17024
|
+
import * as path24 from "path";
|
|
17003
17025
|
function isTracked(gitRoot, p) {
|
|
17004
17026
|
try {
|
|
17005
|
-
execFileSync3("git", ["ls-files", "--error-unmatch", "--",
|
|
17027
|
+
execFileSync3("git", ["ls-files", "--error-unmatch", "--", path24.relative(gitRoot, p)], {
|
|
17006
17028
|
cwd: gitRoot,
|
|
17007
17029
|
stdio: ["pipe", "pipe", "pipe"]
|
|
17008
17030
|
});
|
|
@@ -17012,10 +17034,10 @@ function isTracked(gitRoot, p) {
|
|
|
17012
17034
|
}
|
|
17013
17035
|
}
|
|
17014
17036
|
function moveDir(gitRoot, from, to) {
|
|
17015
|
-
fs18.mkdirSync(
|
|
17037
|
+
fs18.mkdirSync(path24.dirname(to), { recursive: true });
|
|
17016
17038
|
if (isTracked(gitRoot, from)) {
|
|
17017
17039
|
try {
|
|
17018
|
-
execFileSync3("git", ["mv",
|
|
17040
|
+
execFileSync3("git", ["mv", path24.relative(gitRoot, from), path24.relative(gitRoot, to)], {
|
|
17019
17041
|
cwd: gitRoot,
|
|
17020
17042
|
stdio: ["pipe", "pipe", "pipe"]
|
|
17021
17043
|
});
|
|
@@ -17032,12 +17054,12 @@ async function migrateCommand(_args) {
|
|
|
17032
17054
|
console.error("Not inside a git repository.");
|
|
17033
17055
|
process.exit(1);
|
|
17034
17056
|
}
|
|
17035
|
-
const rel = (p) =>
|
|
17036
|
-
const newWs =
|
|
17037
|
-
const legacyWs =
|
|
17038
|
-
const legacyOrg =
|
|
17039
|
-
const newHubs =
|
|
17040
|
-
const newOrg =
|
|
17057
|
+
const rel = (p) => path24.relative(gitRoot, p);
|
|
17058
|
+
const newWs = path24.join(gitRoot, WAYAI_LAYOUT.wsDir);
|
|
17059
|
+
const legacyWs = path24.join(gitRoot, WAYAI_LAYOUT.legacy.wsDir);
|
|
17060
|
+
const legacyOrg = path24.join(gitRoot, WAYAI_LAYOUT.legacy.orgAtRoot);
|
|
17061
|
+
const newHubs = path24.join(newWs, WAYAI_LAYOUT.hubsSubdir);
|
|
17062
|
+
const newOrg = path24.join(newWs, WAYAI_LAYOUT.orgSubdir);
|
|
17041
17063
|
const hasLegacyWs = isDirectory(legacyWs);
|
|
17042
17064
|
const hasLegacyOrg = isDirectory(legacyOrg);
|
|
17043
17065
|
if (!hasLegacyWs && !hasLegacyOrg) {
|
|
@@ -17112,7 +17134,7 @@ __export(send_message_exports, {
|
|
|
17112
17134
|
sendMessageCommand: () => sendMessageCommand
|
|
17113
17135
|
});
|
|
17114
17136
|
import * as fs19 from "fs";
|
|
17115
|
-
import * as
|
|
17137
|
+
import * as path25 from "path";
|
|
17116
17138
|
function statAttachment(filePath) {
|
|
17117
17139
|
let stat;
|
|
17118
17140
|
try {
|
|
@@ -17128,8 +17150,8 @@ function statAttachment(filePath) {
|
|
|
17128
17150
|
return { filePath, size: stat.size };
|
|
17129
17151
|
}
|
|
17130
17152
|
function readAttachment(filePath, size) {
|
|
17131
|
-
const fileName =
|
|
17132
|
-
const ext =
|
|
17153
|
+
const fileName = path25.basename(filePath);
|
|
17154
|
+
const ext = path25.extname(fileName).replace(/^\./, "");
|
|
17133
17155
|
return {
|
|
17134
17156
|
file_name: fileName,
|
|
17135
17157
|
file_binary: fs19.readFileSync(filePath).toString("base64"),
|
|
@@ -17179,7 +17201,7 @@ async function sendMessageCommand(args2) {
|
|
|
17179
17201
|
}
|
|
17180
17202
|
const stats = filePaths.map(statAttachment);
|
|
17181
17203
|
const filesTotal = stats.reduce((sum, s) => sum + base64Length(s.size), 0);
|
|
17182
|
-
const fileDetail = () => stats.map((s) => `${
|
|
17204
|
+
const fileDetail = () => stats.map((s) => `${path25.basename(s.filePath)} ${asMb(s.size)} MB`).join(", ");
|
|
17183
17205
|
if (filesTotal > MAX_MESSAGE_BODY_BYTES2) {
|
|
17184
17206
|
console.error(
|
|
17185
17207
|
`Error: ${fileDetail()} encode to ~${asMb(filesTotal)} MB, over the ${asMb(MAX_MESSAGE_BODY_BYTES2)} MB per-request limit.`
|
|
@@ -18640,7 +18662,7 @@ __export(eval_capture_exports, {
|
|
|
18640
18662
|
evalCaptureCommand: () => evalCaptureCommand
|
|
18641
18663
|
});
|
|
18642
18664
|
import * as fs20 from "fs";
|
|
18643
|
-
import * as
|
|
18665
|
+
import * as path26 from "path";
|
|
18644
18666
|
import * as yaml7 from "js-yaml";
|
|
18645
18667
|
function isValidSetName(name) {
|
|
18646
18668
|
if (name.length === 0 || name === "." || name === "..") return false;
|
|
@@ -18706,15 +18728,15 @@ async function evalCaptureCommand(args2) {
|
|
|
18706
18728
|
const setFolderName = targetSetName;
|
|
18707
18729
|
const scenarioName = parsed.evalName ?? `Capture ${parsed.conversationId.slice(0, 8)}`;
|
|
18708
18730
|
const slug = slugify(scenarioName);
|
|
18709
|
-
const evalsDir =
|
|
18710
|
-
const targetDir =
|
|
18711
|
-
const targetPath =
|
|
18712
|
-
if (!targetPath.startsWith(evalsDir +
|
|
18713
|
-
console.error(`Resolved path "${
|
|
18731
|
+
const evalsDir = path26.join(hubFolder, "evals");
|
|
18732
|
+
const targetDir = path26.join(evalsDir, setFolderName);
|
|
18733
|
+
const targetPath = path26.join(targetDir, `${slug}.yaml`);
|
|
18734
|
+
if (!targetPath.startsWith(evalsDir + path26.sep)) {
|
|
18735
|
+
console.error(`Resolved path "${path26.relative(hubFolder, targetPath)}" escapes evals/. Aborting.`);
|
|
18714
18736
|
process.exit(1);
|
|
18715
18737
|
}
|
|
18716
18738
|
if (fs20.existsSync(targetPath)) {
|
|
18717
|
-
console.error(`File already exists: ${
|
|
18739
|
+
console.error(`File already exists: ${path26.relative(hubFolder, targetPath)}. Use --name to choose a different name.`);
|
|
18718
18740
|
process.exit(1);
|
|
18719
18741
|
}
|
|
18720
18742
|
console.log("Resolving scenario set...");
|
|
@@ -18748,7 +18770,7 @@ async function evalCaptureCommand(args2) {
|
|
|
18748
18770
|
const yamlObj = buildEvalYamlObject(evalEntry, slug);
|
|
18749
18771
|
fs20.mkdirSync(targetDir, { recursive: true });
|
|
18750
18772
|
fs20.writeFileSync(targetPath, yaml7.dump(yamlObj, YAML_DUMP_OPTIONS), "utf-8");
|
|
18751
|
-
const relPath =
|
|
18773
|
+
const relPath = path26.relative(process.cwd(), targetPath);
|
|
18752
18774
|
console.log(`
|
|
18753
18775
|
Wrote ${relPath}`);
|
|
18754
18776
|
console.log("Run `wayai pull` to refresh the agent display name, then commit. The scenario is already on the platform.");
|
|
@@ -19976,22 +19998,22 @@ var init_set_connection_credential = __esm({
|
|
|
19976
19998
|
|
|
19977
19999
|
// src/lib/org-workspace.ts
|
|
19978
20000
|
import * as fs21 from "fs";
|
|
19979
|
-
import * as
|
|
20001
|
+
import * as path27 from "path";
|
|
19980
20002
|
import * as yaml8 from "js-yaml";
|
|
19981
20003
|
function getOrgDir(gitRoot) {
|
|
19982
20004
|
return resolveLayout(gitRoot).orgDir;
|
|
19983
20005
|
}
|
|
19984
20006
|
function orgManifestExists(orgDir) {
|
|
19985
|
-
return fs21.existsSync(
|
|
20007
|
+
return fs21.existsSync(path27.join(orgDir, ORG_MANIFEST_NAME));
|
|
19986
20008
|
}
|
|
19987
20009
|
function parseOrgResources(orgDir) {
|
|
19988
|
-
const manifestPath =
|
|
20010
|
+
const manifestPath = path27.join(orgDir, ORG_MANIFEST_NAME);
|
|
19989
20011
|
let manifest = {};
|
|
19990
20012
|
if (fs21.existsSync(manifestPath)) {
|
|
19991
20013
|
manifest = yaml8.load(fs21.readFileSync(manifestPath, "utf-8")) ?? {};
|
|
19992
20014
|
}
|
|
19993
20015
|
const rawResources = Array.isArray(manifest.resources) ? manifest.resources : [];
|
|
19994
|
-
const resourcesDir =
|
|
20016
|
+
const resourcesDir = path27.join(orgDir, "resources");
|
|
19995
20017
|
const resources = rawResources.map((res) => {
|
|
19996
20018
|
const resource = { name: res.name };
|
|
19997
20019
|
if (res.id) resource.id = res.id;
|
|
@@ -20003,7 +20025,7 @@ function parseOrgResources(orgDir) {
|
|
|
20003
20025
|
if (res.environment) resource.environment = res.environment;
|
|
20004
20026
|
if (Array.isArray(res.tags)) resource.tags = res.tags;
|
|
20005
20027
|
if (Array.isArray(res.folders)) resource.folders = res.folders;
|
|
20006
|
-
const resDir =
|
|
20028
|
+
const resDir = path27.join(resourcesDir, slugify(resource.name));
|
|
20007
20029
|
if (fs21.existsSync(resDir)) {
|
|
20008
20030
|
const files = scanResourceFiles(resDir, "");
|
|
20009
20031
|
if (files.length > 0) resource.files = files;
|
|
@@ -20020,21 +20042,21 @@ function writeOrgResources(orgDir, payload) {
|
|
|
20020
20042
|
return rest;
|
|
20021
20043
|
});
|
|
20022
20044
|
fs21.writeFileSync(
|
|
20023
|
-
|
|
20045
|
+
path27.join(orgDir, ORG_MANIFEST_NAME),
|
|
20024
20046
|
yaml8.dump({ version: 1, resources: manifestResources }, YAML_DUMP_OPTIONS),
|
|
20025
20047
|
"utf-8"
|
|
20026
20048
|
);
|
|
20027
|
-
const resourcesDir =
|
|
20049
|
+
const resourcesDir = path27.join(orgDir, "resources");
|
|
20028
20050
|
const currentSlugs = /* @__PURE__ */ new Set();
|
|
20029
20051
|
for (const resource of resources) {
|
|
20030
20052
|
const resSlug = slugify(resource.name);
|
|
20031
20053
|
currentSlugs.add(resSlug);
|
|
20032
|
-
writeResourceFileTree(
|
|
20054
|
+
writeResourceFileTree(path27.join(resourcesDir, resSlug), resource.files || [], orgDir);
|
|
20033
20055
|
}
|
|
20034
20056
|
if (fs21.existsSync(resourcesDir)) {
|
|
20035
20057
|
for (const entry of fs21.readdirSync(resourcesDir, { withFileTypes: true })) {
|
|
20036
20058
|
if (entry.isDirectory() && !currentSlugs.has(entry.name)) {
|
|
20037
|
-
fs21.rmSync(
|
|
20059
|
+
fs21.rmSync(path27.join(resourcesDir, entry.name), { recursive: true, force: true });
|
|
20038
20060
|
}
|
|
20039
20061
|
}
|
|
20040
20062
|
}
|
|
@@ -20043,7 +20065,7 @@ async function downloadOrgBinaryFiles(orgDir, payload) {
|
|
|
20043
20065
|
let count = 0;
|
|
20044
20066
|
for (const resource of payload.resources ?? []) {
|
|
20045
20067
|
if (!resource.files) continue;
|
|
20046
|
-
const resDir =
|
|
20068
|
+
const resDir = path27.join(orgDir, "resources", slugify(resource.name));
|
|
20047
20069
|
count += await downloadBinaryFiles(resDir, resource.files, orgDir);
|
|
20048
20070
|
}
|
|
20049
20071
|
if (count > 0) console.log(`Downloaded ${count} binary resource file(s).`);
|
|
@@ -20300,13 +20322,13 @@ var init_list = __esm({
|
|
|
20300
20322
|
|
|
20301
20323
|
// src/lib/template-files.ts
|
|
20302
20324
|
import * as fs22 from "fs";
|
|
20303
|
-
import * as
|
|
20325
|
+
import * as path28 from "path";
|
|
20304
20326
|
function readDirToFileMap(rootDir) {
|
|
20305
20327
|
const out = {};
|
|
20306
20328
|
function walk(dir, prefix) {
|
|
20307
20329
|
const entries = fs22.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
20308
20330
|
for (const entry of entries) {
|
|
20309
|
-
const abs =
|
|
20331
|
+
const abs = path28.join(dir, entry.name);
|
|
20310
20332
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
20311
20333
|
if (entry.isDirectory()) walk(abs, rel);
|
|
20312
20334
|
else if (entry.isFile()) out[rel] = fs22.readFileSync(abs, "utf-8");
|
|
@@ -20321,7 +20343,7 @@ function detectCollisions(targetDir, files) {
|
|
|
20321
20343
|
if (!isSafeTemplatePath2(rel)) {
|
|
20322
20344
|
throw new Error(`Refusing to inspect unsafe template path: ${rel}`);
|
|
20323
20345
|
}
|
|
20324
|
-
if (fs22.existsSync(
|
|
20346
|
+
if (fs22.existsSync(path28.join(targetDir, rel))) collisions.push(rel);
|
|
20325
20347
|
}
|
|
20326
20348
|
return collisions.sort((a, b) => a.localeCompare(b));
|
|
20327
20349
|
}
|
|
@@ -20331,8 +20353,8 @@ function writeFileMap(targetDir, files) {
|
|
|
20331
20353
|
if (!isSafeTemplatePath2(rel)) {
|
|
20332
20354
|
throw new Error(`Refusing to write unsafe template path: ${rel}`);
|
|
20333
20355
|
}
|
|
20334
|
-
const abs =
|
|
20335
|
-
fs22.mkdirSync(
|
|
20356
|
+
const abs = path28.join(targetDir, rel);
|
|
20357
|
+
fs22.mkdirSync(path28.dirname(abs), { recursive: true });
|
|
20336
20358
|
fs22.writeFileSync(abs, body, "utf-8");
|
|
20337
20359
|
written.push(rel);
|
|
20338
20360
|
}
|
|
@@ -20343,7 +20365,7 @@ function listDirRelFiles(dir) {
|
|
|
20343
20365
|
function walk(d, prefix) {
|
|
20344
20366
|
for (const entry of fs22.readdirSync(d, { withFileTypes: true })) {
|
|
20345
20367
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
20346
|
-
if (entry.isDirectory()) walk(
|
|
20368
|
+
if (entry.isDirectory()) walk(path28.join(d, entry.name), rel);
|
|
20347
20369
|
else if (entry.isFile()) out.push(rel);
|
|
20348
20370
|
}
|
|
20349
20371
|
}
|
|
@@ -20370,7 +20392,7 @@ var templates_exports = {};
|
|
|
20370
20392
|
__export(templates_exports, {
|
|
20371
20393
|
templateCommand: () => templateCommand
|
|
20372
20394
|
});
|
|
20373
|
-
import * as
|
|
20395
|
+
import * as path29 from "path";
|
|
20374
20396
|
async function templateCommand(args2) {
|
|
20375
20397
|
const [sub, ...rest] = args2;
|
|
20376
20398
|
switch (sub) {
|
|
@@ -20486,8 +20508,8 @@ async function runTemplatePull(rest) {
|
|
|
20486
20508
|
console.log(`Template "${slug}" has no WayAI slice${hint}`);
|
|
20487
20509
|
return;
|
|
20488
20510
|
}
|
|
20489
|
-
const targetDir =
|
|
20490
|
-
const relTarget =
|
|
20511
|
+
const targetDir = path29.join(resolveLayout(gitRoot).hubsDir, slug);
|
|
20512
|
+
const relTarget = path29.relative(gitRoot, targetDir);
|
|
20491
20513
|
const collisions = detectCollisions(targetDir, detail.files);
|
|
20492
20514
|
if (dryRun) {
|
|
20493
20515
|
const planned = Object.keys(detail.files).sort((a, b) => a.localeCompare(b));
|
|
@@ -20678,7 +20700,7 @@ __export(admin_exports, {
|
|
|
20678
20700
|
adminCommand: () => adminCommand
|
|
20679
20701
|
});
|
|
20680
20702
|
import * as fs23 from "fs";
|
|
20681
|
-
import * as
|
|
20703
|
+
import * as path30 from "path";
|
|
20682
20704
|
import * as yaml10 from "js-yaml";
|
|
20683
20705
|
async function adminCommand(args2) {
|
|
20684
20706
|
const [group, ...afterGroup] = args2;
|
|
@@ -21132,10 +21154,10 @@ function loadTemplateManifest(slug) {
|
|
|
21132
21154
|
console.error("Not in a git repository. Run from the tutorials/ authoring repo.");
|
|
21133
21155
|
process.exit(1);
|
|
21134
21156
|
}
|
|
21135
|
-
const templateDir =
|
|
21136
|
-
const manifestPath =
|
|
21157
|
+
const templateDir = path30.join(resolveTemplatesDir(gitRoot), slug);
|
|
21158
|
+
const manifestPath = path30.join(templateDir, "template.yaml");
|
|
21137
21159
|
if (!fs23.existsSync(manifestPath)) {
|
|
21138
|
-
console.error(`template.yaml not found at ${
|
|
21160
|
+
console.error(`template.yaml not found at ${path30.relative(gitRoot, manifestPath)}`);
|
|
21139
21161
|
process.exit(1);
|
|
21140
21162
|
}
|
|
21141
21163
|
let manifest;
|
|
@@ -21170,19 +21192,19 @@ async function runTemplatePush(positional) {
|
|
|
21170
21192
|
const wayaiEntry = manifest.wayai.locales[locale];
|
|
21171
21193
|
let wayaiFiles;
|
|
21172
21194
|
if (wayaiEntry) {
|
|
21173
|
-
const sliceDir =
|
|
21195
|
+
const sliceDir = path30.resolve(templateDir, wayaiEntry.source);
|
|
21174
21196
|
if (!fs23.existsSync(sliceDir)) {
|
|
21175
21197
|
console.error(`wayai source not found for ${locale}: ${wayaiEntry.source} (resolved ${sliceDir})`);
|
|
21176
21198
|
process.exit(1);
|
|
21177
21199
|
}
|
|
21178
21200
|
if (!isInsideTemplateDir(sliceDir, templateDir)) {
|
|
21179
21201
|
console.warn(
|
|
21180
|
-
`Note: ${locale} source points at a live hub (${wayaiEntry.source}); it can go stale on a hub recreate. Run \`wayai admin template freeze ${slug} --locale ${locale} --from ${
|
|
21202
|
+
`Note: ${locale} source points at a live hub (${wayaiEntry.source}); it can go stale on a hub recreate. Run \`wayai admin template freeze ${slug} --locale ${locale} --from ${path30.basename(sliceDir)}\` to commit a stable frozen slice.`
|
|
21181
21203
|
);
|
|
21182
21204
|
}
|
|
21183
21205
|
wayaiFiles = readDirToFileMap(sliceDir);
|
|
21184
21206
|
}
|
|
21185
|
-
const readmePath =
|
|
21207
|
+
const readmePath = path30.join(templateDir, `README.${locale}.md`);
|
|
21186
21208
|
const readmeMarkdown = fs23.existsSync(readmePath) ? fs23.readFileSync(readmePath, "utf-8") : "";
|
|
21187
21209
|
locales[locale] = {
|
|
21188
21210
|
readmeMarkdown,
|
|
@@ -21194,9 +21216,9 @@ async function runTemplatePush(positional) {
|
|
|
21194
21216
|
}
|
|
21195
21217
|
let hero;
|
|
21196
21218
|
if (manifest.hero) {
|
|
21197
|
-
const heroPath =
|
|
21219
|
+
const heroPath = path30.resolve(templateDir, manifest.hero);
|
|
21198
21220
|
if (fs23.existsSync(heroPath)) {
|
|
21199
|
-
hero = { filename:
|
|
21221
|
+
hero = { filename: path30.basename(heroPath), dataBase64: fs23.readFileSync(heroPath).toString("base64") };
|
|
21200
21222
|
} else {
|
|
21201
21223
|
console.error(`Warning: hero not found at ${manifest.hero}; skipping.`);
|
|
21202
21224
|
}
|
|
@@ -21222,7 +21244,7 @@ async function runTemplatePush(positional) {
|
|
|
21222
21244
|
}
|
|
21223
21245
|
}
|
|
21224
21246
|
function isInsideTemplateDir(dest, templateDir) {
|
|
21225
|
-
return dest === templateDir || dest.startsWith(templateDir +
|
|
21247
|
+
return dest === templateDir || dest.startsWith(templateDir + path30.sep);
|
|
21226
21248
|
}
|
|
21227
21249
|
async function runTemplateFreeze(positional, flagArgs) {
|
|
21228
21250
|
const slug = positional[0];
|
|
@@ -21283,7 +21305,7 @@ async function runTemplateFreeze(positional, flagArgs) {
|
|
|
21283
21305
|
process.exit(1);
|
|
21284
21306
|
}
|
|
21285
21307
|
const entry = manifest.wayai.locales[targetLocale];
|
|
21286
|
-
const dest =
|
|
21308
|
+
const dest = path30.resolve(templateDir, entry.source);
|
|
21287
21309
|
if (!isInsideTemplateDir(dest, templateDir) || dest === templateDir) {
|
|
21288
21310
|
console.error(
|
|
21289
21311
|
`source for "${targetLocale}" must be a committed path inside ${slug}/ (e.g. "./${targetLocale}/slice"), but resolved to:
|
|
@@ -21302,7 +21324,7 @@ Set it to a subdir of the template folder and re-run freeze.`
|
|
|
21302
21324
|
const hubs = scanWorkspaceHubs(hubsDir);
|
|
21303
21325
|
if (hubs.length) {
|
|
21304
21326
|
console.error("Available hubs:");
|
|
21305
|
-
for (const h of hubs) console.error(` ${
|
|
21327
|
+
for (const h of hubs) console.error(` ${path30.basename(h.hubFolder)} (${h.hubId})`);
|
|
21306
21328
|
}
|
|
21307
21329
|
console.error(`
|
|
21308
21330
|
Pass --from <folder|uuid>${selector ? "." : " or run `wayai use <hub>` first."}`);
|
|
@@ -21310,12 +21332,12 @@ Pass --from <folder|uuid>${selector ? "." : " or run `wayai use <hub>` first."}`
|
|
|
21310
21332
|
}
|
|
21311
21333
|
const liveFiles = readDirToFileMap(hubFolder);
|
|
21312
21334
|
if (Object.keys(liveFiles).length === 0) {
|
|
21313
|
-
console.error(`Source hub folder has no files to freeze: ${
|
|
21335
|
+
console.error(`Source hub folder has no files to freeze: ${path30.relative(gitRoot, hubFolder)}`);
|
|
21314
21336
|
process.exit(1);
|
|
21315
21337
|
}
|
|
21316
21338
|
const sanitized = sanitizeWayaiSliceFiles(liveFiles);
|
|
21317
|
-
const relDest =
|
|
21318
|
-
const relSrc =
|
|
21339
|
+
const relDest = path30.relative(gitRoot, dest);
|
|
21340
|
+
const relSrc = path30.relative(gitRoot, hubFolder);
|
|
21319
21341
|
if (dryRun) {
|
|
21320
21342
|
const existing = new Set(listDirRelFiles(dest));
|
|
21321
21343
|
const nextKeys = new Set(Object.keys(sanitized));
|
|
@@ -21375,7 +21397,7 @@ async function runTemplatePull2(positional, flagArgs) {
|
|
|
21375
21397
|
console.log(JSON.stringify(detail, null, 2));
|
|
21376
21398
|
return;
|
|
21377
21399
|
}
|
|
21378
|
-
const target = outDir ?
|
|
21400
|
+
const target = outDir ? path30.resolve(outDir) : path30.join(process.cwd(), "template-store-pull", slug);
|
|
21379
21401
|
const written = writeFileMap(target, detail.files);
|
|
21380
21402
|
console.log(`Pulled "${slug}" from the store \u2192 ${target} (${written.length} file${written.length === 1 ? "" : "s"}).`);
|
|
21381
21403
|
const uses = detail.uses ?? [];
|
|
@@ -21398,13 +21420,13 @@ async function runSkillInstall(positional) {
|
|
|
21398
21420
|
throw err;
|
|
21399
21421
|
}
|
|
21400
21422
|
const root = findGitRoot() ?? process.cwd();
|
|
21401
|
-
const present = HARNESS_SKILL_DIRS.filter((dir) => fs23.existsSync(
|
|
21423
|
+
const present = HARNESS_SKILL_DIRS.filter((dir) => fs23.existsSync(path30.join(root, dir)));
|
|
21402
21424
|
const targets = present.length > 0 ? present : HARNESS_SKILL_DIRS;
|
|
21403
21425
|
const fileCount = Object.keys(res.files).length;
|
|
21404
21426
|
const relDirs = targets.map((harness) => {
|
|
21405
|
-
const targetDir =
|
|
21427
|
+
const targetDir = path30.join(root, harness, "skills", name);
|
|
21406
21428
|
writeFileMap(targetDir, res.files);
|
|
21407
|
-
return `${
|
|
21429
|
+
return `${path30.relative(root, targetDir)}/`;
|
|
21408
21430
|
});
|
|
21409
21431
|
console.log(`Installed skill "${name}" (${fileCount} file${fileCount === 1 ? "" : "s"}) \u2192 ${relDirs.join(", ")}`);
|
|
21410
21432
|
console.log("Reload your agent (e.g. restart Claude Code) to pick up the skill.");
|
|
@@ -22748,7 +22770,7 @@ var init_update = __esm({
|
|
|
22748
22770
|
init_sentry();
|
|
22749
22771
|
import { readFileSync as readFileSync20 } from "fs";
|
|
22750
22772
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
22751
|
-
import { dirname as dirname9, join as
|
|
22773
|
+
import { dirname as dirname9, join as join28 } from "path";
|
|
22752
22774
|
|
|
22753
22775
|
// src/lib/errors.ts
|
|
22754
22776
|
init_api_client();
|
|
@@ -22928,7 +22950,7 @@ Run \`wayai admin skill install\` to update.`);
|
|
|
22928
22950
|
|
|
22929
22951
|
// src/index.ts
|
|
22930
22952
|
var __dirname = dirname9(fileURLToPath2(import.meta.url));
|
|
22931
|
-
var pkg = JSON.parse(readFileSync20(
|
|
22953
|
+
var pkg = JSON.parse(readFileSync20(join28(__dirname, "..", "package.json"), "utf-8"));
|
|
22932
22954
|
var [, , command, ...args] = process.argv;
|
|
22933
22955
|
var isBackgroundRefresh = command === REFRESH_COMMAND;
|
|
22934
22956
|
if (!isBackgroundRefresh) initSentry(command, pkg.version);
|