synartesis 0.6.12 → 0.6.13
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/CHANGELOG.md +168 -0
- package/README.md +103 -2
- package/dist/{chunk-FUVEHRJP.js → chunk-6X7GPUGJ.js} +231 -46
- package/dist/cli.js +192 -43
- package/dist/proxy.js +21 -3
- package/manifests/filesystem.yaml +8 -0
- package/manifests/git.yaml +8 -0
- package/manifests/github.yaml +10 -0
- package/manifests/memory.yaml +18 -0
- package/package.json +3 -2
|
@@ -257,6 +257,19 @@ CREATE INDEX IF NOT EXISTS actions_by_run ON actions(run_id, seq);
|
|
|
257
257
|
-- two-kilobyte snapshots: 61ms to 0.01ms, and 56ms to 0.00ms.
|
|
258
258
|
CREATE INDEX IF NOT EXISTS actions_approved ON actions(server, tool, status, approved_at);
|
|
259
259
|
CREATE INDEX IF NOT EXISTS actions_gated ON actions(status, ts);
|
|
260
|
+
|
|
261
|
+
-- Covering, and that is the whole point. listRuns needs a count and three
|
|
262
|
+
-- status tallies per run, and without this the group-by scans the table --
|
|
263
|
+
-- which carries the snapshots, so the cost of listing sessions grew with the
|
|
264
|
+
-- size of the data those sessions touched, not with how many there were.
|
|
265
|
+
-- Adding status to the run index lets sqlite answer entirely from the index.
|
|
266
|
+
-- Measured on forty runs of five hundred actions with two-kilobyte snapshots,
|
|
267
|
+
-- a hundred-megabyte journal: 76ms to 2ms.
|
|
268
|
+
--
|
|
269
|
+
-- Added the same way as the two above and for the same reason: no row changes,
|
|
270
|
+
-- no meaning changes, IF NOT EXISTS makes it idempotent, and an older build
|
|
271
|
+
-- opening the same file afterwards neither notices nor cares.
|
|
272
|
+
CREATE INDEX IF NOT EXISTS actions_run_status ON actions(run_id, status);
|
|
260
273
|
`;
|
|
261
274
|
|
|
262
275
|
// src/journal/journal.ts
|
|
@@ -268,6 +281,13 @@ var runSchema = z.object({
|
|
|
268
281
|
ended_at: z.string().nullable(),
|
|
269
282
|
status: z.enum(["active", "complete", "rolled_back", "partial"])
|
|
270
283
|
});
|
|
284
|
+
var tallySchema = z.object({
|
|
285
|
+
run_id: z.string(),
|
|
286
|
+
actions: z.number(),
|
|
287
|
+
unknown: z.number().nullable(),
|
|
288
|
+
waiting: z.number().nullable(),
|
|
289
|
+
applied: z.number().nullable()
|
|
290
|
+
});
|
|
271
291
|
var actionSchema = z.object({
|
|
272
292
|
id: z.string(),
|
|
273
293
|
run_id: z.string(),
|
|
@@ -699,6 +719,30 @@ var SqliteJournal = class {
|
|
|
699
719
|
() => this.#db.prepare("SELECT * FROM actions WHERE run_id = ? ORDER BY seq").all(runId).map(toAction)
|
|
700
720
|
);
|
|
701
721
|
}
|
|
722
|
+
tallyRuns() {
|
|
723
|
+
return this.#run("tallyRuns", () => {
|
|
724
|
+
const rows = this.#db.prepare(
|
|
725
|
+
`SELECT run_id,
|
|
726
|
+
COUNT(*) AS actions,
|
|
727
|
+
SUM(status = 'pending') AS unknown,
|
|
728
|
+
SUM(status = 'gated') AS waiting,
|
|
729
|
+
SUM(status = 'applied') AS applied
|
|
730
|
+
FROM actions
|
|
731
|
+
GROUP BY run_id`
|
|
732
|
+
).all();
|
|
733
|
+
const tally = /* @__PURE__ */ new Map();
|
|
734
|
+
for (const row of rows) {
|
|
735
|
+
const counts = tallySchema.parse(row);
|
|
736
|
+
tally.set(counts.run_id, {
|
|
737
|
+
actions: counts.actions,
|
|
738
|
+
unknown: counts.unknown ?? 0,
|
|
739
|
+
waiting: counts.waiting ?? 0,
|
|
740
|
+
applied: counts.applied ?? 0
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
return tally;
|
|
744
|
+
});
|
|
745
|
+
}
|
|
702
746
|
recentActions(limit) {
|
|
703
747
|
return this.#run(
|
|
704
748
|
"recentActions",
|
|
@@ -973,17 +1017,20 @@ var toolPolicy = z2.strictObject({
|
|
|
973
1017
|
gate: z2.enum(["always", "on_write", "never"]).optional(),
|
|
974
1018
|
refusal: z2.enum(["uncertain", "clean"]).optional(),
|
|
975
1019
|
snapshot: callTemplate.optional(),
|
|
976
|
-
inverse: callTemplate.optional()
|
|
1020
|
+
inverse: callTemplate.optional(),
|
|
1021
|
+
verify: callTemplate.optional()
|
|
977
1022
|
});
|
|
978
1023
|
var serverSpec = z2.strictObject({
|
|
979
1024
|
command: z2.string().min(1),
|
|
980
1025
|
args: z2.array(z2.string()).default([]),
|
|
981
|
-
env: z2.record(z2.string(), z2.string()).optional()
|
|
1026
|
+
env: z2.record(z2.string(), z2.string()).optional(),
|
|
1027
|
+
provenance: z2.enum(["live", "documented"]).optional()
|
|
982
1028
|
});
|
|
983
1029
|
var manifestSchema = z2.strictObject({
|
|
984
1030
|
version: z2.literal(1),
|
|
985
1031
|
servers: z2.record(z2.string(), serverSpec),
|
|
986
|
-
tools: z2.array(toolPolicy).default([])
|
|
1032
|
+
tools: z2.array(toolPolicy).default([]),
|
|
1033
|
+
pins: z2.record(z2.string(), z2.record(z2.string(), z2.string().min(1))).optional()
|
|
987
1034
|
});
|
|
988
1035
|
var Source = class {
|
|
989
1036
|
constructor(doc, lines, file) {
|
|
@@ -1066,6 +1113,11 @@ function validate(source, manifest) {
|
|
|
1066
1113
|
if (servers.length === 0) {
|
|
1067
1114
|
source.fail(["servers"], "at least one server must be declared");
|
|
1068
1115
|
}
|
|
1116
|
+
for (const name of Object.keys(manifest.pins ?? {})) {
|
|
1117
|
+
if (!servers.includes(name)) {
|
|
1118
|
+
source.fail(["pins", name], `pins name server ${name}, which is not declared`);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1069
1121
|
const seen = /* @__PURE__ */ new Map();
|
|
1070
1122
|
manifest.tools.forEach((policy, index) => {
|
|
1071
1123
|
const path = ["tools", index];
|
|
@@ -1087,6 +1139,9 @@ function validate(source, manifest) {
|
|
|
1087
1139
|
`${policy.match} names server ${segment}, which is not declared`
|
|
1088
1140
|
);
|
|
1089
1141
|
}
|
|
1142
|
+
if (policy.verify !== void 0 && policy.class === "readonly") {
|
|
1143
|
+
source.fail([...path, "verify"], "a readonly tool has no post-state to check for drift");
|
|
1144
|
+
}
|
|
1090
1145
|
const needsInverse = policy.class === "reversible" || policy.class === "compensable";
|
|
1091
1146
|
if (needsInverse && policy.inverse === void 0) {
|
|
1092
1147
|
source.fail(path, `a ${policy.class} tool must declare an inverse`);
|
|
@@ -1130,7 +1185,8 @@ function withGate(policy) {
|
|
|
1130
1185
|
gate,
|
|
1131
1186
|
refusal: policy.refusal ?? "uncertain",
|
|
1132
1187
|
...policy.snapshot === void 0 ? {} : { snapshot: toCall(policy.snapshot) },
|
|
1133
|
-
...policy.inverse === void 0 ? {} : { inverse: toCall(policy.inverse) }
|
|
1188
|
+
...policy.inverse === void 0 ? {} : { inverse: toCall(policy.inverse) },
|
|
1189
|
+
...policy.verify === void 0 ? {} : { verify: toCall(policy.verify) }
|
|
1134
1190
|
};
|
|
1135
1191
|
}
|
|
1136
1192
|
function parseManifest(text, file) {
|
|
@@ -1166,11 +1222,13 @@ function parseManifest(text, file) {
|
|
|
1166
1222
|
{
|
|
1167
1223
|
command: spec.command,
|
|
1168
1224
|
args: spec.args,
|
|
1169
|
-
...spec.env === void 0 ? {} : { env: expandEnvironment(source, ["servers", name], spec.env) }
|
|
1225
|
+
...spec.env === void 0 ? {} : { env: expandEnvironment(source, ["servers", name], spec.env) },
|
|
1226
|
+
...spec.provenance === void 0 ? {} : { provenance: spec.provenance }
|
|
1170
1227
|
}
|
|
1171
1228
|
])
|
|
1172
1229
|
),
|
|
1173
|
-
tools: parsed.data.tools.map(withGate)
|
|
1230
|
+
tools: parsed.data.tools.map(withGate),
|
|
1231
|
+
...parsed.data.pins === void 0 ? {} : { pins: parsed.data.pins }
|
|
1174
1232
|
};
|
|
1175
1233
|
validate(source, manifest);
|
|
1176
1234
|
return manifest;
|
|
@@ -1193,8 +1251,41 @@ function loadManifest(path) {
|
|
|
1193
1251
|
return parseManifest(text, path);
|
|
1194
1252
|
}
|
|
1195
1253
|
|
|
1196
|
-
// src/manifest/
|
|
1197
|
-
import {
|
|
1254
|
+
// src/manifest/pin.ts
|
|
1255
|
+
import { createHash } from "crypto";
|
|
1256
|
+
|
|
1257
|
+
// src/manifest/match.ts
|
|
1258
|
+
function toRegExp(pattern) {
|
|
1259
|
+
const source = pattern.split("*").map((literal) => literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("[^.]*");
|
|
1260
|
+
return new RegExp(`^${source}$`);
|
|
1261
|
+
}
|
|
1262
|
+
function literalLength(pattern) {
|
|
1263
|
+
return pattern.length - pattern.split("*").length + 1;
|
|
1264
|
+
}
|
|
1265
|
+
function failClosed(qualifiedName) {
|
|
1266
|
+
return { match: qualifiedName, class: "irreversible", gate: "always", refusal: "uncertain" };
|
|
1267
|
+
}
|
|
1268
|
+
function createPolicyResolver(manifest) {
|
|
1269
|
+
const compiled = manifest.tools.map((policy) => ({
|
|
1270
|
+
policy,
|
|
1271
|
+
test: toRegExp(policy.match),
|
|
1272
|
+
specificity: literalLength(policy.match),
|
|
1273
|
+
wildcards: policy.match.split("*").length - 1
|
|
1274
|
+
})).sort((a, b) => b.specificity - a.specificity || a.wildcards - b.wildcards);
|
|
1275
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1276
|
+
return {
|
|
1277
|
+
resolve(qualifiedName) {
|
|
1278
|
+
const cached2 = cache.get(qualifiedName);
|
|
1279
|
+
if (cached2 !== void 0) {
|
|
1280
|
+
return cached2;
|
|
1281
|
+
}
|
|
1282
|
+
const hit = compiled.find((candidate) => candidate.test.test(qualifiedName));
|
|
1283
|
+
const match = hit === void 0 ? { policy: failClosed(qualifiedName), matched: false } : { policy: hit.policy, matched: true };
|
|
1284
|
+
cache.set(qualifiedName, match);
|
|
1285
|
+
return match;
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1198
1289
|
|
|
1199
1290
|
// src/manifest/types.ts
|
|
1200
1291
|
function qualify(server, tool) {
|
|
@@ -1208,13 +1299,77 @@ function splitQualified(qualified) {
|
|
|
1208
1299
|
return { server: qualified.slice(0, dot), tool: qualified.slice(dot + 1) };
|
|
1209
1300
|
}
|
|
1210
1301
|
|
|
1302
|
+
// src/manifest/pin.ts
|
|
1303
|
+
function fingerprint(inputSchema) {
|
|
1304
|
+
return `sha256:${createHash("sha256").update(canonical(inputSchema)).digest("hex")}`;
|
|
1305
|
+
}
|
|
1306
|
+
function auditPins(server, advertised, manifest) {
|
|
1307
|
+
const pins = manifest.pins?.[server];
|
|
1308
|
+
if (pins === void 0) {
|
|
1309
|
+
return [];
|
|
1310
|
+
}
|
|
1311
|
+
const resolver = createPolicyResolver(manifest);
|
|
1312
|
+
const faults = [];
|
|
1313
|
+
const present = /* @__PURE__ */ new Set();
|
|
1314
|
+
for (const tool of advertised) {
|
|
1315
|
+
present.add(tool.name);
|
|
1316
|
+
if (!resolver.resolve(qualify(server, tool.name)).matched) {
|
|
1317
|
+
continue;
|
|
1318
|
+
}
|
|
1319
|
+
const found = fingerprint(tool.inputSchema);
|
|
1320
|
+
const pinned = pins[tool.name];
|
|
1321
|
+
if (pinned === void 0) {
|
|
1322
|
+
faults.push({ kind: "unpinned", tool: tool.name, found });
|
|
1323
|
+
} else if (pinned !== found) {
|
|
1324
|
+
faults.push({ kind: "moved", tool: tool.name, pinned, found });
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
for (const name of Object.keys(pins)) {
|
|
1328
|
+
if (!present.has(name)) {
|
|
1329
|
+
faults.push({ kind: "gone", tool: name });
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
return faults;
|
|
1333
|
+
}
|
|
1334
|
+
function pinBlock(shapes, manifest) {
|
|
1335
|
+
const resolver = createPolicyResolver(manifest);
|
|
1336
|
+
const lines = ["pins:"];
|
|
1337
|
+
for (const server of [...shapes.keys()].sort()) {
|
|
1338
|
+
const governed = (shapes.get(server) ?? []).filter((tool) => resolver.resolve(qualify(server, tool.name)).matched).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
1339
|
+
if (governed.length === 0) {
|
|
1340
|
+
continue;
|
|
1341
|
+
}
|
|
1342
|
+
lines.push(` ${server}:`);
|
|
1343
|
+
for (const tool of governed) {
|
|
1344
|
+
lines.push(` ${tool.name}: "${fingerprint(tool.inputSchema)}"`);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
return lines.join("\n");
|
|
1348
|
+
}
|
|
1349
|
+
function explainPins(server, faults) {
|
|
1350
|
+
return faults.map((fault) => {
|
|
1351
|
+
switch (fault.kind) {
|
|
1352
|
+
case "moved":
|
|
1353
|
+
return `${server}.${fault.tool} no longer has the shape it was pinned at. Its policy was written for the old one, so the snapshot and inverse it carries may no longer describe this tool.
|
|
1354
|
+
pinned ${fault.pinned}
|
|
1355
|
+
now ${fault.found}`;
|
|
1356
|
+
case "unpinned":
|
|
1357
|
+
return `${server}.${fault.tool} is governed by a policy and has no pin, on a server where everything else is pinned.
|
|
1358
|
+
add ${fault.tool}: "${fault.found}"`;
|
|
1359
|
+
case "gone":
|
|
1360
|
+
return `${server}.${fault.tool} is pinned but the server does not expose it, so the pin vouches for nothing. Remove it, or connect the server that has it.`;
|
|
1361
|
+
}
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1211
1365
|
// src/manifest/verify.ts
|
|
1366
|
+
import { z as z3 } from "zod";
|
|
1212
1367
|
var listSchema = z3.looseObject({
|
|
1213
|
-
tools: z3.array(z3.looseObject({ name: z3.string() })),
|
|
1368
|
+
tools: z3.array(z3.looseObject({ name: z3.string(), inputSchema: z3.unknown() })),
|
|
1214
1369
|
nextCursor: z3.string().optional()
|
|
1215
1370
|
});
|
|
1216
|
-
async function
|
|
1217
|
-
const
|
|
1371
|
+
async function toolShapes(upstream) {
|
|
1372
|
+
const shapes = [];
|
|
1218
1373
|
let cursor;
|
|
1219
1374
|
do {
|
|
1220
1375
|
const page = listSchema.parse(
|
|
@@ -1224,16 +1379,19 @@ async function toolNames(upstream) {
|
|
|
1224
1379
|
)
|
|
1225
1380
|
);
|
|
1226
1381
|
for (const tool of page.tools) {
|
|
1227
|
-
|
|
1382
|
+
shapes.push({ name: tool.name, inputSchema: tool.inputSchema });
|
|
1228
1383
|
}
|
|
1229
1384
|
cursor = page.nextCursor;
|
|
1230
1385
|
} while (cursor !== void 0);
|
|
1231
|
-
return
|
|
1386
|
+
return shapes;
|
|
1232
1387
|
}
|
|
1233
1388
|
async function verifyAgainstServers(upstreams, manifest) {
|
|
1389
|
+
const shapes = /* @__PURE__ */ new Map();
|
|
1234
1390
|
const available = /* @__PURE__ */ new Map();
|
|
1235
1391
|
for (const upstream of upstreams) {
|
|
1236
|
-
|
|
1392
|
+
const advertised = await toolShapes(upstream);
|
|
1393
|
+
shapes.set(upstream.name, advertised);
|
|
1394
|
+
available.set(upstream.name, new Set(advertised.map((tool) => tool.name)));
|
|
1237
1395
|
}
|
|
1238
1396
|
const problems = [];
|
|
1239
1397
|
const check = (qualified, role, match) => {
|
|
@@ -1259,11 +1417,50 @@ async function verifyAgainstServers(upstreams, manifest) {
|
|
|
1259
1417
|
if (policy.inverse !== void 0) {
|
|
1260
1418
|
check(policy.inverse.tool, "inverse", policy.match);
|
|
1261
1419
|
}
|
|
1420
|
+
if (policy.verify !== void 0) {
|
|
1421
|
+
check(policy.verify.tool, "verify", policy.match);
|
|
1422
|
+
}
|
|
1262
1423
|
}
|
|
1263
1424
|
if (problems.length > 0) {
|
|
1264
1425
|
throw new ManifestError(`the manifest calls tools that do not exist:
|
|
1265
1426
|
${problems.join("\n ")}`);
|
|
1266
1427
|
}
|
|
1428
|
+
const drifted = [];
|
|
1429
|
+
for (const upstream of upstreams) {
|
|
1430
|
+
const faults = auditPins(upstream.name, shapes.get(upstream.name) ?? [], manifest);
|
|
1431
|
+
drifted.push(...explainPins(upstream.name, faults));
|
|
1432
|
+
}
|
|
1433
|
+
if (drifted.length > 0) {
|
|
1434
|
+
throw new ManifestError(
|
|
1435
|
+
"a pinned tool no longer matches the policy written for it:\n " + drifted.join("\n ") + "\n\n Review what changed before trusting undo on these tools. `synartesis pin` prints the block for the servers you have now."
|
|
1436
|
+
);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
// src/manifest/standing.ts
|
|
1441
|
+
function standing(manifest) {
|
|
1442
|
+
return Object.entries(manifest.servers).map(([server, spec]) => ({
|
|
1443
|
+
server,
|
|
1444
|
+
provenance: spec.provenance ?? "unstated"
|
|
1445
|
+
}));
|
|
1446
|
+
}
|
|
1447
|
+
function untested(manifest) {
|
|
1448
|
+
return standing(manifest).filter((entry) => entry.provenance === "documented").map((entry) => entry.server);
|
|
1449
|
+
}
|
|
1450
|
+
function describeStanding(entry) {
|
|
1451
|
+
switch (entry.provenance) {
|
|
1452
|
+
case "live":
|
|
1453
|
+
return "checked against the real server";
|
|
1454
|
+
case "documented":
|
|
1455
|
+
return "written from documentation, never run against the real server";
|
|
1456
|
+
case "unstated":
|
|
1457
|
+
return "no claim either way";
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
function warnUntested(servers) {
|
|
1461
|
+
const names = servers.join(", ");
|
|
1462
|
+
const these = servers.length === 1 ? "this policy has" : "these policies have";
|
|
1463
|
+
return `${names}: ${these} never been run against the real server. The classes and inverses here come from documentation, so undo may not work where it says it will. Run \`synartesis check\` against your own credentials, and expect to correct something.`;
|
|
1267
1464
|
}
|
|
1268
1465
|
|
|
1269
1466
|
// src/proxy/routing.ts
|
|
@@ -1390,37 +1587,17 @@ async function start(spec) {
|
|
|
1390
1587
|
return { client };
|
|
1391
1588
|
}
|
|
1392
1589
|
|
|
1393
|
-
// src/
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
}
|
|
1401
|
-
function failClosed(qualifiedName) {
|
|
1402
|
-
return { match: qualifiedName, class: "irreversible", gate: "always", refusal: "uncertain" };
|
|
1403
|
-
}
|
|
1404
|
-
function createPolicyResolver(manifest) {
|
|
1405
|
-
const compiled = manifest.tools.map((policy) => ({
|
|
1406
|
-
policy,
|
|
1407
|
-
test: toRegExp(policy.match),
|
|
1408
|
-
specificity: literalLength(policy.match),
|
|
1409
|
-
wildcards: policy.match.split("*").length - 1
|
|
1410
|
-
})).sort((a, b) => b.specificity - a.specificity || a.wildcards - b.wildcards);
|
|
1411
|
-
const cache = /* @__PURE__ */ new Map();
|
|
1412
|
-
return {
|
|
1413
|
-
resolve(qualifiedName) {
|
|
1414
|
-
const cached2 = cache.get(qualifiedName);
|
|
1415
|
-
if (cached2 !== void 0) {
|
|
1416
|
-
return cached2;
|
|
1417
|
-
}
|
|
1418
|
-
const hit = compiled.find((candidate) => candidate.test.test(qualifiedName));
|
|
1419
|
-
const match = hit === void 0 ? { policy: failClosed(qualifiedName), matched: false } : { policy: hit.policy, matched: true };
|
|
1420
|
-
cache.set(qualifiedName, match);
|
|
1421
|
-
return match;
|
|
1590
|
+
// src/idempotency.ts
|
|
1591
|
+
var IDEMPOTENCY_META_KEY = "synartesis.dev/idempotency-key";
|
|
1592
|
+
function withIdempotencyKey(meta, key) {
|
|
1593
|
+
const merged = {};
|
|
1594
|
+
if (typeof meta === "object" && meta !== null) {
|
|
1595
|
+
for (const [name, value] of Object.entries(meta)) {
|
|
1596
|
+
merged[name] = value;
|
|
1422
1597
|
}
|
|
1423
|
-
}
|
|
1598
|
+
}
|
|
1599
|
+
merged[IDEMPOTENCY_META_KEY] = key;
|
|
1600
|
+
return merged;
|
|
1424
1601
|
}
|
|
1425
1602
|
|
|
1426
1603
|
// src/proxy/snapshot.ts
|
|
@@ -1582,9 +1759,17 @@ export {
|
|
|
1582
1759
|
wasRefused,
|
|
1583
1760
|
parseManifest,
|
|
1584
1761
|
loadManifest,
|
|
1762
|
+
createPolicyResolver,
|
|
1585
1763
|
qualify,
|
|
1764
|
+
pinBlock,
|
|
1765
|
+
toolShapes,
|
|
1586
1766
|
verifyAgainstServers,
|
|
1587
|
-
|
|
1767
|
+
standing,
|
|
1768
|
+
untested,
|
|
1769
|
+
describeStanding,
|
|
1770
|
+
warnUntested,
|
|
1771
|
+
IDEMPOTENCY_META_KEY,
|
|
1772
|
+
withIdempotencyKey,
|
|
1588
1773
|
createRouter,
|
|
1589
1774
|
refusal,
|
|
1590
1775
|
toPayload,
|
|
@@ -1597,4 +1782,4 @@ export {
|
|
|
1597
1782
|
observeState,
|
|
1598
1783
|
connectStdioUpstream
|
|
1599
1784
|
};
|
|
1600
|
-
//# sourceMappingURL=chunk-
|
|
1785
|
+
//# sourceMappingURL=chunk-6X7GPUGJ.js.map
|