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 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 : 8 * 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}/${directiveId}`, {
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 {
@@ -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 : 8 * 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) merged.paths = 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[0];
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.90.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.90.0",
33
- "@githolon/dsl": "^0.90.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": {