githolon 0.90.0 → 0.91.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/cli.mjs +130 -5
- package/dist/dev-server.mjs +95 -4
- package/package.json +3 -3
package/dist/cli.mjs
CHANGED
|
@@ -418,6 +418,7 @@ __export(engine_exports, {
|
|
|
418
418
|
readPosture: () => readPosture,
|
|
419
419
|
readShape: () => readShape,
|
|
420
420
|
rebirthCompact: () => rebirthCompact,
|
|
421
|
+
reconcileBornPacks: () => reconcileBornPacks,
|
|
421
422
|
refreshProjection: () => refreshProjection,
|
|
422
423
|
refusedSummary: () => refusedSummary,
|
|
423
424
|
replicateIntents: () => replicateIntents,
|
|
@@ -1172,7 +1173,7 @@ async function admitIntentOffers(eng, ws, ledger, offers, { receiptFence = null,
|
|
|
1172
1173
|
let blob = offer?.blob instanceof Uint8Array ? offer.blob : null;
|
|
1173
1174
|
try {
|
|
1174
1175
|
if (!blob && typeof offer?.intentB64 === "string") blob = Uint8Array.from(atob(offer.intentB64), (c) => c.charCodeAt(0));
|
|
1175
|
-
const maxOfferBytes = offer?.staged ? 64 * 1024 * 1024 :
|
|
1176
|
+
const maxOfferBytes = offer?.staged ? 64 * 1024 * 1024 : 32 * 1024 * 1024;
|
|
1176
1177
|
if (!blob || blob.byteLength === 0 || blob.byteLength > maxOfferBytes) {
|
|
1177
1178
|
rec.rejected.push({ oid: String(offer?.oid || "").slice(0, 10), error: `intent offer must carry intentB64 <= ${maxOfferBytes / (1024 * 1024)} MiB` });
|
|
1178
1179
|
continue;
|
|
@@ -1816,6 +1817,25 @@ function packBornChildren(eng, born) {
|
|
|
1816
1817
|
}
|
|
1817
1818
|
});
|
|
1818
1819
|
}
|
|
1820
|
+
function reconcileBornPacks(eng, children) {
|
|
1821
|
+
const born = [];
|
|
1822
|
+
const missing = [];
|
|
1823
|
+
for (const child of Array.isArray(children) ? children : []) {
|
|
1824
|
+
if (typeof child !== "string" || !child) continue;
|
|
1825
|
+
const head = kgit.resolveRef(eng, gitdirOf(child), BRANCH);
|
|
1826
|
+
if (!head || !/^[0-9a-f]{40}$/i.test(head)) {
|
|
1827
|
+
missing.push(child);
|
|
1828
|
+
continue;
|
|
1829
|
+
}
|
|
1830
|
+
const pack = packDelta(eng, child, "", head);
|
|
1831
|
+
if (!pack || !pack.length) {
|
|
1832
|
+
missing.push(child);
|
|
1833
|
+
continue;
|
|
1834
|
+
}
|
|
1835
|
+
born.push({ workspace: child, head, packB64: b64Bytes(pack), packBytes: pack.length });
|
|
1836
|
+
}
|
|
1837
|
+
return { born, missing };
|
|
1838
|
+
}
|
|
1819
1839
|
async function ingestPackAsCustody(eng, ws, packBytes, head) {
|
|
1820
1840
|
if (!packBytes || !packBytes.length) throw new Error(`ingestPackAsCustody: empty pack for '${ws}'`);
|
|
1821
1841
|
if (!/^[0-9a-f]{40}$/i.test(head || "")) throw new Error(`ingestPackAsCustody: bad head '${head}' for '${ws}'`);
|
|
@@ -3144,7 +3164,7 @@ async function signAndPostOffer(ws, domain, directiveId, payload, opts) {
|
|
|
3144
3164
|
}
|
|
3145
3165
|
err2(`this cloud has no /v2/stage content-addressed lane \u2014 relaying the ${sealed.intentBytes.length}-char sealed offer inline (a 413 here means the worker needs upgrading to accept law this large signed)`);
|
|
3146
3166
|
}
|
|
3147
|
-
const verdict = await holon.postGovernanceOffer({ directiveId, intentBytes: sealed.intentBytes, ws });
|
|
3167
|
+
const verdict = await holon.postGovernanceOffer({ directiveId, intentBytes: sealed.intentBytes, ws, domain: resolvedDomain });
|
|
3148
3168
|
return { ...verdict, principal };
|
|
3149
3169
|
}
|
|
3150
3170
|
async function relaySealedOfferViaStage(cloud, ws, directiveId, intentBytesB64, fetchImpl) {
|
|
@@ -3504,7 +3524,7 @@ async function governanceOffer(ws, directiveId, opts) {
|
|
|
3504
3524
|
const domain = await discoverSigningDomain(holon, ws, directiveId, opts.domain);
|
|
3505
3525
|
const device = await ensureDeviceKey(holon, principal);
|
|
3506
3526
|
const sealed = await holon.signGovernanceOffer({ domain, directiveId, payload, authorSecret: device.secret, actor: canonicalPrincipal(principal) });
|
|
3507
|
-
writeFileSync4(opts.save, JSON.stringify({ ws, directiveId, principal, intentBytes: sealed.intentBytes }, null, 2) + "\n");
|
|
3527
|
+
writeFileSync4(opts.save, JSON.stringify({ ws, domain, directiveId, principal, intentBytes: sealed.intentBytes }, null, 2) + "\n");
|
|
3508
3528
|
} catch (e) {
|
|
3509
3529
|
err2(e.message);
|
|
3510
3530
|
return 1;
|
|
@@ -3536,15 +3556,16 @@ async function governanceRelay(ws, file, opts) {
|
|
|
3536
3556
|
err2(`cannot read offer file ${file}: ${e.message}`);
|
|
3537
3557
|
return 1;
|
|
3538
3558
|
}
|
|
3539
|
-
const { directiveId, intentBytes } = saved;
|
|
3559
|
+
const { directiveId, intentBytes, domain } = saved;
|
|
3540
3560
|
if (typeof directiveId !== "string" || typeof intentBytes !== "string") {
|
|
3541
3561
|
err2(`${file} is not a saved offer (expected { directiveId, intentBytes })`);
|
|
3542
3562
|
return 1;
|
|
3543
3563
|
}
|
|
3544
3564
|
const cloud = cloudBase(opts.cloud);
|
|
3565
|
+
const route = typeof domain === "string" && domain.length > 0 ? `${encodeURIComponent(domain)}/${encodeURIComponent(directiveId)}` : encodeURIComponent(directiveId);
|
|
3545
3566
|
let r;
|
|
3546
3567
|
try {
|
|
3547
|
-
r = await fetch(`${cloud}/v2/workspaces/${ws}/${
|
|
3568
|
+
r = await fetch(`${cloud}/v2/workspaces/${ws}/${route}`, {
|
|
3548
3569
|
method: "POST",
|
|
3549
3570
|
headers: { "content-type": "application/json" },
|
|
3550
3571
|
body: JSON.stringify({ intentBytes })
|
|
@@ -3555,6 +3576,10 @@ async function governanceRelay(ws, file, opts) {
|
|
|
3555
3576
|
}
|
|
3556
3577
|
const text = await r.text();
|
|
3557
3578
|
if (!r.ok) {
|
|
3579
|
+
if (r.status === 409 && domain === void 0) {
|
|
3580
|
+
err2(`${directiveId} is declared by more than one domain on ${ws} \u2014 re-run \`gov --save\` (it now records the signing domain), or the worker's candidates were: ${text}`);
|
|
3581
|
+
return 1;
|
|
3582
|
+
}
|
|
3558
3583
|
err2(`${directiveId} refused (${r.status}): ${text}`);
|
|
3559
3584
|
return 1;
|
|
3560
3585
|
}
|
|
@@ -4766,6 +4791,97 @@ var init_lifecycle = __esm({
|
|
|
4766
4791
|
}
|
|
4767
4792
|
});
|
|
4768
4793
|
|
|
4794
|
+
// src/capabilities.ts
|
|
4795
|
+
var capabilities_exports = {};
|
|
4796
|
+
__export(capabilities_exports, {
|
|
4797
|
+
INLINE_OFFER_CAP_CHARS: () => INLINE_OFFER_CAP_CHARS,
|
|
4798
|
+
negotiateDeployRoute: () => negotiateDeployRoute,
|
|
4799
|
+
openapiRoutesDirective: () => openapiRoutesDirective,
|
|
4800
|
+
probeCloudCapabilities: () => probeCloudCapabilities,
|
|
4801
|
+
probeOfferLane: () => probeOfferLane,
|
|
4802
|
+
probeStageLane: () => probeStageLane
|
|
4803
|
+
});
|
|
4804
|
+
async function probeStageLane(cloud, fetchImpl = fetch) {
|
|
4805
|
+
try {
|
|
4806
|
+
const r = await fetchImpl(`${cloud}/v2/stage/${ZERO_SHA}`, { method: "GET" });
|
|
4807
|
+
const body = await r.json().catch(() => void 0);
|
|
4808
|
+
return typeof body?.staged === "boolean";
|
|
4809
|
+
} catch {
|
|
4810
|
+
return void 0;
|
|
4811
|
+
}
|
|
4812
|
+
}
|
|
4813
|
+
function openapiRoutesDirective(spec, domain, directiveId) {
|
|
4814
|
+
const paths = spec?.paths ?? {};
|
|
4815
|
+
const wantPath = `/${domain}/${directiveId}`;
|
|
4816
|
+
const wantBare = `/${directiveId}`;
|
|
4817
|
+
const wantOp = `${domain}__${directiveId}`;
|
|
4818
|
+
for (const [p, ops] of Object.entries(paths)) {
|
|
4819
|
+
if (p === wantPath || p === wantBare) return true;
|
|
4820
|
+
for (const method of Object.values(ops ?? {})) {
|
|
4821
|
+
if (method?.operationId === wantOp) return true;
|
|
4822
|
+
}
|
|
4823
|
+
}
|
|
4824
|
+
return false;
|
|
4825
|
+
}
|
|
4826
|
+
async function probeOfferLane(cloud, ws, route, fetchImpl = fetch) {
|
|
4827
|
+
try {
|
|
4828
|
+
const r = await fetchImpl(`${cloud}/v2/workspaces/${ws}/openapi.json`, { method: "GET" });
|
|
4829
|
+
if (!r.ok) return { served: false, routesTarget: void 0 };
|
|
4830
|
+
const spec = await r.json().catch(() => void 0);
|
|
4831
|
+
if (spec === void 0 || typeof spec !== "object") return { served: false, routesTarget: void 0 };
|
|
4832
|
+
const served = true;
|
|
4833
|
+
const routesTarget = route === void 0 ? void 0 : openapiRoutesDirective(spec, route.domain, route.directiveId);
|
|
4834
|
+
return { served, routesTarget };
|
|
4835
|
+
} catch {
|
|
4836
|
+
return { served: void 0, routesTarget: void 0 };
|
|
4837
|
+
}
|
|
4838
|
+
}
|
|
4839
|
+
async function probeCloudCapabilities(cloud, ws, route, fetchImpl = fetch) {
|
|
4840
|
+
const [stage, offer] = await Promise.all([
|
|
4841
|
+
probeStageLane(cloud, fetchImpl),
|
|
4842
|
+
probeOfferLane(cloud, ws, route, fetchImpl)
|
|
4843
|
+
]);
|
|
4844
|
+
return {
|
|
4845
|
+
cloud,
|
|
4846
|
+
stage,
|
|
4847
|
+
offerLane: offer.served,
|
|
4848
|
+
routesTarget: offer.routesTarget,
|
|
4849
|
+
openDeploy: void 0
|
|
4850
|
+
// the open /domains lane is universal in every worker/dev-server; not separately probed
|
|
4851
|
+
};
|
|
4852
|
+
}
|
|
4853
|
+
function negotiateDeployRoute(caps, opts) {
|
|
4854
|
+
if (opts.signed) {
|
|
4855
|
+
if (caps.offerLane === false) {
|
|
4856
|
+
return {
|
|
4857
|
+
fail: `${caps.cloud} does not serve the SIGNED OFFER lane \u2014 POST /v2/workspaces/${opts.ws}/${opts.route.domain}/${opts.route.directiveId} is not routed (the workspace serves no self-describing OpenAPI, so this cloud has no gateway to route a signed offer). This is often a lightweight cloud (e.g. \`githolon cloud dev\`) without the offer gateway.
|
|
4858
|
+
\u2192 deploy the OPEN (unsigned) lane instead (drop --as/--principal) if this workspace is unwarranted, or target a cloud that serves the offer gateway.`
|
|
4859
|
+
};
|
|
4860
|
+
}
|
|
4861
|
+
if (caps.offerLane === true && caps.routesTarget === false) {
|
|
4862
|
+
return {
|
|
4863
|
+
fail: `${caps.cloud} serves the offer gateway for ${opts.ws}, but its OpenAPI does not name the route ${opts.route.domain}/${opts.route.directiveId} \u2014 the ${opts.route.domain} controller is not installed here, so a signed ${opts.route.directiveId} would 404.
|
|
4864
|
+
\u2192 confirm the workspace is born and the controller is deployed, or deploy the OPEN lane (drop --as).`
|
|
4865
|
+
};
|
|
4866
|
+
}
|
|
4867
|
+
if (opts.sealedSizeChars > INLINE_OFFER_CAP_CHARS && caps.stage === false) {
|
|
4868
|
+
return {
|
|
4869
|
+
fail: `this signed package is ~${Math.round(opts.sealedSizeChars / (1024 * 1024))} MiB sealed \u2014 over the ${INLINE_OFFER_CAP_CHARS / (1024 * 1024)} MiB inline offer cap \u2014 and ${caps.cloud} has no /v2/stage content-addressed lane to carry it.
|
|
4870
|
+
\u2192 target a cloud that serves /v2/stage, or shrink the package (githolon compile --compact).`
|
|
4871
|
+
};
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
return void 0;
|
|
4875
|
+
}
|
|
4876
|
+
var INLINE_OFFER_CAP_CHARS, ZERO_SHA;
|
|
4877
|
+
var init_capabilities = __esm({
|
|
4878
|
+
"src/capabilities.ts"() {
|
|
4879
|
+
"use strict";
|
|
4880
|
+
INLINE_OFFER_CAP_CHARS = 8 * 1024 * 1024;
|
|
4881
|
+
ZERO_SHA = "0".repeat(64);
|
|
4882
|
+
}
|
|
4883
|
+
});
|
|
4884
|
+
|
|
4769
4885
|
// src/cloud.ts
|
|
4770
4886
|
var cloud_exports = {};
|
|
4771
4887
|
__export(cloud_exports, {
|
|
@@ -5189,6 +5305,15 @@ async function deploy2(ws, opts) {
|
|
|
5189
5305
|
return 1;
|
|
5190
5306
|
}
|
|
5191
5307
|
const targetHash2 = createHash3("sha256").update(Buffer.from(packageUsda, "utf8")).digest("hex");
|
|
5308
|
+
const { probeCloudCapabilities: probeCloudCapabilities2, negotiateDeployRoute: negotiateDeployRoute2 } = await Promise.resolve().then(() => (init_capabilities(), capabilities_exports));
|
|
5309
|
+
const route = { domain: "nomos", directiveId: "installDomain" };
|
|
5310
|
+
const caps = await probeCloudCapabilities2(cloud, ws, route);
|
|
5311
|
+
const refusal = negotiateDeployRoute2(caps, { signed: true, ws, sealedSizeChars: packageUsda.length, route });
|
|
5312
|
+
if (refusal !== void 0) {
|
|
5313
|
+
err4(`signed deploy '${ws}' cannot proceed on ${cloud}:
|
|
5314
|
+
${refusal.fail}`);
|
|
5315
|
+
return 1;
|
|
5316
|
+
}
|
|
5192
5317
|
const { signAndPostOffer: signAndPostOffer2, resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
|
|
5193
5318
|
let v;
|
|
5194
5319
|
try {
|
package/dist/dev-server.mjs
CHANGED
|
@@ -631,7 +631,7 @@ async function admitIntentOffers(eng, ws, ledger, offers, { receiptFence = null,
|
|
|
631
631
|
let blob = offer?.blob instanceof Uint8Array ? offer.blob : null;
|
|
632
632
|
try {
|
|
633
633
|
if (!blob && typeof offer?.intentB64 === "string") blob = Uint8Array.from(atob(offer.intentB64), (c) => c.charCodeAt(0));
|
|
634
|
-
const maxOfferBytes = offer?.staged ? 64 * 1024 * 1024 :
|
|
634
|
+
const maxOfferBytes = offer?.staged ? 64 * 1024 * 1024 : 32 * 1024 * 1024;
|
|
635
635
|
if (!blob || blob.byteLength === 0 || blob.byteLength > maxOfferBytes) {
|
|
636
636
|
rec.rejected.push({ oid: String(offer?.oid || "").slice(0, 10), error: `intent offer must carry intentB64 <= ${maxOfferBytes / (1024 * 1024)} MiB` });
|
|
637
637
|
continue;
|
|
@@ -1622,6 +1622,46 @@ function resolveGatewayOperation(spec, opName, method) {
|
|
|
1622
1622
|
return { op: null, ambiguous: null };
|
|
1623
1623
|
}
|
|
1624
1624
|
|
|
1625
|
+
// ../cloud/cloudflare-shell/src/staged-intents.mjs
|
|
1626
|
+
var STAGE_THRESHOLD_CHARS = 4 * 1024 * 1024;
|
|
1627
|
+
var MAX_INLINE_INTENT_CHARS = 8 * 1024 * 1024;
|
|
1628
|
+
var MAX_STAGED_INTENT_BYTES = 64 * 1024 * 1024;
|
|
1629
|
+
var stagedIntentKey = (sha256) => typeof sha256 === "string" && /^[0-9a-f]{64}$/.test(sha256) ? `staged-intents/${sha256}` : null;
|
|
1630
|
+
async function sha256hexBytes(bytes) {
|
|
1631
|
+
const d = await crypto.subtle.digest("SHA-256", bytes);
|
|
1632
|
+
return [...new Uint8Array(d)].map((x) => x.toString(16).padStart(2, "0")).join("");
|
|
1633
|
+
}
|
|
1634
|
+
var b64OfBytes = (bytes) => {
|
|
1635
|
+
let s = "";
|
|
1636
|
+
for (let i = 0; i < bytes.length; i += 32768) s += String.fromCharCode.apply(null, bytes.subarray(i, i + 32768));
|
|
1637
|
+
return btoa(s);
|
|
1638
|
+
};
|
|
1639
|
+
async function stagePut(bucket, sha256, bytes) {
|
|
1640
|
+
const key = stagedIntentKey(sha256);
|
|
1641
|
+
if (key === null) return { status: 400, body: { ok: false, error: "stage-bad-ref: the path must name sha256 as 64 lowercase hex" } };
|
|
1642
|
+
if (!(bytes instanceof Uint8Array) || bytes.length === 0) return { status: 400, body: { ok: false, error: "stage-empty: PUT a non-empty raw body (the sealed intent bytes)" } };
|
|
1643
|
+
if (bytes.length > MAX_STAGED_INTENT_BYTES) return { status: 413, body: { ok: false, error: `stage-too-large: staged bytes are capped at ${MAX_STAGED_INTENT_BYTES} bytes` } };
|
|
1644
|
+
const got = await sha256hexBytes(bytes);
|
|
1645
|
+
if (got !== sha256) return { status: 400, body: { ok: false, error: `stage-hash-mismatch: sha256(body) is ${got}, not the named ${sha256} \u2014 stage bytes under their own hash` } };
|
|
1646
|
+
const existing = typeof bucket.head === "function" ? await bucket.head(key) : await bucket.get(key);
|
|
1647
|
+
if (existing) return { status: 200, body: { ok: true, sha256, size: bytes.length, alreadyStaged: true } };
|
|
1648
|
+
await bucket.put(key, bytes);
|
|
1649
|
+
return { status: 200, body: { ok: true, sha256, size: bytes.length, staged: true } };
|
|
1650
|
+
}
|
|
1651
|
+
async function resolveStagedIntent(bucket, ref) {
|
|
1652
|
+
const sha256 = ref && typeof ref === "object" && typeof ref.sha256 === "string" ? ref.sha256 : "";
|
|
1653
|
+
const key = stagedIntentKey(sha256);
|
|
1654
|
+
if (key === null) return { ok: false, status: 400, error: "staged-intent-bad-ref: intentRef must be { sha256: <64 lowercase hex>, size? }" };
|
|
1655
|
+
const obj = await bucket.get(key);
|
|
1656
|
+
if (!obj) return { ok: false, status: 422, error: `staged-intent-missing: no staged bytes for sha256 ${sha256} \u2014 PUT /v2/stage/${sha256} (raw sealed intent bytes) first, then re-offer { intentRef }` };
|
|
1657
|
+
const bytes = new Uint8Array(await obj.arrayBuffer());
|
|
1658
|
+
if (bytes.length > MAX_STAGED_INTENT_BYTES) return { ok: false, status: 413, error: `staged-intent-too-large: staged bytes exceed the ${MAX_STAGED_INTENT_BYTES}-byte cap` };
|
|
1659
|
+
if (typeof ref.size === "number" && ref.size !== bytes.length) return { ok: false, status: 422, error: `staged-intent-size-mismatch: the ref names ${ref.size} bytes but ${bytes.length} are staged` };
|
|
1660
|
+
const got = await sha256hexBytes(bytes);
|
|
1661
|
+
if (got !== sha256) return { ok: false, status: 422, error: `staged-intent-hash-mismatch: staged bytes hash to ${got}, not the offered ${sha256} \u2014 the signed offer's hash is the binding; refuse` };
|
|
1662
|
+
return { ok: true, intentB64: b64OfBytes(bytes), sha256, size: bytes.length };
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1625
1665
|
// ../cloud/dev-server/custody.mjs
|
|
1626
1666
|
import { spawn, execFileSync } from "node:child_process";
|
|
1627
1667
|
import { existsSync, mkdirSync, rmSync, readdirSync } from "node:fs";
|
|
@@ -1893,6 +1933,20 @@ async function startDevCloud(opts = {}) {
|
|
|
1893
1933
|
replica &= (1n << 63n) - 1n;
|
|
1894
1934
|
const eng = await createEngine({ wasmModule, bootstrapPkg, nomosPkg, replica, installedBy: "nomos-cloud-dev" });
|
|
1895
1935
|
const custody = new FilesystemGitCustody(dataDir, origin);
|
|
1936
|
+
const stagedBytes = /* @__PURE__ */ new Map();
|
|
1937
|
+
const stageBucket = {
|
|
1938
|
+
async get(k) {
|
|
1939
|
+
const b = stagedBytes.get(k);
|
|
1940
|
+
return b ? { arrayBuffer: async () => b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength) } : null;
|
|
1941
|
+
},
|
|
1942
|
+
async put(k, bytes) {
|
|
1943
|
+
stagedBytes.set(k, bytes);
|
|
1944
|
+
},
|
|
1945
|
+
async head(k) {
|
|
1946
|
+
const b = stagedBytes.get(k);
|
|
1947
|
+
return b ? { size: b.length } : null;
|
|
1948
|
+
}
|
|
1949
|
+
};
|
|
1896
1950
|
let chain = Promise.resolve();
|
|
1897
1951
|
const serialize = (fn) => {
|
|
1898
1952
|
const r = chain.then(fn, fn);
|
|
@@ -1956,9 +2010,25 @@ async function startDevCloud(opts = {}) {
|
|
|
1956
2010
|
const authTokenOf = (req) => String(req.headers["x-nomos-auth"] || "").replace(/^Bearer\s+/i, "");
|
|
1957
2011
|
function openapiFor(ws) {
|
|
1958
2012
|
const merged = { openapi: "3.1.0", info: { title: `${ws} (nomos workspace)`, version: "0", "x-nomos-workspace": ws }, paths: {} };
|
|
2013
|
+
const domOf = (o) => String(o && o["x-nomos"] && o["x-nomos"].domain ? o["x-nomos"].domain : "domain");
|
|
2014
|
+
const mergeInto = (frag) => {
|
|
2015
|
+
for (const [p, methods] of Object.entries(frag.paths || {})) {
|
|
2016
|
+
for (const [m, op] of Object.entries(methods)) {
|
|
2017
|
+
const existing = merged.paths[p] && merged.paths[p][m];
|
|
2018
|
+
if (existing === void 0) {
|
|
2019
|
+
(merged.paths[p] ||= {})[m] = op;
|
|
2020
|
+
} else if (JSON.stringify(existing) !== JSON.stringify(op)) {
|
|
2021
|
+
(merged.paths[`/${domOf(existing)}${p}`] ||= {})[m] = existing;
|
|
2022
|
+
(merged.paths[`/${domOf(op)}${p}`] ||= {})[m] = op;
|
|
2023
|
+
delete merged.paths[p][m];
|
|
2024
|
+
if (Object.keys(merged.paths[p]).length === 0) delete merged.paths[p];
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
};
|
|
1959
2029
|
try {
|
|
1960
2030
|
const spec = describe(eng, ws);
|
|
1961
|
-
if (spec && spec.paths)
|
|
2031
|
+
if (spec && spec.paths) mergeInto(spec);
|
|
1962
2032
|
} catch {
|
|
1963
2033
|
}
|
|
1964
2034
|
merged.paths["/openapi.json"] = {
|
|
@@ -1982,6 +2052,22 @@ async function startDevCloud(opts = {}) {
|
|
|
1982
2052
|
if (url.pathname === "/v1/runtime/packages") {
|
|
1983
2053
|
return sendJson(res, { bootstrap: bootstrapPkg, nomos: nomosPkg });
|
|
1984
2054
|
}
|
|
2055
|
+
{
|
|
2056
|
+
const m = url.pathname.match(/^\/v2\/stage\/([0-9a-f]{64})$/);
|
|
2057
|
+
if (m) {
|
|
2058
|
+
const sha = m[1];
|
|
2059
|
+
if (req.method === "PUT" || req.method === "POST") {
|
|
2060
|
+
const bytes = new Uint8Array(await readBody(req));
|
|
2061
|
+
const r = await stagePut(stageBucket, sha, bytes);
|
|
2062
|
+
return sendJson(res, r.body, r.status);
|
|
2063
|
+
}
|
|
2064
|
+
if (req.method === "GET" || req.method === "HEAD") {
|
|
2065
|
+
const head = await stageBucket.head(`staged-intents/${sha}`);
|
|
2066
|
+
if (!head) return sendJson(res, { ok: false, staged: false, sha256: sha }, 404);
|
|
2067
|
+
return sendJson(res, { ok: true, staged: true, sha256: sha, size: head.size }, 200);
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
1985
2071
|
if (seg[0] === "git" && seg[1] === "nomos") {
|
|
1986
2072
|
return await gitBackend(req, res, seg.slice(2).join("/"), null);
|
|
1987
2073
|
}
|
|
@@ -2209,8 +2295,8 @@ async function startDevCloud(opts = {}) {
|
|
|
2209
2295
|
if (sub[0] === "subscribe") {
|
|
2210
2296
|
return sendJson(res, { ok: false, error: "subscribe requires a WebSocket upgrade" }, 426);
|
|
2211
2297
|
}
|
|
2212
|
-
if (sub.length === 1 && (req.method === "POST" || req.method === "GET")) {
|
|
2213
|
-
const opName = sub
|
|
2298
|
+
if ((sub.length === 1 || sub.length === 2) && (req.method === "POST" || req.method === "GET")) {
|
|
2299
|
+
const opName = sub.join("/");
|
|
2214
2300
|
const method = req.method.toLowerCase();
|
|
2215
2301
|
return serialize(async () => {
|
|
2216
2302
|
const ledger = ledgerFor(ws);
|
|
@@ -2234,6 +2320,11 @@ async function startDevCloud(opts = {}) {
|
|
|
2234
2320
|
const authToken = authTokenOf(req);
|
|
2235
2321
|
if (x.kind === "offer" && method === "post") {
|
|
2236
2322
|
const body = JSON.parse((await readBody(req)).toString("utf8") || "{}");
|
|
2323
|
+
if (body.intentRef && typeof body.intentRef === "object") {
|
|
2324
|
+
const resolved2 = await resolveStagedIntent(stageBucket, body.intentRef);
|
|
2325
|
+
if (!resolved2.ok) return sendJson(res, { ok: false, refused: true, error: resolved2.error }, resolved2.status);
|
|
2326
|
+
body.intentBytes = resolved2.intentB64;
|
|
2327
|
+
}
|
|
2237
2328
|
try {
|
|
2238
2329
|
let out;
|
|
2239
2330
|
if (typeof body.intentBytes === "string" && body.intentBytes) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "githolon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.91.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "githolon — the Nomos developer CLI: Rails-style generators for @githolon/dsl domains + the package compiler. Kernel-independent.",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@bjorn3/browser_wasi_shim": "0.4.2",
|
|
32
|
-
"@githolon/client": "^0.
|
|
33
|
-
"@githolon/dsl": "^0.
|
|
32
|
+
"@githolon/client": "^0.91.0",
|
|
33
|
+
"@githolon/dsl": "^0.91.0",
|
|
34
34
|
"isomorphic-git": "^1.38.4"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|