githolon 0.100.3 → 0.100.5

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
@@ -3098,6 +3098,7 @@ __export(governance_exports, {
3098
3098
  certifyEra: () => certifyEra,
3099
3099
  consentRebirth: () => consentRebirth,
3100
3100
  discoverSigningDomain: () => discoverSigningDomain,
3101
+ ensureApplicationWorkspace: () => ensureApplicationWorkspace,
3101
3102
  ensureSovereignHome: () => ensureSovereignHome,
3102
3103
  exchangeOnboardingToken: () => exchangeOnboardingToken,
3103
3104
  executeRepairRebirth: () => executeRepairRebirth,
@@ -3277,6 +3278,20 @@ async function exchangeOnboardingToken(session, keyHash, idp = process.env["NOMO
3277
3278
  }
3278
3279
  return { token: body.token, issuer: body.issuer, subject: body.subject };
3279
3280
  }
3281
+ async function workspaceMainHead(cloud, workspace) {
3282
+ return fetch(
3283
+ `${cloud}/v2/workspaces/${encodeURIComponent(workspace)}/git/info/refs?service=git-upload-pack`
3284
+ ).then(async (r) => r.ok ? /([0-9a-f]{40}) refs\/heads\/main\b/.exec(await r.text())?.[1] : void 0).catch(() => void 0);
3285
+ }
3286
+ async function waitForWorkspaceMain(cloud, workspace, timeoutMs = 3e4) {
3287
+ const deadline = Date.now() + timeoutMs;
3288
+ while (Date.now() < deadline) {
3289
+ const head = await workspaceMainHead(cloud, workspace);
3290
+ if (head !== void 0) return head;
3291
+ await new Promise((resolve6) => setTimeout(resolve6, 250));
3292
+ }
3293
+ return void 0;
3294
+ }
3280
3295
  async function ensureSovereignHome(opts = {}) {
3281
3296
  const cloud = cloudBase(opts.cloud);
3282
3297
  const uid = opts.principal ?? currentPrincipal();
@@ -3421,6 +3436,123 @@ async function ensureSovereignHome(opts = {}) {
3421
3436
  }
3422
3437
  return { home, principal: actor, device, created: !exists };
3423
3438
  }
3439
+ async function ensureApplicationWorkspace(application, opts = {}) {
3440
+ const cloud = cloudBase(opts.cloud);
3441
+ const statusUrl = `${cloud}/v2/workspaces/${encodeURIComponent(application)}`;
3442
+ const current = await fetch(statusUrl).catch(() => null);
3443
+ if (current?.ok) {
3444
+ const status2 = await current.json().catch(() => null);
3445
+ const principal = String(status2?.lineage?.principal ?? "");
3446
+ const session = canonicalPrincipal(currentPrincipal() ?? "");
3447
+ if (await waitForWorkspaceMain(cloud, application) === void 0) {
3448
+ throw new Error(`application '${application}' is born but canonical ledger main did not become readable`);
3449
+ }
3450
+ return {
3451
+ application,
3452
+ parent: String(status2?.lineage?.parent ?? ""),
3453
+ principal,
3454
+ created: false,
3455
+ ...principal !== "" && session !== "" && sameIdentity(principal, session) ? { deployPrincipal: principal } : {}
3456
+ };
3457
+ }
3458
+ if (current !== null && current.status !== 404 && current.status !== 409) {
3459
+ throw new Error(`could not inspect deploy target '${application}' (${current.status}); refusing to guess that it is unborn`);
3460
+ }
3461
+ const home = await ensureSovereignHome(opts);
3462
+ if (application === home.home) {
3463
+ return {
3464
+ application,
3465
+ parent: "root",
3466
+ principal: home.principal,
3467
+ created: home.created,
3468
+ deployPrincipal: home.principal
3469
+ };
3470
+ }
3471
+ if (await fetch(statusUrl).then((r) => r.ok).catch(() => false)) {
3472
+ if (await waitForWorkspaceMain(cloud, application) === void 0) {
3473
+ throw new Error(`application '${application}' is born but canonical ledger main did not become readable`);
3474
+ }
3475
+ return {
3476
+ application,
3477
+ parent: home.home,
3478
+ principal: home.principal,
3479
+ created: false,
3480
+ deployPrincipal: home.principal
3481
+ };
3482
+ }
3483
+ const domains = await fetch(`${cloud}/v2/workspaces/${encodeURIComponent(home.home)}/domains`).then(async (r) => {
3484
+ if (!r.ok) throw new Error(`application parent law discovery failed (${r.status})`);
3485
+ return await r.json();
3486
+ });
3487
+ const frameworkHash = domains.currentLaw?.["nomos"];
3488
+ const lawHash = domains.currentLaw?.["workspaces"];
3489
+ if (!frameworkHash || !lawHash) {
3490
+ throw new Error(`the signed session home does not carry the framework law needed to birth '${application}'`);
3491
+ }
3492
+ const parent = await connectGovernance(cloud, home.home);
3493
+ const birthCert = await parent.certSign({
3494
+ child: application,
3495
+ founder: home.principal,
3496
+ verified: true,
3497
+ parentKey: home.device.public,
3498
+ parentId: home.home,
3499
+ epoch: 0,
3500
+ secret: home.device.secret
3501
+ });
3502
+ const bare = lineageIdentity(home.principal);
3503
+ const now = Date.now();
3504
+ const birth = await fetch(
3505
+ `${cloud}/v2/workspaces/${encodeURIComponent(home.home)}/workspaces/birthChild`,
3506
+ {
3507
+ method: "POST",
3508
+ headers: { "content-type": "application/json" },
3509
+ body: JSON.stringify({
3510
+ name: application,
3511
+ parent: home.home,
3512
+ principal: bare,
3513
+ tier: "edge",
3514
+ requestedBy: bare,
3515
+ requestedAt: now,
3516
+ foundingAdminHash: home.device.keyHash,
3517
+ bornAt: new Date(now).toISOString(),
3518
+ frameworkHash,
3519
+ lawHash,
3520
+ platformCert: JSON.stringify(birthCert),
3521
+ actor: home.principal,
3522
+ authorSecret: home.device.secret
3523
+ })
3524
+ }
3525
+ );
3526
+ const body = await birth.json().catch(() => null);
3527
+ const failed = body?.birthOutcomes?.find((outcome) => outcome.status !== "born");
3528
+ if (!birth.ok || body?.ok === false || failed) {
3529
+ if (!await fetch(statusUrl).then((r) => r.ok).catch(() => false)) {
3530
+ throw new Error(
3531
+ `Nomos could not create application '${application}' (${birth.status}): ${failed?.reason ?? String(body?.error ?? JSON.stringify(body))}`
3532
+ );
3533
+ }
3534
+ }
3535
+ if (await waitForWorkspaceMain(cloud, application) === void 0) {
3536
+ throw new Error(`Nomos admitted application '${application}' but canonical ledger main did not become readable`);
3537
+ }
3538
+ const admin = await signAndPostOffer(
3539
+ application,
3540
+ "workspaces",
3541
+ "grantAdmin",
3542
+ { principal: bare, label: "application founder", grantedBy: bare, grantedAt: now },
3543
+ { ...opts, principal: bare }
3544
+ );
3545
+ if (!admin.ok) {
3546
+ throw new Error(`application '${application}' refused its founding admin (${admin.status}): ${JSON.stringify(admin.body)}`);
3547
+ }
3548
+ return {
3549
+ application,
3550
+ parent: home.home,
3551
+ principal: home.principal,
3552
+ created: true,
3553
+ deployPrincipal: home.principal
3554
+ };
3555
+ }
3424
3556
  async function signAndPostWorkspaceRetirement(parent, workspaceId, creator, opts) {
3425
3557
  return signAndPostPreparedOffer(
3426
3558
  parent,
@@ -3438,12 +3570,15 @@ async function signAndPostChildLifecycle(parent, child, creator, action, opts) {
3438
3570
  return signAndPostPreparedOffer(
3439
3571
  parent,
3440
3572
  void 0,
3441
- (actor) => action === "retire" ? {
3442
- directiveId: "retireChild",
3443
- payload: { child, principal: creator, retiredBy: lineageIdentity(actor), retiredAt: Date.now() }
3444
- } : {
3445
- directiveId: "reclaimChild",
3446
- payload: { child, reclaimedBy: lineageIdentity(actor), reclaimedAt: Date.now() }
3573
+ (actor) => {
3574
+ const creatorActing = creator !== "" && sameIdentity(creator, actor);
3575
+ return action === "retire" ? {
3576
+ directiveId: creatorActing ? "retireChild" : "retireChildAsAdmin",
3577
+ payload: { child, principal: creator, retiredBy: lineageIdentity(actor), retiredAt: Date.now() }
3578
+ } : {
3579
+ directiveId: creatorActing ? "reclaimChild" : "reclaimChildAsAdmin",
3580
+ payload: { child, reclaimedBy: lineageIdentity(actor), reclaimedAt: Date.now() }
3581
+ };
3447
3582
  },
3448
3583
  opts,
3449
3584
  "self-if-unenrolled",
@@ -5627,6 +5762,22 @@ async function deploy2(ws, opts) {
5627
5762
  return 1;
5628
5763
  }
5629
5764
  const fileName = file.split("/").pop();
5765
+ try {
5766
+ const { ensureApplicationWorkspace: ensureApplicationWorkspace2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
5767
+ const prepared = await ensureApplicationWorkspace2(ws, opts);
5768
+ if (prepared.deployPrincipal !== void 0 && opts.principal === void 0) {
5769
+ opts = {
5770
+ ...opts,
5771
+ principal: prepared.deployPrincipal.startsWith("user:") ? prepared.deployPrincipal.slice("user:".length) : prepared.deployPrincipal
5772
+ };
5773
+ }
5774
+ if (prepared.created) {
5775
+ out4(` created application ${ws} under your Nomos home`);
5776
+ }
5777
+ } catch (e) {
5778
+ err4(`deploy '${ws}' could not prepare the application: ${e.message}`);
5779
+ return 1;
5780
+ }
5630
5781
  const beforeLaw = await fetch(`${cloud}/v2/workspaces/${ws}/domains`).then((br) => br.json()).then((bd) => bd.ok === true ? bd : {}).catch(() => ({}));
5631
5782
  const before = beforeLaw.domains ?? [];
5632
5783
  const currentLaw2 = beforeLaw.currentLaw ?? {};
@@ -1846,32 +1846,42 @@ function aliasIdsFor(x, path2) {
1846
1846
  return new Set(ids);
1847
1847
  }
1848
1848
  function resolveGatewayOperation(spec, opName, method) {
1849
- if (!spec || !spec.paths || !opName || !method) return { op: null };
1849
+ if (!spec || !opName || !method) return { op: null };
1850
+ const routeTables = [
1851
+ spec.paths ?? {},
1852
+ spec["x-nomos-framework-routes"]?.paths ?? {}
1853
+ ];
1850
1854
  const normalizedMethod = String(method).toLowerCase();
1851
1855
  const exactPath = `/${String(opName).replace(/^\/+/, "")}`;
1852
- const exact = spec.paths[exactPath]?.[normalizedMethod];
1853
- if (exact) return { op: exact, path: exactPath, ambiguous: null };
1856
+ for (const paths of routeTables) {
1857
+ const exact = paths[exactPath]?.[normalizedMethod];
1858
+ if (exact) return { op: exact, path: exactPath, ambiguous: null };
1859
+ }
1854
1860
  const slash = String(opName).indexOf("/");
1855
1861
  if (slash > 0) {
1856
1862
  const requestedDomain = String(opName).slice(0, slash);
1857
1863
  const requestedAlias = String(opName).slice(slash + 1);
1858
1864
  const matches2 = [];
1859
- for (const [path2, methods] of Object.entries(spec.paths)) {
1860
- const op = methods?.[normalizedMethod];
1861
- const x = op?.["x-nomos"];
1862
- if (!op || !x || x.domain !== requestedDomain) continue;
1863
- if (aliasIdsFor(x, path2).has(requestedAlias)) matches2.push({ op, path: path2 });
1865
+ for (const paths of routeTables) {
1866
+ for (const [path2, methods] of Object.entries(paths)) {
1867
+ const op = methods?.[normalizedMethod];
1868
+ const x = op?.["x-nomos"];
1869
+ if (!op || !x || x.domain !== requestedDomain) continue;
1870
+ if (aliasIdsFor(x, path2).has(requestedAlias)) matches2.push({ op, path: path2 });
1871
+ }
1864
1872
  }
1865
1873
  if (matches2.length === 1) return { ...matches2[0], ambiguous: null };
1866
1874
  if (matches2.length > 1) return { op: null, ambiguous: matches2.map((m) => m.path).sort() };
1867
1875
  return { op: null, ambiguous: null };
1868
1876
  }
1869
1877
  const matches = [];
1870
- for (const [path2, methods] of Object.entries(spec.paths)) {
1871
- const op = methods?.[normalizedMethod];
1872
- const x = op?.["x-nomos"];
1873
- if (!op || !x) continue;
1874
- if (aliasIdsFor(x, path2).has(opName)) matches.push({ op, path: path2 });
1878
+ for (const paths of routeTables) {
1879
+ for (const [path2, methods] of Object.entries(paths)) {
1880
+ const op = methods?.[normalizedMethod];
1881
+ const x = op?.["x-nomos"];
1882
+ if (!op || !x) continue;
1883
+ if (aliasIdsFor(x, path2).has(opName)) matches.push({ op, path: path2 });
1884
+ }
1875
1885
  }
1876
1886
  if (matches.length === 1) return { ...matches[0], ambiguous: null };
1877
1887
  if (matches.length > 1) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "githolon",
3
- "version": "0.100.3",
3
+ "version": "0.100.5",
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.100.3",
33
- "@githolon/dsl": "^0.100.3",
32
+ "@githolon/client": "^0.100.5",
33
+ "@githolon/dsl": "^0.100.5",
34
34
  "isomorphic-git": "^1.38.4"
35
35
  },
36
36
  "devDependencies": {