@rogatio/cli 5.2.3 → 5.3.0
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/node/index.js +2352 -2094
- package/package.json +1 -1
package/dist/node/index.js
CHANGED
|
@@ -1161,123 +1161,474 @@ function validateProjectDetailed(value) {
|
|
|
1161
1161
|
import { isIP as isIP2 } from "node:net";
|
|
1162
1162
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
1163
1163
|
import { createServer } from "node:http";
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
import { join as posixJoin3 } from "node:path/posix";
|
|
1186
|
-
var __create = Object.create;
|
|
1187
|
-
var __defProp = Object.defineProperty;
|
|
1188
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
1189
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
1190
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
1191
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
1192
|
-
var __esm = (fn, res, err) => function __init() {
|
|
1193
|
-
if (err) throw err[0];
|
|
1194
|
-
try {
|
|
1195
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
1196
|
-
} catch (e) {
|
|
1197
|
-
throw err = [e], e;
|
|
1198
|
-
}
|
|
1199
|
-
};
|
|
1200
|
-
var __commonJS = (cb, mod) => function __require() {
|
|
1201
|
-
try {
|
|
1202
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
1203
|
-
} catch (e) {
|
|
1204
|
-
throw mod = 0, e;
|
|
1205
|
-
}
|
|
1206
|
-
};
|
|
1207
|
-
var __export = (target, all) => {
|
|
1208
|
-
for (var name in all)
|
|
1209
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
1164
|
+
|
|
1165
|
+
// packages/compiler/dist/node/index.js
|
|
1166
|
+
var ISSUE_CODES = {
|
|
1167
|
+
required: "schema.required",
|
|
1168
|
+
additionalProperties: "schema.unknown-property",
|
|
1169
|
+
type: "schema.invalid-type",
|
|
1170
|
+
format: "schema.invalid-format",
|
|
1171
|
+
const: "schema.invalid-value",
|
|
1172
|
+
enum: "schema.invalid-value",
|
|
1173
|
+
pattern: "schema.invalid-value",
|
|
1174
|
+
minLength: "schema.out-of-range",
|
|
1175
|
+
maxLength: "schema.out-of-range",
|
|
1176
|
+
minItems: "schema.out-of-range",
|
|
1177
|
+
maxItems: "schema.out-of-range",
|
|
1178
|
+
minimum: "schema.out-of-range",
|
|
1179
|
+
maximum: "schema.out-of-range",
|
|
1180
|
+
uniqueItems: "schema.out-of-range",
|
|
1181
|
+
ownProperties: "schema.invalid-structure",
|
|
1182
|
+
uniqueId: "schema.duplicate-id",
|
|
1183
|
+
effectiveOrigin: "schema.no-effective-origin",
|
|
1184
|
+
maxRulesPerProject: "schema.rule-limit"
|
|
1210
1185
|
};
|
|
1211
|
-
var
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1186
|
+
var SAFE_PARAM_KEYS = /* @__PURE__ */ new Set([
|
|
1187
|
+
"additionalProperty",
|
|
1188
|
+
"actual",
|
|
1189
|
+
"allowedValue",
|
|
1190
|
+
"allowedValues",
|
|
1191
|
+
"format",
|
|
1192
|
+
"i",
|
|
1193
|
+
"j",
|
|
1194
|
+
"limit",
|
|
1195
|
+
"missingProperty",
|
|
1196
|
+
"previousPath",
|
|
1197
|
+
"type",
|
|
1198
|
+
"headerName",
|
|
1199
|
+
"headerOperation",
|
|
1200
|
+
"headerDirection"
|
|
1201
|
+
]);
|
|
1202
|
+
var MESSAGES = {
|
|
1203
|
+
"schema.required": "Required project data is missing.",
|
|
1204
|
+
"schema.unknown-property": "The project contains an unknown property.",
|
|
1205
|
+
"schema.invalid-type": "The project contains a value with an invalid type.",
|
|
1206
|
+
"schema.invalid-format": "The project contains a value with an invalid format.",
|
|
1207
|
+
"schema.invalid-value": "The project contains an invalid value.",
|
|
1208
|
+
"schema.out-of-range": "The project contains a value outside its allowed bounds.",
|
|
1209
|
+
"schema.invalid-structure": "The project contains invalid structure.",
|
|
1210
|
+
"schema.duplicate-id": "Project and rule IDs must be unique.",
|
|
1211
|
+
"schema.no-effective-origin": "Each rule must have at least one effective origin.",
|
|
1212
|
+
"schema.rule-limit": "The project contains too many rules.",
|
|
1213
|
+
"compiler.invariant": "The compiler could not normalize validated project data.",
|
|
1214
|
+
"compiler.forbidden-header": "The header name is forbidden for this direction.",
|
|
1215
|
+
"compiler.header-value-required": "Header value is required for set and append operations.",
|
|
1216
|
+
"compiler.header-value-unexpected": "Header value must not be provided for remove operation.",
|
|
1217
|
+
"compiler.invalid-header-direction": "Invalid header direction.",
|
|
1218
|
+
"compiler.invalid-header-operation": "Invalid header operation."
|
|
1218
1219
|
};
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
var exporter = makeExporter(Reflect3);
|
|
1235
|
-
if (typeof root.Reflect !== "undefined") {
|
|
1236
|
-
exporter = makeExporter(root.Reflect, exporter);
|
|
1237
|
-
}
|
|
1238
|
-
factory(exporter, root);
|
|
1239
|
-
if (typeof root.Reflect === "undefined") {
|
|
1240
|
-
root.Reflect = Reflect3;
|
|
1241
|
-
}
|
|
1242
|
-
function makeExporter(target, previous) {
|
|
1243
|
-
return function(key, value) {
|
|
1244
|
-
Object.defineProperty(target, key, { configurable: true, writable: true, value });
|
|
1245
|
-
if (previous)
|
|
1246
|
-
previous(key, value);
|
|
1247
|
-
};
|
|
1248
|
-
}
|
|
1249
|
-
function functionThis() {
|
|
1250
|
-
try {
|
|
1251
|
-
return Function("return this;")();
|
|
1252
|
-
} catch (_) {
|
|
1253
|
-
}
|
|
1220
|
+
function copySafeParams(params) {
|
|
1221
|
+
const safe = {};
|
|
1222
|
+
for (const key of Object.keys(params)) {
|
|
1223
|
+
if (!SAFE_PARAM_KEYS.has(key)) continue;
|
|
1224
|
+
const value = params[key];
|
|
1225
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null) {
|
|
1226
|
+
safe[key] = value;
|
|
1227
|
+
continue;
|
|
1228
|
+
}
|
|
1229
|
+
if (Array.isArray(value)) {
|
|
1230
|
+
const values = [];
|
|
1231
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
1232
|
+
const item = value[index];
|
|
1233
|
+
if (typeof item === "string" || typeof item === "number" || typeof item === "boolean" || item === null) {
|
|
1234
|
+
values.push(item);
|
|
1254
1235
|
}
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1236
|
+
}
|
|
1237
|
+
safe[key] = values;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
return safe;
|
|
1241
|
+
}
|
|
1242
|
+
function compareCodeUnits(left, right) {
|
|
1243
|
+
if (left < right) return -1;
|
|
1244
|
+
if (left > right) return 1;
|
|
1245
|
+
return 0;
|
|
1246
|
+
}
|
|
1247
|
+
function compareDiagnostics(left, right) {
|
|
1248
|
+
const leftParams = stableParams(left.params);
|
|
1249
|
+
const rightParams = stableParams(right.params);
|
|
1250
|
+
return compareCodeUnits(left.path, right.path) || compareCodeUnits(left.code, right.code) || compareCodeUnits(left.message, right.message) || compareCodeUnits(leftParams, rightParams);
|
|
1251
|
+
}
|
|
1252
|
+
function stableParams(params) {
|
|
1253
|
+
const keys = Object.keys(params).sort(compareCodeUnits);
|
|
1254
|
+
return keys.map((key) => `${JSON.stringify(key)}:${JSON.stringify(params[key])}`).join(",");
|
|
1255
|
+
}
|
|
1256
|
+
function mapValidationIssues(issues) {
|
|
1257
|
+
return issues.map((issue) => {
|
|
1258
|
+
const code = Object.hasOwn(ISSUE_CODES, issue.keyword) ? ISSUE_CODES[issue.keyword] : "schema.invalid-value";
|
|
1259
|
+
return {
|
|
1260
|
+
code,
|
|
1261
|
+
severity: "error",
|
|
1262
|
+
path: issue.instancePath,
|
|
1263
|
+
message: MESSAGES[code],
|
|
1264
|
+
params: copySafeParams(issue.params)
|
|
1265
|
+
};
|
|
1266
|
+
}).sort(compareDiagnostics);
|
|
1267
|
+
}
|
|
1268
|
+
function invariantDiagnostic(path2 = "") {
|
|
1269
|
+
return {
|
|
1270
|
+
code: "compiler.invariant",
|
|
1271
|
+
severity: "error",
|
|
1272
|
+
path: path2,
|
|
1273
|
+
message: MESSAGES["compiler.invariant"],
|
|
1274
|
+
params: {}
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
function compareCodeUnits2(left, right) {
|
|
1278
|
+
if (left < right) return -1;
|
|
1279
|
+
if (left > right) return 1;
|
|
1280
|
+
return 0;
|
|
1281
|
+
}
|
|
1282
|
+
function snapshotOwnData2(value, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
1283
|
+
if (value === null || typeof value !== "object") {
|
|
1284
|
+
return { valid: true, value };
|
|
1285
|
+
}
|
|
1286
|
+
if (ancestors.has(value)) return { valid: false };
|
|
1287
|
+
ancestors.add(value);
|
|
1288
|
+
try {
|
|
1289
|
+
if (Object.getOwnPropertySymbols(value).length > 0) {
|
|
1290
|
+
return { valid: false };
|
|
1291
|
+
}
|
|
1292
|
+
if (Array.isArray(value)) {
|
|
1293
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
1294
|
+
if (lengthDescriptor === void 0 || !("value" in lengthDescriptor) || !Number.isSafeInteger(lengthDescriptor.value) || lengthDescriptor.value < 0 || lengthDescriptor.value > LIMITS.maxRulesPerProject) {
|
|
1295
|
+
return { valid: false };
|
|
1296
|
+
}
|
|
1297
|
+
const length = lengthDescriptor.value;
|
|
1298
|
+
for (const propertyName of Object.getOwnPropertyNames(value)) {
|
|
1299
|
+
if (propertyName === "length") continue;
|
|
1300
|
+
const index = Number(propertyName);
|
|
1301
|
+
if (!Number.isInteger(index) || index < 0 || index >= length || String(index) !== propertyName) {
|
|
1302
|
+
return { valid: false };
|
|
1260
1303
|
}
|
|
1261
|
-
|
|
1262
|
-
|
|
1304
|
+
}
|
|
1305
|
+
const snapshot2 = new Array(length);
|
|
1306
|
+
for (let index = 0; index < length; index += 1) {
|
|
1307
|
+
const descriptor = Object.getOwnPropertyDescriptor(
|
|
1308
|
+
value,
|
|
1309
|
+
String(index)
|
|
1310
|
+
);
|
|
1311
|
+
if (descriptor === void 0 || !("value" in descriptor) || !descriptor.enumerable) {
|
|
1312
|
+
return { valid: false };
|
|
1263
1313
|
}
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1314
|
+
const child = snapshotOwnData2(descriptor.value, ancestors);
|
|
1315
|
+
if (!child.valid) return child;
|
|
1316
|
+
snapshot2[index] = child.value;
|
|
1317
|
+
}
|
|
1318
|
+
return { valid: true, value: snapshot2 };
|
|
1319
|
+
}
|
|
1320
|
+
const snapshot = /* @__PURE__ */ Object.create(null);
|
|
1321
|
+
for (const key of Object.getOwnPropertyNames(value)) {
|
|
1322
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1323
|
+
if (descriptor === void 0 || !("value" in descriptor) || !descriptor.enumerable) {
|
|
1324
|
+
return { valid: false };
|
|
1325
|
+
}
|
|
1326
|
+
const child = snapshotOwnData2(descriptor.value, ancestors);
|
|
1327
|
+
if (!child.valid) return child;
|
|
1328
|
+
Object.defineProperty(snapshot, key, {
|
|
1329
|
+
configurable: true,
|
|
1330
|
+
enumerable: true,
|
|
1331
|
+
value: child.value,
|
|
1332
|
+
writable: true
|
|
1333
|
+
});
|
|
1334
|
+
}
|
|
1335
|
+
return { valid: true, value: snapshot };
|
|
1336
|
+
} catch {
|
|
1337
|
+
return { valid: false };
|
|
1338
|
+
} finally {
|
|
1339
|
+
ancestors.delete(value);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
var CompilerInvariantError = class extends Error {
|
|
1343
|
+
constructor(path2) {
|
|
1344
|
+
super("invalid normalized origin");
|
|
1345
|
+
this.path = path2;
|
|
1346
|
+
this.name = "CompilerInvariantError";
|
|
1347
|
+
}
|
|
1348
|
+
path;
|
|
1349
|
+
};
|
|
1350
|
+
function normalizeOrigins(group, rule, groupIndex, ruleIndex) {
|
|
1351
|
+
const origins = /* @__PURE__ */ new Set();
|
|
1352
|
+
const sourceArrays = [group.origins, rule.origins];
|
|
1353
|
+
for (let sourceIndex = 0; sourceIndex < sourceArrays.length; sourceIndex += 1) {
|
|
1354
|
+
const source = sourceArrays[sourceIndex];
|
|
1355
|
+
for (let originIndex = 0; originIndex < source.length; originIndex += 1) {
|
|
1356
|
+
const normalized = normalizeSiteOrigin(source[originIndex]);
|
|
1357
|
+
if (normalized === null) {
|
|
1358
|
+
const path2 = sourceIndex === 0 ? `/groups/${groupIndex}/origins/${originIndex}` : `/groups/${groupIndex}/rules/${ruleIndex}/origins/${originIndex}`;
|
|
1359
|
+
throw new CompilerInvariantError(path2);
|
|
1360
|
+
}
|
|
1361
|
+
origins.add(normalized);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
const normalizedOrigins = [];
|
|
1365
|
+
for (const origin of origins) normalizedOrigins.push(origin);
|
|
1366
|
+
normalizedOrigins.sort(compareCodeUnits2);
|
|
1367
|
+
return normalizedOrigins;
|
|
1368
|
+
}
|
|
1369
|
+
function canonicalResourceTypes(resourceTypes) {
|
|
1370
|
+
const ordered = [];
|
|
1371
|
+
for (let canonicalIndex = 0; canonicalIndex < RESOURCE_TYPES.length; canonicalIndex += 1) {
|
|
1372
|
+
const canonical = RESOURCE_TYPES[canonicalIndex];
|
|
1373
|
+
for (let sourceIndex = 0; sourceIndex < resourceTypes.length; sourceIndex += 1) {
|
|
1374
|
+
if (resourceTypes[sourceIndex] === canonical) ordered.push(canonical);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
return ordered;
|
|
1378
|
+
}
|
|
1379
|
+
function compileMatcher(group, rule, groupIndex, ruleIndex) {
|
|
1380
|
+
const matcher = {
|
|
1381
|
+
urlRegex: { source: rule.urlRegex, flags: "" },
|
|
1382
|
+
origins: normalizeOrigins(group, rule, groupIndex, ruleIndex),
|
|
1383
|
+
resourceTypes: canonicalResourceTypes(rule.resourceTypes),
|
|
1384
|
+
priority: rule.priority
|
|
1385
|
+
};
|
|
1386
|
+
if (rule.method !== void 0) matcher.method = rule.method;
|
|
1387
|
+
return matcher;
|
|
1388
|
+
}
|
|
1389
|
+
function compileOperations(project) {
|
|
1390
|
+
const operations = [];
|
|
1391
|
+
for (let groupIndex = 0; groupIndex < project.groups.length; groupIndex += 1) {
|
|
1392
|
+
const group = project.groups[groupIndex];
|
|
1393
|
+
for (let ruleIndex = 0; ruleIndex < group.rules.length; ruleIndex += 1) {
|
|
1394
|
+
const rule = group.rules[ruleIndex];
|
|
1395
|
+
const matcher = compileMatcher(group, rule, groupIndex, ruleIndex);
|
|
1396
|
+
if (rule.type === "redirect") {
|
|
1397
|
+
const operation = {
|
|
1398
|
+
kind: "redirect",
|
|
1399
|
+
groupId: group.id,
|
|
1400
|
+
ruleId: rule.id,
|
|
1401
|
+
matcher,
|
|
1402
|
+
redirect: { destination: rule.redirect?.destination ?? "" }
|
|
1403
|
+
};
|
|
1404
|
+
operations.push(operation);
|
|
1405
|
+
} else if (rule.type === "query" && rule.action && "type" in rule.action && rule.action.type === "query") {
|
|
1406
|
+
const action = rule.action;
|
|
1407
|
+
const operation = {
|
|
1408
|
+
kind: "query",
|
|
1409
|
+
groupId: group.id,
|
|
1410
|
+
ruleId: rule.id,
|
|
1411
|
+
matcher,
|
|
1412
|
+
action
|
|
1413
|
+
};
|
|
1414
|
+
operations.push(operation);
|
|
1415
|
+
} else if (rule.type === "header") {
|
|
1416
|
+
const operation = {
|
|
1417
|
+
kind: "header",
|
|
1418
|
+
groupId: group.id,
|
|
1419
|
+
ruleId: rule.id,
|
|
1420
|
+
matcher,
|
|
1421
|
+
header: {
|
|
1422
|
+
direction: rule.headerDirection ?? "request",
|
|
1423
|
+
operation: rule.headerOperation ?? "set",
|
|
1424
|
+
name: rule.headerName ?? "",
|
|
1425
|
+
...rule.headerValue !== void 0 ? { value: rule.headerValue } : {}
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1428
|
+
operations.push(operation);
|
|
1429
|
+
} else if (rule.type === "mock") {
|
|
1430
|
+
const operation = {
|
|
1431
|
+
kind: "mock",
|
|
1432
|
+
groupId: group.id,
|
|
1433
|
+
ruleId: rule.id,
|
|
1434
|
+
matcher,
|
|
1435
|
+
mock: rule.mock ?? { status: 200, body: "" }
|
|
1436
|
+
};
|
|
1437
|
+
operations.push(operation);
|
|
1438
|
+
} else if (rule.type === "response-body") {
|
|
1439
|
+
const operation = {
|
|
1440
|
+
kind: "response-body",
|
|
1441
|
+
groupId: group.id,
|
|
1442
|
+
ruleId: rule.id,
|
|
1443
|
+
matcher,
|
|
1444
|
+
responseBody: rule.responseBody ?? { replacements: [] }
|
|
1445
|
+
};
|
|
1446
|
+
operations.push(operation);
|
|
1447
|
+
} else if (rule.type === "request-body") {
|
|
1448
|
+
const operation = {
|
|
1449
|
+
kind: "request-body",
|
|
1450
|
+
groupId: group.id,
|
|
1451
|
+
ruleId: rule.id,
|
|
1452
|
+
matcher,
|
|
1453
|
+
requestBody: rule.requestBody ?? { mode: "replace", body: "" }
|
|
1454
|
+
};
|
|
1455
|
+
operations.push(operation);
|
|
1456
|
+
} else {
|
|
1457
|
+
const operation = {
|
|
1458
|
+
kind: "matcher",
|
|
1459
|
+
groupId: group.id,
|
|
1460
|
+
ruleId: rule.id,
|
|
1461
|
+
matcher
|
|
1462
|
+
};
|
|
1463
|
+
operations.push(operation);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
return operations;
|
|
1468
|
+
}
|
|
1469
|
+
function compileProject(value) {
|
|
1470
|
+
const snapshot = snapshotOwnData2(value);
|
|
1471
|
+
if (!snapshot.valid) {
|
|
1472
|
+
return {
|
|
1473
|
+
ok: false,
|
|
1474
|
+
operations: [],
|
|
1475
|
+
diagnostics: mapValidationIssues([
|
|
1476
|
+
{
|
|
1477
|
+
instancePath: "",
|
|
1478
|
+
keyword: "ownProperties",
|
|
1479
|
+
message: "must contain only own data properties",
|
|
1480
|
+
params: {}
|
|
1481
|
+
}
|
|
1482
|
+
])
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
let validation;
|
|
1486
|
+
try {
|
|
1487
|
+
validation = validateProjectDetailed(snapshot.value);
|
|
1488
|
+
} catch {
|
|
1489
|
+
return { ok: false, operations: [], diagnostics: [invariantDiagnostic()] };
|
|
1490
|
+
}
|
|
1491
|
+
if (!validation.valid) {
|
|
1492
|
+
return {
|
|
1493
|
+
ok: false,
|
|
1494
|
+
operations: [],
|
|
1495
|
+
diagnostics: mapValidationIssues(validation.errors)
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
try {
|
|
1499
|
+
return {
|
|
1500
|
+
ok: true,
|
|
1501
|
+
operations: compileOperations(validation.data),
|
|
1502
|
+
diagnostics: []
|
|
1503
|
+
};
|
|
1504
|
+
} catch (error) {
|
|
1505
|
+
const path2 = error instanceof CompilerInvariantError ? error.path : "";
|
|
1506
|
+
return {
|
|
1507
|
+
ok: false,
|
|
1508
|
+
operations: [],
|
|
1509
|
+
diagnostics: [invariantDiagnostic(path2)]
|
|
1510
|
+
};
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
// packages/runtime/dist/node/index.js
|
|
1515
|
+
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
1516
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
1517
|
+
import { readFile as readFile2, realpath as realpath2, stat } from "node:fs/promises";
|
|
1518
|
+
import { isAbsolute as isAbsolute2, relative as relative2, resolve, sep } from "node:path";
|
|
1519
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
1520
|
+
import { mkdir as mkdir2, readFile as readFile3, rename, rm, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
|
|
1521
|
+
import { basename, dirname as dirname2, isAbsolute as isAbsolute3, join as join2, relative as relative3 } from "node:path";
|
|
1522
|
+
import { generateKeyPairSync as generateKeyPairSync2, webcrypto } from "node:crypto";
|
|
1523
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
1524
|
+
import { accessSync, constants as constants2, unlinkSync, writeFileSync } from "node:fs";
|
|
1525
|
+
import { tmpdir } from "node:os";
|
|
1526
|
+
import { dirname as dirname3, join as join3 } from "node:path";
|
|
1527
|
+
import { join as posixJoin } from "node:path/posix";
|
|
1528
|
+
import { spawn as spawn2, spawnSync as spawnSync2 } from "node:child_process";
|
|
1529
|
+
import { accessSync as accessSync2, constants as constants3, mkdirSync, unlinkSync as unlinkSync2 } from "node:fs";
|
|
1530
|
+
import { dirname as dirname4 } from "node:path";
|
|
1531
|
+
import { join as posixJoin2 } from "node:path/posix";
|
|
1532
|
+
import { spawn as spawn3, spawnSync as spawnSync3 } from "node:child_process";
|
|
1533
|
+
import { accessSync as accessSync3, constants as constants4, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1534
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
1535
|
+
import { join as join4 } from "node:path";
|
|
1536
|
+
import { join as posixJoin3 } from "node:path/posix";
|
|
1537
|
+
var __create = Object.create;
|
|
1538
|
+
var __defProp = Object.defineProperty;
|
|
1539
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
1540
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
1541
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
1542
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
1543
|
+
var __esm = (fn, res, err) => function __init() {
|
|
1544
|
+
if (err) throw err[0];
|
|
1545
|
+
try {
|
|
1546
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
1547
|
+
} catch (e) {
|
|
1548
|
+
throw err = [e], e;
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1551
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
1552
|
+
try {
|
|
1553
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
1554
|
+
} catch (e) {
|
|
1555
|
+
throw mod = 0, e;
|
|
1556
|
+
}
|
|
1557
|
+
};
|
|
1558
|
+
var __export = (target, all) => {
|
|
1559
|
+
for (var name in all)
|
|
1560
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
1561
|
+
};
|
|
1562
|
+
var __copyProps = (to, from, except, desc) => {
|
|
1563
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
1564
|
+
for (let key of __getOwnPropNames(from))
|
|
1565
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
1566
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
1567
|
+
}
|
|
1568
|
+
return to;
|
|
1569
|
+
};
|
|
1570
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
1571
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
1572
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
1573
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
1574
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
1575
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
1576
|
+
mod
|
|
1577
|
+
));
|
|
1578
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
1579
|
+
var require_Reflect = __commonJS({
|
|
1580
|
+
"node_modules/.pnpm/reflect-metadata@0.2.2/node_modules/reflect-metadata/Reflect.js"() {
|
|
1581
|
+
var Reflect2;
|
|
1582
|
+
(function(Reflect3) {
|
|
1583
|
+
(function(factory) {
|
|
1584
|
+
var root = typeof globalThis === "object" ? globalThis : typeof global === "object" ? global : typeof self === "object" ? self : typeof this === "object" ? this : sloppyModeThis();
|
|
1585
|
+
var exporter = makeExporter(Reflect3);
|
|
1586
|
+
if (typeof root.Reflect !== "undefined") {
|
|
1587
|
+
exporter = makeExporter(root.Reflect, exporter);
|
|
1588
|
+
}
|
|
1589
|
+
factory(exporter, root);
|
|
1590
|
+
if (typeof root.Reflect === "undefined") {
|
|
1591
|
+
root.Reflect = Reflect3;
|
|
1592
|
+
}
|
|
1593
|
+
function makeExporter(target, previous) {
|
|
1594
|
+
return function(key, value) {
|
|
1595
|
+
Object.defineProperty(target, key, { configurable: true, writable: true, value });
|
|
1596
|
+
if (previous)
|
|
1597
|
+
previous(key, value);
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1600
|
+
function functionThis() {
|
|
1601
|
+
try {
|
|
1602
|
+
return Function("return this;")();
|
|
1603
|
+
} catch (_) {
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
function indirectEvalThis() {
|
|
1607
|
+
try {
|
|
1608
|
+
return (void 0, eval)("(function() { return this; })()");
|
|
1609
|
+
} catch (_) {
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
function sloppyModeThis() {
|
|
1613
|
+
return functionThis() || indirectEvalThis();
|
|
1614
|
+
}
|
|
1615
|
+
})(function(exporter, root) {
|
|
1616
|
+
var hasOwn2 = Object.prototype.hasOwnProperty;
|
|
1617
|
+
var supportsSymbol = typeof Symbol === "function";
|
|
1618
|
+
var toPrimitiveSymbol = supportsSymbol && typeof Symbol.toPrimitive !== "undefined" ? Symbol.toPrimitive : "@@toPrimitive";
|
|
1619
|
+
var iteratorSymbol = supportsSymbol && typeof Symbol.iterator !== "undefined" ? Symbol.iterator : "@@iterator";
|
|
1620
|
+
var supportsCreate = typeof Object.create === "function";
|
|
1621
|
+
var supportsProto = { __proto__: [] } instanceof Array;
|
|
1622
|
+
var downLevel = !supportsCreate && !supportsProto;
|
|
1623
|
+
var HashMap = {
|
|
1624
|
+
// create an object in dictionary mode (a.k.a. "slow" mode in v8)
|
|
1625
|
+
create: supportsCreate ? function() {
|
|
1626
|
+
return MakeDictionary(/* @__PURE__ */ Object.create(null));
|
|
1627
|
+
} : supportsProto ? function() {
|
|
1628
|
+
return MakeDictionary({ __proto__: null });
|
|
1629
|
+
} : function() {
|
|
1630
|
+
return MakeDictionary({});
|
|
1631
|
+
},
|
|
1281
1632
|
has: downLevel ? function(map, key) {
|
|
1282
1633
|
return hasOwn2.call(map, key);
|
|
1283
1634
|
} : function(map, key) {
|
|
@@ -19894,798 +20245,1182 @@ async function fetchWithRetry(url, init, signal) {
|
|
|
19894
20245
|
if (response.ok) {
|
|
19895
20246
|
return response;
|
|
19896
20247
|
}
|
|
19897
|
-
const shouldRetry = response.status === 429 || response.status >= 500;
|
|
19898
|
-
if (shouldRetry && attempt < MAX_RETRIES) {
|
|
19899
|
-
const retryAfter = response.headers.get("Retry-After");
|
|
19900
|
-
const delay2 = retryAfter ? parseInt(retryAfter, 10) * 1e3 : calculateRetryDelay(attempt);
|
|
19901
|
-
await sleep(Math.min(delay2, 1e4));
|
|
19902
|
-
continue;
|
|
20248
|
+
const shouldRetry = response.status === 429 || response.status >= 500;
|
|
20249
|
+
if (shouldRetry && attempt < MAX_RETRIES) {
|
|
20250
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
20251
|
+
const delay2 = retryAfter ? parseInt(retryAfter, 10) * 1e3 : calculateRetryDelay(attempt);
|
|
20252
|
+
await sleep(Math.min(delay2, 1e4));
|
|
20253
|
+
continue;
|
|
20254
|
+
}
|
|
20255
|
+
const errorText = await response.text().catch(() => "");
|
|
20256
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
20257
|
+
} catch (e) {
|
|
20258
|
+
lastError = e;
|
|
20259
|
+
if (e instanceof DOMException && e.name === "AbortError") {
|
|
20260
|
+
throw e;
|
|
20261
|
+
}
|
|
20262
|
+
if (attempt < MAX_RETRIES && e.name !== "AbortError") {
|
|
20263
|
+
await sleep(calculateRetryDelay(attempt));
|
|
20264
|
+
continue;
|
|
20265
|
+
}
|
|
20266
|
+
throw e;
|
|
20267
|
+
}
|
|
20268
|
+
}
|
|
20269
|
+
throw lastError;
|
|
20270
|
+
}
|
|
20271
|
+
async function fetchStreamWithRetry(url, init, signal) {
|
|
20272
|
+
let lastError;
|
|
20273
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
20274
|
+
if (signal?.aborted) {
|
|
20275
|
+
throw new DOMException("Aborted", "AbortError");
|
|
20276
|
+
}
|
|
20277
|
+
try {
|
|
20278
|
+
const controller = new AbortController();
|
|
20279
|
+
const timeoutId = setTimeout(() => controller.abort(), STREAM_TIMEOUT);
|
|
20280
|
+
if (signal) {
|
|
20281
|
+
signal.addEventListener("abort", () => controller.abort());
|
|
20282
|
+
}
|
|
20283
|
+
const response = await fetch(url, {
|
|
20284
|
+
...init,
|
|
20285
|
+
signal: controller.signal
|
|
20286
|
+
});
|
|
20287
|
+
clearTimeout(timeoutId);
|
|
20288
|
+
if (response.ok) {
|
|
20289
|
+
return response;
|
|
20290
|
+
}
|
|
20291
|
+
const shouldRetry = response.status === 429 || response.status >= 500;
|
|
20292
|
+
if (shouldRetry && attempt < MAX_RETRIES) {
|
|
20293
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
20294
|
+
const delay2 = retryAfter ? parseInt(retryAfter, 10) * 1e3 : calculateRetryDelay(attempt);
|
|
20295
|
+
await sleep(Math.min(delay2, 1e4));
|
|
20296
|
+
continue;
|
|
20297
|
+
}
|
|
20298
|
+
const errorText = await response.text().catch(() => "");
|
|
20299
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
20300
|
+
} catch (e) {
|
|
20301
|
+
lastError = e;
|
|
20302
|
+
if (e instanceof DOMException && e.name === "AbortError") {
|
|
20303
|
+
throw e;
|
|
20304
|
+
}
|
|
20305
|
+
if (attempt < MAX_RETRIES && e.name !== "AbortError") {
|
|
20306
|
+
await sleep(calculateRetryDelay(attempt));
|
|
20307
|
+
continue;
|
|
20308
|
+
}
|
|
20309
|
+
throw e;
|
|
20310
|
+
}
|
|
20311
|
+
}
|
|
20312
|
+
throw lastError;
|
|
20313
|
+
}
|
|
20314
|
+
function sanitizeResponse(text) {
|
|
20315
|
+
return text;
|
|
20316
|
+
}
|
|
20317
|
+
function createAIClient(config) {
|
|
20318
|
+
const baseUrl = config.providerUrl.replace(/\/$/, "");
|
|
20319
|
+
const chatUrl = `${baseUrl}/chat/completions`;
|
|
20320
|
+
const headers = {
|
|
20321
|
+
"Content-Type": "application/json",
|
|
20322
|
+
Authorization: `Bearer ${config.apiKey}`
|
|
20323
|
+
};
|
|
20324
|
+
return {
|
|
20325
|
+
async complete(options) {
|
|
20326
|
+
const body = JSON.stringify({
|
|
20327
|
+
model: options.model,
|
|
20328
|
+
messages: options.messages,
|
|
20329
|
+
temperature: options.temperature ?? 0.7,
|
|
20330
|
+
stream: false,
|
|
20331
|
+
response_format: options.responseFormat
|
|
20332
|
+
});
|
|
20333
|
+
const response = await fetchWithRetry(
|
|
20334
|
+
chatUrl,
|
|
20335
|
+
{
|
|
20336
|
+
method: "POST",
|
|
20337
|
+
headers,
|
|
20338
|
+
body
|
|
20339
|
+
},
|
|
20340
|
+
options.signal
|
|
20341
|
+
);
|
|
20342
|
+
const data = await response.json();
|
|
20343
|
+
const content = data.choices[0]?.message?.content ?? "";
|
|
20344
|
+
const usage = data.usage ? {
|
|
20345
|
+
promptTokens: data.usage.prompt_tokens,
|
|
20346
|
+
completionTokens: data.usage.completion_tokens
|
|
20347
|
+
} : void 0;
|
|
20348
|
+
return { content: sanitizeResponse(content), usage };
|
|
20349
|
+
},
|
|
20350
|
+
async *stream(options) {
|
|
20351
|
+
const body = JSON.stringify({
|
|
20352
|
+
model: options.model,
|
|
20353
|
+
messages: options.messages,
|
|
20354
|
+
temperature: options.temperature ?? 0.7,
|
|
20355
|
+
stream: true,
|
|
20356
|
+
stream_options: { include_usage: true }
|
|
20357
|
+
});
|
|
20358
|
+
const response = await fetchStreamWithRetry(
|
|
20359
|
+
chatUrl,
|
|
20360
|
+
{
|
|
20361
|
+
method: "POST",
|
|
20362
|
+
headers,
|
|
20363
|
+
body
|
|
20364
|
+
},
|
|
20365
|
+
options.signal
|
|
20366
|
+
);
|
|
20367
|
+
if (!response.body) {
|
|
20368
|
+
throw new Error("No response body");
|
|
20369
|
+
}
|
|
20370
|
+
const reader = response.body.getReader();
|
|
20371
|
+
const decoder = new TextDecoder();
|
|
20372
|
+
let buffer = "";
|
|
20373
|
+
try {
|
|
20374
|
+
while (true) {
|
|
20375
|
+
if (options.signal?.aborted) {
|
|
20376
|
+
throw new DOMException("Aborted", "AbortError");
|
|
20377
|
+
}
|
|
20378
|
+
const { done, value } = await reader.read();
|
|
20379
|
+
if (done) break;
|
|
20380
|
+
buffer += decoder.decode(value, { stream: true });
|
|
20381
|
+
const lines = buffer.split("\n");
|
|
20382
|
+
buffer = lines.pop() ?? "";
|
|
20383
|
+
for (const line of lines) {
|
|
20384
|
+
if (!line.startsWith("data: ")) continue;
|
|
20385
|
+
const data = line.slice(6).trim();
|
|
20386
|
+
if (data === "[DONE]") continue;
|
|
20387
|
+
try {
|
|
20388
|
+
const parsed = JSON.parse(data);
|
|
20389
|
+
const delta = parsed.choices[0]?.delta?.content ?? "";
|
|
20390
|
+
const finishReason = parsed.choices[0]?.finish_reason;
|
|
20391
|
+
const usage = parsed.usage ? {
|
|
20392
|
+
promptTokens: parsed.usage.prompt_tokens,
|
|
20393
|
+
completionTokens: parsed.usage.completion_tokens
|
|
20394
|
+
} : void 0;
|
|
20395
|
+
if (delta) {
|
|
20396
|
+
yield { delta, done: false };
|
|
20397
|
+
}
|
|
20398
|
+
if (finishReason === "stop") {
|
|
20399
|
+
yield { delta: "", done: true, usage };
|
|
20400
|
+
return;
|
|
20401
|
+
}
|
|
20402
|
+
} catch {
|
|
20403
|
+
}
|
|
20404
|
+
}
|
|
20405
|
+
}
|
|
20406
|
+
} finally {
|
|
20407
|
+
reader.releaseLock();
|
|
20408
|
+
}
|
|
20409
|
+
yield { delta: "", done: true };
|
|
20410
|
+
}
|
|
20411
|
+
};
|
|
20412
|
+
}
|
|
20413
|
+
function getConfigDir() {
|
|
20414
|
+
const platform = process.platform;
|
|
20415
|
+
if (platform === "win32") {
|
|
20416
|
+
const localAppData = process.env.LOCALAPPDATA;
|
|
20417
|
+
if (localAppData) {
|
|
20418
|
+
return path.join(localAppData, "rogatio");
|
|
20419
|
+
}
|
|
20420
|
+
const userProfile = process.env.USERPROFILE;
|
|
20421
|
+
if (userProfile) {
|
|
20422
|
+
return path.join(userProfile, "AppData", "Local", "rogatio");
|
|
20423
|
+
}
|
|
20424
|
+
throw new Error("Unable to determine Windows config directory");
|
|
20425
|
+
}
|
|
20426
|
+
if (platform === "darwin") {
|
|
20427
|
+
const home2 = process.env.HOME;
|
|
20428
|
+
if (!home2) throw new Error("HOME not set");
|
|
20429
|
+
return path.join(home2, "Library", "Application Support", "rogatio");
|
|
20430
|
+
}
|
|
20431
|
+
const xdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
20432
|
+
if (xdgConfigHome) {
|
|
20433
|
+
return path.join(xdgConfigHome, "rogatio");
|
|
20434
|
+
}
|
|
20435
|
+
const home = process.env.HOME;
|
|
20436
|
+
if (!home) throw new Error("HOME not set");
|
|
20437
|
+
return path.join(home, ".config", "rogatio");
|
|
20438
|
+
}
|
|
20439
|
+
function getProviderConfigPath() {
|
|
20440
|
+
return path.join(getConfigDir(), "provider.json");
|
|
20441
|
+
}
|
|
20442
|
+
async function readProviderConfig() {
|
|
20443
|
+
const configPath = getProviderConfigPath();
|
|
20444
|
+
try {
|
|
20445
|
+
const content = await fs.readFile(configPath, "utf8");
|
|
20446
|
+
return JSON.parse(content);
|
|
20447
|
+
} catch (e) {
|
|
20448
|
+
if (e.code === "ENOENT") {
|
|
20449
|
+
return null;
|
|
20450
|
+
}
|
|
20451
|
+
throw e;
|
|
20452
|
+
}
|
|
20453
|
+
}
|
|
20454
|
+
async function writeProviderConfig(config) {
|
|
20455
|
+
const configPath = getProviderConfigPath();
|
|
20456
|
+
const configDir = path.dirname(configPath);
|
|
20457
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
20458
|
+
const content = JSON.stringify(config, null, 2);
|
|
20459
|
+
await fs.writeFile(configPath, content, { mode: 384 });
|
|
20460
|
+
}
|
|
20461
|
+
async function deleteProviderConfig() {
|
|
20462
|
+
const configPath = getProviderConfigPath();
|
|
20463
|
+
try {
|
|
20464
|
+
await fs.unlink(configPath);
|
|
20465
|
+
} catch (e) {
|
|
20466
|
+
if (e.code !== "ENOENT") {
|
|
20467
|
+
throw e;
|
|
20468
|
+
}
|
|
20469
|
+
}
|
|
20470
|
+
}
|
|
20471
|
+
function runtimeError(code) {
|
|
20472
|
+
return { code };
|
|
20473
|
+
}
|
|
20474
|
+
function failure(code) {
|
|
20475
|
+
return { ok: false, error: runtimeError(code) };
|
|
20476
|
+
}
|
|
20477
|
+
function normalizeLogicalPath(value) {
|
|
20478
|
+
if (typeof value !== "string" || value.length === 0) return null;
|
|
20479
|
+
if (value.includes("\\") || value.includes("%") || hasControl(value)) {
|
|
20480
|
+
return null;
|
|
20481
|
+
}
|
|
20482
|
+
if (value.startsWith("/") || value.endsWith("/") || value.includes("//")) {
|
|
20483
|
+
return null;
|
|
20484
|
+
}
|
|
20485
|
+
const parts = value.split("/");
|
|
20486
|
+
if (parts.some(
|
|
20487
|
+
(part) => part.length === 0 || part === "." || part === ".." || part.includes(":") || /[*?[\]]/.test(part)
|
|
20488
|
+
)) {
|
|
20489
|
+
return null;
|
|
20490
|
+
}
|
|
20491
|
+
return parts.join("/");
|
|
20492
|
+
}
|
|
20493
|
+
var MAX_ARRAY_LENGTH = 4096;
|
|
20494
|
+
var MAX_OBJECT_PROPERTIES = 256;
|
|
20495
|
+
function snapshotOwnData3(value, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
20496
|
+
if (value === null || typeof value !== "object") {
|
|
20497
|
+
return { valid: true, value };
|
|
20498
|
+
}
|
|
20499
|
+
if (ancestors.has(value)) return { valid: false };
|
|
20500
|
+
ancestors.add(value);
|
|
20501
|
+
try {
|
|
20502
|
+
if (Object.getOwnPropertySymbols(value).length > 0) {
|
|
20503
|
+
return { valid: false };
|
|
20504
|
+
}
|
|
20505
|
+
if (Array.isArray(value)) {
|
|
20506
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
20507
|
+
if (lengthDescriptor === void 0 || !("value" in lengthDescriptor) || !Number.isSafeInteger(lengthDescriptor.value) || lengthDescriptor.value < 0 || lengthDescriptor.value > MAX_ARRAY_LENGTH) {
|
|
20508
|
+
return { valid: false };
|
|
20509
|
+
}
|
|
20510
|
+
const length = lengthDescriptor.value;
|
|
20511
|
+
for (const propertyName of Object.getOwnPropertyNames(value)) {
|
|
20512
|
+
if (propertyName === "length") continue;
|
|
20513
|
+
const index = Number(propertyName);
|
|
20514
|
+
if (!Number.isInteger(index) || index < 0 || index >= length || String(index) !== propertyName) {
|
|
20515
|
+
return { valid: false };
|
|
20516
|
+
}
|
|
19903
20517
|
}
|
|
19904
|
-
const
|
|
19905
|
-
|
|
19906
|
-
|
|
19907
|
-
|
|
19908
|
-
|
|
19909
|
-
|
|
20518
|
+
const snapshot2 = new Array(length);
|
|
20519
|
+
for (let index = 0; index < length; index += 1) {
|
|
20520
|
+
const descriptor = Object.getOwnPropertyDescriptor(
|
|
20521
|
+
value,
|
|
20522
|
+
String(index)
|
|
20523
|
+
);
|
|
20524
|
+
if (descriptor === void 0 || !("value" in descriptor) || !descriptor.enumerable) {
|
|
20525
|
+
return { valid: false };
|
|
20526
|
+
}
|
|
20527
|
+
const child = snapshotOwnData3(descriptor.value, ancestors);
|
|
20528
|
+
if (!child.valid) return child;
|
|
20529
|
+
snapshot2[index] = child.value;
|
|
19910
20530
|
}
|
|
19911
|
-
|
|
19912
|
-
|
|
19913
|
-
|
|
20531
|
+
return { valid: true, value: snapshot2 };
|
|
20532
|
+
}
|
|
20533
|
+
const names = Object.getOwnPropertyNames(value);
|
|
20534
|
+
if (names.length > MAX_OBJECT_PROPERTIES) return { valid: false };
|
|
20535
|
+
const snapshot = /* @__PURE__ */ Object.create(null);
|
|
20536
|
+
for (const key of names) {
|
|
20537
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
20538
|
+
if (descriptor === void 0 || !("value" in descriptor) || !descriptor.enumerable) {
|
|
20539
|
+
return { valid: false };
|
|
19914
20540
|
}
|
|
19915
|
-
|
|
20541
|
+
const child = snapshotOwnData3(descriptor.value, ancestors);
|
|
20542
|
+
if (!child.valid) return child;
|
|
20543
|
+
Object.defineProperty(snapshot, key, {
|
|
20544
|
+
configurable: true,
|
|
20545
|
+
enumerable: true,
|
|
20546
|
+
value: child.value,
|
|
20547
|
+
writable: true
|
|
20548
|
+
});
|
|
20549
|
+
}
|
|
20550
|
+
return { valid: true, value: snapshot };
|
|
20551
|
+
} catch {
|
|
20552
|
+
return { valid: false };
|
|
20553
|
+
} finally {
|
|
20554
|
+
ancestors.delete(value);
|
|
20555
|
+
}
|
|
20556
|
+
}
|
|
20557
|
+
function hasOwn(value, key) {
|
|
20558
|
+
return Object.hasOwn(value, key);
|
|
20559
|
+
}
|
|
20560
|
+
var ID_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
20561
|
+
function hasValidPercentEncoding(value) {
|
|
20562
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
20563
|
+
if (value[index] !== "%") continue;
|
|
20564
|
+
if (!/^[0-9a-fA-F]{2}$/.test(value.slice(index + 1, index + 3))) {
|
|
20565
|
+
return false;
|
|
20566
|
+
}
|
|
20567
|
+
index += 2;
|
|
20568
|
+
}
|
|
20569
|
+
return true;
|
|
20570
|
+
}
|
|
20571
|
+
function rawAuthority(value) {
|
|
20572
|
+
const separator = value.indexOf("://");
|
|
20573
|
+
if (separator < 0) return null;
|
|
20574
|
+
const afterScheme = separator + 3;
|
|
20575
|
+
let end = value.length;
|
|
20576
|
+
for (const marker of ["/", "?", "#"]) {
|
|
20577
|
+
const index = value.indexOf(marker, afterScheme);
|
|
20578
|
+
if (index >= 0 && index < end) end = index;
|
|
20579
|
+
}
|
|
20580
|
+
return value.slice(afterScheme, end);
|
|
20581
|
+
}
|
|
20582
|
+
function hasNonAscii(value) {
|
|
20583
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
20584
|
+
if (value.charCodeAt(index) > 127) return true;
|
|
20585
|
+
}
|
|
20586
|
+
return false;
|
|
20587
|
+
}
|
|
20588
|
+
function validHostname(hostname) {
|
|
20589
|
+
const unbracketed = hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
|
|
20590
|
+
if (unbracketed.includes("%") || unbracketed.endsWith(".")) return false;
|
|
20591
|
+
if (isIP2(unbracketed) !== 0) return true;
|
|
20592
|
+
if (unbracketed.length === 0 || unbracketed.length > 253) return false;
|
|
20593
|
+
return unbracketed.split(".").every((label2) => ID_PATTERN.test(label2));
|
|
20594
|
+
}
|
|
20595
|
+
function canonicalizeOutboundTarget(value) {
|
|
20596
|
+
if (typeof value !== "string" || value.length === 0) return null;
|
|
20597
|
+
if (value.trim() !== value || hasControl(value)) return null;
|
|
20598
|
+
if (value.includes("\\") || value.includes("#")) return null;
|
|
20599
|
+
if (!hasValidPercentEncoding(value)) return null;
|
|
20600
|
+
if (/%(?:2f|2F|5c|5C)/.test(value)) return null;
|
|
20601
|
+
const authority = rawAuthority(value);
|
|
20602
|
+
if (authority === null || authority.length === 0) return null;
|
|
20603
|
+
if (authority.includes("@") || authority.includes("*") || hasNonAscii(authority)) {
|
|
20604
|
+
return null;
|
|
20605
|
+
}
|
|
20606
|
+
let url;
|
|
20607
|
+
try {
|
|
20608
|
+
url = new URL(value);
|
|
20609
|
+
} catch {
|
|
20610
|
+
return null;
|
|
20611
|
+
}
|
|
20612
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return null;
|
|
20613
|
+
if (url.username.length > 0 || url.password.length > 0) return null;
|
|
20614
|
+
if (url.hash.length > 0 || url.hostname.length === 0) return null;
|
|
20615
|
+
if (!validHostname(url.hostname)) return null;
|
|
20616
|
+
const defaultPort = url.protocol === "https:" ? 443 : 80;
|
|
20617
|
+
const port = url.port.length === 0 ? defaultPort : Number(url.port);
|
|
20618
|
+
if (!Number.isInteger(port) || port !== 80 && port !== 443) return null;
|
|
20619
|
+
if (url.protocol === "http:" && port !== 80) return null;
|
|
20620
|
+
if (url.protocol === "https:" && port !== 443) return null;
|
|
20621
|
+
const canonical = url.href;
|
|
20622
|
+
if (canonical.includes("#") || canonical.includes("@")) return null;
|
|
20623
|
+
return canonical;
|
|
20624
|
+
}
|
|
20625
|
+
function targetOrigin(value) {
|
|
20626
|
+
const origin = normalizeSiteOrigin(new URL(value).origin);
|
|
20627
|
+
return origin;
|
|
20628
|
+
}
|
|
20629
|
+
function isOriginAllowed(target, origins) {
|
|
20630
|
+
const origin = targetOrigin(target);
|
|
20631
|
+
return origin !== null && origins.includes(origin);
|
|
20632
|
+
}
|
|
20633
|
+
function validMethod(value) {
|
|
20634
|
+
return typeof value === "string" && HTTP_METHODS.includes(value);
|
|
20635
|
+
}
|
|
20636
|
+
function validId(value) {
|
|
20637
|
+
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(value);
|
|
20638
|
+
}
|
|
20639
|
+
function exactDescriptor(value) {
|
|
20640
|
+
const snapshot = snapshotOwnData3(value);
|
|
20641
|
+
if (!snapshot.valid || snapshot.value === null || typeof snapshot.value !== "object" || Array.isArray(snapshot.value))
|
|
20642
|
+
return null;
|
|
20643
|
+
const record = snapshot.value;
|
|
20644
|
+
const keys = ["groupId", "ruleId", "operationId", "kind", "target", "method"];
|
|
20645
|
+
if (Object.keys(record).length !== keys.length || !keys.every((key) => hasOwn(record, key)))
|
|
20646
|
+
return null;
|
|
20647
|
+
if (!validId(record.groupId) || !validId(record.ruleId) || !validId(record.operationId) || record.kind !== "outbound-http" && record.kind !== "confined-file" || typeof record.target !== "string" || !validMethod(record.method))
|
|
20648
|
+
return null;
|
|
20649
|
+
return {
|
|
20650
|
+
groupId: record.groupId,
|
|
20651
|
+
ruleId: record.ruleId,
|
|
20652
|
+
operationId: record.operationId,
|
|
20653
|
+
kind: record.kind,
|
|
20654
|
+
target: record.target,
|
|
20655
|
+
method: record.method
|
|
20656
|
+
};
|
|
20657
|
+
}
|
|
20658
|
+
function canonicalTarget(kind, target) {
|
|
20659
|
+
return kind === "outbound-http" ? canonicalizeOutboundTarget(target) : normalizeLogicalPath(target);
|
|
20660
|
+
}
|
|
20661
|
+
function matchesGrant(descriptor, grant) {
|
|
20662
|
+
return descriptor.groupId === grant.groupId && descriptor.ruleId === grant.ruleId && descriptor.operationId === grant.operationId && descriptor.kind === grant.kind && descriptor.target === grant.target && descriptor.method === grant.method;
|
|
20663
|
+
}
|
|
20664
|
+
function authorizeExact(preset, value) {
|
|
20665
|
+
const descriptor = exactDescriptor(value);
|
|
20666
|
+
if (descriptor === null) return failure("runtime.authorization-denied");
|
|
20667
|
+
const target = canonicalTarget(descriptor.kind, descriptor.target);
|
|
20668
|
+
if (target === null) return failure("runtime.authorization-denied");
|
|
20669
|
+
for (const grant of preset.grants) {
|
|
20670
|
+
if (!matchesGrant({ ...descriptor, target }, grant)) continue;
|
|
20671
|
+
const matcher = preset.matchers.find(
|
|
20672
|
+
(candidate) => candidate.groupId === grant.groupId && candidate.ruleId === grant.ruleId
|
|
20673
|
+
);
|
|
20674
|
+
if (matcher === void 0 || grant.kind === "outbound-http" && !isOriginAllowed(grant.target, matcher.matcher.origins)) {
|
|
20675
|
+
return failure("runtime.authorization-denied");
|
|
20676
|
+
}
|
|
20677
|
+
return {
|
|
20678
|
+
ok: true,
|
|
20679
|
+
value: Object.freeze({
|
|
20680
|
+
groupId: grant.groupId,
|
|
20681
|
+
ruleId: grant.ruleId,
|
|
20682
|
+
operationId: grant.operationId,
|
|
20683
|
+
kind: grant.kind,
|
|
20684
|
+
target: grant.target,
|
|
20685
|
+
method: grant.method,
|
|
20686
|
+
presetDigest: preset.digest
|
|
20687
|
+
})
|
|
20688
|
+
};
|
|
20689
|
+
}
|
|
20690
|
+
return failure("runtime.authorization-denied");
|
|
20691
|
+
}
|
|
20692
|
+
var RUNTIME_LIMITS = Object.freeze({
|
|
20693
|
+
maxPresetBytes: 262144,
|
|
20694
|
+
maxRequestLineBytes: 8192,
|
|
20695
|
+
maxHeaderCount: 64,
|
|
20696
|
+
maxRequestHeaderBytes: 32768,
|
|
20697
|
+
maxControlBodyBytes: 65536,
|
|
20698
|
+
maxResponseHeaderBytes: 32768,
|
|
20699
|
+
maxResponseBodyBytes: 4194304,
|
|
20700
|
+
maxFileBytes: 4194304,
|
|
20701
|
+
maxConcurrentSessions: 16,
|
|
20702
|
+
maxConcurrentOperations: 32,
|
|
20703
|
+
maxOperationsPerSession: 4,
|
|
20704
|
+
maxDnsAddresses: 32,
|
|
20705
|
+
bootstrapLifetimeMs: 6e4,
|
|
20706
|
+
sessionLifetimeMs: 6e5,
|
|
20707
|
+
connectTimeoutMs: 2e3,
|
|
20708
|
+
responseHeaderTimeoutMs: 5e3,
|
|
20709
|
+
bodyIdleTimeoutMs: 1e4,
|
|
20710
|
+
operationTimeoutMs: 3e4,
|
|
20711
|
+
maxRedirects: 0,
|
|
20712
|
+
maxRequestBodyBytes: 4194304,
|
|
20713
|
+
maxRequestBodyPatternLength: 2048,
|
|
20714
|
+
maxRequestBodyReplacementLength: 4096,
|
|
20715
|
+
maxRequestBodyOperations: 32,
|
|
20716
|
+
maxRequestBodyTransforms: 32,
|
|
20717
|
+
maxRegexDeadlineMs: 250,
|
|
20718
|
+
maxLocalOrigins: 32
|
|
20719
|
+
});
|
|
20720
|
+
var PROTOCOL = "v1";
|
|
20721
|
+
var ENVELOPE_MAX_BYTES = 64 * 1024;
|
|
20722
|
+
var FORBIDDEN_BODY_KEYS = /* @__PURE__ */ new Set(["body", "requestBody", "responseBody"]);
|
|
20723
|
+
var MOCK_BODY_KEY = "mockBody";
|
|
20724
|
+
var MOCK_BODY_ALLOWED_TYPES = /* @__PURE__ */ new Set(["mock.response"]);
|
|
20725
|
+
var BODY_CHECK_EXEMPT_TYPES = /* @__PURE__ */ new Set(["runtime.project.set"]);
|
|
20726
|
+
var ENVELOPE_MESSAGE_TYPES = /* @__PURE__ */ new Set([
|
|
20727
|
+
"runtime.start",
|
|
20728
|
+
"runtime.stop",
|
|
20729
|
+
"runtime.status",
|
|
20730
|
+
"runtime.project.set",
|
|
20731
|
+
"authority.grant",
|
|
20732
|
+
"authority.revoke",
|
|
20733
|
+
"transform.request",
|
|
20734
|
+
"transform.result",
|
|
20735
|
+
"pair.request",
|
|
20736
|
+
"pair.response",
|
|
20737
|
+
"authorize.request",
|
|
20738
|
+
"authorize.response",
|
|
20739
|
+
"mock.connect",
|
|
20740
|
+
"mock.request",
|
|
20741
|
+
"mock.response",
|
|
20742
|
+
"ai.complete",
|
|
20743
|
+
"ai.stream.chunk",
|
|
20744
|
+
"ai.error"
|
|
20745
|
+
]);
|
|
20746
|
+
var EnvelopeError = class extends Error {
|
|
20747
|
+
constructor(message) {
|
|
20748
|
+
super(message);
|
|
20749
|
+
this.name = "EnvelopeError";
|
|
20750
|
+
}
|
|
20751
|
+
};
|
|
20752
|
+
function containsBodyKey(value) {
|
|
20753
|
+
if (value === null || typeof value !== "object") return false;
|
|
20754
|
+
if (Array.isArray(value)) {
|
|
20755
|
+
for (const item of value) {
|
|
20756
|
+
if (containsBodyKey(item)) return true;
|
|
19916
20757
|
}
|
|
20758
|
+
return false;
|
|
19917
20759
|
}
|
|
19918
|
-
|
|
20760
|
+
for (const key of Object.keys(value)) {
|
|
20761
|
+
if (FORBIDDEN_BODY_KEYS.has(key)) return true;
|
|
20762
|
+
if (containsBodyKey(value[key])) return true;
|
|
20763
|
+
}
|
|
20764
|
+
return false;
|
|
19919
20765
|
}
|
|
19920
|
-
|
|
19921
|
-
|
|
19922
|
-
|
|
19923
|
-
|
|
19924
|
-
|
|
19925
|
-
|
|
19926
|
-
|
|
19927
|
-
|
|
19928
|
-
|
|
19929
|
-
|
|
19930
|
-
|
|
19931
|
-
}
|
|
19932
|
-
const response = await fetch(url, {
|
|
19933
|
-
...init,
|
|
19934
|
-
signal: controller.signal
|
|
19935
|
-
});
|
|
19936
|
-
clearTimeout(timeoutId);
|
|
19937
|
-
if (response.ok) {
|
|
19938
|
-
return response;
|
|
19939
|
-
}
|
|
19940
|
-
const shouldRetry = response.status === 429 || response.status >= 500;
|
|
19941
|
-
if (shouldRetry && attempt < MAX_RETRIES) {
|
|
19942
|
-
const retryAfter = response.headers.get("Retry-After");
|
|
19943
|
-
const delay2 = retryAfter ? parseInt(retryAfter, 10) * 1e3 : calculateRetryDelay(attempt);
|
|
19944
|
-
await sleep(Math.min(delay2, 1e4));
|
|
19945
|
-
continue;
|
|
19946
|
-
}
|
|
19947
|
-
const errorText = await response.text().catch(() => "");
|
|
19948
|
-
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
19949
|
-
} catch (e) {
|
|
19950
|
-
lastError = e;
|
|
19951
|
-
if (e instanceof DOMException && e.name === "AbortError") {
|
|
19952
|
-
throw e;
|
|
19953
|
-
}
|
|
19954
|
-
if (attempt < MAX_RETRIES && e.name !== "AbortError") {
|
|
19955
|
-
await sleep(calculateRetryDelay(attempt));
|
|
19956
|
-
continue;
|
|
19957
|
-
}
|
|
19958
|
-
throw e;
|
|
19959
|
-
}
|
|
20766
|
+
function assertNoBodyContent(type, metadata) {
|
|
20767
|
+
if (BODY_CHECK_EXEMPT_TYPES.has(type)) return;
|
|
20768
|
+
if (containsBodyKey(metadata)) {
|
|
20769
|
+
throw new EnvelopeError(
|
|
20770
|
+
"envelope must not carry request or response body content"
|
|
20771
|
+
);
|
|
20772
|
+
}
|
|
20773
|
+
if (MOCK_BODY_KEY in metadata && !MOCK_BODY_ALLOWED_TYPES.has(type)) {
|
|
20774
|
+
throw new EnvelopeError(
|
|
20775
|
+
"mockBody is only permitted on the mock.response envelope"
|
|
20776
|
+
);
|
|
19960
20777
|
}
|
|
19961
|
-
throw lastError;
|
|
19962
20778
|
}
|
|
19963
|
-
function
|
|
19964
|
-
return
|
|
20779
|
+
function utf8ByteLength(value) {
|
|
20780
|
+
return Buffer.byteLength(value, "utf8");
|
|
19965
20781
|
}
|
|
19966
|
-
function
|
|
19967
|
-
|
|
19968
|
-
|
|
19969
|
-
|
|
19970
|
-
|
|
19971
|
-
|
|
19972
|
-
}
|
|
19973
|
-
|
|
19974
|
-
|
|
19975
|
-
|
|
19976
|
-
|
|
19977
|
-
|
|
19978
|
-
|
|
19979
|
-
|
|
19980
|
-
response_format: options.responseFormat
|
|
19981
|
-
});
|
|
19982
|
-
const response = await fetchWithRetry(
|
|
19983
|
-
chatUrl,
|
|
19984
|
-
{
|
|
19985
|
-
method: "POST",
|
|
19986
|
-
headers,
|
|
19987
|
-
body
|
|
19988
|
-
},
|
|
19989
|
-
options.signal
|
|
19990
|
-
);
|
|
19991
|
-
const data = await response.json();
|
|
19992
|
-
const content = data.choices[0]?.message?.content ?? "";
|
|
19993
|
-
const usage = data.usage ? {
|
|
19994
|
-
promptTokens: data.usage.prompt_tokens,
|
|
19995
|
-
completionTokens: data.usage.completion_tokens
|
|
19996
|
-
} : void 0;
|
|
19997
|
-
return { content: sanitizeResponse(content), usage };
|
|
19998
|
-
},
|
|
19999
|
-
async *stream(options) {
|
|
20000
|
-
const body = JSON.stringify({
|
|
20001
|
-
model: options.model,
|
|
20002
|
-
messages: options.messages,
|
|
20003
|
-
temperature: options.temperature ?? 0.7,
|
|
20004
|
-
stream: true,
|
|
20005
|
-
stream_options: { include_usage: true }
|
|
20006
|
-
});
|
|
20007
|
-
const response = await fetchStreamWithRetry(
|
|
20008
|
-
chatUrl,
|
|
20009
|
-
{
|
|
20010
|
-
method: "POST",
|
|
20011
|
-
headers,
|
|
20012
|
-
body
|
|
20013
|
-
},
|
|
20014
|
-
options.signal
|
|
20015
|
-
);
|
|
20016
|
-
if (!response.body) {
|
|
20017
|
-
throw new Error("No response body");
|
|
20018
|
-
}
|
|
20019
|
-
const reader = response.body.getReader();
|
|
20020
|
-
const decoder = new TextDecoder();
|
|
20021
|
-
let buffer = "";
|
|
20022
|
-
try {
|
|
20023
|
-
while (true) {
|
|
20024
|
-
if (options.signal?.aborted) {
|
|
20025
|
-
throw new DOMException("Aborted", "AbortError");
|
|
20026
|
-
}
|
|
20027
|
-
const { done, value } = await reader.read();
|
|
20028
|
-
if (done) break;
|
|
20029
|
-
buffer += decoder.decode(value, { stream: true });
|
|
20030
|
-
const lines = buffer.split("\n");
|
|
20031
|
-
buffer = lines.pop() ?? "";
|
|
20032
|
-
for (const line of lines) {
|
|
20033
|
-
if (!line.startsWith("data: ")) continue;
|
|
20034
|
-
const data = line.slice(6).trim();
|
|
20035
|
-
if (data === "[DONE]") continue;
|
|
20036
|
-
try {
|
|
20037
|
-
const parsed = JSON.parse(data);
|
|
20038
|
-
const delta = parsed.choices[0]?.delta?.content ?? "";
|
|
20039
|
-
const finishReason = parsed.choices[0]?.finish_reason;
|
|
20040
|
-
const usage = parsed.usage ? {
|
|
20041
|
-
promptTokens: parsed.usage.prompt_tokens,
|
|
20042
|
-
completionTokens: parsed.usage.completion_tokens
|
|
20043
|
-
} : void 0;
|
|
20044
|
-
if (delta) {
|
|
20045
|
-
yield { delta, done: false };
|
|
20046
|
-
}
|
|
20047
|
-
if (finishReason === "stop") {
|
|
20048
|
-
yield { delta: "", done: true, usage };
|
|
20049
|
-
return;
|
|
20050
|
-
}
|
|
20051
|
-
} catch {
|
|
20052
|
-
}
|
|
20053
|
-
}
|
|
20054
|
-
}
|
|
20055
|
-
} finally {
|
|
20056
|
-
reader.releaseLock();
|
|
20057
|
-
}
|
|
20058
|
-
yield { delta: "", done: true };
|
|
20059
|
-
}
|
|
20782
|
+
function serializeEnvelope(input) {
|
|
20783
|
+
if (!ENVELOPE_MESSAGE_TYPES.has(input.type)) {
|
|
20784
|
+
throw new EnvelopeError(`unknown envelope type: ${String(input.type)}`);
|
|
20785
|
+
}
|
|
20786
|
+
if (input.metadata === void 0 || typeof input.metadata !== "object") {
|
|
20787
|
+
throw new EnvelopeError("envelope metadata is required");
|
|
20788
|
+
}
|
|
20789
|
+
assertNoBodyContent(input.type, input.metadata);
|
|
20790
|
+
const envelope = {
|
|
20791
|
+
protocol: PROTOCOL,
|
|
20792
|
+
type: input.type,
|
|
20793
|
+
metadata: input.metadata,
|
|
20794
|
+
...input.requestId !== void 0 ? { requestId: input.requestId } : {},
|
|
20795
|
+
...input.timestamp !== void 0 ? { timestamp: input.timestamp } : {}
|
|
20060
20796
|
};
|
|
20797
|
+
const serialized = JSON.stringify(envelope);
|
|
20798
|
+
if (utf8ByteLength(serialized) > ENVELOPE_MAX_BYTES) {
|
|
20799
|
+
throw new EnvelopeError("envelope exceeds maximum size");
|
|
20800
|
+
}
|
|
20801
|
+
return serialized;
|
|
20061
20802
|
}
|
|
20062
|
-
function
|
|
20063
|
-
|
|
20064
|
-
|
|
20065
|
-
|
|
20066
|
-
|
|
20067
|
-
|
|
20068
|
-
}
|
|
20069
|
-
const userProfile = process.env.USERPROFILE;
|
|
20070
|
-
if (userProfile) {
|
|
20071
|
-
return path.join(userProfile, "AppData", "Local", "rogatio");
|
|
20072
|
-
}
|
|
20073
|
-
throw new Error("Unable to determine Windows config directory");
|
|
20803
|
+
function parseEnvelope(json) {
|
|
20804
|
+
let parsed;
|
|
20805
|
+
try {
|
|
20806
|
+
parsed = JSON.parse(json);
|
|
20807
|
+
} catch {
|
|
20808
|
+
throw new EnvelopeError("envelope is not valid JSON");
|
|
20074
20809
|
}
|
|
20075
|
-
if (
|
|
20076
|
-
|
|
20077
|
-
if (!home2) throw new Error("HOME not set");
|
|
20078
|
-
return path.join(home2, "Library", "Application Support", "rogatio");
|
|
20810
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
20811
|
+
throw new EnvelopeError("envelope must be an object");
|
|
20079
20812
|
}
|
|
20080
|
-
const
|
|
20081
|
-
if (
|
|
20082
|
-
|
|
20813
|
+
const record = parsed;
|
|
20814
|
+
if (record.protocol !== PROTOCOL) {
|
|
20815
|
+
throw new EnvelopeError("envelope protocol mismatch");
|
|
20083
20816
|
}
|
|
20084
|
-
|
|
20085
|
-
|
|
20086
|
-
|
|
20817
|
+
if (typeof record.type !== "string" || !ENVELOPE_MESSAGE_TYPES.has(record.type)) {
|
|
20818
|
+
throw new EnvelopeError("envelope type invalid");
|
|
20819
|
+
}
|
|
20820
|
+
if (typeof record.metadata !== "object" || record.metadata === null) {
|
|
20821
|
+
throw new EnvelopeError("envelope metadata missing");
|
|
20822
|
+
}
|
|
20823
|
+
assertNoBodyContent(
|
|
20824
|
+
record.type,
|
|
20825
|
+
record.metadata
|
|
20826
|
+
);
|
|
20827
|
+
const envelope = {
|
|
20828
|
+
protocol: PROTOCOL,
|
|
20829
|
+
type: record.type,
|
|
20830
|
+
metadata: record.metadata,
|
|
20831
|
+
...typeof record.requestId === "string" ? { requestId: record.requestId } : {},
|
|
20832
|
+
...typeof record.timestamp === "number" ? { timestamp: record.timestamp } : {}
|
|
20833
|
+
};
|
|
20834
|
+
return envelope;
|
|
20835
|
+
}
|
|
20836
|
+
var TOKEN_BYTES = 32;
|
|
20837
|
+
var TOKEN_LENGTH = 43;
|
|
20838
|
+
function createCapabilityState(preset, now) {
|
|
20839
|
+
const bootstrap = randomBytes(TOKEN_BYTES).toString("base64url");
|
|
20840
|
+
return {
|
|
20841
|
+
presetDigest: preset.digest,
|
|
20842
|
+
bootstrap,
|
|
20843
|
+
bootstrapDigest: digestToken(bootstrap),
|
|
20844
|
+
bootstrapExpiresAt: now + RUNTIME_LIMITS.bootstrapLifetimeMs,
|
|
20845
|
+
sessions: [],
|
|
20846
|
+
consumed: false,
|
|
20847
|
+
closed: false
|
|
20848
|
+
};
|
|
20087
20849
|
}
|
|
20088
|
-
function
|
|
20089
|
-
return
|
|
20850
|
+
function digestToken(value) {
|
|
20851
|
+
return createHash("sha256").update(value, "utf8").digest();
|
|
20090
20852
|
}
|
|
20091
|
-
|
|
20092
|
-
|
|
20853
|
+
function isToken(value) {
|
|
20854
|
+
if (typeof value !== "string" || value.length !== TOKEN_LENGTH) return false;
|
|
20855
|
+
if (!/^[A-Za-z0-9_-]+$/.test(value)) return false;
|
|
20093
20856
|
try {
|
|
20094
|
-
|
|
20095
|
-
|
|
20096
|
-
|
|
20097
|
-
if (e.code === "ENOENT") {
|
|
20098
|
-
return null;
|
|
20099
|
-
}
|
|
20100
|
-
throw e;
|
|
20857
|
+
return Buffer.from(value, "base64url").length === TOKEN_BYTES;
|
|
20858
|
+
} catch {
|
|
20859
|
+
return false;
|
|
20101
20860
|
}
|
|
20102
20861
|
}
|
|
20103
|
-
|
|
20104
|
-
const
|
|
20105
|
-
|
|
20106
|
-
await fs.mkdir(configDir, { recursive: true });
|
|
20107
|
-
const content = JSON.stringify(config, null, 2);
|
|
20108
|
-
await fs.writeFile(configPath, content, { mode: 384 });
|
|
20109
|
-
}
|
|
20110
|
-
async function deleteProviderConfig() {
|
|
20111
|
-
const configPath = getProviderConfigPath();
|
|
20112
|
-
try {
|
|
20113
|
-
await fs.unlink(configPath);
|
|
20114
|
-
} catch (e) {
|
|
20115
|
-
if (e.code !== "ENOENT") {
|
|
20116
|
-
throw e;
|
|
20117
|
-
}
|
|
20118
|
-
}
|
|
20862
|
+
function sameToken(value, expectedDigest) {
|
|
20863
|
+
const actual = digestToken(value);
|
|
20864
|
+
return actual.length === expectedDigest.length && timingSafeEqual(actual, expectedDigest);
|
|
20119
20865
|
}
|
|
20120
|
-
function
|
|
20121
|
-
|
|
20866
|
+
function sameDigest(value, expected) {
|
|
20867
|
+
if (!isSha256Digest(value) || value.length !== expected.length) return false;
|
|
20868
|
+
const actual = Buffer.from(value, "ascii");
|
|
20869
|
+
const wanted = Buffer.from(expected, "ascii");
|
|
20870
|
+
return timingSafeEqual(actual, wanted);
|
|
20122
20871
|
}
|
|
20123
|
-
function
|
|
20124
|
-
|
|
20872
|
+
function cleanSessions(state, now) {
|
|
20873
|
+
for (let index = state.sessions.length - 1; index >= 0; index -= 1) {
|
|
20874
|
+
if (state.sessions[index]?.expiresAt <= now)
|
|
20875
|
+
state.sessions.splice(index, 1);
|
|
20876
|
+
}
|
|
20125
20877
|
}
|
|
20126
|
-
function
|
|
20127
|
-
|
|
20128
|
-
if (
|
|
20129
|
-
return
|
|
20878
|
+
function pairCapability(state, capability, digest, now) {
|
|
20879
|
+
cleanSessions(state, now);
|
|
20880
|
+
if (state.closed || state.consumed || !isToken(capability) || typeof digest !== "string" || !sameDigest(digest, state.presetDigest) || now >= state.bootstrapExpiresAt || !sameToken(capability, state.bootstrapDigest)) {
|
|
20881
|
+
return failure("runtime.pairing-denied");
|
|
20130
20882
|
}
|
|
20131
|
-
if (
|
|
20132
|
-
return
|
|
20883
|
+
if (state.sessions.length >= RUNTIME_LIMITS.maxConcurrentSessions) {
|
|
20884
|
+
return failure("runtime.overloaded");
|
|
20133
20885
|
}
|
|
20134
|
-
|
|
20135
|
-
|
|
20136
|
-
|
|
20137
|
-
|
|
20886
|
+
state.consumed = true;
|
|
20887
|
+
const sessionCapability = randomBytes(TOKEN_BYTES).toString("base64url");
|
|
20888
|
+
const expiresAt = now + RUNTIME_LIMITS.sessionLifetimeMs;
|
|
20889
|
+
state.sessions.push({
|
|
20890
|
+
digest: Buffer.from(state.presetDigest, "ascii"),
|
|
20891
|
+
expiresAt,
|
|
20892
|
+
tokenDigest: digestToken(sessionCapability),
|
|
20893
|
+
activeOperations: 0
|
|
20894
|
+
});
|
|
20895
|
+
return {
|
|
20896
|
+
ok: true,
|
|
20897
|
+
value: {
|
|
20898
|
+
sessionCapability,
|
|
20899
|
+
expiresInMs: RUNTIME_LIMITS.sessionLifetimeMs
|
|
20900
|
+
}
|
|
20901
|
+
};
|
|
20902
|
+
}
|
|
20903
|
+
function findSession(state, capability, digest, now) {
|
|
20904
|
+
cleanSessions(state, now);
|
|
20905
|
+
if (state.closed || !isToken(capability) || typeof digest !== "string")
|
|
20138
20906
|
return null;
|
|
20907
|
+
if (!sameDigest(digest, state.presetDigest)) return null;
|
|
20908
|
+
const candidate = digestToken(capability);
|
|
20909
|
+
for (const session of state.sessions) {
|
|
20910
|
+
if (candidate.length === session.tokenDigest.length && timingSafeEqual(candidate, session.tokenDigest) && sameDigest(digest, session.digest.toString("ascii"))) {
|
|
20911
|
+
return session;
|
|
20912
|
+
}
|
|
20139
20913
|
}
|
|
20140
|
-
return
|
|
20914
|
+
return null;
|
|
20141
20915
|
}
|
|
20142
|
-
|
|
20143
|
-
|
|
20144
|
-
|
|
20145
|
-
if (value === null || typeof value !== "object") {
|
|
20146
|
-
return { valid: true, value };
|
|
20916
|
+
function acquireOperation(_state, session) {
|
|
20917
|
+
if (session.activeOperations >= RUNTIME_LIMITS.maxOperationsPerSession) {
|
|
20918
|
+
return failure("runtime.overloaded");
|
|
20147
20919
|
}
|
|
20148
|
-
|
|
20149
|
-
|
|
20150
|
-
|
|
20151
|
-
|
|
20152
|
-
|
|
20153
|
-
}
|
|
20154
|
-
if (Array.isArray(value)) {
|
|
20155
|
-
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
20156
|
-
if (lengthDescriptor === void 0 || !("value" in lengthDescriptor) || !Number.isSafeInteger(lengthDescriptor.value) || lengthDescriptor.value < 0 || lengthDescriptor.value > MAX_ARRAY_LENGTH) {
|
|
20157
|
-
return { valid: false };
|
|
20158
|
-
}
|
|
20159
|
-
const length = lengthDescriptor.value;
|
|
20160
|
-
for (const propertyName of Object.getOwnPropertyNames(value)) {
|
|
20161
|
-
if (propertyName === "length") continue;
|
|
20162
|
-
const index = Number(propertyName);
|
|
20163
|
-
if (!Number.isInteger(index) || index < 0 || index >= length || String(index) !== propertyName) {
|
|
20164
|
-
return { valid: false };
|
|
20165
|
-
}
|
|
20166
|
-
}
|
|
20167
|
-
const snapshot2 = new Array(length);
|
|
20168
|
-
for (let index = 0; index < length; index += 1) {
|
|
20169
|
-
const descriptor = Object.getOwnPropertyDescriptor(
|
|
20170
|
-
value,
|
|
20171
|
-
String(index)
|
|
20172
|
-
);
|
|
20173
|
-
if (descriptor === void 0 || !("value" in descriptor) || !descriptor.enumerable) {
|
|
20174
|
-
return { valid: false };
|
|
20175
|
-
}
|
|
20176
|
-
const child = snapshotOwnData2(descriptor.value, ancestors);
|
|
20177
|
-
if (!child.valid) return child;
|
|
20178
|
-
snapshot2[index] = child.value;
|
|
20179
|
-
}
|
|
20180
|
-
return { valid: true, value: snapshot2 };
|
|
20181
|
-
}
|
|
20182
|
-
const names = Object.getOwnPropertyNames(value);
|
|
20183
|
-
if (names.length > MAX_OBJECT_PROPERTIES) return { valid: false };
|
|
20184
|
-
const snapshot = /* @__PURE__ */ Object.create(null);
|
|
20185
|
-
for (const key of names) {
|
|
20186
|
-
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
20187
|
-
if (descriptor === void 0 || !("value" in descriptor) || !descriptor.enumerable) {
|
|
20188
|
-
return { valid: false };
|
|
20189
|
-
}
|
|
20190
|
-
const child = snapshotOwnData2(descriptor.value, ancestors);
|
|
20191
|
-
if (!child.valid) return child;
|
|
20192
|
-
Object.defineProperty(snapshot, key, {
|
|
20193
|
-
configurable: true,
|
|
20194
|
-
enumerable: true,
|
|
20195
|
-
value: child.value,
|
|
20196
|
-
writable: true
|
|
20197
|
-
});
|
|
20920
|
+
session.activeOperations += 1;
|
|
20921
|
+
return {
|
|
20922
|
+
ok: true,
|
|
20923
|
+
value: () => {
|
|
20924
|
+
session.activeOperations = Math.max(0, session.activeOperations - 1);
|
|
20198
20925
|
}
|
|
20199
|
-
|
|
20200
|
-
} catch {
|
|
20201
|
-
return { valid: false };
|
|
20202
|
-
} finally {
|
|
20203
|
-
ancestors.delete(value);
|
|
20204
|
-
}
|
|
20205
|
-
}
|
|
20206
|
-
function hasOwn(value, key) {
|
|
20207
|
-
return Object.hasOwn(value, key);
|
|
20926
|
+
};
|
|
20208
20927
|
}
|
|
20209
|
-
|
|
20210
|
-
|
|
20211
|
-
|
|
20212
|
-
|
|
20213
|
-
|
|
20214
|
-
return false;
|
|
20215
|
-
}
|
|
20216
|
-
index += 2;
|
|
20217
|
-
}
|
|
20218
|
-
return true;
|
|
20928
|
+
function closeCapabilityState(state) {
|
|
20929
|
+
state.closed = true;
|
|
20930
|
+
state.consumed = true;
|
|
20931
|
+
state.sessions.length = 0;
|
|
20932
|
+
state.bootstrapDigest.fill(0);
|
|
20219
20933
|
}
|
|
20220
|
-
|
|
20221
|
-
|
|
20222
|
-
|
|
20223
|
-
|
|
20224
|
-
|
|
20225
|
-
for (const marker of ["/", "?", "#"]) {
|
|
20226
|
-
const index = value.indexOf(marker, afterScheme);
|
|
20227
|
-
if (index >= 0 && index < end) end = index;
|
|
20934
|
+
var registeredProvider = null;
|
|
20935
|
+
var currentSession = null;
|
|
20936
|
+
async function startInterception(activation, policyDigest, extensionId, pacOrigins, targetPolicy) {
|
|
20937
|
+
if (registeredProvider === null) {
|
|
20938
|
+
return { kind: "unsupported", reasons: ["no-interception-provider"] };
|
|
20228
20939
|
}
|
|
20229
|
-
|
|
20230
|
-
|
|
20231
|
-
|
|
20232
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
20233
|
-
if (value.charCodeAt(index) > 127) return true;
|
|
20940
|
+
const capabilities = registeredProvider.detect();
|
|
20941
|
+
if (!capabilities.supported) {
|
|
20942
|
+
return { kind: "unsupported", reasons: capabilities.reasons };
|
|
20234
20943
|
}
|
|
20235
|
-
|
|
20236
|
-
}
|
|
20237
|
-
function validHostname(hostname) {
|
|
20238
|
-
const unbracketed = hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
|
|
20239
|
-
if (unbracketed.includes("%") || unbracketed.endsWith(".")) return false;
|
|
20240
|
-
if (isIP2(unbracketed) !== 0) return true;
|
|
20241
|
-
if (unbracketed.length === 0 || unbracketed.length > 253) return false;
|
|
20242
|
-
return unbracketed.split(".").every((label2) => ID_PATTERN.test(label2));
|
|
20243
|
-
}
|
|
20244
|
-
function canonicalizeOutboundTarget(value) {
|
|
20245
|
-
if (typeof value !== "string" || value.length === 0) return null;
|
|
20246
|
-
if (value.trim() !== value || hasControl(value)) return null;
|
|
20247
|
-
if (value.includes("\\") || value.includes("#")) return null;
|
|
20248
|
-
if (!hasValidPercentEncoding(value)) return null;
|
|
20249
|
-
if (/%(?:2f|2F|5c|5C)/.test(value)) return null;
|
|
20250
|
-
const authority = rawAuthority(value);
|
|
20251
|
-
if (authority === null || authority.length === 0) return null;
|
|
20252
|
-
if (authority.includes("@") || authority.includes("*") || hasNonAscii(authority)) {
|
|
20253
|
-
return null;
|
|
20944
|
+
if (currentSession !== null) {
|
|
20945
|
+
return { kind: "unsupported", reasons: ["session-collision"] };
|
|
20254
20946
|
}
|
|
20255
|
-
|
|
20947
|
+
const sessionId = `session-${activation.startedAt}-${Math.random().toString(36).slice(2)}`;
|
|
20256
20948
|
try {
|
|
20257
|
-
|
|
20258
|
-
} catch {
|
|
20259
|
-
return
|
|
20949
|
+
await registeredProvider.start(activation);
|
|
20950
|
+
} catch (error) {
|
|
20951
|
+
return {
|
|
20952
|
+
kind: "unsupported",
|
|
20953
|
+
reasons: ["provider-start-failed", String(error)]
|
|
20954
|
+
};
|
|
20955
|
+
}
|
|
20956
|
+
currentSession = {
|
|
20957
|
+
sessionId,
|
|
20958
|
+
provider: registeredProvider,
|
|
20959
|
+
activation,
|
|
20960
|
+
policyDigest,
|
|
20961
|
+
extensionId,
|
|
20962
|
+
pacOrigins,
|
|
20963
|
+
targetPolicy,
|
|
20964
|
+
startedAt: activation.startedAt
|
|
20965
|
+
};
|
|
20966
|
+
return { kind: "active", provider: registeredProvider.platform };
|
|
20967
|
+
}
|
|
20968
|
+
async function stopInterception() {
|
|
20969
|
+
if (currentSession === null && registeredProvider === null) return;
|
|
20970
|
+
const session = currentSession;
|
|
20971
|
+
currentSession = null;
|
|
20972
|
+
if (session?.provider) {
|
|
20973
|
+
try {
|
|
20974
|
+
await session.provider.stop();
|
|
20975
|
+
} catch {
|
|
20976
|
+
}
|
|
20260
20977
|
}
|
|
20261
|
-
if (url.protocol !== "http:" && url.protocol !== "https:") return null;
|
|
20262
|
-
if (url.username.length > 0 || url.password.length > 0) return null;
|
|
20263
|
-
if (url.hash.length > 0 || url.hostname.length === 0) return null;
|
|
20264
|
-
if (!validHostname(url.hostname)) return null;
|
|
20265
|
-
const defaultPort = url.protocol === "https:" ? 443 : 80;
|
|
20266
|
-
const port = url.port.length === 0 ? defaultPort : Number(url.port);
|
|
20267
|
-
if (!Number.isInteger(port) || port !== 80 && port !== 443) return null;
|
|
20268
|
-
if (url.protocol === "http:" && port !== 80) return null;
|
|
20269
|
-
if (url.protocol === "https:" && port !== 443) return null;
|
|
20270
|
-
const canonical = url.href;
|
|
20271
|
-
if (canonical.includes("#") || canonical.includes("@")) return null;
|
|
20272
|
-
return canonical;
|
|
20273
20978
|
}
|
|
20274
|
-
function
|
|
20275
|
-
|
|
20276
|
-
return origin;
|
|
20979
|
+
function hasActiveSession() {
|
|
20980
|
+
return currentSession !== null;
|
|
20277
20981
|
}
|
|
20278
|
-
function
|
|
20279
|
-
const
|
|
20280
|
-
return
|
|
20982
|
+
function withinRoot2(root, candidate) {
|
|
20983
|
+
const rest = relative2(root, candidate);
|
|
20984
|
+
return rest.length > 0 && rest !== ".." && !rest.startsWith(`..${sep}`) && !isAbsolute2(rest);
|
|
20281
20985
|
}
|
|
20282
|
-
function
|
|
20283
|
-
|
|
20986
|
+
async function readMockFile(root, logicalPath) {
|
|
20987
|
+
const normalized = normalizeLogicalPath(logicalPath);
|
|
20988
|
+
if (normalized === null) return failure("runtime.file-denied");
|
|
20989
|
+
try {
|
|
20990
|
+
const canonicalRoot = await realpath2(root);
|
|
20991
|
+
const candidate = resolve(canonicalRoot, ...normalized.split("/"));
|
|
20992
|
+
const actualPath = await realpath2(candidate);
|
|
20993
|
+
if (!withinRoot2(canonicalRoot, actualPath))
|
|
20994
|
+
return failure("runtime.file-denied");
|
|
20995
|
+
const metadata = await stat(actualPath);
|
|
20996
|
+
if (!metadata.isFile()) return failure("runtime.file-denied");
|
|
20997
|
+
if (metadata.size > RUNTIME_LIMITS.maxFileBytes)
|
|
20998
|
+
return failure("runtime.size-limit");
|
|
20999
|
+
const bytes = await readFile2(actualPath);
|
|
21000
|
+
if (bytes.byteLength > RUNTIME_LIMITS.maxFileBytes)
|
|
21001
|
+
return failure("runtime.size-limit");
|
|
21002
|
+
new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
21003
|
+
return { ok: true, value: bytes };
|
|
21004
|
+
} catch {
|
|
21005
|
+
return failure("runtime.file-denied");
|
|
21006
|
+
}
|
|
20284
21007
|
}
|
|
20285
|
-
function
|
|
20286
|
-
return
|
|
21008
|
+
function mintToken() {
|
|
21009
|
+
return randomBytes2(32).toString("hex");
|
|
20287
21010
|
}
|
|
20288
|
-
function
|
|
20289
|
-
const
|
|
20290
|
-
|
|
20291
|
-
|
|
20292
|
-
|
|
20293
|
-
|
|
20294
|
-
|
|
20295
|
-
|
|
20296
|
-
|
|
20297
|
-
|
|
21011
|
+
async function renderMockResponse(options) {
|
|
21012
|
+
const { mock, fileRoot } = options;
|
|
21013
|
+
let bodyBytes;
|
|
21014
|
+
if (mock.body !== void 0) {
|
|
21015
|
+
bodyBytes = new TextEncoder().encode(mock.body);
|
|
21016
|
+
} else if (mock.file !== void 0) {
|
|
21017
|
+
if (fileRoot === void 0) return failure("runtime.file-denied");
|
|
21018
|
+
const read = await readMockFile(fileRoot, mock.file);
|
|
21019
|
+
if (!read.ok) return failure("runtime.file-denied");
|
|
21020
|
+
bodyBytes = read.value;
|
|
21021
|
+
} else {
|
|
21022
|
+
return failure("runtime.file-denied");
|
|
21023
|
+
}
|
|
21024
|
+
const headers = [];
|
|
21025
|
+
let hasContentType = false;
|
|
21026
|
+
for (const header of mock.headers ?? []) {
|
|
21027
|
+
headers.push([header.name, header.value]);
|
|
21028
|
+
if (header.name.toLowerCase() === "content-type") hasContentType = true;
|
|
21029
|
+
}
|
|
21030
|
+
if (!hasContentType) {
|
|
21031
|
+
headers.push(["Content-Type", "text/plain; charset=UTF-8"]);
|
|
21032
|
+
}
|
|
21033
|
+
headers.push(["Content-Length", String(bodyBytes.byteLength)]);
|
|
21034
|
+
headers.push(["Cache-Control", "no-store"]);
|
|
20298
21035
|
return {
|
|
20299
|
-
|
|
20300
|
-
|
|
20301
|
-
|
|
20302
|
-
|
|
20303
|
-
|
|
20304
|
-
|
|
21036
|
+
ok: true,
|
|
21037
|
+
value: {
|
|
21038
|
+
status: mock.status,
|
|
21039
|
+
headers,
|
|
21040
|
+
bodyBytes
|
|
21041
|
+
}
|
|
20305
21042
|
};
|
|
20306
21043
|
}
|
|
20307
|
-
function
|
|
20308
|
-
|
|
21044
|
+
function stringValue(value) {
|
|
21045
|
+
const encoded = JSON.stringify(value);
|
|
21046
|
+
if (encoded === void 0) throw new Error("invalid string");
|
|
21047
|
+
return encoded;
|
|
20309
21048
|
}
|
|
20310
|
-
function
|
|
20311
|
-
return
|
|
21049
|
+
function arrayValue(values) {
|
|
21050
|
+
return `[${values.map(stringValue).join(",")}]`;
|
|
20312
21051
|
}
|
|
20313
|
-
function
|
|
20314
|
-
const
|
|
20315
|
-
|
|
20316
|
-
|
|
20317
|
-
if (target === null) return failure("runtime.authorization-denied");
|
|
20318
|
-
for (const grant of preset.grants) {
|
|
20319
|
-
if (!matchesGrant({ ...descriptor, target }, grant)) continue;
|
|
20320
|
-
const matcher = preset.matchers.find(
|
|
20321
|
-
(candidate) => candidate.groupId === grant.groupId && candidate.ruleId === grant.ruleId
|
|
20322
|
-
);
|
|
20323
|
-
if (matcher === void 0 || grant.kind === "outbound-http" && !isOriginAllowed(grant.target, matcher.matcher.origins)) {
|
|
20324
|
-
return failure("runtime.authorization-denied");
|
|
20325
|
-
}
|
|
20326
|
-
return {
|
|
20327
|
-
ok: true,
|
|
20328
|
-
value: Object.freeze({
|
|
20329
|
-
groupId: grant.groupId,
|
|
20330
|
-
ruleId: grant.ruleId,
|
|
20331
|
-
operationId: grant.operationId,
|
|
20332
|
-
kind: grant.kind,
|
|
20333
|
-
target: grant.target,
|
|
20334
|
-
method: grant.method,
|
|
20335
|
-
presetDigest: preset.digest
|
|
20336
|
-
})
|
|
20337
|
-
};
|
|
20338
|
-
}
|
|
20339
|
-
return failure("runtime.authorization-denied");
|
|
21052
|
+
function matcherValue(operation) {
|
|
21053
|
+
const matcher = operation.matcher;
|
|
21054
|
+
const method = matcher.method === void 0 ? "" : `,"method":${stringValue(matcher.method)}`;
|
|
21055
|
+
return `{"kind":"matcher","groupId":${stringValue(operation.groupId)},"ruleId":${stringValue(operation.ruleId)},"matcher":{"urlRegex":{"source":${stringValue(matcher.urlRegex.source)},"flags":""},"origins":${arrayValue(matcher.origins)},"resourceTypes":${arrayValue(matcher.resourceTypes)},"priority":${String(matcher.priority)}${method}}}`;
|
|
20340
21056
|
}
|
|
20341
|
-
|
|
20342
|
-
|
|
20343
|
-
|
|
20344
|
-
|
|
20345
|
-
|
|
20346
|
-
|
|
20347
|
-
|
|
20348
|
-
|
|
20349
|
-
|
|
20350
|
-
|
|
20351
|
-
|
|
20352
|
-
|
|
20353
|
-
|
|
20354
|
-
|
|
20355
|
-
|
|
20356
|
-
|
|
20357
|
-
|
|
20358
|
-
|
|
20359
|
-
|
|
20360
|
-
|
|
20361
|
-
|
|
20362
|
-
|
|
20363
|
-
|
|
20364
|
-
maxRequestBodyOperations: 32,
|
|
20365
|
-
maxRequestBodyTransforms: 32,
|
|
20366
|
-
maxRegexDeadlineMs: 250,
|
|
20367
|
-
maxLocalOrigins: 32
|
|
20368
|
-
});
|
|
20369
|
-
var PROTOCOL = "v1";
|
|
20370
|
-
var ENVELOPE_MAX_BYTES = 64 * 1024;
|
|
20371
|
-
var FORBIDDEN_BODY_KEYS = /* @__PURE__ */ new Set(["body", "requestBody", "responseBody"]);
|
|
20372
|
-
var MOCK_BODY_KEY = "mockBody";
|
|
20373
|
-
var MOCK_BODY_ALLOWED_TYPES = /* @__PURE__ */ new Set(["mock.response"]);
|
|
20374
|
-
var ENVELOPE_MESSAGE_TYPES = /* @__PURE__ */ new Set([
|
|
20375
|
-
"runtime.start",
|
|
20376
|
-
"runtime.stop",
|
|
20377
|
-
"runtime.status",
|
|
20378
|
-
"authority.grant",
|
|
20379
|
-
"authority.revoke",
|
|
20380
|
-
"transform.request",
|
|
20381
|
-
"transform.result",
|
|
20382
|
-
"pair.request",
|
|
20383
|
-
"pair.response",
|
|
20384
|
-
"authorize.request",
|
|
20385
|
-
"authorize.response",
|
|
20386
|
-
"mock.connect",
|
|
20387
|
-
"mock.request",
|
|
20388
|
-
"mock.response",
|
|
20389
|
-
"ai.complete",
|
|
20390
|
-
"ai.stream.chunk",
|
|
20391
|
-
"ai.error"
|
|
20392
|
-
]);
|
|
20393
|
-
var EnvelopeError = class extends Error {
|
|
20394
|
-
constructor(message) {
|
|
20395
|
-
super(message);
|
|
20396
|
-
this.name = "EnvelopeError";
|
|
20397
|
-
}
|
|
20398
|
-
};
|
|
20399
|
-
function containsBodyKey(value) {
|
|
20400
|
-
if (value === null || typeof value !== "object") return false;
|
|
20401
|
-
if (Array.isArray(value)) {
|
|
20402
|
-
for (const item of value) {
|
|
20403
|
-
if (containsBodyKey(item)) return true;
|
|
20404
|
-
}
|
|
20405
|
-
return false;
|
|
20406
|
-
}
|
|
20407
|
-
for (const key of Object.keys(value)) {
|
|
20408
|
-
if (FORBIDDEN_BODY_KEYS.has(key)) return true;
|
|
20409
|
-
if (containsBodyKey(value[key])) return true;
|
|
20410
|
-
}
|
|
20411
|
-
return false;
|
|
21057
|
+
function limitsValue(limits) {
|
|
21058
|
+
const fields = [
|
|
21059
|
+
["maxPresetBytes", limits.maxPresetBytes],
|
|
21060
|
+
["maxRequestLineBytes", limits.maxRequestLineBytes],
|
|
21061
|
+
["maxHeaderCount", limits.maxHeaderCount],
|
|
21062
|
+
["maxRequestHeaderBytes", limits.maxRequestHeaderBytes],
|
|
21063
|
+
["maxControlBodyBytes", limits.maxControlBodyBytes],
|
|
21064
|
+
["maxResponseHeaderBytes", limits.maxResponseHeaderBytes],
|
|
21065
|
+
["maxResponseBodyBytes", limits.maxResponseBodyBytes],
|
|
21066
|
+
["maxFileBytes", limits.maxFileBytes],
|
|
21067
|
+
["maxConcurrentSessions", limits.maxConcurrentSessions],
|
|
21068
|
+
["maxConcurrentOperations", limits.maxConcurrentOperations],
|
|
21069
|
+
["maxOperationsPerSession", limits.maxOperationsPerSession],
|
|
21070
|
+
["maxDnsAddresses", limits.maxDnsAddresses],
|
|
21071
|
+
["bootstrapLifetimeMs", limits.bootstrapLifetimeMs],
|
|
21072
|
+
["sessionLifetimeMs", limits.sessionLifetimeMs],
|
|
21073
|
+
["connectTimeoutMs", limits.connectTimeoutMs],
|
|
21074
|
+
["responseHeaderTimeoutMs", limits.responseHeaderTimeoutMs],
|
|
21075
|
+
["bodyIdleTimeoutMs", limits.bodyIdleTimeoutMs],
|
|
21076
|
+
["operationTimeoutMs", limits.operationTimeoutMs],
|
|
21077
|
+
["maxRedirects", limits.maxRedirects]
|
|
21078
|
+
];
|
|
21079
|
+
return `{${fields.map(([key, value]) => `${stringValue(key)}:${String(value)}`).join(",")}}`;
|
|
20412
21080
|
}
|
|
20413
|
-
function
|
|
20414
|
-
|
|
20415
|
-
|
|
20416
|
-
|
|
20417
|
-
|
|
20418
|
-
|
|
20419
|
-
|
|
20420
|
-
|
|
20421
|
-
|
|
20422
|
-
|
|
20423
|
-
}
|
|
21081
|
+
function grantValue(grant) {
|
|
21082
|
+
return `{"groupId":${stringValue(grant.groupId)},"ruleId":${stringValue(grant.ruleId)},"operationId":${stringValue(grant.operationId)},"kind":${stringValue(grant.kind)},"target":${stringValue(grant.target)},"method":${stringValue(grant.method)}}`;
|
|
21083
|
+
}
|
|
21084
|
+
function mockHeaderValue(header) {
|
|
21085
|
+
return `{"name":${stringValue(header.name)},"value":${stringValue(header.value)}}`;
|
|
21086
|
+
}
|
|
21087
|
+
function mockValue(mock) {
|
|
21088
|
+
const headers = mock.headers === void 0 ? "" : `,"headers":[${mock.headers.map(mockHeaderValue).join(",")}]`;
|
|
21089
|
+
const delay2 = mock.delayMs === void 0 ? "" : `,"delayMs":${String(mock.delayMs)}`;
|
|
21090
|
+
const body = mock.body !== void 0 ? `,"body":${stringValue(mock.body)}` : "";
|
|
21091
|
+
const file = mock.file !== void 0 ? `,"file":${stringValue(mock.file)}` : "";
|
|
21092
|
+
return `{"ruleId":${stringValue(mock.ruleId)},"status":${String(mock.status)}${headers}${delay2}${body}${file}}`;
|
|
20424
21093
|
}
|
|
20425
|
-
function
|
|
20426
|
-
return
|
|
21094
|
+
function sortMocks(mocks) {
|
|
21095
|
+
return [...mocks].sort(
|
|
21096
|
+
(left, right) => compareStrings(left.ruleId, right.ruleId)
|
|
21097
|
+
);
|
|
20427
21098
|
}
|
|
20428
|
-
function
|
|
20429
|
-
if (
|
|
20430
|
-
|
|
20431
|
-
|
|
20432
|
-
if (input.metadata === void 0 || typeof input.metadata !== "object") {
|
|
20433
|
-
throw new EnvelopeError("envelope metadata is required");
|
|
20434
|
-
}
|
|
20435
|
-
assertNoBodyContent(input.type, input.metadata);
|
|
20436
|
-
const envelope = {
|
|
20437
|
-
protocol: PROTOCOL,
|
|
20438
|
-
type: input.type,
|
|
20439
|
-
metadata: input.metadata,
|
|
20440
|
-
...input.requestId !== void 0 ? { requestId: input.requestId } : {},
|
|
20441
|
-
...input.timestamp !== void 0 ? { timestamp: input.timestamp } : {}
|
|
20442
|
-
};
|
|
20443
|
-
const serialized = JSON.stringify(envelope);
|
|
20444
|
-
if (utf8ByteLength(serialized) > ENVELOPE_MAX_BYTES) {
|
|
20445
|
-
throw new EnvelopeError("envelope exceeds maximum size");
|
|
20446
|
-
}
|
|
20447
|
-
return serialized;
|
|
21099
|
+
function compareStrings(left, right) {
|
|
21100
|
+
if (left < right) return -1;
|
|
21101
|
+
if (left > right) return 1;
|
|
21102
|
+
return 0;
|
|
20448
21103
|
}
|
|
20449
|
-
function
|
|
20450
|
-
|
|
20451
|
-
|
|
20452
|
-
parsed = JSON.parse(json);
|
|
20453
|
-
} catch {
|
|
20454
|
-
throw new EnvelopeError("envelope is not valid JSON");
|
|
20455
|
-
}
|
|
20456
|
-
if (parsed === null || typeof parsed !== "object") {
|
|
20457
|
-
throw new EnvelopeError("envelope must be an object");
|
|
20458
|
-
}
|
|
20459
|
-
const record = parsed;
|
|
20460
|
-
if (record.protocol !== PROTOCOL) {
|
|
20461
|
-
throw new EnvelopeError("envelope protocol mismatch");
|
|
20462
|
-
}
|
|
20463
|
-
if (typeof record.type !== "string" || !ENVELOPE_MESSAGE_TYPES.has(record.type)) {
|
|
20464
|
-
throw new EnvelopeError("envelope type invalid");
|
|
20465
|
-
}
|
|
20466
|
-
if (typeof record.metadata !== "object" || record.metadata === null) {
|
|
20467
|
-
throw new EnvelopeError("envelope metadata missing");
|
|
20468
|
-
}
|
|
20469
|
-
assertNoBodyContent(
|
|
20470
|
-
record.type,
|
|
20471
|
-
record.metadata
|
|
21104
|
+
function sortGrants(grants) {
|
|
21105
|
+
return [...grants].sort(
|
|
21106
|
+
(left, right) => compareStrings(left.groupId, right.groupId) || compareStrings(left.ruleId, right.ruleId) || compareStrings(left.operationId, right.operationId) || compareStrings(left.kind, right.kind) || compareStrings(left.target, right.target) || compareStrings(left.method, right.method)
|
|
20472
21107
|
);
|
|
20473
|
-
const envelope = {
|
|
20474
|
-
protocol: PROTOCOL,
|
|
20475
|
-
type: record.type,
|
|
20476
|
-
metadata: record.metadata,
|
|
20477
|
-
...typeof record.requestId === "string" ? { requestId: record.requestId } : {},
|
|
20478
|
-
...typeof record.timestamp === "number" ? { timestamp: record.timestamp } : {}
|
|
20479
|
-
};
|
|
20480
|
-
return envelope;
|
|
20481
21108
|
}
|
|
20482
|
-
|
|
20483
|
-
|
|
20484
|
-
|
|
20485
|
-
const
|
|
20486
|
-
return
|
|
20487
|
-
presetDigest: preset.digest,
|
|
20488
|
-
bootstrap,
|
|
20489
|
-
bootstrapDigest: digestToken(bootstrap),
|
|
20490
|
-
bootstrapExpiresAt: now + RUNTIME_LIMITS.bootstrapLifetimeMs,
|
|
20491
|
-
sessions: [],
|
|
20492
|
-
consumed: false,
|
|
20493
|
-
closed: false
|
|
20494
|
-
};
|
|
21109
|
+
function canonicalPresetBytes(preset, grants = preset.grants) {
|
|
21110
|
+
const matchers = preset.matchers.map(matcherValue).join(",");
|
|
21111
|
+
const mocks = preset.mocks === void 0 || preset.mocks.length === 0 ? "" : `,"mocks":[${sortMocks(preset.mocks).map(mockValue).join(",")}]`;
|
|
21112
|
+
const canonical = `{"version":1,"limits":${limitsValue(preset.limits)},"matchers":[${matchers}],"grants":[${sortGrants(grants).map(grantValue).join(",")}]${mocks}}`;
|
|
21113
|
+
return new TextEncoder().encode(canonical);
|
|
20495
21114
|
}
|
|
20496
|
-
function
|
|
20497
|
-
return
|
|
21115
|
+
function digestBytes(bytes) {
|
|
21116
|
+
return formatSha256(createHash2("sha256").update(bytes).digest("hex"));
|
|
20498
21117
|
}
|
|
20499
|
-
|
|
20500
|
-
|
|
20501
|
-
|
|
20502
|
-
|
|
20503
|
-
return Buffer.from(value, "base64url").length === TOKEN_BYTES;
|
|
20504
|
-
} catch {
|
|
20505
|
-
return false;
|
|
20506
|
-
}
|
|
21118
|
+
var ID_PATTERN2 = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
21119
|
+
function exactKeys(value, required) {
|
|
21120
|
+
const keys = Object.keys(value);
|
|
21121
|
+
return keys.length === required.length && required.every((key) => hasOwn(value, key));
|
|
20507
21122
|
}
|
|
20508
|
-
function
|
|
20509
|
-
|
|
20510
|
-
return actual.length === expectedDigest.length && timingSafeEqual(actual, expectedDigest);
|
|
21123
|
+
function validId2(value) {
|
|
21124
|
+
return typeof value === "string" && ID_PATTERN2.test(value);
|
|
20511
21125
|
}
|
|
20512
|
-
function
|
|
20513
|
-
|
|
20514
|
-
const actual = Buffer.from(value, "ascii");
|
|
20515
|
-
const wanted = Buffer.from(expected, "ascii");
|
|
20516
|
-
return timingSafeEqual(actual, wanted);
|
|
21126
|
+
function validMethod2(value) {
|
|
21127
|
+
return typeof value === "string" && HTTP_METHODS.includes(value);
|
|
20517
21128
|
}
|
|
20518
|
-
function
|
|
20519
|
-
|
|
20520
|
-
|
|
20521
|
-
|
|
21129
|
+
function normalizeMockHeader(value) {
|
|
21130
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
21131
|
+
return null;
|
|
21132
|
+
if (!exactKeys(value, ["name", "value"])) return null;
|
|
21133
|
+
const record = value;
|
|
21134
|
+
if (typeof record.name !== "string" || record.name.length === 0 || record.name.length > LIMITS.maxMockHeaderNameLength || hasControl(record.name) || record.name.includes(":"))
|
|
21135
|
+
return null;
|
|
21136
|
+
if (typeof record.value !== "string" || record.value.length > LIMITS.maxMockHeaderValueLength)
|
|
21137
|
+
return null;
|
|
21138
|
+
return { name: record.name, value: record.value };
|
|
21139
|
+
}
|
|
21140
|
+
function normalizeMockConfig(value, matcherById) {
|
|
21141
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
21142
|
+
return null;
|
|
21143
|
+
const record = value;
|
|
21144
|
+
const allowedKeys = [
|
|
21145
|
+
"ruleId",
|
|
21146
|
+
"status",
|
|
21147
|
+
"headers",
|
|
21148
|
+
"delayMs",
|
|
21149
|
+
"body",
|
|
21150
|
+
"file"
|
|
21151
|
+
];
|
|
21152
|
+
if (Object.keys(record).some((key) => !allowedKeys.includes(key)))
|
|
21153
|
+
return null;
|
|
21154
|
+
if (!validId2(record.ruleId) || !matcherById.has(record.ruleId)) return null;
|
|
21155
|
+
if (typeof record.status !== "number" || !Number.isSafeInteger(record.status) || record.status < LIMITS.minMockStatus || record.status > LIMITS.maxMockStatus)
|
|
21156
|
+
return null;
|
|
21157
|
+
let headers;
|
|
21158
|
+
if (hasOwn(record, "headers")) {
|
|
21159
|
+
if (!Array.isArray(record.headers) || record.headers.length > LIMITS.maxMockHeadersPerRule)
|
|
21160
|
+
return null;
|
|
21161
|
+
const normalizedHeaders = [];
|
|
21162
|
+
for (const headerValue2 of record.headers) {
|
|
21163
|
+
const header = normalizeMockHeader(headerValue2);
|
|
21164
|
+
if (header === null) return null;
|
|
21165
|
+
normalizedHeaders.push(header);
|
|
21166
|
+
}
|
|
21167
|
+
headers = Object.freeze(normalizedHeaders);
|
|
21168
|
+
}
|
|
21169
|
+
let delayMs;
|
|
21170
|
+
if (hasOwn(record, "delayMs")) {
|
|
21171
|
+
if (typeof record.delayMs !== "number" || !Number.isSafeInteger(record.delayMs) || record.delayMs < 0 || record.delayMs > LIMITS.maxMockDelayMs)
|
|
21172
|
+
return null;
|
|
21173
|
+
delayMs = record.delayMs;
|
|
21174
|
+
}
|
|
21175
|
+
const body = record.body;
|
|
21176
|
+
const file = record.file;
|
|
21177
|
+
const bodySet = typeof body === "string";
|
|
21178
|
+
const fileSet = typeof file === "string";
|
|
21179
|
+
if (bodySet === fileSet) return null;
|
|
21180
|
+
if (bodySet && body.length > LIMITS.maxMockInlineBodyLength) return null;
|
|
21181
|
+
if (fileSet) {
|
|
21182
|
+
if (file.length === 0 || file.length > LIMITS.maxMockFilePathLength || hasControl(file))
|
|
21183
|
+
return null;
|
|
20522
21184
|
}
|
|
21185
|
+
return Object.freeze({
|
|
21186
|
+
ruleId: record.ruleId,
|
|
21187
|
+
status: record.status,
|
|
21188
|
+
...headers === void 0 ? {} : { headers },
|
|
21189
|
+
...delayMs === void 0 ? {} : { delayMs },
|
|
21190
|
+
...bodySet ? { body } : {},
|
|
21191
|
+
...fileSet ? { file } : {}
|
|
21192
|
+
});
|
|
20523
21193
|
}
|
|
20524
|
-
function
|
|
20525
|
-
|
|
20526
|
-
|
|
20527
|
-
|
|
21194
|
+
function validResourceType(value) {
|
|
21195
|
+
return typeof value === "string" && RESOURCE_TYPES.includes(value);
|
|
21196
|
+
}
|
|
21197
|
+
function freezeMatcher(operation) {
|
|
21198
|
+
const matcher = Object.freeze({
|
|
21199
|
+
urlRegex: Object.freeze({
|
|
21200
|
+
source: operation.matcher.urlRegex.source,
|
|
21201
|
+
flags: ""
|
|
21202
|
+
}),
|
|
21203
|
+
origins: Object.freeze([...operation.matcher.origins]),
|
|
21204
|
+
resourceTypes: Object.freeze([...operation.matcher.resourceTypes]),
|
|
21205
|
+
priority: operation.matcher.priority,
|
|
21206
|
+
...operation.matcher.method === void 0 ? {} : { method: operation.matcher.method }
|
|
21207
|
+
});
|
|
21208
|
+
return Object.freeze({
|
|
21209
|
+
kind: "matcher",
|
|
21210
|
+
groupId: operation.groupId,
|
|
21211
|
+
ruleId: operation.ruleId,
|
|
21212
|
+
matcher
|
|
21213
|
+
});
|
|
21214
|
+
}
|
|
21215
|
+
function normalizeMatcher(value) {
|
|
21216
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
21217
|
+
return null;
|
|
21218
|
+
const requiredKeys = ["kind", "groupId", "ruleId", "matcher"];
|
|
21219
|
+
if (!requiredKeys.every((key) => hasOwn(value, key)) || Object.keys(value).some(
|
|
21220
|
+
(key) => key !== "kind" && key !== "groupId" && key !== "ruleId" && key !== "matcher" && key !== "action"
|
|
21221
|
+
))
|
|
21222
|
+
return null;
|
|
21223
|
+
const record = value;
|
|
21224
|
+
if (record.kind !== "matcher" || !validId2(record.groupId) || !validId2(record.ruleId))
|
|
21225
|
+
return null;
|
|
21226
|
+
const matcherValue2 = record.matcher;
|
|
21227
|
+
if (matcherValue2 === null || typeof matcherValue2 !== "object" || Array.isArray(matcherValue2) || !exactKeys(matcherValue2, [
|
|
21228
|
+
"urlRegex",
|
|
21229
|
+
"origins",
|
|
21230
|
+
"resourceTypes",
|
|
21231
|
+
"priority"
|
|
21232
|
+
]) && !exactKeys(matcherValue2, [
|
|
21233
|
+
"urlRegex",
|
|
21234
|
+
"origins",
|
|
21235
|
+
"resourceTypes",
|
|
21236
|
+
"priority",
|
|
21237
|
+
"method"
|
|
21238
|
+
])) {
|
|
21239
|
+
return null;
|
|
20528
21240
|
}
|
|
20529
|
-
|
|
20530
|
-
|
|
21241
|
+
const matcher = matcherValue2;
|
|
21242
|
+
const regexValue = matcher.urlRegex;
|
|
21243
|
+
if (regexValue === null || typeof regexValue !== "object" || Array.isArray(regexValue) || !exactKeys(regexValue, ["source", "flags"])) {
|
|
21244
|
+
return null;
|
|
21245
|
+
}
|
|
21246
|
+
const regex = regexValue;
|
|
21247
|
+
if (typeof regex.source !== "string" || regex.source.length > LIMITS.maxUrlRegexLength || regex.flags !== "" || compileUrlRegex(regex.source) === null) {
|
|
21248
|
+
return null;
|
|
21249
|
+
}
|
|
21250
|
+
if (!Array.isArray(matcher.origins) || matcher.origins.length === 0 || matcher.origins.length > LIMITS.maxOriginsPerScope)
|
|
21251
|
+
return null;
|
|
21252
|
+
const origins = [];
|
|
21253
|
+
for (const value2 of matcher.origins) {
|
|
21254
|
+
const normalized = normalizeSiteOrigin(value2);
|
|
21255
|
+
if (normalized === null || origins.includes(normalized)) return null;
|
|
21256
|
+
origins.push(normalized);
|
|
21257
|
+
}
|
|
21258
|
+
if (!Array.isArray(matcher.resourceTypes) || matcher.resourceTypes.length === 0 || matcher.resourceTypes.length > LIMITS.maxResourceTypesPerRule)
|
|
21259
|
+
return null;
|
|
21260
|
+
const resourceTypes = [];
|
|
21261
|
+
for (const value2 of matcher.resourceTypes) {
|
|
21262
|
+
if (!validResourceType(value2) || resourceTypes.includes(value2)) return null;
|
|
21263
|
+
resourceTypes.push(value2);
|
|
21264
|
+
}
|
|
21265
|
+
resourceTypes.sort(
|
|
21266
|
+
(left, right) => RESOURCE_TYPES.indexOf(left) - RESOURCE_TYPES.indexOf(right)
|
|
21267
|
+
);
|
|
21268
|
+
if (typeof matcher.priority !== "number" || !Number.isSafeInteger(matcher.priority) || matcher.priority < LIMITS.minPriority || matcher.priority > LIMITS.maxPriority)
|
|
21269
|
+
return null;
|
|
21270
|
+
let method;
|
|
21271
|
+
if (hasOwn(matcher, "method")) {
|
|
21272
|
+
if (!validMethod2(matcher.method)) return null;
|
|
21273
|
+
method = matcher.method;
|
|
20531
21274
|
}
|
|
20532
|
-
|
|
20533
|
-
|
|
20534
|
-
|
|
20535
|
-
|
|
20536
|
-
|
|
20537
|
-
|
|
20538
|
-
|
|
20539
|
-
|
|
20540
|
-
|
|
20541
|
-
|
|
20542
|
-
ok: true,
|
|
20543
|
-
value: {
|
|
20544
|
-
sessionCapability,
|
|
20545
|
-
expiresInMs: RUNTIME_LIMITS.sessionLifetimeMs
|
|
21275
|
+
return freezeMatcher({
|
|
21276
|
+
kind: "matcher",
|
|
21277
|
+
groupId: record.groupId,
|
|
21278
|
+
ruleId: record.ruleId,
|
|
21279
|
+
matcher: {
|
|
21280
|
+
urlRegex: { source: regex.source, flags: "" },
|
|
21281
|
+
origins,
|
|
21282
|
+
resourceTypes,
|
|
21283
|
+
priority: matcher.priority,
|
|
21284
|
+
...method === void 0 ? {} : { method }
|
|
20546
21285
|
}
|
|
20547
|
-
};
|
|
21286
|
+
});
|
|
20548
21287
|
}
|
|
20549
|
-
function
|
|
20550
|
-
|
|
20551
|
-
|
|
20552
|
-
|
|
20553
|
-
|
|
20554
|
-
|
|
20555
|
-
|
|
20556
|
-
|
|
20557
|
-
|
|
20558
|
-
|
|
21288
|
+
function sameLimits(value) {
|
|
21289
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
21290
|
+
return false;
|
|
21291
|
+
const keys = Object.keys(value);
|
|
21292
|
+
const expected = Object.keys(RUNTIME_LIMITS);
|
|
21293
|
+
if (keys.length !== expected.length || !expected.every((key) => hasOwn(value, key)))
|
|
21294
|
+
return false;
|
|
21295
|
+
for (const key of expected) {
|
|
21296
|
+
if (value[key] !== RUNTIME_LIMITS[key])
|
|
21297
|
+
return false;
|
|
20559
21298
|
}
|
|
20560
|
-
return
|
|
21299
|
+
return true;
|
|
20561
21300
|
}
|
|
20562
|
-
function
|
|
20563
|
-
if (
|
|
20564
|
-
return
|
|
21301
|
+
function makeGrant(value, matcherById) {
|
|
21302
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
21303
|
+
return null;
|
|
21304
|
+
if (!exactKeys(value, [
|
|
21305
|
+
"groupId",
|
|
21306
|
+
"ruleId",
|
|
21307
|
+
"operationId",
|
|
21308
|
+
"kind",
|
|
21309
|
+
"target",
|
|
21310
|
+
"method"
|
|
21311
|
+
]))
|
|
21312
|
+
return null;
|
|
21313
|
+
const record = value;
|
|
21314
|
+
if (!validId2(record.groupId) || !validId2(record.ruleId) || !validId2(record.operationId) || record.kind !== "outbound-http" && record.kind !== "confined-file" || !validMethod2(record.method) || typeof record.target !== "string")
|
|
21315
|
+
return null;
|
|
21316
|
+
const matcher = matcherById.get(record.ruleId);
|
|
21317
|
+
if (matcher === void 0 || matcher.groupId !== record.groupId) return null;
|
|
21318
|
+
if (matcher.matcher.method !== void 0 && matcher.matcher.method !== record.method)
|
|
21319
|
+
return null;
|
|
21320
|
+
let target;
|
|
21321
|
+
if (record.kind === "outbound-http") {
|
|
21322
|
+
if (record.method !== "GET" && record.method !== "HEAD") return null;
|
|
21323
|
+
target = canonicalizeOutboundTarget(record.target);
|
|
21324
|
+
if (target === null || !isOriginAllowed(target, matcher.matcher.origins))
|
|
21325
|
+
return null;
|
|
21326
|
+
} else {
|
|
21327
|
+
target = normalizeLogicalPath(record.target);
|
|
20565
21328
|
}
|
|
20566
|
-
|
|
20567
|
-
return {
|
|
20568
|
-
|
|
20569
|
-
|
|
20570
|
-
|
|
20571
|
-
|
|
20572
|
-
|
|
20573
|
-
|
|
20574
|
-
|
|
20575
|
-
state.closed = true;
|
|
20576
|
-
state.consumed = true;
|
|
20577
|
-
state.sessions.length = 0;
|
|
20578
|
-
state.bootstrapDigest.fill(0);
|
|
21329
|
+
if (target === null) return null;
|
|
21330
|
+
return Object.freeze({
|
|
21331
|
+
groupId: record.groupId,
|
|
21332
|
+
ruleId: record.ruleId,
|
|
21333
|
+
operationId: record.operationId,
|
|
21334
|
+
kind: record.kind,
|
|
21335
|
+
target,
|
|
21336
|
+
method: record.method
|
|
21337
|
+
});
|
|
20579
21338
|
}
|
|
20580
|
-
|
|
20581
|
-
|
|
20582
|
-
|
|
20583
|
-
|
|
20584
|
-
return
|
|
21339
|
+
function normalizeRuntimePreset(value) {
|
|
21340
|
+
const snapshot = snapshotOwnData3(value);
|
|
21341
|
+
if (!snapshot.valid || snapshot.value === null || typeof snapshot.value !== "object" || Array.isArray(snapshot.value)) {
|
|
21342
|
+
console.error("[rogatio-host] preset: snapshot invalid");
|
|
21343
|
+
return failure("runtime.invalid-preset");
|
|
20585
21344
|
}
|
|
20586
|
-
const
|
|
20587
|
-
|
|
20588
|
-
|
|
21345
|
+
const record = snapshot.value;
|
|
21346
|
+
const allowedKeys = ["version", "limits", "matchers", "grants", "mocks"];
|
|
21347
|
+
const requiredKeys = ["version", "limits", "matchers", "grants"];
|
|
21348
|
+
const keyCount = Object.keys(record).length;
|
|
21349
|
+
if (Object.keys(record).some((key) => !allowedKeys.includes(key)) || !requiredKeys.every((key) => hasOwn(record, key)) || keyCount !== 4 && keyCount !== 5) {
|
|
21350
|
+
console.error(
|
|
21351
|
+
"[rogatio-host] preset: key check failed, keys=",
|
|
21352
|
+
Object.keys(record)
|
|
21353
|
+
);
|
|
21354
|
+
return failure("runtime.invalid-preset");
|
|
20589
21355
|
}
|
|
20590
|
-
if (
|
|
20591
|
-
|
|
21356
|
+
if (record.version !== 1 || !sameLimits(record.limits)) {
|
|
21357
|
+
console.error(
|
|
21358
|
+
"[rogatio-host] preset: version/limits mismatch, version=",
|
|
21359
|
+
record.version,
|
|
21360
|
+
"limitsOk=",
|
|
21361
|
+
sameLimits(record.limits)
|
|
21362
|
+
);
|
|
21363
|
+
return failure("runtime.invalid-preset");
|
|
20592
21364
|
}
|
|
20593
|
-
|
|
20594
|
-
|
|
20595
|
-
|
|
20596
|
-
} catch (error) {
|
|
20597
|
-
return {
|
|
20598
|
-
kind: "unsupported",
|
|
20599
|
-
reasons: ["provider-start-failed", String(error)]
|
|
20600
|
-
};
|
|
21365
|
+
if (!Array.isArray(record.matchers) || !Array.isArray(record.grants)) {
|
|
21366
|
+
console.error("[rogatio-host] preset: matchers/grants not arrays");
|
|
21367
|
+
return failure("runtime.invalid-preset");
|
|
20601
21368
|
}
|
|
20602
|
-
|
|
20603
|
-
|
|
20604
|
-
|
|
20605
|
-
|
|
20606
|
-
|
|
20607
|
-
|
|
20608
|
-
|
|
20609
|
-
|
|
20610
|
-
|
|
20611
|
-
|
|
20612
|
-
return { kind: "active", provider: registeredProvider.platform };
|
|
20613
|
-
}
|
|
20614
|
-
async function stopInterception() {
|
|
20615
|
-
if (currentSession === null && registeredProvider === null) return;
|
|
20616
|
-
const session = currentSession;
|
|
20617
|
-
currentSession = null;
|
|
20618
|
-
if (session?.provider) {
|
|
20619
|
-
try {
|
|
20620
|
-
await session.provider.stop();
|
|
20621
|
-
} catch {
|
|
21369
|
+
const matchers = [];
|
|
21370
|
+
const matcherById = /* @__PURE__ */ new Map();
|
|
21371
|
+
for (const value2 of record.matchers) {
|
|
21372
|
+
const matcher = normalizeMatcher(value2);
|
|
21373
|
+
if (matcher === null || matcherById.has(matcher.ruleId)) {
|
|
21374
|
+
console.error(
|
|
21375
|
+
"[rogatio-host] preset: matcher normalize failed, ruleId=",
|
|
21376
|
+
value2?.ruleId
|
|
21377
|
+
);
|
|
21378
|
+
return failure("runtime.invalid-preset");
|
|
20622
21379
|
}
|
|
21380
|
+
matcherById.set(matcher.ruleId, matcher);
|
|
21381
|
+
matchers.push(matcher);
|
|
20623
21382
|
}
|
|
20624
|
-
|
|
20625
|
-
|
|
20626
|
-
|
|
20627
|
-
|
|
20628
|
-
|
|
20629
|
-
|
|
20630
|
-
|
|
20631
|
-
|
|
20632
|
-
async function readMockFile(root, logicalPath) {
|
|
20633
|
-
const normalized = normalizeLogicalPath(logicalPath);
|
|
20634
|
-
if (normalized === null) return failure("runtime.file-denied");
|
|
20635
|
-
try {
|
|
20636
|
-
const canonicalRoot = await realpath2(root);
|
|
20637
|
-
const candidate = resolve(canonicalRoot, ...normalized.split("/"));
|
|
20638
|
-
const actualPath = await realpath2(candidate);
|
|
20639
|
-
if (!withinRoot2(canonicalRoot, actualPath))
|
|
20640
|
-
return failure("runtime.file-denied");
|
|
20641
|
-
const metadata = await stat(actualPath);
|
|
20642
|
-
if (!metadata.isFile()) return failure("runtime.file-denied");
|
|
20643
|
-
if (metadata.size > RUNTIME_LIMITS.maxFileBytes)
|
|
20644
|
-
return failure("runtime.size-limit");
|
|
20645
|
-
const bytes = await readFile2(actualPath);
|
|
20646
|
-
if (bytes.byteLength > RUNTIME_LIMITS.maxFileBytes)
|
|
20647
|
-
return failure("runtime.size-limit");
|
|
20648
|
-
new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
20649
|
-
return { ok: true, value: bytes };
|
|
20650
|
-
} catch {
|
|
20651
|
-
return failure("runtime.file-denied");
|
|
20652
|
-
}
|
|
20653
|
-
}
|
|
20654
|
-
function mintToken() {
|
|
20655
|
-
return randomBytes2(32).toString("hex");
|
|
20656
|
-
}
|
|
20657
|
-
async function renderMockResponse(options) {
|
|
20658
|
-
const { mock, fileRoot } = options;
|
|
20659
|
-
let bodyBytes;
|
|
20660
|
-
if (mock.body !== void 0) {
|
|
20661
|
-
bodyBytes = new TextEncoder().encode(mock.body);
|
|
20662
|
-
} else if (mock.file !== void 0) {
|
|
20663
|
-
if (fileRoot === void 0) return failure("runtime.file-denied");
|
|
20664
|
-
const read = await readMockFile(fileRoot, mock.file);
|
|
20665
|
-
if (!read.ok) return failure("runtime.file-denied");
|
|
20666
|
-
bodyBytes = read.value;
|
|
20667
|
-
} else {
|
|
20668
|
-
return failure("runtime.file-denied");
|
|
20669
|
-
}
|
|
20670
|
-
const headers = [];
|
|
20671
|
-
let hasContentType = false;
|
|
20672
|
-
for (const header of mock.headers ?? []) {
|
|
20673
|
-
headers.push([header.name, header.value]);
|
|
20674
|
-
if (header.name.toLowerCase() === "content-type") hasContentType = true;
|
|
21383
|
+
const grants = [];
|
|
21384
|
+
const operationIds = /* @__PURE__ */ new Set();
|
|
21385
|
+
for (const value2 of record.grants) {
|
|
21386
|
+
const grant = makeGrant(value2, matcherById);
|
|
21387
|
+
if (grant === null || operationIds.has(grant.operationId))
|
|
21388
|
+
return failure("runtime.invalid-preset");
|
|
21389
|
+
operationIds.add(grant.operationId);
|
|
21390
|
+
grants.push(grant);
|
|
20675
21391
|
}
|
|
20676
|
-
|
|
20677
|
-
|
|
21392
|
+
const mocks = [];
|
|
21393
|
+
if (hasOwn(record, "mocks")) {
|
|
21394
|
+
if (!Array.isArray(record.mocks)) return failure("runtime.invalid-preset");
|
|
21395
|
+
const seenRuleIds = /* @__PURE__ */ new Set();
|
|
21396
|
+
for (const value2 of record.mocks) {
|
|
21397
|
+
const mock = normalizeMockConfig(value2, matcherById);
|
|
21398
|
+
if (mock === null || seenRuleIds.has(mock.ruleId))
|
|
21399
|
+
return failure("runtime.invalid-preset");
|
|
21400
|
+
seenRuleIds.add(mock.ruleId);
|
|
21401
|
+
mocks.push(mock);
|
|
21402
|
+
}
|
|
20678
21403
|
}
|
|
20679
|
-
|
|
20680
|
-
|
|
20681
|
-
|
|
20682
|
-
|
|
20683
|
-
|
|
20684
|
-
|
|
20685
|
-
|
|
20686
|
-
|
|
21404
|
+
const normalizedPreset = Object.freeze({
|
|
21405
|
+
version: 1,
|
|
21406
|
+
limits: RUNTIME_LIMITS,
|
|
21407
|
+
matchers: Object.freeze(matchers),
|
|
21408
|
+
grants: Object.freeze(grants),
|
|
21409
|
+
...mocks.length > 0 ? { mocks: Object.freeze(mocks) } : {}
|
|
21410
|
+
});
|
|
21411
|
+
const bytes = canonicalPresetBytes(normalizedPreset);
|
|
21412
|
+
if (bytes.byteLength > RUNTIME_LIMITS.maxPresetBytes)
|
|
21413
|
+
return failure("runtime.invalid-preset");
|
|
21414
|
+
const digest = digestBytes(bytes);
|
|
21415
|
+
const exposedBytes = bytes.slice();
|
|
21416
|
+
const normalized = Object.freeze({
|
|
21417
|
+
...normalizedPreset,
|
|
21418
|
+
digest,
|
|
21419
|
+
get canonicalBytes() {
|
|
21420
|
+
return exposedBytes.slice();
|
|
20687
21421
|
}
|
|
20688
|
-
};
|
|
21422
|
+
});
|
|
21423
|
+
return { ok: true, value: normalized };
|
|
20689
21424
|
}
|
|
20690
21425
|
function base64(bytes) {
|
|
20691
21426
|
return Buffer.from(bytes).toString("base64");
|
|
@@ -20695,7 +21430,7 @@ async function delay(ms) {
|
|
|
20695
21430
|
await new Promise((resolve22) => setTimeout(resolve22, ms));
|
|
20696
21431
|
}
|
|
20697
21432
|
function createNativeRuntimeController(options) {
|
|
20698
|
-
|
|
21433
|
+
let preset = options.preset;
|
|
20699
21434
|
const fileRoot = options.fileRoot;
|
|
20700
21435
|
const mockPort = options.mockPort;
|
|
20701
21436
|
const clock = options.clock ?? (() => Date.now());
|
|
@@ -20713,6 +21448,9 @@ function createNativeRuntimeController(options) {
|
|
|
20713
21448
|
if (activation) return { state: "running", activation };
|
|
20714
21449
|
return { state };
|
|
20715
21450
|
}
|
|
21451
|
+
if (!preset) {
|
|
21452
|
+
return { state: "idle" };
|
|
21453
|
+
}
|
|
20716
21454
|
const startedAt = clock();
|
|
20717
21455
|
capability = createCapabilityState(preset, startedAt);
|
|
20718
21456
|
mockTokens.clear();
|
|
@@ -20770,6 +21508,7 @@ function createNativeRuntimeController(options) {
|
|
|
20770
21508
|
async serveMock(token) {
|
|
20771
21509
|
const mock = mockTokens.get(token);
|
|
20772
21510
|
if (mock === void 0) return failure("runtime.mock-unknown");
|
|
21511
|
+
if (!preset) throw new Error("runtime not started");
|
|
20773
21512
|
return renderMockResponse({
|
|
20774
21513
|
mock,
|
|
20775
21514
|
fileRoot,
|
|
@@ -20783,12 +21522,121 @@ function createNativeRuntimeController(options) {
|
|
|
20783
21522
|
return capability.bootstrap;
|
|
20784
21523
|
},
|
|
20785
21524
|
async handleEnvelope(input) {
|
|
20786
|
-
if (state !== "running" || capability === void 0) {
|
|
20787
|
-
throw new Error("runtime not started");
|
|
20788
|
-
}
|
|
20789
21525
|
const now = clock();
|
|
20790
21526
|
const requestId = input.requestId;
|
|
20791
21527
|
const timestamp = now;
|
|
21528
|
+
if (input.type === "runtime.project.set" && state === "idle") {
|
|
21529
|
+
const projectData = input.metadata.project;
|
|
21530
|
+
if (!projectData || typeof projectData !== "object") {
|
|
21531
|
+
return {
|
|
21532
|
+
protocol: "v1",
|
|
21533
|
+
type: "runtime.project.set",
|
|
21534
|
+
...requestId !== void 0 ? { requestId } : {},
|
|
21535
|
+
timestamp,
|
|
21536
|
+
metadata: { ok: false, error: "runtime.project-missing" }
|
|
21537
|
+
};
|
|
21538
|
+
}
|
|
21539
|
+
const schemaResult = validateProjectDetailed(projectData);
|
|
21540
|
+
if (!schemaResult.valid) {
|
|
21541
|
+
return {
|
|
21542
|
+
protocol: "v1",
|
|
21543
|
+
type: "runtime.project.set",
|
|
21544
|
+
...requestId !== void 0 ? { requestId } : {},
|
|
21545
|
+
timestamp,
|
|
21546
|
+
metadata: { ok: false, error: "runtime.project-invalid" }
|
|
21547
|
+
};
|
|
21548
|
+
}
|
|
21549
|
+
const compileResult = compileProject(schemaResult.data);
|
|
21550
|
+
if (!compileResult.ok) {
|
|
21551
|
+
return {
|
|
21552
|
+
protocol: "v1",
|
|
21553
|
+
type: "runtime.project.set",
|
|
21554
|
+
...requestId !== void 0 ? { requestId } : {},
|
|
21555
|
+
timestamp,
|
|
21556
|
+
metadata: {
|
|
21557
|
+
ok: false,
|
|
21558
|
+
error: "runtime.compile-failed",
|
|
21559
|
+
diagnostics: compileResult.diagnostics
|
|
21560
|
+
}
|
|
21561
|
+
};
|
|
21562
|
+
}
|
|
21563
|
+
const matcherOps = [];
|
|
21564
|
+
const mockConfigs = [];
|
|
21565
|
+
for (const op of compileResult.operations) {
|
|
21566
|
+
if (op.kind === "mock") {
|
|
21567
|
+
const mock = op.mock;
|
|
21568
|
+
let file;
|
|
21569
|
+
if (mock.file !== void 0) {
|
|
21570
|
+
file = mock.file;
|
|
21571
|
+
}
|
|
21572
|
+
mockConfigs.push({
|
|
21573
|
+
ruleId: op.ruleId,
|
|
21574
|
+
status: mock.status,
|
|
21575
|
+
...mock.headers !== void 0 ? { headers: mock.headers } : {},
|
|
21576
|
+
...mock.delayMs !== void 0 ? { delayMs: mock.delayMs } : {},
|
|
21577
|
+
...mock.body !== void 0 ? { body: mock.body } : {},
|
|
21578
|
+
...file !== void 0 ? { file } : {}
|
|
21579
|
+
});
|
|
21580
|
+
}
|
|
21581
|
+
if ("matcher" in op) {
|
|
21582
|
+
matcherOps.push({
|
|
21583
|
+
kind: "matcher",
|
|
21584
|
+
groupId: op.groupId,
|
|
21585
|
+
ruleId: op.ruleId,
|
|
21586
|
+
matcher: op.matcher
|
|
21587
|
+
});
|
|
21588
|
+
}
|
|
21589
|
+
}
|
|
21590
|
+
const presetInput = {
|
|
21591
|
+
version: 1,
|
|
21592
|
+
limits: RUNTIME_LIMITS,
|
|
21593
|
+
matchers: matcherOps,
|
|
21594
|
+
grants: [],
|
|
21595
|
+
...mockConfigs.length > 0 ? { mocks: mockConfigs } : {}
|
|
21596
|
+
};
|
|
21597
|
+
console.error(
|
|
21598
|
+
"[rogatio-host] normalizeRuntimePreset input: matchers=",
|
|
21599
|
+
matcherOps.length,
|
|
21600
|
+
"mocks=",
|
|
21601
|
+
mockConfigs.length,
|
|
21602
|
+
"grants=0"
|
|
21603
|
+
);
|
|
21604
|
+
const normalized = normalizeRuntimePreset(presetInput);
|
|
21605
|
+
console.error(
|
|
21606
|
+
"[rogatio-host] normalizeRuntimePreset result: ok=",
|
|
21607
|
+
normalized.ok
|
|
21608
|
+
);
|
|
21609
|
+
if (!normalized.ok) {
|
|
21610
|
+
return {
|
|
21611
|
+
protocol: "v1",
|
|
21612
|
+
type: "runtime.project.set",
|
|
21613
|
+
...requestId !== void 0 ? { requestId } : {},
|
|
21614
|
+
timestamp,
|
|
21615
|
+
metadata: { ok: false, error: "runtime.preset-invalid" }
|
|
21616
|
+
};
|
|
21617
|
+
}
|
|
21618
|
+
preset = normalized.value;
|
|
21619
|
+
const startResult = await this.start();
|
|
21620
|
+
if (startResult.state !== "running") {
|
|
21621
|
+
return {
|
|
21622
|
+
protocol: "v1",
|
|
21623
|
+
type: "runtime.project.set",
|
|
21624
|
+
...requestId !== void 0 ? { requestId } : {},
|
|
21625
|
+
timestamp,
|
|
21626
|
+
metadata: { ok: false, error: "runtime.start-failed" }
|
|
21627
|
+
};
|
|
21628
|
+
}
|
|
21629
|
+
return {
|
|
21630
|
+
protocol: "v1",
|
|
21631
|
+
type: "runtime.project.set",
|
|
21632
|
+
...requestId !== void 0 ? { requestId } : {},
|
|
21633
|
+
timestamp,
|
|
21634
|
+
metadata: { ok: true, presetDigest: preset.digest }
|
|
21635
|
+
};
|
|
21636
|
+
}
|
|
21637
|
+
if (state !== "running" || capability === void 0) {
|
|
21638
|
+
throw new Error("runtime not started");
|
|
21639
|
+
}
|
|
20792
21640
|
switch (input.type) {
|
|
20793
21641
|
case "pair.request": {
|
|
20794
21642
|
const meta = input.metadata;
|
|
@@ -20844,6 +21692,7 @@ function createNativeRuntimeController(options) {
|
|
|
20844
21692
|
metadata: { authorized: false, error: op.error.code }
|
|
20845
21693
|
};
|
|
20846
21694
|
}
|
|
21695
|
+
if (!preset) throw new Error("runtime not started");
|
|
20847
21696
|
const result = authorizeExact(preset, meta.descriptor);
|
|
20848
21697
|
op.value();
|
|
20849
21698
|
if (!result.ok) {
|
|
@@ -20873,6 +21722,7 @@ function createNativeRuntimeController(options) {
|
|
|
20873
21722
|
};
|
|
20874
21723
|
}
|
|
20875
21724
|
case "mock.connect": {
|
|
21725
|
+
if (!preset) throw new Error("runtime not started");
|
|
20876
21726
|
const mocks = [...mockTokens.entries()].map(([token, mock]) => ({
|
|
20877
21727
|
ruleId: mock.ruleId,
|
|
20878
21728
|
token
|
|
@@ -20915,6 +21765,7 @@ function createNativeRuntimeController(options) {
|
|
|
20915
21765
|
return renderMock(mock, requestId, timestamp);
|
|
20916
21766
|
}
|
|
20917
21767
|
case "runtime.status": {
|
|
21768
|
+
if (!preset) throw new Error("runtime not started");
|
|
20918
21769
|
return {
|
|
20919
21770
|
protocol: "v1",
|
|
20920
21771
|
type: "runtime.status",
|
|
@@ -20988,602 +21839,268 @@ function createNativeRuntimeController(options) {
|
|
|
20988
21839
|
for await (const chunk of aiClient.stream({
|
|
20989
21840
|
messages: [...meta.messages],
|
|
20990
21841
|
model: meta.model,
|
|
20991
|
-
temperature: meta.temperature
|
|
20992
|
-
})) {
|
|
20993
|
-
return {
|
|
20994
|
-
protocol: "v1",
|
|
20995
|
-
type: "ai.stream.chunk",
|
|
20996
|
-
...requestId !== void 0 ? { requestId } : {},
|
|
20997
|
-
timestamp,
|
|
20998
|
-
metadata: {
|
|
20999
|
-
delta: chunk.delta,
|
|
21000
|
-
done: chunk.done,
|
|
21001
|
-
usage: chunk.usage
|
|
21002
|
-
}
|
|
21003
|
-
};
|
|
21004
|
-
}
|
|
21005
|
-
return {
|
|
21006
|
-
protocol: "v1",
|
|
21007
|
-
type: "ai.stream.chunk",
|
|
21008
|
-
...requestId !== void 0 ? { requestId } : {},
|
|
21009
|
-
timestamp,
|
|
21010
|
-
metadata: {
|
|
21011
|
-
delta: "",
|
|
21012
|
-
done: true,
|
|
21013
|
-
usage: void 0
|
|
21014
|
-
}
|
|
21015
|
-
};
|
|
21016
|
-
} catch (e) {
|
|
21017
|
-
return {
|
|
21018
|
-
protocol: "v1",
|
|
21019
|
-
type: "ai.error",
|
|
21020
|
-
...requestId !== void 0 ? { requestId } : {},
|
|
21021
|
-
timestamp,
|
|
21022
|
-
metadata: {
|
|
21023
|
-
code: "ai.error",
|
|
21024
|
-
message: e instanceof Error ? e.message : "AI streaming failed",
|
|
21025
|
-
retryable: true
|
|
21026
|
-
}
|
|
21027
|
-
};
|
|
21028
|
-
}
|
|
21029
|
-
}
|
|
21030
|
-
default: {
|
|
21031
|
-
return {
|
|
21032
|
-
protocol: "v1",
|
|
21033
|
-
type: input.type,
|
|
21034
|
-
...requestId !== void 0 ? { requestId } : {},
|
|
21035
|
-
timestamp,
|
|
21036
|
-
metadata: { error: "runtime.request-malformed" }
|
|
21037
|
-
};
|
|
21038
|
-
}
|
|
21039
|
-
}
|
|
21040
|
-
}
|
|
21041
|
-
};
|
|
21042
|
-
async function renderMock(mock, requestId, timestamp) {
|
|
21043
|
-
if (mock.delayMs !== void 0) await delay(mock.delayMs);
|
|
21044
|
-
const rendered = await renderMockResponse({
|
|
21045
|
-
mock,
|
|
21046
|
-
fileRoot,
|
|
21047
|
-
presetDigest: preset.digest
|
|
21048
|
-
});
|
|
21049
|
-
if (!rendered.ok) {
|
|
21050
|
-
const body = JSON.stringify({
|
|
21051
|
-
ok: false,
|
|
21052
|
-
error: { code: rendered.error.code }
|
|
21053
|
-
});
|
|
21054
|
-
const metadata2 = {
|
|
21055
|
-
status: 500,
|
|
21056
|
-
headers: [["Content-Type", "application/json"]],
|
|
21057
|
-
mockBody: base64(new TextEncoder().encode(body))
|
|
21058
|
-
};
|
|
21059
|
-
return {
|
|
21060
|
-
protocol: "v1",
|
|
21061
|
-
type: "mock.response",
|
|
21062
|
-
...requestId !== void 0 ? { requestId } : {},
|
|
21063
|
-
timestamp,
|
|
21064
|
-
metadata: metadata2
|
|
21065
|
-
};
|
|
21066
|
-
}
|
|
21067
|
-
const headers = [];
|
|
21068
|
-
for (const header of rendered.value.headers) {
|
|
21069
|
-
headers.push([header[0], header[1]]);
|
|
21070
|
-
}
|
|
21071
|
-
const metadata = {
|
|
21072
|
-
status: rendered.value.status,
|
|
21073
|
-
headers,
|
|
21074
|
-
mockBody: base64(rendered.value.bodyBytes)
|
|
21075
|
-
};
|
|
21076
|
-
return {
|
|
21077
|
-
protocol: "v1",
|
|
21078
|
-
type: "mock.response",
|
|
21079
|
-
...requestId !== void 0 ? { requestId } : {},
|
|
21080
|
-
timestamp,
|
|
21081
|
-
metadata
|
|
21082
|
-
};
|
|
21083
|
-
}
|
|
21084
|
-
}
|
|
21085
|
-
function getCurrentSession2() {
|
|
21086
|
-
return null;
|
|
21087
|
-
}
|
|
21088
|
-
function encodeEnvelopeFrame(envelope) {
|
|
21089
|
-
const json = new TextEncoder().encode(serializeEnvelope(envelope));
|
|
21090
|
-
const out = new Uint8Array(4 + json.byteLength);
|
|
21091
|
-
new DataView(out.buffer).setUint32(0, json.byteLength, true);
|
|
21092
|
-
out.set(json, 4);
|
|
21093
|
-
return out;
|
|
21094
|
-
}
|
|
21095
|
-
function decodeEnvelopeFrame(buffer) {
|
|
21096
|
-
if (buffer.byteLength < 4) throw new Error("frame too small");
|
|
21097
|
-
const length = new DataView(buffer.buffer, buffer.byteOffset, 4).getUint32(
|
|
21098
|
-
0,
|
|
21099
|
-
true
|
|
21100
|
-
);
|
|
21101
|
-
if (buffer.byteLength !== 4 + length)
|
|
21102
|
-
throw new Error("frame length mismatch");
|
|
21103
|
-
const json = new TextDecoder("utf-8", { fatal: true }).decode(
|
|
21104
|
-
buffer.slice(4)
|
|
21105
|
-
);
|
|
21106
|
-
return parseEnvelope(json);
|
|
21107
|
-
}
|
|
21108
|
-
function startMockFaucet(port, controller) {
|
|
21109
|
-
const server = createServer((req, res) => {
|
|
21110
|
-
void (async () => {
|
|
21111
|
-
try {
|
|
21112
|
-
const url = new URL(req.url ?? "", "http://localhost");
|
|
21113
|
-
const match = /^\/mock\/([a-f0-9]+)$/.exec(url.pathname);
|
|
21114
|
-
if (!match || req.method !== "GET") {
|
|
21115
|
-
res.writeHead(404);
|
|
21116
|
-
res.end();
|
|
21117
|
-
return;
|
|
21118
|
-
}
|
|
21119
|
-
const result = await controller.serveMock(match[1]);
|
|
21120
|
-
if (!result.ok) {
|
|
21121
|
-
res.writeHead(404);
|
|
21122
|
-
res.end();
|
|
21123
|
-
return;
|
|
21124
|
-
}
|
|
21125
|
-
const { status, headers, bodyBytes } = result.value;
|
|
21126
|
-
res.writeHead(
|
|
21127
|
-
status,
|
|
21128
|
-
Object.fromEntries(
|
|
21129
|
-
headers.map((h) => [h[0], h[1]])
|
|
21130
|
-
)
|
|
21131
|
-
);
|
|
21132
|
-
res.end(Buffer2.from(bodyBytes));
|
|
21133
|
-
} catch {
|
|
21134
|
-
if (!res.headersSent) res.writeHead(500);
|
|
21135
|
-
res.end();
|
|
21136
|
-
}
|
|
21137
|
-
})();
|
|
21138
|
-
});
|
|
21139
|
-
return new Promise(
|
|
21140
|
-
(resolve22) => server.listen(port, () => resolve22(server))
|
|
21141
|
-
);
|
|
21142
|
-
}
|
|
21143
|
-
function createNativeHost(options) {
|
|
21144
|
-
const controller = createNativeRuntimeController({
|
|
21145
|
-
preset: options.preset,
|
|
21146
|
-
fileRoot: options.fileRoot,
|
|
21147
|
-
...options.mockPort !== void 0 ? { mockPort: options.mockPort } : {},
|
|
21148
|
-
...options.clock ? { clock: options.clock } : {}
|
|
21149
|
-
});
|
|
21150
|
-
let faucet = null;
|
|
21151
|
-
return {
|
|
21152
|
-
controller,
|
|
21153
|
-
mockPort: options.mockPort ?? null,
|
|
21154
|
-
async start() {
|
|
21155
|
-
await controller.start();
|
|
21156
|
-
if (options.mockPort !== void 0) {
|
|
21157
|
-
faucet = await startMockFaucet(options.mockPort, controller);
|
|
21158
|
-
}
|
|
21159
|
-
},
|
|
21160
|
-
async stop() {
|
|
21161
|
-
if (faucet) {
|
|
21162
|
-
await new Promise((resolve22) => faucet?.close(() => resolve22()));
|
|
21163
|
-
faucet = null;
|
|
21164
|
-
}
|
|
21165
|
-
await controller.stop();
|
|
21166
|
-
},
|
|
21167
|
-
async processFrame(frame) {
|
|
21168
|
-
let envelope;
|
|
21169
|
-
try {
|
|
21170
|
-
envelope = decodeEnvelopeFrame(frame);
|
|
21171
|
-
} catch {
|
|
21172
|
-
return null;
|
|
21173
|
-
}
|
|
21174
|
-
let response;
|
|
21175
|
-
try {
|
|
21176
|
-
response = await controller.handleEnvelope(envelope);
|
|
21177
|
-
} catch {
|
|
21178
|
-
return null;
|
|
21179
|
-
}
|
|
21180
|
-
try {
|
|
21181
|
-
return encodeEnvelopeFrame(response);
|
|
21182
|
-
} catch {
|
|
21183
|
-
return null;
|
|
21842
|
+
temperature: meta.temperature
|
|
21843
|
+
})) {
|
|
21844
|
+
return {
|
|
21845
|
+
protocol: "v1",
|
|
21846
|
+
type: "ai.stream.chunk",
|
|
21847
|
+
...requestId !== void 0 ? { requestId } : {},
|
|
21848
|
+
timestamp,
|
|
21849
|
+
metadata: {
|
|
21850
|
+
delta: chunk.delta,
|
|
21851
|
+
done: chunk.done,
|
|
21852
|
+
usage: chunk.usage
|
|
21853
|
+
}
|
|
21854
|
+
};
|
|
21855
|
+
}
|
|
21856
|
+
return {
|
|
21857
|
+
protocol: "v1",
|
|
21858
|
+
type: "ai.stream.chunk",
|
|
21859
|
+
...requestId !== void 0 ? { requestId } : {},
|
|
21860
|
+
timestamp,
|
|
21861
|
+
metadata: {
|
|
21862
|
+
delta: "",
|
|
21863
|
+
done: true,
|
|
21864
|
+
usage: void 0
|
|
21865
|
+
}
|
|
21866
|
+
};
|
|
21867
|
+
} catch (e) {
|
|
21868
|
+
return {
|
|
21869
|
+
protocol: "v1",
|
|
21870
|
+
type: "ai.error",
|
|
21871
|
+
...requestId !== void 0 ? { requestId } : {},
|
|
21872
|
+
timestamp,
|
|
21873
|
+
metadata: {
|
|
21874
|
+
code: "ai.error",
|
|
21875
|
+
message: e instanceof Error ? e.message : "AI streaming failed",
|
|
21876
|
+
retryable: true
|
|
21877
|
+
}
|
|
21878
|
+
};
|
|
21879
|
+
}
|
|
21880
|
+
}
|
|
21881
|
+
default: {
|
|
21882
|
+
return {
|
|
21883
|
+
protocol: "v1",
|
|
21884
|
+
type: input.type,
|
|
21885
|
+
...requestId !== void 0 ? { requestId } : {},
|
|
21886
|
+
timestamp,
|
|
21887
|
+
metadata: { error: "runtime.request-malformed" }
|
|
21888
|
+
};
|
|
21889
|
+
}
|
|
21184
21890
|
}
|
|
21185
21891
|
}
|
|
21186
21892
|
};
|
|
21187
|
-
|
|
21188
|
-
|
|
21189
|
-
|
|
21190
|
-
|
|
21191
|
-
|
|
21192
|
-
|
|
21193
|
-
|
|
21194
|
-
let buffer = Buffer2.alloc(0);
|
|
21195
|
-
stdin.on("data", (chunk) => {
|
|
21196
|
-
buffer = Buffer2.concat([
|
|
21197
|
-
buffer,
|
|
21198
|
-
Buffer2.isBuffer(chunk) ? chunk : Buffer2.from(chunk)
|
|
21199
|
-
]);
|
|
21200
|
-
void (async () => {
|
|
21201
|
-
for (; ; ) {
|
|
21202
|
-
if (buffer.length < 4) break;
|
|
21203
|
-
const length = buffer.readUInt32LE(0);
|
|
21204
|
-
if (buffer.length < 4 + length) break;
|
|
21205
|
-
const frame = new Uint8Array(
|
|
21206
|
-
buffer.buffer,
|
|
21207
|
-
buffer.byteOffset + 4,
|
|
21208
|
-
length
|
|
21209
|
-
);
|
|
21210
|
-
const response = await host.processFrame(frame);
|
|
21211
|
-
buffer = buffer.subarray(4 + length);
|
|
21212
|
-
if (response && stdout.writable) {
|
|
21213
|
-
stdout.write(Buffer2.from(response));
|
|
21214
|
-
}
|
|
21215
|
-
}
|
|
21216
|
-
})();
|
|
21217
|
-
});
|
|
21218
|
-
await new Promise((resolve22) => {
|
|
21219
|
-
stdin.on("end", () => {
|
|
21220
|
-
void host.stop().then(resolve22);
|
|
21893
|
+
async function renderMock(mock, requestId, timestamp) {
|
|
21894
|
+
if (mock.delayMs !== void 0) await delay(mock.delayMs);
|
|
21895
|
+
if (!preset) throw new Error("runtime not started");
|
|
21896
|
+
const rendered = await renderMockResponse({
|
|
21897
|
+
mock,
|
|
21898
|
+
fileRoot,
|
|
21899
|
+
presetDigest: preset.digest
|
|
21221
21900
|
});
|
|
21222
|
-
|
|
21223
|
-
|
|
21224
|
-
|
|
21225
|
-
|
|
21226
|
-
|
|
21227
|
-
|
|
21228
|
-
|
|
21229
|
-
|
|
21230
|
-
|
|
21231
|
-
|
|
21232
|
-
|
|
21233
|
-
|
|
21234
|
-
|
|
21235
|
-
|
|
21236
|
-
|
|
21237
|
-
|
|
21238
|
-
}
|
|
21239
|
-
function limitsValue(limits) {
|
|
21240
|
-
const fields = [
|
|
21241
|
-
["maxPresetBytes", limits.maxPresetBytes],
|
|
21242
|
-
["maxRequestLineBytes", limits.maxRequestLineBytes],
|
|
21243
|
-
["maxHeaderCount", limits.maxHeaderCount],
|
|
21244
|
-
["maxRequestHeaderBytes", limits.maxRequestHeaderBytes],
|
|
21245
|
-
["maxControlBodyBytes", limits.maxControlBodyBytes],
|
|
21246
|
-
["maxResponseHeaderBytes", limits.maxResponseHeaderBytes],
|
|
21247
|
-
["maxResponseBodyBytes", limits.maxResponseBodyBytes],
|
|
21248
|
-
["maxFileBytes", limits.maxFileBytes],
|
|
21249
|
-
["maxConcurrentSessions", limits.maxConcurrentSessions],
|
|
21250
|
-
["maxConcurrentOperations", limits.maxConcurrentOperations],
|
|
21251
|
-
["maxOperationsPerSession", limits.maxOperationsPerSession],
|
|
21252
|
-
["maxDnsAddresses", limits.maxDnsAddresses],
|
|
21253
|
-
["bootstrapLifetimeMs", limits.bootstrapLifetimeMs],
|
|
21254
|
-
["sessionLifetimeMs", limits.sessionLifetimeMs],
|
|
21255
|
-
["connectTimeoutMs", limits.connectTimeoutMs],
|
|
21256
|
-
["responseHeaderTimeoutMs", limits.responseHeaderTimeoutMs],
|
|
21257
|
-
["bodyIdleTimeoutMs", limits.bodyIdleTimeoutMs],
|
|
21258
|
-
["operationTimeoutMs", limits.operationTimeoutMs],
|
|
21259
|
-
["maxRedirects", limits.maxRedirects]
|
|
21260
|
-
];
|
|
21261
|
-
return `{${fields.map(([key, value]) => `${stringValue(key)}:${String(value)}`).join(",")}}`;
|
|
21262
|
-
}
|
|
21263
|
-
function grantValue(grant) {
|
|
21264
|
-
return `{"groupId":${stringValue(grant.groupId)},"ruleId":${stringValue(grant.ruleId)},"operationId":${stringValue(grant.operationId)},"kind":${stringValue(grant.kind)},"target":${stringValue(grant.target)},"method":${stringValue(grant.method)}}`;
|
|
21265
|
-
}
|
|
21266
|
-
function mockHeaderValue(header) {
|
|
21267
|
-
return `{"name":${stringValue(header.name)},"value":${stringValue(header.value)}}`;
|
|
21268
|
-
}
|
|
21269
|
-
function mockValue(mock) {
|
|
21270
|
-
const headers = mock.headers === void 0 ? "" : `,"headers":[${mock.headers.map(mockHeaderValue).join(",")}]`;
|
|
21271
|
-
const delay2 = mock.delayMs === void 0 ? "" : `,"delayMs":${String(mock.delayMs)}`;
|
|
21272
|
-
const body = mock.body !== void 0 ? `,"body":${stringValue(mock.body)}` : "";
|
|
21273
|
-
const file = mock.file !== void 0 ? `,"file":${stringValue(mock.file)}` : "";
|
|
21274
|
-
return `{"ruleId":${stringValue(mock.ruleId)},"status":${String(mock.status)}${headers}${delay2}${body}${file}}`;
|
|
21275
|
-
}
|
|
21276
|
-
function sortMocks(mocks) {
|
|
21277
|
-
return [...mocks].sort(
|
|
21278
|
-
(left, right) => compareStrings(left.ruleId, right.ruleId)
|
|
21279
|
-
);
|
|
21280
|
-
}
|
|
21281
|
-
function compareStrings(left, right) {
|
|
21282
|
-
if (left < right) return -1;
|
|
21283
|
-
if (left > right) return 1;
|
|
21284
|
-
return 0;
|
|
21285
|
-
}
|
|
21286
|
-
function sortGrants(grants) {
|
|
21287
|
-
return [...grants].sort(
|
|
21288
|
-
(left, right) => compareStrings(left.groupId, right.groupId) || compareStrings(left.ruleId, right.ruleId) || compareStrings(left.operationId, right.operationId) || compareStrings(left.kind, right.kind) || compareStrings(left.target, right.target) || compareStrings(left.method, right.method)
|
|
21289
|
-
);
|
|
21290
|
-
}
|
|
21291
|
-
function canonicalPresetBytes(preset, grants = preset.grants) {
|
|
21292
|
-
const matchers = preset.matchers.map(matcherValue).join(",");
|
|
21293
|
-
const mocks = preset.mocks === void 0 || preset.mocks.length === 0 ? "" : `,"mocks":[${sortMocks(preset.mocks).map(mockValue).join(",")}]`;
|
|
21294
|
-
const canonical = `{"version":1,"limits":${limitsValue(preset.limits)},"matchers":[${matchers}],"grants":[${sortGrants(grants).map(grantValue).join(",")}]${mocks}}`;
|
|
21295
|
-
return new TextEncoder().encode(canonical);
|
|
21296
|
-
}
|
|
21297
|
-
function digestBytes(bytes) {
|
|
21298
|
-
return formatSha256(createHash3("sha256").update(bytes).digest("hex"));
|
|
21299
|
-
}
|
|
21300
|
-
var ID_PATTERN2 = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
21301
|
-
function exactKeys(value, required) {
|
|
21302
|
-
const keys = Object.keys(value);
|
|
21303
|
-
return keys.length === required.length && required.every((key) => hasOwn(value, key));
|
|
21304
|
-
}
|
|
21305
|
-
function validId2(value) {
|
|
21306
|
-
return typeof value === "string" && ID_PATTERN2.test(value);
|
|
21307
|
-
}
|
|
21308
|
-
function validMethod2(value) {
|
|
21309
|
-
return typeof value === "string" && HTTP_METHODS.includes(value);
|
|
21310
|
-
}
|
|
21311
|
-
function normalizeMockHeader(value) {
|
|
21312
|
-
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
21313
|
-
return null;
|
|
21314
|
-
if (!exactKeys(value, ["name", "value"])) return null;
|
|
21315
|
-
const record = value;
|
|
21316
|
-
if (typeof record.name !== "string" || record.name.length === 0 || record.name.length > LIMITS.maxMockHeaderNameLength || hasControl(record.name) || record.name.includes(":"))
|
|
21317
|
-
return null;
|
|
21318
|
-
if (typeof record.value !== "string" || record.value.length > LIMITS.maxMockHeaderValueLength)
|
|
21319
|
-
return null;
|
|
21320
|
-
return { name: record.name, value: record.value };
|
|
21321
|
-
}
|
|
21322
|
-
function normalizeMockConfig(value, matcherById) {
|
|
21323
|
-
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
21324
|
-
return null;
|
|
21325
|
-
const record = value;
|
|
21326
|
-
const allowedKeys = [
|
|
21327
|
-
"ruleId",
|
|
21328
|
-
"status",
|
|
21329
|
-
"headers",
|
|
21330
|
-
"delayMs",
|
|
21331
|
-
"body",
|
|
21332
|
-
"file"
|
|
21333
|
-
];
|
|
21334
|
-
if (Object.keys(record).some((key) => !allowedKeys.includes(key)))
|
|
21335
|
-
return null;
|
|
21336
|
-
if (!validId2(record.ruleId) || !matcherById.has(record.ruleId)) return null;
|
|
21337
|
-
if (typeof record.status !== "number" || !Number.isSafeInteger(record.status) || record.status < LIMITS.minMockStatus || record.status > LIMITS.maxMockStatus)
|
|
21338
|
-
return null;
|
|
21339
|
-
let headers;
|
|
21340
|
-
if (hasOwn(record, "headers")) {
|
|
21341
|
-
if (!Array.isArray(record.headers) || record.headers.length > LIMITS.maxMockHeadersPerRule)
|
|
21342
|
-
return null;
|
|
21343
|
-
const normalizedHeaders = [];
|
|
21344
|
-
for (const headerValue2 of record.headers) {
|
|
21345
|
-
const header = normalizeMockHeader(headerValue2);
|
|
21346
|
-
if (header === null) return null;
|
|
21347
|
-
normalizedHeaders.push(header);
|
|
21901
|
+
if (!rendered.ok) {
|
|
21902
|
+
const body = JSON.stringify({
|
|
21903
|
+
ok: false,
|
|
21904
|
+
error: { code: rendered.error.code }
|
|
21905
|
+
});
|
|
21906
|
+
const metadata2 = {
|
|
21907
|
+
status: 500,
|
|
21908
|
+
headers: [["Content-Type", "application/json"]],
|
|
21909
|
+
mockBody: base64(new TextEncoder().encode(body))
|
|
21910
|
+
};
|
|
21911
|
+
return {
|
|
21912
|
+
protocol: "v1",
|
|
21913
|
+
type: "mock.response",
|
|
21914
|
+
...requestId !== void 0 ? { requestId } : {},
|
|
21915
|
+
timestamp,
|
|
21916
|
+
metadata: metadata2
|
|
21917
|
+
};
|
|
21348
21918
|
}
|
|
21349
|
-
headers =
|
|
21350
|
-
|
|
21351
|
-
|
|
21352
|
-
|
|
21353
|
-
|
|
21354
|
-
|
|
21355
|
-
|
|
21356
|
-
|
|
21357
|
-
|
|
21358
|
-
|
|
21359
|
-
|
|
21360
|
-
|
|
21361
|
-
|
|
21362
|
-
|
|
21363
|
-
|
|
21364
|
-
|
|
21365
|
-
return null;
|
|
21919
|
+
const headers = [];
|
|
21920
|
+
for (const header of rendered.value.headers) {
|
|
21921
|
+
headers.push([header[0], header[1]]);
|
|
21922
|
+
}
|
|
21923
|
+
const metadata = {
|
|
21924
|
+
status: rendered.value.status,
|
|
21925
|
+
headers,
|
|
21926
|
+
mockBody: base64(rendered.value.bodyBytes)
|
|
21927
|
+
};
|
|
21928
|
+
return {
|
|
21929
|
+
protocol: "v1",
|
|
21930
|
+
type: "mock.response",
|
|
21931
|
+
...requestId !== void 0 ? { requestId } : {},
|
|
21932
|
+
timestamp,
|
|
21933
|
+
metadata
|
|
21934
|
+
};
|
|
21366
21935
|
}
|
|
21367
|
-
return Object.freeze({
|
|
21368
|
-
ruleId: record.ruleId,
|
|
21369
|
-
status: record.status,
|
|
21370
|
-
...headers === void 0 ? {} : { headers },
|
|
21371
|
-
...delayMs === void 0 ? {} : { delayMs },
|
|
21372
|
-
...bodySet ? { body } : {},
|
|
21373
|
-
...fileSet ? { file } : {}
|
|
21374
|
-
});
|
|
21375
21936
|
}
|
|
21376
|
-
function
|
|
21377
|
-
return
|
|
21937
|
+
function getCurrentSession2() {
|
|
21938
|
+
return null;
|
|
21378
21939
|
}
|
|
21379
|
-
function
|
|
21380
|
-
const
|
|
21381
|
-
|
|
21382
|
-
|
|
21383
|
-
|
|
21384
|
-
|
|
21385
|
-
origins: Object.freeze([...operation.matcher.origins]),
|
|
21386
|
-
resourceTypes: Object.freeze([...operation.matcher.resourceTypes]),
|
|
21387
|
-
priority: operation.matcher.priority,
|
|
21388
|
-
...operation.matcher.method === void 0 ? {} : { method: operation.matcher.method }
|
|
21389
|
-
});
|
|
21390
|
-
return Object.freeze({
|
|
21391
|
-
kind: "matcher",
|
|
21392
|
-
groupId: operation.groupId,
|
|
21393
|
-
ruleId: operation.ruleId,
|
|
21394
|
-
matcher
|
|
21395
|
-
});
|
|
21940
|
+
function encodeEnvelopeFrame(envelope) {
|
|
21941
|
+
const json = new TextEncoder().encode(serializeEnvelope(envelope));
|
|
21942
|
+
const out = new Uint8Array(4 + json.byteLength);
|
|
21943
|
+
new DataView(out.buffer).setUint32(0, json.byteLength, true);
|
|
21944
|
+
out.set(json, 4);
|
|
21945
|
+
return out;
|
|
21396
21946
|
}
|
|
21397
|
-
function
|
|
21398
|
-
if (
|
|
21399
|
-
|
|
21400
|
-
|
|
21401
|
-
|
|
21402
|
-
(key) => key !== "kind" && key !== "groupId" && key !== "ruleId" && key !== "matcher" && key !== "action"
|
|
21403
|
-
))
|
|
21404
|
-
return null;
|
|
21405
|
-
const record = value;
|
|
21406
|
-
if (record.kind !== "matcher" || !validId2(record.groupId) || !validId2(record.ruleId))
|
|
21407
|
-
return null;
|
|
21408
|
-
const matcherValue2 = record.matcher;
|
|
21409
|
-
if (matcherValue2 === null || typeof matcherValue2 !== "object" || Array.isArray(matcherValue2) || !exactKeys(matcherValue2, [
|
|
21410
|
-
"urlRegex",
|
|
21411
|
-
"origins",
|
|
21412
|
-
"resourceTypes",
|
|
21413
|
-
"priority"
|
|
21414
|
-
]) && !exactKeys(matcherValue2, [
|
|
21415
|
-
"urlRegex",
|
|
21416
|
-
"origins",
|
|
21417
|
-
"resourceTypes",
|
|
21418
|
-
"priority",
|
|
21419
|
-
"method"
|
|
21420
|
-
])) {
|
|
21421
|
-
return null;
|
|
21422
|
-
}
|
|
21423
|
-
const matcher = matcherValue2;
|
|
21424
|
-
const regexValue = matcher.urlRegex;
|
|
21425
|
-
if (regexValue === null || typeof regexValue !== "object" || Array.isArray(regexValue) || !exactKeys(regexValue, ["source", "flags"])) {
|
|
21426
|
-
return null;
|
|
21427
|
-
}
|
|
21428
|
-
const regex = regexValue;
|
|
21429
|
-
if (typeof regex.source !== "string" || regex.source.length > LIMITS.maxUrlRegexLength || regex.flags !== "" || compileUrlRegex(regex.source) === null) {
|
|
21430
|
-
return null;
|
|
21431
|
-
}
|
|
21432
|
-
if (!Array.isArray(matcher.origins) || matcher.origins.length === 0 || matcher.origins.length > LIMITS.maxOriginsPerScope)
|
|
21433
|
-
return null;
|
|
21434
|
-
const origins = [];
|
|
21435
|
-
for (const value2 of matcher.origins) {
|
|
21436
|
-
const normalized = normalizeSiteOrigin(value2);
|
|
21437
|
-
if (normalized === null || origins.includes(normalized)) return null;
|
|
21438
|
-
origins.push(normalized);
|
|
21439
|
-
}
|
|
21440
|
-
if (!Array.isArray(matcher.resourceTypes) || matcher.resourceTypes.length === 0 || matcher.resourceTypes.length > LIMITS.maxResourceTypesPerRule)
|
|
21441
|
-
return null;
|
|
21442
|
-
const resourceTypes = [];
|
|
21443
|
-
for (const value2 of matcher.resourceTypes) {
|
|
21444
|
-
if (!validResourceType(value2) || resourceTypes.includes(value2)) return null;
|
|
21445
|
-
resourceTypes.push(value2);
|
|
21446
|
-
}
|
|
21447
|
-
resourceTypes.sort(
|
|
21448
|
-
(left, right) => RESOURCE_TYPES.indexOf(left) - RESOURCE_TYPES.indexOf(right)
|
|
21947
|
+
function decodeEnvelopeFrame(buffer) {
|
|
21948
|
+
if (buffer.byteLength < 4) throw new Error("frame too small");
|
|
21949
|
+
const length = new DataView(buffer.buffer, buffer.byteOffset, 4).getUint32(
|
|
21950
|
+
0,
|
|
21951
|
+
true
|
|
21449
21952
|
);
|
|
21450
|
-
if (
|
|
21451
|
-
|
|
21452
|
-
|
|
21453
|
-
|
|
21454
|
-
|
|
21455
|
-
|
|
21456
|
-
}
|
|
21457
|
-
return freezeMatcher({
|
|
21458
|
-
kind: "matcher",
|
|
21459
|
-
groupId: record.groupId,
|
|
21460
|
-
ruleId: record.ruleId,
|
|
21461
|
-
matcher: {
|
|
21462
|
-
urlRegex: { source: regex.source, flags: "" },
|
|
21463
|
-
origins,
|
|
21464
|
-
resourceTypes,
|
|
21465
|
-
priority: matcher.priority,
|
|
21466
|
-
...method === void 0 ? {} : { method }
|
|
21467
|
-
}
|
|
21468
|
-
});
|
|
21469
|
-
}
|
|
21470
|
-
function sameLimits(value) {
|
|
21471
|
-
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
21472
|
-
return false;
|
|
21473
|
-
const keys = Object.keys(value);
|
|
21474
|
-
const expected = Object.keys(RUNTIME_LIMITS);
|
|
21475
|
-
if (keys.length !== expected.length || !expected.every((key) => hasOwn(value, key)))
|
|
21476
|
-
return false;
|
|
21477
|
-
for (const key of expected) {
|
|
21478
|
-
if (value[key] !== RUNTIME_LIMITS[key])
|
|
21479
|
-
return false;
|
|
21480
|
-
}
|
|
21481
|
-
return true;
|
|
21953
|
+
if (buffer.byteLength !== 4 + length)
|
|
21954
|
+
throw new Error("frame length mismatch");
|
|
21955
|
+
const json = new TextDecoder("utf-8", { fatal: true }).decode(
|
|
21956
|
+
buffer.slice(4)
|
|
21957
|
+
);
|
|
21958
|
+
return parseEnvelope(json);
|
|
21482
21959
|
}
|
|
21483
|
-
function
|
|
21484
|
-
|
|
21485
|
-
|
|
21486
|
-
|
|
21487
|
-
|
|
21488
|
-
|
|
21489
|
-
|
|
21490
|
-
|
|
21491
|
-
|
|
21492
|
-
|
|
21493
|
-
|
|
21494
|
-
|
|
21495
|
-
|
|
21496
|
-
|
|
21497
|
-
|
|
21498
|
-
|
|
21499
|
-
|
|
21500
|
-
|
|
21501
|
-
|
|
21502
|
-
|
|
21503
|
-
|
|
21504
|
-
|
|
21505
|
-
|
|
21506
|
-
|
|
21507
|
-
|
|
21508
|
-
|
|
21509
|
-
|
|
21510
|
-
|
|
21511
|
-
|
|
21512
|
-
|
|
21513
|
-
groupId: record.groupId,
|
|
21514
|
-
ruleId: record.ruleId,
|
|
21515
|
-
operationId: record.operationId,
|
|
21516
|
-
kind: record.kind,
|
|
21517
|
-
target,
|
|
21518
|
-
method: record.method
|
|
21960
|
+
function startMockFaucet(port, controller) {
|
|
21961
|
+
const server = createServer((req, res) => {
|
|
21962
|
+
void (async () => {
|
|
21963
|
+
try {
|
|
21964
|
+
const url = new URL(req.url ?? "", "http://localhost");
|
|
21965
|
+
const match = /^\/mock\/([a-f0-9]+)$/.exec(url.pathname);
|
|
21966
|
+
if (!match || req.method !== "GET") {
|
|
21967
|
+
res.writeHead(404);
|
|
21968
|
+
res.end();
|
|
21969
|
+
return;
|
|
21970
|
+
}
|
|
21971
|
+
const result = await controller.serveMock(match[1]);
|
|
21972
|
+
if (!result.ok) {
|
|
21973
|
+
res.writeHead(404);
|
|
21974
|
+
res.end();
|
|
21975
|
+
return;
|
|
21976
|
+
}
|
|
21977
|
+
const { status, headers, bodyBytes } = result.value;
|
|
21978
|
+
res.writeHead(
|
|
21979
|
+
status,
|
|
21980
|
+
Object.fromEntries(
|
|
21981
|
+
headers.map((h) => [h[0], h[1]])
|
|
21982
|
+
)
|
|
21983
|
+
);
|
|
21984
|
+
res.end(Buffer2.from(bodyBytes));
|
|
21985
|
+
} catch {
|
|
21986
|
+
if (!res.headersSent) res.writeHead(500);
|
|
21987
|
+
res.end();
|
|
21988
|
+
}
|
|
21989
|
+
})();
|
|
21519
21990
|
});
|
|
21991
|
+
return new Promise(
|
|
21992
|
+
(resolve22) => server.listen(port, () => resolve22(server))
|
|
21993
|
+
);
|
|
21520
21994
|
}
|
|
21521
|
-
function
|
|
21522
|
-
const
|
|
21523
|
-
|
|
21524
|
-
|
|
21525
|
-
|
|
21526
|
-
|
|
21527
|
-
|
|
21528
|
-
|
|
21529
|
-
|
|
21530
|
-
|
|
21531
|
-
|
|
21532
|
-
|
|
21533
|
-
|
|
21534
|
-
|
|
21535
|
-
|
|
21536
|
-
|
|
21537
|
-
|
|
21538
|
-
|
|
21539
|
-
|
|
21540
|
-
|
|
21541
|
-
|
|
21542
|
-
|
|
21543
|
-
|
|
21544
|
-
|
|
21545
|
-
|
|
21546
|
-
|
|
21547
|
-
|
|
21548
|
-
|
|
21549
|
-
|
|
21550
|
-
|
|
21551
|
-
|
|
21552
|
-
|
|
21553
|
-
|
|
21554
|
-
|
|
21555
|
-
|
|
21556
|
-
|
|
21557
|
-
|
|
21558
|
-
|
|
21559
|
-
|
|
21560
|
-
|
|
21561
|
-
|
|
21562
|
-
|
|
21563
|
-
|
|
21995
|
+
function createNativeHost(options) {
|
|
21996
|
+
const controller = createNativeRuntimeController({
|
|
21997
|
+
preset: options.preset,
|
|
21998
|
+
fileRoot: options.fileRoot,
|
|
21999
|
+
...options.mockPort !== void 0 ? { mockPort: options.mockPort } : {},
|
|
22000
|
+
...options.clock ? { clock: options.clock } : {}
|
|
22001
|
+
});
|
|
22002
|
+
let faucet = null;
|
|
22003
|
+
return {
|
|
22004
|
+
controller,
|
|
22005
|
+
mockPort: options.mockPort ?? null,
|
|
22006
|
+
async start() {
|
|
22007
|
+
await controller.start();
|
|
22008
|
+
if (options.mockPort !== void 0) {
|
|
22009
|
+
faucet = await startMockFaucet(options.mockPort, controller);
|
|
22010
|
+
}
|
|
22011
|
+
},
|
|
22012
|
+
async stop() {
|
|
22013
|
+
if (faucet) {
|
|
22014
|
+
await new Promise((resolve22) => faucet?.close(() => resolve22()));
|
|
22015
|
+
faucet = null;
|
|
22016
|
+
}
|
|
22017
|
+
await controller.stop();
|
|
22018
|
+
},
|
|
22019
|
+
async processFrame(frame) {
|
|
22020
|
+
let envelope;
|
|
22021
|
+
try {
|
|
22022
|
+
envelope = decodeEnvelopeFrame(frame);
|
|
22023
|
+
} catch (error) {
|
|
22024
|
+
console.error(
|
|
22025
|
+
"[rogatio-host] decodeEnvelopeFrame failed:",
|
|
22026
|
+
error instanceof Error ? error.message : String(error)
|
|
22027
|
+
);
|
|
22028
|
+
return null;
|
|
22029
|
+
}
|
|
22030
|
+
console.error(
|
|
22031
|
+
"[rogatio-host] received envelope:",
|
|
22032
|
+
envelope.type,
|
|
22033
|
+
"requestId:",
|
|
22034
|
+
envelope.requestId
|
|
22035
|
+
);
|
|
22036
|
+
let response;
|
|
22037
|
+
try {
|
|
22038
|
+
response = await controller.handleEnvelope(envelope);
|
|
22039
|
+
} catch (error) {
|
|
22040
|
+
console.error(
|
|
22041
|
+
"[rogatio-host] handleEnvelope failed:",
|
|
22042
|
+
error instanceof Error ? error.message : String(error)
|
|
22043
|
+
);
|
|
22044
|
+
return null;
|
|
22045
|
+
}
|
|
22046
|
+
try {
|
|
22047
|
+
return encodeEnvelopeFrame(response);
|
|
22048
|
+
} catch {
|
|
22049
|
+
return null;
|
|
22050
|
+
}
|
|
21564
22051
|
}
|
|
21565
|
-
}
|
|
21566
|
-
|
|
21567
|
-
|
|
21568
|
-
|
|
21569
|
-
|
|
21570
|
-
|
|
21571
|
-
|
|
22052
|
+
};
|
|
22053
|
+
}
|
|
22054
|
+
async function runNativeHost(options) {
|
|
22055
|
+
const host = createNativeHost(options);
|
|
22056
|
+
await host.start();
|
|
22057
|
+
const stdin = options.stdin ?? process.stdin;
|
|
22058
|
+
const stdout = options.stdout ?? process.stdout;
|
|
22059
|
+
if (options.onReady) options.onReady();
|
|
22060
|
+
let buffer = Buffer2.alloc(0);
|
|
22061
|
+
stdin.on("data", (chunk) => {
|
|
22062
|
+
const buf = Buffer2.isBuffer(chunk) ? chunk : Buffer2.from(chunk);
|
|
22063
|
+
console.error(
|
|
22064
|
+
"[rogatio-host] stdin data:",
|
|
22065
|
+
buf.length,
|
|
22066
|
+
"bytes, buffer:",
|
|
22067
|
+
buffer.length
|
|
22068
|
+
);
|
|
22069
|
+
buffer = Buffer2.concat([buffer, buf]);
|
|
22070
|
+
void (async () => {
|
|
22071
|
+
for (; ; ) {
|
|
22072
|
+
if (buffer.length < 4) break;
|
|
22073
|
+
const length = buffer.readUInt32LE(0);
|
|
22074
|
+
if (buffer.length < 4 + length) break;
|
|
22075
|
+
const frame = new Uint8Array(
|
|
22076
|
+
buffer.buffer,
|
|
22077
|
+
buffer.byteOffset,
|
|
22078
|
+
4 + length
|
|
22079
|
+
);
|
|
22080
|
+
console.error("[rogatio-host] processing frame:", 4 + length, "bytes");
|
|
22081
|
+
const response = await host.processFrame(frame);
|
|
22082
|
+
buffer = buffer.subarray(4 + length);
|
|
22083
|
+
if (response && stdout.writable) {
|
|
22084
|
+
console.error(
|
|
22085
|
+
"[rogatio-host] writing response:",
|
|
22086
|
+
response.length,
|
|
22087
|
+
"bytes"
|
|
22088
|
+
);
|
|
22089
|
+
stdout.write(Buffer2.from(response));
|
|
22090
|
+
} else {
|
|
22091
|
+
console.error("[rogatio-host] no response to write");
|
|
22092
|
+
}
|
|
22093
|
+
}
|
|
22094
|
+
})();
|
|
21572
22095
|
});
|
|
21573
|
-
|
|
21574
|
-
|
|
21575
|
-
|
|
21576
|
-
|
|
21577
|
-
const exposedBytes = bytes.slice();
|
|
21578
|
-
const normalized = Object.freeze({
|
|
21579
|
-
...normalizedPreset,
|
|
21580
|
-
digest,
|
|
21581
|
-
get canonicalBytes() {
|
|
21582
|
-
return exposedBytes.slice();
|
|
21583
|
-
}
|
|
22096
|
+
await new Promise((resolve22) => {
|
|
22097
|
+
stdin.on("end", () => {
|
|
22098
|
+
void host.stop().then(resolve22);
|
|
22099
|
+
});
|
|
21584
22100
|
});
|
|
21585
|
-
return { ok: true, value: normalized };
|
|
21586
22101
|
}
|
|
22102
|
+
var NATIVE_FRAME_MAX_BYTES = 64 * 1024;
|
|
22103
|
+
var NATIVE_POLICY_MAX_BYTES = 256 * 1024;
|
|
21587
22104
|
var PRIVATE_RANGES = [
|
|
21588
22105
|
{ start: ipToInt("10.0.0.0"), end: ipToInt("10.255.255.255") },
|
|
21589
22106
|
{ start: ipToInt("172.16.0.0"), end: ipToInt("172.31.255.255") },
|
|
@@ -21910,7 +22427,53 @@ function createRequestBodyTrustController(options = {}) {
|
|
|
21910
22427
|
capabilityReasons: caps.reasons
|
|
21911
22428
|
};
|
|
21912
22429
|
}
|
|
21913
|
-
|
|
22430
|
+
async function verify() {
|
|
22431
|
+
const reasons = [];
|
|
22432
|
+
let manifestExists = false;
|
|
22433
|
+
let manifestValid = false;
|
|
22434
|
+
let binaryExists = false;
|
|
22435
|
+
let binaryExecutable = false;
|
|
22436
|
+
let allowedOriginsCount = 0;
|
|
22437
|
+
try {
|
|
22438
|
+
const raw = await readFile3(manifestPath(), "utf8");
|
|
22439
|
+
manifestExists = true;
|
|
22440
|
+
const parsed = JSON.parse(raw);
|
|
22441
|
+
if (isWellFormedManifest(parsed)) {
|
|
22442
|
+
manifestValid = true;
|
|
22443
|
+
const manifest = parsed;
|
|
22444
|
+
allowedOriginsCount = manifest.allowed_origins.length;
|
|
22445
|
+
try {
|
|
22446
|
+
const fileStat = await stat2(manifest.path);
|
|
22447
|
+
binaryExists = fileStat.isFile();
|
|
22448
|
+
binaryExecutable = (fileStat.mode & 73) !== 0;
|
|
22449
|
+
if (!binaryExists) reasons.push("binary-not-found");
|
|
22450
|
+
if (!binaryExecutable) reasons.push("binary-not-executable");
|
|
22451
|
+
} catch {
|
|
22452
|
+
binaryExists = false;
|
|
22453
|
+
reasons.push("binary-not-found");
|
|
22454
|
+
}
|
|
22455
|
+
if (allowedOriginsCount === 0) reasons.push("no-allowed-origins");
|
|
22456
|
+
} else {
|
|
22457
|
+
reasons.push("manifest-invalid");
|
|
22458
|
+
}
|
|
22459
|
+
} catch {
|
|
22460
|
+
reasons.push("manifest-not-found");
|
|
22461
|
+
}
|
|
22462
|
+
const caTrusted = await existsFile(caKeyFile) && await existsFile(caCertFile);
|
|
22463
|
+
if (!caTrusted) reasons.push("ca-not-trusted");
|
|
22464
|
+
const ok = manifestExists && manifestValid && binaryExists && binaryExecutable && caTrusted && allowedOriginsCount > 0;
|
|
22465
|
+
return {
|
|
22466
|
+
ok,
|
|
22467
|
+
manifestExists,
|
|
22468
|
+
manifestValid,
|
|
22469
|
+
binaryExists,
|
|
22470
|
+
binaryExecutable,
|
|
22471
|
+
caTrusted,
|
|
22472
|
+
allowedOriginsCount,
|
|
22473
|
+
reasons
|
|
22474
|
+
};
|
|
22475
|
+
}
|
|
22476
|
+
return { install, uninstall, status, verify };
|
|
21914
22477
|
}
|
|
21915
22478
|
var darwinAdapter = {
|
|
21916
22479
|
platform: "darwin",
|
|
@@ -22257,7 +22820,102 @@ function selectTrustPlatformAdapter(platform) {
|
|
|
22257
22820
|
}
|
|
22258
22821
|
}
|
|
22259
22822
|
|
|
22260
|
-
// packages/cli/src/
|
|
22823
|
+
// packages/cli/src/help.ts
|
|
22824
|
+
function showEditHelp() {
|
|
22825
|
+
console.log(`Usage: rogatio edit [options] [path]
|
|
22826
|
+
|
|
22827
|
+
Launch browser-based editor for .rogatio.json project file.
|
|
22828
|
+
|
|
22829
|
+
Arguments:
|
|
22830
|
+
path Path to .rogatio.json (default: .rogatio.json in current directory)
|
|
22831
|
+
|
|
22832
|
+
Options:
|
|
22833
|
+
--port <n> Fixed port for editor server (default: random)
|
|
22834
|
+
--help, -h Show this help
|
|
22835
|
+
|
|
22836
|
+
The editor runs in your default browser and communicates with a local server
|
|
22837
|
+
bound to 127.0.0.1. Changes are saved atomically to the project file.`);
|
|
22838
|
+
}
|
|
22839
|
+
function showVerifyHelp() {
|
|
22840
|
+
console.log(`Usage: rogatio verify [options] [path]
|
|
22841
|
+
|
|
22842
|
+
Validate a .rogatio.json project file using schema and compiler.
|
|
22843
|
+
|
|
22844
|
+
Arguments:
|
|
22845
|
+
path Path to .rogatio.json (default: .rogatio.json in current directory)
|
|
22846
|
+
Use '-' to read from stdin
|
|
22847
|
+
|
|
22848
|
+
Options:
|
|
22849
|
+
--json Output diagnostics as JSON
|
|
22850
|
+
--help, -h Show this help
|
|
22851
|
+
|
|
22852
|
+
Exit codes:
|
|
22853
|
+
0 Valid (no diagnostics)
|
|
22854
|
+
1 Invalid (diagnostics present)
|
|
22855
|
+
2 Error (IO, parse, or unexpected failure)`);
|
|
22856
|
+
}
|
|
22857
|
+
function showRuntimeHelp() {
|
|
22858
|
+
console.log(`Usage: rogatio runtime <command> [options]
|
|
22859
|
+
rogatio runtime host [path]
|
|
22860
|
+
|
|
22861
|
+
Native messaging runtime control for response-body and request-body rules.
|
|
22862
|
+
|
|
22863
|
+
Commands:
|
|
22864
|
+
install Install the native-messaging host manifest and (on capable
|
|
22865
|
+
platforms) provision the device-local CA (requires --extension-id).
|
|
22866
|
+
CA trust requires root/admin privileges (Linux: sudo, macOS: keychain
|
|
22867
|
+
password, Windows: Administrator).
|
|
22868
|
+
uninstall Remove the native-messaging host manifest and device-local CA trust
|
|
22869
|
+
(idempotent).
|
|
22870
|
+
host [path] Run the native-messaging runtime host. The browser launches this
|
|
22871
|
+
process via the native-messaging manifest to handle rule matching.
|
|
22872
|
+
|
|
22873
|
+
The lifecycle of the runtime (start/stop) is driven from the extension's
|
|
22874
|
+
Start/Stop controls, not the CLI. Run 'rogatio runtime install --extension-id
|
|
22875
|
+
<id>' once to register the host, then use the extension.
|
|
22876
|
+
|
|
22877
|
+
Options:
|
|
22878
|
+
--extension-id Extension ID for native messaging manifest (required for install)
|
|
22879
|
+
--root <dir> Root for confined file mocks (default: project directory)
|
|
22880
|
+
--help, -h Show this help
|
|
22881
|
+
|
|
22882
|
+
Exit codes:
|
|
22883
|
+
0 Stopped cleanly / success
|
|
22884
|
+
1 Invalid project (diagnostics present) or file outside the root
|
|
22885
|
+
2 Error (IO or usage)`);
|
|
22886
|
+
}
|
|
22887
|
+
function showTestHelp() {
|
|
22888
|
+
console.log(`Usage: rogatio test [options] [path] [url...]
|
|
22889
|
+
|
|
22890
|
+
Run offline dry-run tests against a .rogatio.json project file.
|
|
22891
|
+
|
|
22892
|
+
Arguments:
|
|
22893
|
+
path Path to .rogatio.json (default: .rogatio.json in current directory)
|
|
22894
|
+
Use '-' to read project JSON from stdin
|
|
22895
|
+
url... URLs to test; when path is omitted, first URL is detected automatically
|
|
22896
|
+
|
|
22897
|
+
Options:
|
|
22898
|
+
--urls <list> Comma-separated list of URLs to test
|
|
22899
|
+
--urls-file <path> Path to JSON file containing array of test cases
|
|
22900
|
+
Each case: { "url": "...", "method"?: "...", "resourceType"?: "..." }
|
|
22901
|
+
Use '-' to read from stdin
|
|
22902
|
+
--method <m> Default HTTP method for all test cases (GET, POST, etc.)
|
|
22903
|
+
--resource-type <t> Default resource type for all test cases
|
|
22904
|
+
--max-cases <n> Maximum number of test cases (default: 256)
|
|
22905
|
+
--json Output results as JSON
|
|
22906
|
+
--help, -h Show this help
|
|
22907
|
+
|
|
22908
|
+
Test case format (JSON):
|
|
22909
|
+
[
|
|
22910
|
+
{ "url": "https://example.com/", "method": "GET", "resourceType": "main_frame" },
|
|
22911
|
+
{ "url": "https://example.com/script.js" }
|
|
22912
|
+
]
|
|
22913
|
+
|
|
22914
|
+
Exit codes:
|
|
22915
|
+
0 Success (all valid, results may include non-matches)
|
|
22916
|
+
1 Validation/compile/test errors
|
|
22917
|
+
2 Usage error (invalid arguments, missing input)`);
|
|
22918
|
+
}
|
|
22261
22919
|
function showAIHelp() {
|
|
22262
22920
|
console.log(`Usage: rogatio ai <command> [options]
|
|
22263
22921
|
|
|
@@ -22273,6 +22931,8 @@ Commands:
|
|
|
22273
22931
|
Options:
|
|
22274
22932
|
--help, -h Show this help`);
|
|
22275
22933
|
}
|
|
22934
|
+
|
|
22935
|
+
// packages/cli/src/commands/ai.ts
|
|
22276
22936
|
async function promptInput(prompt) {
|
|
22277
22937
|
process.stdout.write(prompt);
|
|
22278
22938
|
for await (const chunk of process.stdin) {
|
|
@@ -22349,550 +23009,201 @@ async function aiSetup() {
|
|
|
22349
23009
|
}
|
|
22350
23010
|
const apiKey = await promptSecret("API Key: ");
|
|
22351
23011
|
if (!apiKey) {
|
|
22352
|
-
console.error("Error: API Key is required");
|
|
22353
|
-
return 1;
|
|
22354
|
-
}
|
|
22355
|
-
const config = { providerUrl, model, apiKey };
|
|
22356
|
-
try {
|
|
22357
|
-
await writeProviderConfig(config);
|
|
22358
|
-
console.log("AI provider configured successfully");
|
|
22359
|
-
return 0;
|
|
22360
|
-
} catch (e) {
|
|
22361
|
-
console.error(`Error writing config: ${e}`);
|
|
22362
|
-
return 1;
|
|
22363
|
-
}
|
|
22364
|
-
}
|
|
22365
|
-
async function aiList() {
|
|
22366
|
-
const config = await readProviderConfig();
|
|
22367
|
-
if (!config) {
|
|
22368
|
-
console.log("No AI provider configured");
|
|
22369
|
-
return 0;
|
|
22370
|
-
}
|
|
22371
|
-
console.log(`Provider: ${config.providerUrl}`);
|
|
22372
|
-
console.log(`Model: ${config.model}`);
|
|
22373
|
-
console.log(`API Key: ${redactKey(config.apiKey)}`);
|
|
22374
|
-
return 0;
|
|
22375
|
-
}
|
|
22376
|
-
async function aiShow() {
|
|
22377
|
-
const config = await readProviderConfig();
|
|
22378
|
-
if (!config) {
|
|
22379
|
-
console.log("No AI provider configured");
|
|
22380
|
-
return 0;
|
|
22381
|
-
}
|
|
22382
|
-
console.log(
|
|
22383
|
-
JSON.stringify(
|
|
22384
|
-
{
|
|
22385
|
-
providerUrl: config.providerUrl,
|
|
22386
|
-
model: config.model,
|
|
22387
|
-
apiKey: redactKey(config.apiKey)
|
|
22388
|
-
},
|
|
22389
|
-
null,
|
|
22390
|
-
2
|
|
22391
|
-
)
|
|
22392
|
-
);
|
|
22393
|
-
return 0;
|
|
22394
|
-
}
|
|
22395
|
-
async function aiDelete() {
|
|
22396
|
-
const config = await readProviderConfig();
|
|
22397
|
-
if (!config) {
|
|
22398
|
-
console.log("No AI provider configured");
|
|
22399
|
-
return 0;
|
|
22400
|
-
}
|
|
22401
|
-
const confirm = await promptInput(
|
|
22402
|
-
"Delete AI provider configuration? [y/N]: "
|
|
22403
|
-
);
|
|
22404
|
-
if (confirm.toLowerCase() !== "y") {
|
|
22405
|
-
console.log("Cancelled");
|
|
22406
|
-
return 0;
|
|
22407
|
-
}
|
|
22408
|
-
try {
|
|
22409
|
-
await deleteProviderConfig();
|
|
22410
|
-
console.log("AI provider configuration deleted");
|
|
22411
|
-
return 0;
|
|
22412
|
-
} catch (e) {
|
|
22413
|
-
console.error(`Error deleting config: ${e}`);
|
|
22414
|
-
return 1;
|
|
22415
|
-
}
|
|
22416
|
-
}
|
|
22417
|
-
async function aiTest() {
|
|
22418
|
-
const config = await readProviderConfig();
|
|
22419
|
-
if (!config) {
|
|
22420
|
-
console.error("No AI provider configured. Run 'rogatio ai setup' first.");
|
|
23012
|
+
console.error("Error: API Key is required");
|
|
22421
23013
|
return 1;
|
|
22422
23014
|
}
|
|
22423
|
-
|
|
22424
|
-
const client = createAIClient(config);
|
|
23015
|
+
const config = { providerUrl, model, apiKey };
|
|
22425
23016
|
try {
|
|
22426
|
-
|
|
22427
|
-
|
|
22428
|
-
messages: [{ role: "user", content: "Hello" }],
|
|
22429
|
-
model: config.model,
|
|
22430
|
-
temperature: 0
|
|
22431
|
-
});
|
|
22432
|
-
const elapsed = Date.now() - start;
|
|
22433
|
-
console.log(`Connection successful (${elapsed}ms)`);
|
|
22434
|
-
console.log(
|
|
22435
|
-
`Response: ${result.content.slice(0, 100)}${result.content.length > 100 ? "..." : ""}`
|
|
22436
|
-
);
|
|
22437
|
-
if (result.usage) {
|
|
22438
|
-
console.log(
|
|
22439
|
-
`Usage: ${result.usage.promptTokens} prompt + ${result.usage.completionTokens} completion tokens`
|
|
22440
|
-
);
|
|
22441
|
-
}
|
|
23017
|
+
await writeProviderConfig(config);
|
|
23018
|
+
console.log("AI provider configured successfully");
|
|
22442
23019
|
return 0;
|
|
22443
23020
|
} catch (e) {
|
|
22444
|
-
console.error(`
|
|
23021
|
+
console.error(`Error writing config: ${e}`);
|
|
22445
23022
|
return 1;
|
|
22446
23023
|
}
|
|
22447
23024
|
}
|
|
22448
|
-
function
|
|
22449
|
-
|
|
22450
|
-
|
|
22451
|
-
|
|
22452
|
-
|
|
22453
|
-
// packages/cli/src/commands/edit.ts
|
|
22454
|
-
import { resolve as resolve4 } from "node:path";
|
|
22455
|
-
|
|
22456
|
-
// packages/cli/src/server/http.ts
|
|
22457
|
-
import { randomInt } from "node:crypto";
|
|
22458
|
-
import {
|
|
22459
|
-
createServer as createHttpServer
|
|
22460
|
-
} from "node:http";
|
|
22461
|
-
var HttpServerError = class extends Error {
|
|
22462
|
-
code;
|
|
22463
|
-
constructor(code, message, cause) {
|
|
22464
|
-
super(message, { cause });
|
|
22465
|
-
this.name = "HttpServerError";
|
|
22466
|
-
this.code = code;
|
|
22467
|
-
}
|
|
22468
|
-
};
|
|
22469
|
-
function createServer2(handler, options = {}) {
|
|
22470
|
-
const server = createHttpServer(handler);
|
|
22471
|
-
const preferredPort = options.port;
|
|
22472
|
-
let port = null;
|
|
22473
|
-
let started = false;
|
|
22474
|
-
async function listenOn(candidatePort) {
|
|
22475
|
-
await new Promise((resolve9, reject) => {
|
|
22476
|
-
server.once("error", reject);
|
|
22477
|
-
server.listen(candidatePort, "127.0.0.1", () => {
|
|
22478
|
-
server.off("error", reject);
|
|
22479
|
-
resolve9();
|
|
22480
|
-
});
|
|
22481
|
-
});
|
|
22482
|
-
}
|
|
22483
|
-
return {
|
|
22484
|
-
get port() {
|
|
22485
|
-
if (port === null)
|
|
22486
|
-
throw new HttpServerError("not-started", "Server not started");
|
|
22487
|
-
return port;
|
|
22488
|
-
},
|
|
22489
|
-
async start() {
|
|
22490
|
-
if (started) return;
|
|
22491
|
-
if (preferredPort !== void 0) {
|
|
22492
|
-
try {
|
|
22493
|
-
await listenOn(preferredPort);
|
|
22494
|
-
port = preferredPort;
|
|
22495
|
-
started = true;
|
|
22496
|
-
return;
|
|
22497
|
-
} catch (e) {
|
|
22498
|
-
throw new HttpServerError(
|
|
22499
|
-
"listen-failed",
|
|
22500
|
-
`Failed to start server on port ${preferredPort}: ${e}`,
|
|
22501
|
-
e
|
|
22502
|
-
);
|
|
22503
|
-
}
|
|
22504
|
-
}
|
|
22505
|
-
const maxRetries = 8;
|
|
22506
|
-
let lastError = null;
|
|
22507
|
-
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
22508
|
-
const candidatePort = randomInt(1024, 65535);
|
|
22509
|
-
try {
|
|
22510
|
-
await listenOn(candidatePort);
|
|
22511
|
-
port = candidatePort;
|
|
22512
|
-
started = true;
|
|
22513
|
-
return;
|
|
22514
|
-
} catch (e) {
|
|
22515
|
-
lastError = e;
|
|
22516
|
-
const code = e.code;
|
|
22517
|
-
if (code !== "EADDRINUSE" && code !== "EACCES") {
|
|
22518
|
-
throw new HttpServerError(
|
|
22519
|
-
"listen-failed",
|
|
22520
|
-
`Failed to start server: ${e}`,
|
|
22521
|
-
e
|
|
22522
|
-
);
|
|
22523
|
-
}
|
|
22524
|
-
}
|
|
22525
|
-
}
|
|
22526
|
-
throw new HttpServerError(
|
|
22527
|
-
"port-exhausted",
|
|
22528
|
-
`Failed to bind to port after ${maxRetries} attempts`,
|
|
22529
|
-
lastError ?? void 0
|
|
22530
|
-
);
|
|
22531
|
-
},
|
|
22532
|
-
async stop() {
|
|
22533
|
-
if (!started) return;
|
|
22534
|
-
await new Promise((resolve9) => {
|
|
22535
|
-
server.close(() => resolve9());
|
|
22536
|
-
});
|
|
22537
|
-
started = false;
|
|
22538
|
-
}
|
|
22539
|
-
};
|
|
22540
|
-
}
|
|
22541
|
-
|
|
22542
|
-
// packages/cli/src/server/routes.ts
|
|
22543
|
-
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
22544
|
-
import { readFile as readFile4 } from "node:fs/promises";
|
|
22545
|
-
import { resolve as resolve2 } from "node:path";
|
|
22546
|
-
|
|
22547
|
-
// packages/compiler/dist/node/index.js
|
|
22548
|
-
var ISSUE_CODES = {
|
|
22549
|
-
required: "schema.required",
|
|
22550
|
-
additionalProperties: "schema.unknown-property",
|
|
22551
|
-
type: "schema.invalid-type",
|
|
22552
|
-
format: "schema.invalid-format",
|
|
22553
|
-
const: "schema.invalid-value",
|
|
22554
|
-
enum: "schema.invalid-value",
|
|
22555
|
-
pattern: "schema.invalid-value",
|
|
22556
|
-
minLength: "schema.out-of-range",
|
|
22557
|
-
maxLength: "schema.out-of-range",
|
|
22558
|
-
minItems: "schema.out-of-range",
|
|
22559
|
-
maxItems: "schema.out-of-range",
|
|
22560
|
-
minimum: "schema.out-of-range",
|
|
22561
|
-
maximum: "schema.out-of-range",
|
|
22562
|
-
uniqueItems: "schema.out-of-range",
|
|
22563
|
-
ownProperties: "schema.invalid-structure",
|
|
22564
|
-
uniqueId: "schema.duplicate-id",
|
|
22565
|
-
effectiveOrigin: "schema.no-effective-origin",
|
|
22566
|
-
maxRulesPerProject: "schema.rule-limit"
|
|
22567
|
-
};
|
|
22568
|
-
var SAFE_PARAM_KEYS = /* @__PURE__ */ new Set([
|
|
22569
|
-
"additionalProperty",
|
|
22570
|
-
"actual",
|
|
22571
|
-
"allowedValue",
|
|
22572
|
-
"allowedValues",
|
|
22573
|
-
"format",
|
|
22574
|
-
"i",
|
|
22575
|
-
"j",
|
|
22576
|
-
"limit",
|
|
22577
|
-
"missingProperty",
|
|
22578
|
-
"previousPath",
|
|
22579
|
-
"type",
|
|
22580
|
-
"headerName",
|
|
22581
|
-
"headerOperation",
|
|
22582
|
-
"headerDirection"
|
|
22583
|
-
]);
|
|
22584
|
-
var MESSAGES = {
|
|
22585
|
-
"schema.required": "Required project data is missing.",
|
|
22586
|
-
"schema.unknown-property": "The project contains an unknown property.",
|
|
22587
|
-
"schema.invalid-type": "The project contains a value with an invalid type.",
|
|
22588
|
-
"schema.invalid-format": "The project contains a value with an invalid format.",
|
|
22589
|
-
"schema.invalid-value": "The project contains an invalid value.",
|
|
22590
|
-
"schema.out-of-range": "The project contains a value outside its allowed bounds.",
|
|
22591
|
-
"schema.invalid-structure": "The project contains invalid structure.",
|
|
22592
|
-
"schema.duplicate-id": "Project and rule IDs must be unique.",
|
|
22593
|
-
"schema.no-effective-origin": "Each rule must have at least one effective origin.",
|
|
22594
|
-
"schema.rule-limit": "The project contains too many rules.",
|
|
22595
|
-
"compiler.invariant": "The compiler could not normalize validated project data.",
|
|
22596
|
-
"compiler.forbidden-header": "The header name is forbidden for this direction.",
|
|
22597
|
-
"compiler.header-value-required": "Header value is required for set and append operations.",
|
|
22598
|
-
"compiler.header-value-unexpected": "Header value must not be provided for remove operation.",
|
|
22599
|
-
"compiler.invalid-header-direction": "Invalid header direction.",
|
|
22600
|
-
"compiler.invalid-header-operation": "Invalid header operation."
|
|
22601
|
-
};
|
|
22602
|
-
function copySafeParams(params) {
|
|
22603
|
-
const safe = {};
|
|
22604
|
-
for (const key of Object.keys(params)) {
|
|
22605
|
-
if (!SAFE_PARAM_KEYS.has(key)) continue;
|
|
22606
|
-
const value = params[key];
|
|
22607
|
-
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null) {
|
|
22608
|
-
safe[key] = value;
|
|
22609
|
-
continue;
|
|
22610
|
-
}
|
|
22611
|
-
if (Array.isArray(value)) {
|
|
22612
|
-
const values = [];
|
|
22613
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
22614
|
-
const item = value[index];
|
|
22615
|
-
if (typeof item === "string" || typeof item === "number" || typeof item === "boolean" || item === null) {
|
|
22616
|
-
values.push(item);
|
|
22617
|
-
}
|
|
22618
|
-
}
|
|
22619
|
-
safe[key] = values;
|
|
22620
|
-
}
|
|
23025
|
+
async function aiList() {
|
|
23026
|
+
const config = await readProviderConfig();
|
|
23027
|
+
if (!config) {
|
|
23028
|
+
console.log("No AI provider configured");
|
|
23029
|
+
return 0;
|
|
22621
23030
|
}
|
|
22622
|
-
|
|
22623
|
-
}
|
|
22624
|
-
|
|
22625
|
-
if (left < right) return -1;
|
|
22626
|
-
if (left > right) return 1;
|
|
23031
|
+
console.log(`Provider: ${config.providerUrl}`);
|
|
23032
|
+
console.log(`Model: ${config.model}`);
|
|
23033
|
+
console.log(`API Key: ${redactKey(config.apiKey)}`);
|
|
22627
23034
|
return 0;
|
|
22628
23035
|
}
|
|
22629
|
-
function
|
|
22630
|
-
const
|
|
22631
|
-
|
|
22632
|
-
|
|
22633
|
-
|
|
22634
|
-
|
|
22635
|
-
|
|
22636
|
-
|
|
22637
|
-
|
|
22638
|
-
|
|
22639
|
-
|
|
22640
|
-
|
|
22641
|
-
|
|
22642
|
-
|
|
22643
|
-
|
|
22644
|
-
|
|
22645
|
-
|
|
22646
|
-
params: copySafeParams(issue.params)
|
|
22647
|
-
};
|
|
22648
|
-
}).sort(compareDiagnostics);
|
|
22649
|
-
}
|
|
22650
|
-
function invariantDiagnostic(path2 = "") {
|
|
22651
|
-
return {
|
|
22652
|
-
code: "compiler.invariant",
|
|
22653
|
-
severity: "error",
|
|
22654
|
-
path: path2,
|
|
22655
|
-
message: MESSAGES["compiler.invariant"],
|
|
22656
|
-
params: {}
|
|
22657
|
-
};
|
|
22658
|
-
}
|
|
22659
|
-
function compareCodeUnits2(left, right) {
|
|
22660
|
-
if (left < right) return -1;
|
|
22661
|
-
if (left > right) return 1;
|
|
23036
|
+
async function aiShow() {
|
|
23037
|
+
const config = await readProviderConfig();
|
|
23038
|
+
if (!config) {
|
|
23039
|
+
console.log("No AI provider configured");
|
|
23040
|
+
return 0;
|
|
23041
|
+
}
|
|
23042
|
+
console.log(
|
|
23043
|
+
JSON.stringify(
|
|
23044
|
+
{
|
|
23045
|
+
providerUrl: config.providerUrl,
|
|
23046
|
+
model: config.model,
|
|
23047
|
+
apiKey: redactKey(config.apiKey)
|
|
23048
|
+
},
|
|
23049
|
+
null,
|
|
23050
|
+
2
|
|
23051
|
+
)
|
|
23052
|
+
);
|
|
22662
23053
|
return 0;
|
|
22663
23054
|
}
|
|
22664
|
-
function
|
|
22665
|
-
|
|
22666
|
-
|
|
23055
|
+
async function aiDelete() {
|
|
23056
|
+
const config = await readProviderConfig();
|
|
23057
|
+
if (!config) {
|
|
23058
|
+
console.log("No AI provider configured");
|
|
23059
|
+
return 0;
|
|
23060
|
+
}
|
|
23061
|
+
const confirm = await promptInput(
|
|
23062
|
+
"Delete AI provider configuration? [y/N]: "
|
|
23063
|
+
);
|
|
23064
|
+
if (confirm.toLowerCase() !== "y") {
|
|
23065
|
+
console.log("Cancelled");
|
|
23066
|
+
return 0;
|
|
22667
23067
|
}
|
|
22668
|
-
if (ancestors.has(value)) return { valid: false };
|
|
22669
|
-
ancestors.add(value);
|
|
22670
23068
|
try {
|
|
22671
|
-
|
|
22672
|
-
|
|
22673
|
-
|
|
22674
|
-
|
|
22675
|
-
|
|
22676
|
-
|
|
22677
|
-
return { valid: false };
|
|
22678
|
-
}
|
|
22679
|
-
const length = lengthDescriptor.value;
|
|
22680
|
-
for (const propertyName of Object.getOwnPropertyNames(value)) {
|
|
22681
|
-
if (propertyName === "length") continue;
|
|
22682
|
-
const index = Number(propertyName);
|
|
22683
|
-
if (!Number.isInteger(index) || index < 0 || index >= length || String(index) !== propertyName) {
|
|
22684
|
-
return { valid: false };
|
|
22685
|
-
}
|
|
22686
|
-
}
|
|
22687
|
-
const snapshot2 = new Array(length);
|
|
22688
|
-
for (let index = 0; index < length; index += 1) {
|
|
22689
|
-
const descriptor = Object.getOwnPropertyDescriptor(
|
|
22690
|
-
value,
|
|
22691
|
-
String(index)
|
|
22692
|
-
);
|
|
22693
|
-
if (descriptor === void 0 || !("value" in descriptor) || !descriptor.enumerable) {
|
|
22694
|
-
return { valid: false };
|
|
22695
|
-
}
|
|
22696
|
-
const child = snapshotOwnData3(descriptor.value, ancestors);
|
|
22697
|
-
if (!child.valid) return child;
|
|
22698
|
-
snapshot2[index] = child.value;
|
|
22699
|
-
}
|
|
22700
|
-
return { valid: true, value: snapshot2 };
|
|
22701
|
-
}
|
|
22702
|
-
const snapshot = /* @__PURE__ */ Object.create(null);
|
|
22703
|
-
for (const key of Object.getOwnPropertyNames(value)) {
|
|
22704
|
-
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
22705
|
-
if (descriptor === void 0 || !("value" in descriptor) || !descriptor.enumerable) {
|
|
22706
|
-
return { valid: false };
|
|
22707
|
-
}
|
|
22708
|
-
const child = snapshotOwnData3(descriptor.value, ancestors);
|
|
22709
|
-
if (!child.valid) return child;
|
|
22710
|
-
Object.defineProperty(snapshot, key, {
|
|
22711
|
-
configurable: true,
|
|
22712
|
-
enumerable: true,
|
|
22713
|
-
value: child.value,
|
|
22714
|
-
writable: true
|
|
22715
|
-
});
|
|
22716
|
-
}
|
|
22717
|
-
return { valid: true, value: snapshot };
|
|
22718
|
-
} catch {
|
|
22719
|
-
return { valid: false };
|
|
22720
|
-
} finally {
|
|
22721
|
-
ancestors.delete(value);
|
|
23069
|
+
await deleteProviderConfig();
|
|
23070
|
+
console.log("AI provider configuration deleted");
|
|
23071
|
+
return 0;
|
|
23072
|
+
} catch (e) {
|
|
23073
|
+
console.error(`Error deleting config: ${e}`);
|
|
23074
|
+
return 1;
|
|
22722
23075
|
}
|
|
22723
23076
|
}
|
|
22724
|
-
|
|
22725
|
-
|
|
22726
|
-
|
|
22727
|
-
|
|
22728
|
-
|
|
22729
|
-
}
|
|
22730
|
-
path;
|
|
22731
|
-
};
|
|
22732
|
-
function normalizeOrigins(group, rule, groupIndex, ruleIndex) {
|
|
22733
|
-
const origins = /* @__PURE__ */ new Set();
|
|
22734
|
-
const sourceArrays = [group.origins, rule.origins];
|
|
22735
|
-
for (let sourceIndex = 0; sourceIndex < sourceArrays.length; sourceIndex += 1) {
|
|
22736
|
-
const source = sourceArrays[sourceIndex];
|
|
22737
|
-
for (let originIndex = 0; originIndex < source.length; originIndex += 1) {
|
|
22738
|
-
const normalized = normalizeSiteOrigin(source[originIndex]);
|
|
22739
|
-
if (normalized === null) {
|
|
22740
|
-
const path2 = sourceIndex === 0 ? `/groups/${groupIndex}/origins/${originIndex}` : `/groups/${groupIndex}/rules/${ruleIndex}/origins/${originIndex}`;
|
|
22741
|
-
throw new CompilerInvariantError(path2);
|
|
22742
|
-
}
|
|
22743
|
-
origins.add(normalized);
|
|
22744
|
-
}
|
|
23077
|
+
async function aiTest() {
|
|
23078
|
+
const config = await readProviderConfig();
|
|
23079
|
+
if (!config) {
|
|
23080
|
+
console.error("No AI provider configured. Run 'rogatio ai setup' first.");
|
|
23081
|
+
return 1;
|
|
22745
23082
|
}
|
|
22746
|
-
|
|
22747
|
-
|
|
22748
|
-
|
|
22749
|
-
|
|
22750
|
-
|
|
22751
|
-
|
|
22752
|
-
|
|
22753
|
-
|
|
22754
|
-
|
|
22755
|
-
|
|
22756
|
-
|
|
23083
|
+
console.log("Testing connection to AI provider...");
|
|
23084
|
+
const client = createAIClient(config);
|
|
23085
|
+
try {
|
|
23086
|
+
const start = Date.now();
|
|
23087
|
+
const result = await client.complete({
|
|
23088
|
+
messages: [{ role: "user", content: "Hello" }],
|
|
23089
|
+
model: config.model,
|
|
23090
|
+
temperature: 0
|
|
23091
|
+
});
|
|
23092
|
+
const elapsed = Date.now() - start;
|
|
23093
|
+
console.log(`Connection successful (${elapsed}ms)`);
|
|
23094
|
+
console.log(
|
|
23095
|
+
`Response: ${result.content.slice(0, 100)}${result.content.length > 100 ? "..." : ""}`
|
|
23096
|
+
);
|
|
23097
|
+
if (result.usage) {
|
|
23098
|
+
console.log(
|
|
23099
|
+
`Usage: ${result.usage.promptTokens} prompt + ${result.usage.completionTokens} completion tokens`
|
|
23100
|
+
);
|
|
22757
23101
|
}
|
|
23102
|
+
return 0;
|
|
23103
|
+
} catch (e) {
|
|
23104
|
+
console.error(`Connection failed: ${e}`);
|
|
23105
|
+
return 1;
|
|
22758
23106
|
}
|
|
22759
|
-
return ordered;
|
|
22760
23107
|
}
|
|
22761
|
-
function
|
|
22762
|
-
|
|
22763
|
-
|
|
22764
|
-
origins: normalizeOrigins(group, rule, groupIndex, ruleIndex),
|
|
22765
|
-
resourceTypes: canonicalResourceTypes(rule.resourceTypes),
|
|
22766
|
-
priority: rule.priority
|
|
22767
|
-
};
|
|
22768
|
-
if (rule.method !== void 0) matcher.method = rule.method;
|
|
22769
|
-
return matcher;
|
|
23108
|
+
function redactKey(key) {
|
|
23109
|
+
if (key.length <= 8) return "****";
|
|
23110
|
+
return `${key.slice(0, 4)}****${key.slice(-4)}`;
|
|
22770
23111
|
}
|
|
22771
|
-
|
|
22772
|
-
|
|
22773
|
-
|
|
22774
|
-
|
|
22775
|
-
|
|
22776
|
-
|
|
22777
|
-
|
|
22778
|
-
|
|
22779
|
-
|
|
22780
|
-
|
|
22781
|
-
|
|
22782
|
-
|
|
22783
|
-
|
|
22784
|
-
|
|
22785
|
-
|
|
22786
|
-
|
|
22787
|
-
|
|
22788
|
-
|
|
22789
|
-
|
|
22790
|
-
|
|
22791
|
-
|
|
22792
|
-
|
|
22793
|
-
|
|
22794
|
-
|
|
22795
|
-
|
|
22796
|
-
|
|
22797
|
-
|
|
22798
|
-
|
|
22799
|
-
|
|
22800
|
-
|
|
22801
|
-
|
|
22802
|
-
|
|
22803
|
-
|
|
22804
|
-
|
|
22805
|
-
|
|
22806
|
-
|
|
22807
|
-
|
|
23112
|
+
|
|
23113
|
+
// packages/cli/src/commands/edit.ts
|
|
23114
|
+
import { resolve as resolve4 } from "node:path";
|
|
23115
|
+
|
|
23116
|
+
// packages/cli/src/server/http.ts
|
|
23117
|
+
import { randomInt } from "node:crypto";
|
|
23118
|
+
import {
|
|
23119
|
+
createServer as createHttpServer
|
|
23120
|
+
} from "node:http";
|
|
23121
|
+
var HttpServerError = class extends Error {
|
|
23122
|
+
code;
|
|
23123
|
+
constructor(code, message, cause) {
|
|
23124
|
+
super(message, { cause });
|
|
23125
|
+
this.name = "HttpServerError";
|
|
23126
|
+
this.code = code;
|
|
23127
|
+
}
|
|
23128
|
+
};
|
|
23129
|
+
function createServer2(handler, options = {}) {
|
|
23130
|
+
const server = createHttpServer(handler);
|
|
23131
|
+
const preferredPort = options.port;
|
|
23132
|
+
let port = null;
|
|
23133
|
+
let started = false;
|
|
23134
|
+
async function listenOn(candidatePort) {
|
|
23135
|
+
await new Promise((resolve9, reject) => {
|
|
23136
|
+
server.once("error", reject);
|
|
23137
|
+
server.listen(candidatePort, "127.0.0.1", () => {
|
|
23138
|
+
server.off("error", reject);
|
|
23139
|
+
resolve9();
|
|
23140
|
+
});
|
|
23141
|
+
});
|
|
23142
|
+
}
|
|
23143
|
+
return {
|
|
23144
|
+
get port() {
|
|
23145
|
+
if (port === null)
|
|
23146
|
+
throw new HttpServerError("not-started", "Server not started");
|
|
23147
|
+
return port;
|
|
23148
|
+
},
|
|
23149
|
+
async start() {
|
|
23150
|
+
if (started) return;
|
|
23151
|
+
if (preferredPort !== void 0) {
|
|
23152
|
+
try {
|
|
23153
|
+
await listenOn(preferredPort);
|
|
23154
|
+
port = preferredPort;
|
|
23155
|
+
started = true;
|
|
23156
|
+
return;
|
|
23157
|
+
} catch (e) {
|
|
23158
|
+
throw new HttpServerError(
|
|
23159
|
+
"listen-failed",
|
|
23160
|
+
`Failed to start server on port ${preferredPort}: ${e}`,
|
|
23161
|
+
e
|
|
23162
|
+
);
|
|
23163
|
+
}
|
|
23164
|
+
}
|
|
23165
|
+
const maxRetries = 8;
|
|
23166
|
+
let lastError = null;
|
|
23167
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
23168
|
+
const candidatePort = randomInt(1024, 65535);
|
|
23169
|
+
try {
|
|
23170
|
+
await listenOn(candidatePort);
|
|
23171
|
+
port = candidatePort;
|
|
23172
|
+
started = true;
|
|
23173
|
+
return;
|
|
23174
|
+
} catch (e) {
|
|
23175
|
+
lastError = e;
|
|
23176
|
+
const code = e.code;
|
|
23177
|
+
if (code !== "EADDRINUSE" && code !== "EACCES") {
|
|
23178
|
+
throw new HttpServerError(
|
|
23179
|
+
"listen-failed",
|
|
23180
|
+
`Failed to start server: ${e}`,
|
|
23181
|
+
e
|
|
23182
|
+
);
|
|
22808
23183
|
}
|
|
22809
|
-
}
|
|
22810
|
-
operations.push(operation);
|
|
22811
|
-
} else if (rule.type === "mock") {
|
|
22812
|
-
const operation = {
|
|
22813
|
-
kind: "mock",
|
|
22814
|
-
groupId: group.id,
|
|
22815
|
-
ruleId: rule.id,
|
|
22816
|
-
matcher,
|
|
22817
|
-
mock: rule.mock ?? { status: 200, body: "" }
|
|
22818
|
-
};
|
|
22819
|
-
operations.push(operation);
|
|
22820
|
-
} else if (rule.type === "response-body") {
|
|
22821
|
-
const operation = {
|
|
22822
|
-
kind: "response-body",
|
|
22823
|
-
groupId: group.id,
|
|
22824
|
-
ruleId: rule.id,
|
|
22825
|
-
matcher,
|
|
22826
|
-
responseBody: rule.responseBody ?? { replacements: [] }
|
|
22827
|
-
};
|
|
22828
|
-
operations.push(operation);
|
|
22829
|
-
} else if (rule.type === "request-body") {
|
|
22830
|
-
const operation = {
|
|
22831
|
-
kind: "request-body",
|
|
22832
|
-
groupId: group.id,
|
|
22833
|
-
ruleId: rule.id,
|
|
22834
|
-
matcher,
|
|
22835
|
-
requestBody: rule.requestBody ?? { mode: "replace", body: "" }
|
|
22836
|
-
};
|
|
22837
|
-
operations.push(operation);
|
|
22838
|
-
} else {
|
|
22839
|
-
const operation = {
|
|
22840
|
-
kind: "matcher",
|
|
22841
|
-
groupId: group.id,
|
|
22842
|
-
ruleId: rule.id,
|
|
22843
|
-
matcher
|
|
22844
|
-
};
|
|
22845
|
-
operations.push(operation);
|
|
23184
|
+
}
|
|
22846
23185
|
}
|
|
23186
|
+
throw new HttpServerError(
|
|
23187
|
+
"port-exhausted",
|
|
23188
|
+
`Failed to bind to port after ${maxRetries} attempts`,
|
|
23189
|
+
lastError ?? void 0
|
|
23190
|
+
);
|
|
23191
|
+
},
|
|
23192
|
+
async stop() {
|
|
23193
|
+
if (!started) return;
|
|
23194
|
+
await new Promise((resolve9) => {
|
|
23195
|
+
server.close(() => resolve9());
|
|
23196
|
+
});
|
|
23197
|
+
started = false;
|
|
22847
23198
|
}
|
|
22848
|
-
}
|
|
22849
|
-
return operations;
|
|
22850
|
-
}
|
|
22851
|
-
function compileProject(value) {
|
|
22852
|
-
const snapshot = snapshotOwnData3(value);
|
|
22853
|
-
if (!snapshot.valid) {
|
|
22854
|
-
return {
|
|
22855
|
-
ok: false,
|
|
22856
|
-
operations: [],
|
|
22857
|
-
diagnostics: mapValidationIssues([
|
|
22858
|
-
{
|
|
22859
|
-
instancePath: "",
|
|
22860
|
-
keyword: "ownProperties",
|
|
22861
|
-
message: "must contain only own data properties",
|
|
22862
|
-
params: {}
|
|
22863
|
-
}
|
|
22864
|
-
])
|
|
22865
|
-
};
|
|
22866
|
-
}
|
|
22867
|
-
let validation;
|
|
22868
|
-
try {
|
|
22869
|
-
validation = validateProjectDetailed(snapshot.value);
|
|
22870
|
-
} catch {
|
|
22871
|
-
return { ok: false, operations: [], diagnostics: [invariantDiagnostic()] };
|
|
22872
|
-
}
|
|
22873
|
-
if (!validation.valid) {
|
|
22874
|
-
return {
|
|
22875
|
-
ok: false,
|
|
22876
|
-
operations: [],
|
|
22877
|
-
diagnostics: mapValidationIssues(validation.errors)
|
|
22878
|
-
};
|
|
22879
|
-
}
|
|
22880
|
-
try {
|
|
22881
|
-
return {
|
|
22882
|
-
ok: true,
|
|
22883
|
-
operations: compileOperations(validation.data),
|
|
22884
|
-
diagnostics: []
|
|
22885
|
-
};
|
|
22886
|
-
} catch (error) {
|
|
22887
|
-
const path2 = error instanceof CompilerInvariantError ? error.path : "";
|
|
22888
|
-
return {
|
|
22889
|
-
ok: false,
|
|
22890
|
-
operations: [],
|
|
22891
|
-
diagnostics: [invariantDiagnostic(path2)]
|
|
22892
|
-
};
|
|
22893
|
-
}
|
|
23199
|
+
};
|
|
22894
23200
|
}
|
|
22895
23201
|
|
|
23202
|
+
// packages/cli/src/server/routes.ts
|
|
23203
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
23204
|
+
import { readFile as readFile4 } from "node:fs/promises";
|
|
23205
|
+
import { resolve as resolve2 } from "node:path";
|
|
23206
|
+
|
|
22896
23207
|
// packages/dry-run/dist/node/index.js
|
|
22897
23208
|
function parseTestUrl(input) {
|
|
22898
23209
|
if (typeof input !== "string" || input.length === 0) {
|
|
@@ -23921,10 +24232,7 @@ async function editCommand(args, options = {}) {
|
|
|
23921
24232
|
} else if (!arg.startsWith("-")) {
|
|
23922
24233
|
positionalArgs.push(arg);
|
|
23923
24234
|
} else if (arg === "--help") {
|
|
23924
|
-
|
|
23925
|
-
Options:
|
|
23926
|
-
--port <n> Fixed port (default: random)
|
|
23927
|
-
--help Show help`);
|
|
24235
|
+
showEditHelp();
|
|
23928
24236
|
return { exitCode: Promise.resolve(0), shutdown: () => {
|
|
23929
24237
|
} };
|
|
23930
24238
|
} else {
|
|
@@ -24242,40 +24550,8 @@ function generateEditorHtml(apiBase, csrfToken, filePath, aiConfigured) {
|
|
|
24242
24550
|
}
|
|
24243
24551
|
|
|
24244
24552
|
// packages/cli/src/commands/runtime.ts
|
|
24553
|
+
import { chmod, mkdir as mkdir4, rm as rm2, writeFile as writeFile4 } from "node:fs/promises";
|
|
24245
24554
|
import { dirname as dirname7, isAbsolute, join as join6, relative, resolve as resolve5, sep as sep2 } from "node:path";
|
|
24246
|
-
function showRuntimeHelp() {
|
|
24247
|
-
console.log(`Usage: rogatio runtime <command> [options]
|
|
24248
|
-
rogatio runtime host [path]
|
|
24249
|
-
|
|
24250
|
-
Native messaging runtime control for response-body and request-body rules. The
|
|
24251
|
-
runtime no longer serves an HTTP mock server; mock delivery happens in the
|
|
24252
|
-
consolidated native-messaging host (spec REQ-001..REQ-005).
|
|
24253
|
-
|
|
24254
|
-
Request-body trust commands:
|
|
24255
|
-
install Install the native-messaging host manifest and (on capable
|
|
24256
|
-
platforms) provision the device-local CA (requires --extension-id).
|
|
24257
|
-
CA trust requires root/admin: Linux (sudo), macOS (keychain
|
|
24258
|
-
password), Windows (Administrator).
|
|
24259
|
-
uninstall Remove the native-messaging host manifest and the device-local CA trust (idempotent)
|
|
24260
|
-
|
|
24261
|
-
Native host command:
|
|
24262
|
-
host [path] Run the consolidated native-messaging runtime host. The browser
|
|
24263
|
-
launches this process via the native-messaging manifest; it reads
|
|
24264
|
-
pairing/authorization/mock envelopes from stdin and writes
|
|
24265
|
-
responses to stdout.
|
|
24266
|
-
|
|
24267
|
-
The lifecycle of the runtime (start/stop) is driven from the extension's
|
|
24268
|
-
Start/Stop controls, not the CLI. Run 'rogatio runtime install --extension-id
|
|
24269
|
-
<id>' once to register the host, then use the extension.
|
|
24270
|
-
|
|
24271
|
-
Options:
|
|
24272
|
-
--extension-id Extension ID for native messaging manifest (required for install)
|
|
24273
|
-
--root <dir> Root for confined file mocks (default: project directory)
|
|
24274
|
-
--help, -h Show this help
|
|
24275
|
-
|
|
24276
|
-
The device-local CA / PAC routing capability is invoked from the unified
|
|
24277
|
-
install command on capable platforms; it does not have a separate verb.`);
|
|
24278
|
-
}
|
|
24279
24555
|
function toMatcherOperations2(operations) {
|
|
24280
24556
|
return operations.map(({ groupId, ruleId, matcher }) => ({
|
|
24281
24557
|
kind: "matcher",
|
|
@@ -24319,6 +24595,28 @@ function buildMockConfigs(operations, root) {
|
|
|
24319
24595
|
}
|
|
24320
24596
|
return { ok: true, value: configs };
|
|
24321
24597
|
}
|
|
24598
|
+
function generateHostWrapper(rogatioCommand, logPath) {
|
|
24599
|
+
const parts = rogatioCommand.split(" ");
|
|
24600
|
+
const nodeBin = parts[0] ?? "node";
|
|
24601
|
+
const cliScript = parts.slice(1).join(" ");
|
|
24602
|
+
return `#!/bin/sh
|
|
24603
|
+
# Rogatio native messaging host wrapper \u2014 generated by \`rogatio runtime install\`
|
|
24604
|
+
# Chrome launches this script when the extension connects via connectNative().
|
|
24605
|
+
# The extension sends the project data as the first envelope after launch.
|
|
24606
|
+
# stderr is redirected to a log file for debugging.
|
|
24607
|
+
exec "${nodeBin}" "${cliScript}" runtime host 2>>"${logPath}"
|
|
24608
|
+
`;
|
|
24609
|
+
}
|
|
24610
|
+
async function writeHostWrapper(hostPath, installRoot) {
|
|
24611
|
+
await mkdir4(installRoot, { recursive: true });
|
|
24612
|
+
const logPath = join6(installRoot, "runtime-host.log");
|
|
24613
|
+
const script = generateHostWrapper(
|
|
24614
|
+
`${process.execPath} ${process.argv[1] ?? "rogatio"}`,
|
|
24615
|
+
logPath
|
|
24616
|
+
);
|
|
24617
|
+
await writeFile4(hostPath, script, "utf8");
|
|
24618
|
+
await chmod(hostPath, 493);
|
|
24619
|
+
}
|
|
24322
24620
|
function makeTrustController() {
|
|
24323
24621
|
const platform = process.platform;
|
|
24324
24622
|
const adapter = selectTrustPlatformAdapter(platform);
|
|
@@ -24356,7 +24654,7 @@ function reportTrust(subcommand, result, okMessage) {
|
|
|
24356
24654
|
console.error(hint);
|
|
24357
24655
|
}
|
|
24358
24656
|
}
|
|
24359
|
-
return
|
|
24657
|
+
return 1;
|
|
24360
24658
|
}
|
|
24361
24659
|
console.error(
|
|
24362
24660
|
`runtime ${subcommand} failed: ${(result.reasons ?? ["unknown"]).join(", ")}`
|
|
@@ -24382,19 +24680,85 @@ async function trustRuntimeCommand(args) {
|
|
|
24382
24680
|
}
|
|
24383
24681
|
}
|
|
24384
24682
|
const controller = makeTrustController();
|
|
24683
|
+
const installRoot = defaultTrustInstallRoot(process.platform);
|
|
24684
|
+
const hostPath = join6(installRoot, "runtime-host");
|
|
24385
24685
|
switch (subcommand) {
|
|
24386
|
-
case "install":
|
|
24686
|
+
case "install": {
|
|
24687
|
+
const installResult = await controller.install(extensionId ?? "");
|
|
24688
|
+
if (installResult.ok) {
|
|
24689
|
+
try {
|
|
24690
|
+
await writeHostWrapper(hostPath, installRoot);
|
|
24691
|
+
} catch (error) {
|
|
24692
|
+
console.error(
|
|
24693
|
+
`Warning: manifest and CA trusted, but failed to create runtime-host wrapper: ${error instanceof Error ? error.message : String(error)}`
|
|
24694
|
+
);
|
|
24695
|
+
}
|
|
24696
|
+
}
|
|
24387
24697
|
return reportTrust(
|
|
24388
24698
|
"install",
|
|
24389
|
-
|
|
24390
|
-
"runtime install complete: manifest + device-local CA trusted"
|
|
24699
|
+
installResult,
|
|
24700
|
+
"runtime install complete: manifest + device-local CA trusted + runtime-host wrapper created\nNote: reload the extension at chrome://extensions for the native host to be detected."
|
|
24391
24701
|
);
|
|
24392
|
-
|
|
24702
|
+
}
|
|
24703
|
+
case "uninstall": {
|
|
24704
|
+
const uninstallResult = await controller.uninstall();
|
|
24705
|
+
try {
|
|
24706
|
+
await rm2(hostPath, { force: true });
|
|
24707
|
+
} catch {
|
|
24708
|
+
}
|
|
24709
|
+
try {
|
|
24710
|
+
await rm2(join6(installRoot, "runtime-host.log"), { force: true });
|
|
24711
|
+
} catch {
|
|
24712
|
+
}
|
|
24393
24713
|
return reportTrust(
|
|
24394
24714
|
"uninstall",
|
|
24395
|
-
|
|
24715
|
+
uninstallResult,
|
|
24396
24716
|
"runtime uninstall complete: manifest + device-local CA removed"
|
|
24397
24717
|
);
|
|
24718
|
+
}
|
|
24719
|
+
case "verify": {
|
|
24720
|
+
const result = await controller.verify();
|
|
24721
|
+
if (result.ok) {
|
|
24722
|
+
console.log(
|
|
24723
|
+
"runtime verification passed: manifest, binary, and CA trust all OK"
|
|
24724
|
+
);
|
|
24725
|
+
return 0;
|
|
24726
|
+
}
|
|
24727
|
+
console.error("runtime verification failed:");
|
|
24728
|
+
if (!result.manifestExists) console.error(" manifest not found");
|
|
24729
|
+
if (result.manifestExists && !result.manifestValid)
|
|
24730
|
+
console.error(" manifest invalid");
|
|
24731
|
+
if (!result.binaryExists)
|
|
24732
|
+
console.error(" runtime-host binary not found");
|
|
24733
|
+
if (result.binaryExists && !result.binaryExecutable)
|
|
24734
|
+
console.error(" runtime-host binary not executable");
|
|
24735
|
+
if (result.allowedOriginsCount === 0)
|
|
24736
|
+
console.error(" no allowed_origins in manifest");
|
|
24737
|
+
if (!result.caTrusted) console.error(" device-local CA not trusted");
|
|
24738
|
+
for (const reason of result.reasons) {
|
|
24739
|
+
if (reason === "manifest-not-found")
|
|
24740
|
+
console.error(
|
|
24741
|
+
" Hint: run 'rogatio runtime install --extension-id <id>' to register the host manifest"
|
|
24742
|
+
);
|
|
24743
|
+
if (reason === "binary-not-found")
|
|
24744
|
+
console.error(
|
|
24745
|
+
` Hint: ensure the runtime-host binary exists at the path specified in the manifest`
|
|
24746
|
+
);
|
|
24747
|
+
if (reason === "binary-not-executable")
|
|
24748
|
+
console.error(
|
|
24749
|
+
" Hint: ensure the runtime-host binary has execute permission (chmod +x)"
|
|
24750
|
+
);
|
|
24751
|
+
if (reason === "no-allowed-origins")
|
|
24752
|
+
console.error(
|
|
24753
|
+
" Hint: re-run 'rogatio runtime install --extension-id <id>' to pin your extension ID"
|
|
24754
|
+
);
|
|
24755
|
+
if (reason === "ca-not-trusted")
|
|
24756
|
+
console.error(
|
|
24757
|
+
" Hint: re-run 'rogatio runtime install --extension-id <id>' with elevated privileges to trust the CA"
|
|
24758
|
+
);
|
|
24759
|
+
}
|
|
24760
|
+
return 1;
|
|
24761
|
+
}
|
|
24398
24762
|
default:
|
|
24399
24763
|
console.error(`Error: unknown runtime subcommand: ${subcommand ?? ""}`);
|
|
24400
24764
|
showRuntimeHelp();
|
|
@@ -24432,6 +24796,16 @@ async function runtimeHostCommand(args) {
|
|
|
24432
24796
|
return 2;
|
|
24433
24797
|
}
|
|
24434
24798
|
const inputPath = positional[0];
|
|
24799
|
+
if (!inputPath) {
|
|
24800
|
+
await runNativeHost({
|
|
24801
|
+
fileRoot: root,
|
|
24802
|
+
...mockPort !== void 0 ? { mockPort } : {},
|
|
24803
|
+
onReady: () => console.error(
|
|
24804
|
+
"rogatio runtime-host active (deferred, waiting for project)"
|
|
24805
|
+
)
|
|
24806
|
+
});
|
|
24807
|
+
return 0;
|
|
24808
|
+
}
|
|
24435
24809
|
let filePath;
|
|
24436
24810
|
let projectData;
|
|
24437
24811
|
try {
|
|
@@ -24443,7 +24817,7 @@ async function runtimeHostCommand(args) {
|
|
|
24443
24817
|
filePath = "<stdin>";
|
|
24444
24818
|
projectData = JSON.parse(chunks.join(""));
|
|
24445
24819
|
} else {
|
|
24446
|
-
filePath =
|
|
24820
|
+
filePath = resolve5(inputPath);
|
|
24447
24821
|
projectData = await readProject(filePath);
|
|
24448
24822
|
}
|
|
24449
24823
|
} catch (error) {
|
|
@@ -24500,7 +24874,7 @@ async function runtimeCommand(args, _options = {}) {
|
|
|
24500
24874
|
return 0;
|
|
24501
24875
|
}
|
|
24502
24876
|
const first = args[0];
|
|
24503
|
-
if (first === "install" || first === "trust" || first === "uninstall")
|
|
24877
|
+
if (first === "install" || first === "trust" || first === "uninstall" || first === "verify")
|
|
24504
24878
|
return trustRuntimeCommand(args);
|
|
24505
24879
|
if (first === "host") return runtimeHostCommand(args.slice(1));
|
|
24506
24880
|
console.error(
|
|
@@ -24994,7 +25368,7 @@ async function handleVerify(args) {
|
|
|
24994
25368
|
}
|
|
24995
25369
|
async function handleRuntime(args) {
|
|
24996
25370
|
if (args.includes("--help") || args.includes("-h")) {
|
|
24997
|
-
|
|
25371
|
+
showRuntimeHelp();
|
|
24998
25372
|
return 0;
|
|
24999
25373
|
}
|
|
25000
25374
|
const result = await runtimeCommand(args);
|
|
@@ -25002,7 +25376,7 @@ async function handleRuntime(args) {
|
|
|
25002
25376
|
}
|
|
25003
25377
|
async function handleAI(args) {
|
|
25004
25378
|
if (args.includes("--help") || args.includes("-h")) {
|
|
25005
|
-
|
|
25379
|
+
showAIHelp();
|
|
25006
25380
|
return 0;
|
|
25007
25381
|
}
|
|
25008
25382
|
return await aiCommand(args);
|
|
@@ -25039,8 +25413,8 @@ Commands:
|
|
|
25039
25413
|
edit [path] Launch browser editor for .rogatio.json
|
|
25040
25414
|
test [path] [url...] Run offline dry-run tests against .rogatio.json
|
|
25041
25415
|
verify [path] Validate .rogatio.json file
|
|
25042
|
-
runtime <install|uninstall|host> Native messaging runtime control
|
|
25043
|
-
runtime host [path] Run the
|
|
25416
|
+
runtime <install|uninstall|host> Native messaging runtime control
|
|
25417
|
+
runtime host [path] Run the native-messaging runtime host
|
|
25044
25418
|
|
|
25045
25419
|
Global Options:
|
|
25046
25420
|
--help, -h Show help
|
|
@@ -25049,122 +25423,6 @@ Global Options:
|
|
|
25049
25423
|
Run 'rogatio <command> --help' for command-specific help.`);
|
|
25050
25424
|
return 0;
|
|
25051
25425
|
}
|
|
25052
|
-
function showEditHelp() {
|
|
25053
|
-
console.log(`Usage: rogatio edit [options] [path]
|
|
25054
|
-
|
|
25055
|
-
Launch browser-based editor for .rogatio.json project file.
|
|
25056
|
-
|
|
25057
|
-
Arguments:
|
|
25058
|
-
path Path to .rogatio.json (default: .rogatio.json in current directory)
|
|
25059
|
-
|
|
25060
|
-
Options:
|
|
25061
|
-
--port <n> Fixed port for editor server (default: random)
|
|
25062
|
-
--help, -h Show this help
|
|
25063
|
-
|
|
25064
|
-
The editor runs in your default browser and communicates with a local server
|
|
25065
|
-
bound to 127.0.0.1. Changes are saved atomically to the project file.`);
|
|
25066
|
-
}
|
|
25067
|
-
function showVerifyHelp() {
|
|
25068
|
-
console.log(`Usage: rogatio verify [options] [path]
|
|
25069
|
-
|
|
25070
|
-
Validate a .rogatio.json project file using schema and compiler.
|
|
25071
|
-
|
|
25072
|
-
Arguments:
|
|
25073
|
-
path Path to .rogatio.json (default: .rogatio.json in current directory)
|
|
25074
|
-
Use '-' to read from stdin
|
|
25075
|
-
|
|
25076
|
-
Options:
|
|
25077
|
-
--json Output diagnostics as JSON
|
|
25078
|
-
--help, -h Show this help
|
|
25079
|
-
|
|
25080
|
-
Exit codes:
|
|
25081
|
-
0 Valid (no diagnostics)
|
|
25082
|
-
1 Invalid (diagnostics present)
|
|
25083
|
-
2 Error (IO, parse, or unexpected failure)`);
|
|
25084
|
-
}
|
|
25085
|
-
function showRuntimeHelp2() {
|
|
25086
|
-
console.log(`Usage: rogatio runtime <command> [options]
|
|
25087
|
-
rogatio runtime host [path]
|
|
25088
|
-
|
|
25089
|
-
Native messaging runtime control for response-body and request-body rules. The
|
|
25090
|
-
runtime no longer serves an HTTP mock server; mock delivery happens in the
|
|
25091
|
-
consolidated native-messaging host (spec REQ-001..REQ-005).
|
|
25092
|
-
|
|
25093
|
-
Request-body trust commands:
|
|
25094
|
-
install Install the native-messaging host manifest and (on capable
|
|
25095
|
-
platforms) provision the device-local CA (requires --extension-id)
|
|
25096
|
-
uninstall Remove the native-messaging host manifest and the device-local CA trust (idempotent)
|
|
25097
|
-
|
|
25098
|
-
Native host command:
|
|
25099
|
-
host [path] Run the consolidated native-messaging runtime host. The browser
|
|
25100
|
-
launches this process via the native-messaging manifest; it reads
|
|
25101
|
-
pairing/authorization/mock envelopes from stdin and writes
|
|
25102
|
-
responses to stdout.
|
|
25103
|
-
|
|
25104
|
-
The lifecycle of the runtime (start/stop) is driven from the extension's
|
|
25105
|
-
Start/Stop controls, not the CLI. Run 'rogatio runtime install --extension-id
|
|
25106
|
-
<id>' once to register the host, then use the extension.
|
|
25107
|
-
|
|
25108
|
-
Options:
|
|
25109
|
-
--root <dir> Root for confined file mocks (default: project directory)
|
|
25110
|
-
--extension-id Extension ID for native messaging manifest (required for install)
|
|
25111
|
-
--help, -h Show this help
|
|
25112
|
-
|
|
25113
|
-
The device-local CA / PAC routing capability is invoked from the unified
|
|
25114
|
-
install command on capable platforms; it does not have a separate verb.
|
|
25115
|
-
|
|
25116
|
-
Exit codes:
|
|
25117
|
-
0 Stopped cleanly / success
|
|
25118
|
-
1 Invalid project (diagnostics present) or file outside the root
|
|
25119
|
-
2 Error (IO or usage)`);
|
|
25120
|
-
}
|
|
25121
|
-
function showTestHelp() {
|
|
25122
|
-
console.log(`Usage: rogatio test [options] [path] [url...]
|
|
25123
|
-
|
|
25124
|
-
Run offline dry-run tests against a .rogatio.json project file.
|
|
25125
|
-
|
|
25126
|
-
Arguments:
|
|
25127
|
-
path Path to .rogatio.json (default: .rogatio.json in current directory)
|
|
25128
|
-
Use '-' to read project JSON from stdin
|
|
25129
|
-
url... URLs to test; when path is omitted, first URL is detected automatically
|
|
25130
|
-
|
|
25131
|
-
Options:
|
|
25132
|
-
--urls <list> Comma-separated list of URLs to test
|
|
25133
|
-
--urls-file <path> Path to JSON file containing array of test cases
|
|
25134
|
-
Each case: { "url": "...", "method"?: "...", "resourceType"?: "..." }
|
|
25135
|
-
Use '-' to read from stdin
|
|
25136
|
-
--method <m> Default HTTP method for all test cases (GET, POST, etc.)
|
|
25137
|
-
--resource-type <t> Default resource type for all test cases
|
|
25138
|
-
--max-cases <n> Maximum number of test cases (default: 256)
|
|
25139
|
-
--json Output results as JSON
|
|
25140
|
-
--help, -h Show this help
|
|
25141
|
-
|
|
25142
|
-
Test case format (JSON):
|
|
25143
|
-
[
|
|
25144
|
-
{ "url": "https://example.com/", "method": "GET", "resourceType": "main_frame" },
|
|
25145
|
-
{ "url": "https://example.com/script.js" }
|
|
25146
|
-
]
|
|
25147
|
-
|
|
25148
|
-
Exit codes:
|
|
25149
|
-
0 Success (all valid, results may include non-matches)
|
|
25150
|
-
1 Validation/compile/test errors
|
|
25151
|
-
2 Usage error (invalid arguments, missing input)`);
|
|
25152
|
-
}
|
|
25153
|
-
function showAIHelp2() {
|
|
25154
|
-
console.log(`Usage: rogatio ai <command> [options]
|
|
25155
|
-
|
|
25156
|
-
AI provider configuration commands.
|
|
25157
|
-
|
|
25158
|
-
Commands:
|
|
25159
|
-
setup Interactively configure AI provider (URL, model, API key)
|
|
25160
|
-
ls List configured AI provider
|
|
25161
|
-
show Show current AI provider configuration (key redacted)
|
|
25162
|
-
delete Delete AI provider configuration
|
|
25163
|
-
test Test connection to AI provider
|
|
25164
|
-
|
|
25165
|
-
Options:
|
|
25166
|
-
--help, -h Show this help`);
|
|
25167
|
-
}
|
|
25168
25426
|
if (process.argv[1] !== void 0 && realpathSync.native(fileURLToPath2(import.meta.url)) === realpathSync.native(resolve8(process.argv[1]))) {
|
|
25169
25427
|
if (basename4(process.argv[1]) === "runtime-host") {
|
|
25170
25428
|
runRuntimeHostEntry().catch((err) => {
|