@volter/world 2.0.32 → 2.0.33

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/src/world.js CHANGED
@@ -5,12 +5,12 @@
5
5
  import { rebaseChangeset, worldBootMarker, listActions, isTwinBookkeeping, pushablePendingActions, pendingActions, applyTwinWrite, twinResources } from '@volter/world-core';
6
6
  import { spawnSync } from 'node:child_process';
7
7
  import clockForm from '@volter/world-core/world-clock';
8
- import { activateScript, approveWorldChangeset, branchWorld, clockFile, checkoutWorld, createWorldChangeset, diffWorld, downWorld, fetchFromOrigin, findWorldChangeset, initWorld, listWorldChangesets, listWorldMarks, listWorlds, markWorld, pushWorldChangeset, rebaseWorldChangeset, replayWorldChangeset, resetWorld, runWithWorldEnv, seedWorld, shellWorld, statusWorld, statusWorldChangeset, upWorld, verifyWorldChangeset, worldLedgers, worldOrigin, deployWorld, loadWorldConfig, writeWorldConfig, readServeRecord, refreshTwin, serveWorld, serveWorldView, findInstalledPackage, rootForControlRoot, sealTwinCredential, sealedCredentialInfo, setTwinRoot, credentialPayloadFrom, rootCredentialInput, withoutVaultSignIn, materializeRoots } from '@volter/world-runtime';
8
+ import { activateScript, approveWorldChangeset, branchWorld, clockFile, checkoutWorld, createWorldChangeset, diffWorld, downWorld, fetchFromOrigin, findWorldChangeset, initWorld, listWorldChangesets, listWorldMarks, listWorlds, markWorld, pushWorldChangeset, rebaseWorldChangeset, replayWorldChangeset, resetWorld, runWithWorldEnv, seedWorld, shellWorld, statusWorld, statusWorldChangeset, upWorld, verifyWorldChangeset, worldLedgers, worldOrigin, bindLegacyCredentials, deployWorld, loadWorldConfig, writeWorldConfig, readServeRecord, refreshTwin, serveWorld, serveWorldView, findInstalledPackage, rootForControlRoot, sealTwinCredential, sealedCredentialInfo, setTwinRoot, credentialPayloadFrom, rootCredentialInput, withoutVaultSignIn, materializeRoots } from '@volter/world-runtime';
9
9
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
10
10
  import { dirname, join, resolve } from 'node:path';
11
11
  import { requireToken, storeToken } from "./credentials.js";
12
12
  import { currentBranch, findWorldRoot, mainBranch, requireWorldRoot, setCurrentBranch, worldConfigRelative, worldEnvPath, worldSeedPath } from "./locate.js";
13
- import { parentEntries, rebaseBranch } from '@volter/world-core';
13
+ import { destinationOf, parentEntries, rebaseBranch } from '@volter/world-core';
14
14
  /** One change as `log` shows it: the write, and the receipt the vendor answered with once pushed. */
15
15
  /** A twin's branch log as the world reads it: the twin's name, the state service it records under, its control root. */
16
16
  /** `30d`, `12h`, `5m`, `90s` in ms. */
@@ -501,12 +501,25 @@ export class World {
501
501
  }
502
502
  }
503
503
  /** Seal the vendor's credential beside the world (a bare token, or a JSON payload). Never readable back. */
504
- sealTwinCredential(vendor, input) { return sealTwinCredential(this.root, vendor, credentialPayloadFrom(input, vendor)); }
504
+ sealTwinCredential(vendor, input) {
505
+ // sealed for the one destination it serves, the twin's root: with none set there is nowhere it may go
506
+ const url = loadWorldConfig(this.configRef(), this.root).config.services.find((s) => s.id === vendor)?.root?.url;
507
+ if (!url)
508
+ return Promise.reject(new Error(`the ${vendor} twin has no root: \`volter twin ${vendor} root <url>\` sets where its credential goes, then seal it`));
509
+ return sealTwinCredential(this.root, vendor, credentialPayloadFrom(input, vendor), undefined, destinationOf(url));
510
+ }
505
511
  /** A root's credential when none is piped in: the signed-in vault, else the repo's `.env` (decision 0033). */
506
512
  rootCredentialInput(vendor) { return rootCredentialInput(this.root, vendor); }
507
513
  /** Refresh one twin from its root now. */
508
514
  async refreshTwin(vendor, opts = {}) {
509
515
  const { controlRoot, root } = this.rootOf(vendor);
516
+ // a credential sealed before bindings is bound to this root first: the person at this machine set both
517
+ // credentials sealed before bindings are bound first, each to its twin's root in world.json (the person at this
518
+ // machine set both); the inventory is the World's own, so it takes every twin's root, not just this one's
519
+ const roots = () => Object.fromEntries(loadWorldConfig(this.configRef(), this.root).config.services.filter((s) => s.id && s.root?.url).map((s) => [s.id, s.root.url]));
520
+ const legacy = await bindLegacyCredentials(this.root, roots);
521
+ if (!legacy.ready)
522
+ throw new Error('the credentials beside this World could not be inventoried');
510
523
  return refreshTwin({ worldRoot: this.root, vendor, controlRoot, root, ...(opts.force ? { force: true } : {}) });
511
524
  }
512
525
  rootOf(vendor) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/world",
3
- "version": "2.0.32",
3
+ "version": "2.0.33",
4
4
  "description": "Run your app against a world of twins. `World` and `Repo` — one method per verb: init, up, run, log, diff, reset, branch, checkout, clone, fetch, changeset, push — and the `volter` command, one client of them.",
5
5
  "keywords": [
6
6
  "volter",
@@ -30,8 +30,8 @@
30
30
  ],
31
31
  "dependencies": {
32
32
  "@modelcontextprotocol/sdk": "^1.30.1",
33
- "@volter/world-core": "2.0.17",
34
- "@volter/world-runtime": "2.0.30",
33
+ "@volter/world-core": "2.0.18",
34
+ "@volter/world-runtime": "2.0.31",
35
35
  "zod": "^4.6.5"
36
36
  },
37
37
  "peerDependencies": {
package/src/world.ts CHANGED
@@ -8,14 +8,14 @@ import clockForm from '@volter/world-core/world-clock';
8
8
  import {
9
9
  activateScript, approveWorldChangeset, branchWorld, clockFile, checkoutWorld, createWorldChangeset, diffWorld, downWorld, fetchFromOrigin, findWorldChangeset, initWorld, listWorldChangesets, listWorldMarks, listWorlds, markWorld, pushWorldChangeset, rebaseWorldChangeset, replayWorldChangeset, resetWorld, runWithWorldEnv, seedWorld, shellWorld,
10
10
  statusWorld, statusWorldChangeset, upWorld, verifyWorldChangeset, worldLedgers, worldOrigin,
11
- deployWorld, loadWorldConfig, writeWorldConfig, readServeRecord, refreshTwin, serveWorld, serveWorldView, findInstalledPackage, type ServedWorld, type ViewConsole, type WorldView, rootForControlRoot, sealTwinCredential, sealedCredentialInfo, setTwinRoot, credentialPayloadFrom, rootCredentialInput, withoutVaultSignIn,
11
+ bindLegacyCredentials, deployWorld, loadWorldConfig, writeWorldConfig, readServeRecord, refreshTwin, serveWorld, serveWorldView, findInstalledPackage, type ServedWorld, type ViewConsole, type WorldView, rootForControlRoot, sealTwinCredential, sealedCredentialInfo, setTwinRoot, credentialPayloadFrom, rootCredentialInput, withoutVaultSignIn,
12
12
  type DeployTwinOutcome, type MaterializedRoot,
13
13
  type ChangesetLocation, type InitOptions, type InitResult, type WorldInstance, materializeRoots } from '@volter/world-runtime';
14
14
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
15
15
  import { dirname, join, resolve } from 'node:path';
16
16
  import { requireToken, storeToken } from './credentials.ts';
17
17
  import { currentBranch, findWorldRoot, mainBranch, requireWorldRoot, setCurrentBranch, worldConfigRelative, worldEnvPath, worldSeedPath } from './locate.ts';
18
- import { parentEntries, rebaseBranch, type Receipt, type RootConfig } from '@volter/world-core';
18
+ import { destinationOf, parentEntries, rebaseBranch, type Receipt, type RootConfig } from '@volter/world-core';
19
19
 
20
20
  export type WorldRef = { name?: string; root?: string };
21
21
  export type WorldMode = 'local' | 'share' | 'sealed';
@@ -460,12 +460,23 @@ export class World {
460
460
  if (inst?.running) { materializeRoots(loadWorldConfig(this.configRef(), this.root).config, inst.dirs.data, this.root); }
461
461
  }
462
462
  /** Seal the vendor's credential beside the world (a bare token, or a JSON payload). Never readable back. */
463
- sealTwinCredential(vendor: string, input: string): Promise<{ placedAt: string; fingerprint: string }> { return sealTwinCredential(this.root, vendor, credentialPayloadFrom(input, vendor)); }
463
+ sealTwinCredential(vendor: string, input: string): Promise<{ placedAt: string; fingerprint: string }> {
464
+ // sealed for the one destination it serves, the twin's root: with none set there is nowhere it may go
465
+ const url = (loadWorldConfig(this.configRef(), this.root).config.services as Array<{ id?: string; root?: { url?: string } }>).find((s) => s.id === vendor)?.root?.url;
466
+ if (!url) return Promise.reject(new Error(`the ${vendor} twin has no root: \`volter twin ${vendor} root <url>\` sets where its credential goes, then seal it`));
467
+ return sealTwinCredential(this.root, vendor, credentialPayloadFrom(input, vendor), undefined, destinationOf(url));
468
+ }
464
469
  /** A root's credential when none is piped in: the signed-in vault, else the repo's `.env` (decision 0033). */
465
470
  rootCredentialInput(vendor: string): ReturnType<typeof rootCredentialInput> { return rootCredentialInput(this.root, vendor); }
466
471
  /** Refresh one twin from its root now. */
467
472
  async refreshTwin(vendor: string, opts: { force?: boolean } = {}): Promise<{ refreshed: boolean; reason?: string; report?: { observed: number; appended: number; unchanged: number; removed: number } }> {
468
473
  const { controlRoot, root } = this.rootOf(vendor);
474
+ // a credential sealed before bindings is bound to this root first: the person at this machine set both
475
+ // credentials sealed before bindings are bound first, each to its twin's root in world.json (the person at this
476
+ // machine set both); the inventory is the World's own, so it takes every twin's root, not just this one's
477
+ const roots = () => Object.fromEntries((loadWorldConfig(this.configRef(), this.root).config.services as Array<{ id?: string; root?: { url?: string } }>).filter((s) => s.id && s.root?.url).map((s) => [s.id!, s.root!.url!]));
478
+ const legacy = await bindLegacyCredentials(this.root, roots);
479
+ if (!legacy.ready) throw new Error('the credentials beside this World could not be inventoried');
469
480
  return refreshTwin({ worldRoot: this.root, vendor, controlRoot, root, ...(opts.force ? { force: true } : {}) });
470
481
  }
471
482
  private rootOf(vendor: string): { controlRoot: string; root: MaterializedRoot } {