@volter/world 3.0.163 → 3.0.165

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.
@@ -13,7 +13,7 @@ You need Node 22.6+, npm and registry access during installation. The browser pa
13
13
  Copy this starter into a new folder:
14
14
 
15
15
  ```console
16
- $ npx @volter/world@3.0.163 example customer-onboarding --dir onboarding
16
+ $ npx @volter/world@3.0.165 example customer-onboarding --dir onboarding
17
17
  $ cd onboarding
18
18
  $ npm install
19
19
  ```
@@ -41,6 +41,8 @@ $ ./node_modules/.bin/volter world log
41
41
 
42
42
  Read the customer returned by Stripe, the signup row returned by SQL and the email returned by Resend. `completion: complete` means delivery was observed, not merely that a send request was accepted. Opaque IDs can differ between fresh Worlds; customer identity, stored fields and delivery status are the useful outcomes.
43
43
 
44
+ The SQL `completion` column retains the last send result. `run.mjs read` and the browser combine that saved row with Resend's current `last_event` to derive `complete` or `partial-email-bounced`. Observing delivery leaves the SQL row unchanged.
45
+
44
46
  ## Observe two different failures
45
47
 
46
48
  A refusing recipient's mail server creates a bounce after the send was accepted:
@@ -83,16 +85,29 @@ Then start the app through the World:
83
85
  $ ./node_modules/.bin/volter world run -- node server.mjs
84
86
  ```
85
87
 
86
- Open the printed app URL in your browser. Submit a signup, advance the World clock in another terminal and reload. The page shows the completion state and full stored outcome. Its server registers its app URL with the World after listening; consumers can discover it with `world app-url`.
88
+ The server registers its app URL with the World after listening. In another terminal, from the same application folder, read that endpoint with the installed operator command:
89
+
90
+ ```console
91
+ $ ./node_modules/.bin/volter-world app-url onboarding --root .
92
+ ```
93
+
94
+ `onboarding` is the World name selected by the init command above; use your current branch's name if you have branched. `--root .` selects this application's World. Open the returned URL or the server's printed app URL in your browser. Submit a signup, advance the World clock in another terminal and reload. The page shows the completion state and full stored outcome. [The CLI reference](https://world.volter.ai/docs/reference/cli.html#the-world-itself) describes endpoint discovery and registration.
95
+
96
+ To have the World start and stop the app with its services, declare `server.mjs` as a process service using [run a full stack](https://world.volter.ai/docs/guides/run-a-full-stack.html#the-app-as-a-service). This server honors the World-assigned `PORT`; use `/` as its HTTP readiness path. The manual command above chooses an available port when `PORT` is absent.
87
97
 
88
- To open the World dashboard, stop the app with Ctrl-C first, then switch the owned compute to the viewer:
98
+ To open the World dashboard, switch the owned compute to the viewer. If you started the app
99
+ manually with `world run`, stop that app with Ctrl-C first. If you declared it as a process
100
+ service, the `down` command below stops it with the World's other services:
89
101
 
90
102
  ```console
91
103
  $ ./node_modules/.bin/volter world down
92
104
  $ ./node_modules/.bin/volter world view --no-open
93
105
  ```
94
106
 
95
- The viewer prints the dashboard URL and keeps running. Start `world run -- node server.mjs` in a second terminal if you want the application alongside it. Open **Stripe** under **Records**, then select the customer to inspect its name, email and stored ID. Open **Resend** to inspect the accepted email and delivery event. **Board** opens the vendor pages declared by the installed twin; its root workspace may cover settings rather than customers. Provisioning remains in full history even when hidden from the business-activity view. Read the application's SQL completion record with `run.mjs read` or its browser page.
107
+ The viewer prints the dashboard URL and keeps running. In manual app mode, start
108
+ `world run -- node server.mjs` in a second terminal if you want the application alongside it.
109
+ With a declared process service, the viewer starts the app as part of the World; no second
110
+ app command is needed. Open **Stripe** under **Records**, then select the customer to inspect its name, email and stored ID. Open **Resend** to inspect the accepted email and delivery event. **Board** opens the vendor pages declared by the installed twin; its root workspace may cover settings rather than customers. Provisioning remains in full history even when hidden from the business-activity view. Read the application's combined signup, customer and delivery result with `run.mjs read` or its browser page.
96
111
 
97
112
  Stop app consumers before stopping the viewer. `down` retains vendor and declared application state. [Resume](https://world.volter.ai/docs/guides/resume-a-world.html) explains continuing it; [demo a workflow](https://world.volter.ai/docs/guides/demo-a-workflow.html) uses this same application for a repeatable baseline, refusal and retry. Reset only your disposable example World; it discards that branch's work.
98
113
 
@@ -11,11 +11,12 @@
11
11
  "pg": "8.16.3"
12
12
  },
13
13
  "devDependencies": {
14
- "@volter/world": "3.0.163",
15
- "@volter/world-core": "3.0.153",
16
- "@volter/world-runtime": "3.0.161",
17
- "@volter/world-console": "3.0.150",
14
+ "@volter/world": "3.0.165",
15
+ "@volter/world-core": "3.0.155",
16
+ "@volter/world-runtime": "3.0.163",
17
+ "@volter/world-console": "3.0.151",
18
18
  "@volter/twin-stripe": "3.0.2",
19
- "@volter/twin-resend": "1.0.1"
19
+ "@volter/twin-resend": "1.0.1",
20
+ "@volter/world-ui": "1.0.155"
20
21
  }
21
22
  }
@@ -25,7 +25,7 @@ const server = createServer(async (request, response) => {
25
25
  response.end(`<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Customer onboarding</title><link rel="stylesheet" href="/brand/tokens.css"><link rel="stylesheet" href="/style.css"><main><header><img src="/brand/world.svg" width="32" height="32" alt="Volter World"><h1>Customer onboarding</h1></header><p>Create a customer, save an application row and request a welcome email.</p><form method="post" action="/signup"><label>Name <input name="name" value="Ada" required></label><label>Email <input name="email" type="email" value="owner@world.test" required></label><button>Create account</button></form><h2>Accounts</h2><p>Reload after advancing the World clock to observe delivery. A refused email leaves the customer and application row intact.</p>${rows.map(r => `<article><h3>${escape(r.name)}</h3><p>${escape(r.email)} · <strong>${escape(r.completion)}</strong></p><p>Stripe: <code>${escape(r.customer_id)}</code> · Email: <code>${escape(r.email_id ?? 'not accepted')}</code></p><details><summary>Stored outcome</summary><pre>${escape(JSON.stringify(r, null, 2))}</pre></details></article>`).join('') || '<p>No customers yet.</p>'}</main></html>`);
26
26
  } catch (error) { response.writeHead(500, { 'Content-Type': 'text/plain' }); response.end(error.message); }
27
27
  });
28
- await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
28
+ await new Promise(resolve => server.listen(Number(process.env.PORT ?? 0), '127.0.0.1', resolve));
29
29
  const url = `http://127.0.0.1:${server.address().port}`;
30
30
  setAppUrl(process.env.VOLTER_WORLD, url, { root: process.cwd() });
31
31
  console.log(`Customer onboarding ${url}`);
@@ -13,7 +13,7 @@ Give two workers the same Stripe baseline, let them change different existing cu
13
13
  Use an empty parent folder. Create the baseline starter there:
14
14
 
15
15
  ```console
16
- $ npx @volter/world@3.0.163 example integration-branches --dir baseline
16
+ $ npx @volter/world@3.0.165 example integration-branches --dir baseline
17
17
  $ cd baseline
18
18
  $ npm install
19
19
  $ ./node_modules/.bin/volter world init --name baseline --twins stripe
@@ -38,9 +38,9 @@ The stopped baseline retains its history and can supply local path remotes. Defa
38
38
  Create three more copies in the same parent folder:
39
39
 
40
40
  ```console
41
- $ npx @volter/world@3.0.163 example integration-branches --dir integration
42
- $ npx @volter/world@3.0.163 example integration-branches --dir worker-a
43
- $ npx @volter/world@3.0.163 example integration-branches --dir worker-b
41
+ $ npx @volter/world@3.0.165 example integration-branches --dir integration
42
+ $ npx @volter/world@3.0.165 example integration-branches --dir worker-a
43
+ $ npx @volter/world@3.0.165 example integration-branches --dir worker-b
44
44
  ```
45
45
 
46
46
  In `integration`, then `worker-a`, then `worker-b`, install dependencies and initialize with the folder's name. Before a first boot or app write, link and pull the baseline:
@@ -9,10 +9,11 @@
9
9
  "stripe": "17.7.0"
10
10
  },
11
11
  "devDependencies": {
12
- "@volter/world": "3.0.163",
13
- "@volter/world-core": "3.0.153",
14
- "@volter/world-runtime": "3.0.161",
15
- "@volter/world-console": "3.0.150",
16
- "@volter/twin-stripe": "3.0.2"
12
+ "@volter/world": "3.0.165",
13
+ "@volter/world-core": "3.0.155",
14
+ "@volter/world-runtime": "3.0.163",
15
+ "@volter/world-console": "3.0.151",
16
+ "@volter/twin-stripe": "3.0.2",
17
+ "@volter/world-ui": "1.0.155"
17
18
  }
18
19
  }
@@ -19,7 +19,7 @@ A catalog page may select a newer default. Keep this example's manifest and inst
19
19
  together; its recorded workflow does not establish behavior for a different release.
20
20
 
21
21
  ```console
22
- $ npx @volter/world@3.0.163 example workplace-agent --dir workplace
22
+ $ npx @volter/world@3.0.165 example workplace-agent --dir workplace
23
23
  $ cd workplace
24
24
  $ npm install
25
25
  $ ./node_modules/.bin/volter world init --name workplace --twins github,linear,slack
@@ -11,12 +11,13 @@
11
11
  "@slack/web-api": "7.19.0"
12
12
  },
13
13
  "devDependencies": {
14
- "@volter/world": "3.0.163",
15
- "@volter/world-core": "3.0.153",
16
- "@volter/world-runtime": "3.0.161",
17
- "@volter/world-console": "3.0.150",
14
+ "@volter/world": "3.0.165",
15
+ "@volter/world-core": "3.0.155",
16
+ "@volter/world-runtime": "3.0.163",
17
+ "@volter/world-console": "3.0.151",
18
18
  "@volter/twin-github": "3.0.9",
19
19
  "@volter/twin-linear": "3.0.5",
20
- "@volter/twin-slack": "3.0.8"
20
+ "@volter/twin-slack": "3.0.8",
21
+ "@volter/world-ui": "1.0.155"
21
22
  }
22
23
  }
@@ -2,7 +2,7 @@ export type Agent = 'claude' | 'cursor' | 'vscode' | 'codex';
2
2
  export declare const AGENTS: readonly Agent[];
3
3
  /** The skill's folder: the one shipped in the package (skills/, copied in at pack time), else this checkout's. */
4
4
  export declare function skillSource(): string;
5
- /** How an agent starts the server: `volter mcp` when the command is on PATH, else this installation by its path. */
5
+ /** Start this installation's server under this runtime; PATH may contain another version of `volter`. */
6
6
  export declare function mcpCommand(): {
7
7
  command: string;
8
8
  args: string[];
@@ -32,10 +32,8 @@ export function skillSource() {
32
32
  }
33
33
  throw new Error('the volter-world skill is not in this installation (skills/volter-world)');
34
34
  }
35
- /** How an agent starts the server: `volter mcp` when the command is on PATH, else this installation by its path. */
35
+ /** Start this installation's server under this runtime; PATH may contain another version of `volter`. */
36
36
  export function mcpCommand() {
37
- if (which('volter'))
38
- return { command: 'volter', args: ['mcp'] };
39
37
  const cli = fileURLToPath(new URL(`./cli${import.meta.url.endsWith('.ts') ? '.ts' : '.js'}`, import.meta.url));
40
38
  return { command: process.execPath, args: [cli, 'mcp'] };
41
39
  }
package/dist/src/cli.js CHANGED
@@ -4,7 +4,7 @@
4
4
  // cwd; `volter twin <vendor> …` runs one twin on its own; `volter remote …` is the hosted remote.
5
5
  // One client of `World`; it adds nothing the SDK does not have.
6
6
  import { detectRepoVendors, errorMessage, formatInitReport, resolveCatalog, findInstalledPackage, packCli, serveConsoleFor, catalogDefaults, CATALOG_INDEX_PACKAGE } from '@volter/world-runtime';
7
- import { readFileSync, realpathSync } from 'node:fs';
7
+ import { realpathSync } from 'node:fs';
8
8
  import { fileURLToPath } from 'node:url';
9
9
  import { spawn, spawnSync } from 'node:child_process';
10
10
  import { createHash } from 'node:crypto';
@@ -15,7 +15,7 @@ import { forgetPlatform, forgetToken, platformOrigin, storePlatform, storeToken,
15
15
  import { parseOriginUrl, World } from "./world.js";
16
16
  import { copyExample, examples } from "./examples.js";
17
17
  import { waitForServeShutdown } from "./serve-shutdown.js";
18
- import { startUpdateCheck } from "./update-notice.js";
18
+ import { currentVersion, startUpdateCheck } from "./update-notice.js";
19
19
  import { withoutVaultSignIn } from '@volter/world-runtime';
20
20
  /** Open a URL in the person's browser; a machine without one only loses the convenience. */
21
21
  function openInBrowser(url) {
@@ -1095,16 +1095,7 @@ async function offerAgents() {
1095
1095
  }
1096
1096
  /** This package's version, for the MCP server's handshake. */
1097
1097
  function ownVersion() {
1098
- // src/cli.ts in the repository, dist/src/cli.js in the published package: the package's manifest is one or two up
1099
- for (const up of ['../package.json', '../../package.json']) {
1100
- try {
1101
- const m = JSON.parse(readFileSync(fileURLToPath(new URL(up, import.meta.url)), 'utf8'));
1102
- if (m.name === '@volter/world' && m.version)
1103
- return m.version;
1104
- }
1105
- catch { /* not there */ }
1106
- }
1107
- return '0.0.0';
1098
+ return currentVersion() ?? '0.0.0';
1108
1099
  }
1109
1100
  async function main() {
1110
1101
  const [noun, verb, ...args] = process.argv.slice(2);
@@ -1,5 +1,5 @@
1
- /** This package's version, read from its own package.json. */
2
- export declare function currentVersion(): string;
1
+ /** This package's version, read from its own package.json, or null when it cannot be established. */
2
+ export declare function currentVersion(): string | null;
3
3
  /** Whether `a` is a newer release than `b` (major.minor.patch; a prerelease is not offered). */
4
4
  export declare function newer(a: string, b: string): boolean;
5
5
  /** Start the day's check if one is due, while the command runs; the returned function, called when the command is
@@ -7,14 +7,18 @@ import { dirname, join } from 'node:path';
7
7
  import { credentialsPath } from "./credentials.js";
8
8
  const DAY = 86_400_000;
9
9
  const REGISTRY = 'https://registry.npmjs.org/@volter/world/latest';
10
- /** This package's version, read from its own package.json. */
10
+ /** This package's version, read from its own package.json, or null when it cannot be established. */
11
11
  export function currentVersion() {
12
- try {
13
- return JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version ?? '0.0.0';
14
- }
15
- catch {
16
- return '0.0.0';
12
+ // src/update-notice.ts in the repository, dist/src/update-notice.js in the published package
13
+ for (const up of ['../package.json', '../../package.json']) {
14
+ try {
15
+ const m = JSON.parse(readFileSync(new URL(up, import.meta.url), 'utf8'));
16
+ if (m.name === '@volter/world' && typeof m.version === 'string' && m.version)
17
+ return m.version;
18
+ }
19
+ catch { /* not there or unreadable */ }
17
20
  }
21
+ return null;
18
22
  }
19
23
  /** Whether `a` is a newer release than `b` (major.minor.patch; a prerelease is not offered). */
20
24
  export function newer(a, b) {
@@ -33,6 +37,9 @@ export function newer(a, b) {
33
37
  export function startUpdateCheck(args, env = process.env) {
34
38
  if (env.VOLTER_NO_UPDATE_NOTIFIER || env.CI || args.includes('--json') || !process.stderr.isTTY)
35
39
  return async () => undefined;
40
+ const here = currentVersion();
41
+ if (!here)
42
+ return async () => undefined;
36
43
  const path = join(dirname(credentialsPath()), 'update-check.json');
37
44
  let kept = null;
38
45
  try {
@@ -53,7 +60,6 @@ export function startUpdateCheck(args, env = process.env) {
53
60
  : Promise.resolve(kept?.latest ?? null);
54
61
  return async () => {
55
62
  const latest = await Promise.race([check, new Promise((r) => setTimeout(() => r(null), 800))]);
56
- const here = currentVersion();
57
63
  if (latest && newer(latest, here))
58
64
  process.stderr.write(`\nvolter ${latest} is out (you have ${here}): npm install -g @volter/world\n`);
59
65
  };
@@ -13,7 +13,7 @@ You need Node 22.6+, npm and registry access during installation. The browser pa
13
13
  Copy this starter into a new folder:
14
14
 
15
15
  ```console
16
- $ npx @volter/world@3.0.163 example customer-onboarding --dir onboarding
16
+ $ npx @volter/world@3.0.165 example customer-onboarding --dir onboarding
17
17
  $ cd onboarding
18
18
  $ npm install
19
19
  ```
@@ -41,6 +41,8 @@ $ ./node_modules/.bin/volter world log
41
41
 
42
42
  Read the customer returned by Stripe, the signup row returned by SQL and the email returned by Resend. `completion: complete` means delivery was observed, not merely that a send request was accepted. Opaque IDs can differ between fresh Worlds; customer identity, stored fields and delivery status are the useful outcomes.
43
43
 
44
+ The SQL `completion` column retains the last send result. `run.mjs read` and the browser combine that saved row with Resend's current `last_event` to derive `complete` or `partial-email-bounced`. Observing delivery leaves the SQL row unchanged.
45
+
44
46
  ## Observe two different failures
45
47
 
46
48
  A refusing recipient's mail server creates a bounce after the send was accepted:
@@ -83,16 +85,29 @@ Then start the app through the World:
83
85
  $ ./node_modules/.bin/volter world run -- node server.mjs
84
86
  ```
85
87
 
86
- Open the printed app URL in your browser. Submit a signup, advance the World clock in another terminal and reload. The page shows the completion state and full stored outcome. Its server registers its app URL with the World after listening; consumers can discover it with `world app-url`.
88
+ The server registers its app URL with the World after listening. In another terminal, from the same application folder, read that endpoint with the installed operator command:
89
+
90
+ ```console
91
+ $ ./node_modules/.bin/volter-world app-url onboarding --root .
92
+ ```
93
+
94
+ `onboarding` is the World name selected by the init command above; use your current branch's name if you have branched. `--root .` selects this application's World. Open the returned URL or the server's printed app URL in your browser. Submit a signup, advance the World clock in another terminal and reload. The page shows the completion state and full stored outcome. [The CLI reference](https://world.volter.ai/docs/reference/cli.html#the-world-itself) describes endpoint discovery and registration.
95
+
96
+ To have the World start and stop the app with its services, declare `server.mjs` as a process service using [run a full stack](https://world.volter.ai/docs/guides/run-a-full-stack.html#the-app-as-a-service). This server honors the World-assigned `PORT`; use `/` as its HTTP readiness path. The manual command above chooses an available port when `PORT` is absent.
87
97
 
88
- To open the World dashboard, stop the app with Ctrl-C first, then switch the owned compute to the viewer:
98
+ To open the World dashboard, switch the owned compute to the viewer. If you started the app
99
+ manually with `world run`, stop that app with Ctrl-C first. If you declared it as a process
100
+ service, the `down` command below stops it with the World's other services:
89
101
 
90
102
  ```console
91
103
  $ ./node_modules/.bin/volter world down
92
104
  $ ./node_modules/.bin/volter world view --no-open
93
105
  ```
94
106
 
95
- The viewer prints the dashboard URL and keeps running. Start `world run -- node server.mjs` in a second terminal if you want the application alongside it. Open **Stripe** under **Records**, then select the customer to inspect its name, email and stored ID. Open **Resend** to inspect the accepted email and delivery event. **Board** opens the vendor pages declared by the installed twin; its root workspace may cover settings rather than customers. Provisioning remains in full history even when hidden from the business-activity view. Read the application's SQL completion record with `run.mjs read` or its browser page.
107
+ The viewer prints the dashboard URL and keeps running. In manual app mode, start
108
+ `world run -- node server.mjs` in a second terminal if you want the application alongside it.
109
+ With a declared process service, the viewer starts the app as part of the World; no second
110
+ app command is needed. Open **Stripe** under **Records**, then select the customer to inspect its name, email and stored ID. Open **Resend** to inspect the accepted email and delivery event. **Board** opens the vendor pages declared by the installed twin; its root workspace may cover settings rather than customers. Provisioning remains in full history even when hidden from the business-activity view. Read the application's combined signup, customer and delivery result with `run.mjs read` or its browser page.
96
111
 
97
112
  Stop app consumers before stopping the viewer. `down` retains vendor and declared application state. [Resume](https://world.volter.ai/docs/guides/resume-a-world.html) explains continuing it; [demo a workflow](https://world.volter.ai/docs/guides/demo-a-workflow.html) uses this same application for a repeatable baseline, refusal and retry. Reset only your disposable example World; it discards that branch's work.
98
113
 
@@ -11,11 +11,12 @@
11
11
  "pg": "8.16.3"
12
12
  },
13
13
  "devDependencies": {
14
- "@volter/world": "3.0.163",
15
- "@volter/world-core": "3.0.153",
16
- "@volter/world-runtime": "3.0.161",
17
- "@volter/world-console": "3.0.150",
14
+ "@volter/world": "3.0.165",
15
+ "@volter/world-core": "3.0.155",
16
+ "@volter/world-runtime": "3.0.163",
17
+ "@volter/world-console": "3.0.151",
18
18
  "@volter/twin-stripe": "3.0.2",
19
- "@volter/twin-resend": "1.0.1"
19
+ "@volter/twin-resend": "1.0.1",
20
+ "@volter/world-ui": "1.0.155"
20
21
  }
21
22
  }
@@ -25,7 +25,7 @@ const server = createServer(async (request, response) => {
25
25
  response.end(`<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Customer onboarding</title><link rel="stylesheet" href="/brand/tokens.css"><link rel="stylesheet" href="/style.css"><main><header><img src="/brand/world.svg" width="32" height="32" alt="Volter World"><h1>Customer onboarding</h1></header><p>Create a customer, save an application row and request a welcome email.</p><form method="post" action="/signup"><label>Name <input name="name" value="Ada" required></label><label>Email <input name="email" type="email" value="owner@world.test" required></label><button>Create account</button></form><h2>Accounts</h2><p>Reload after advancing the World clock to observe delivery. A refused email leaves the customer and application row intact.</p>${rows.map(r => `<article><h3>${escape(r.name)}</h3><p>${escape(r.email)} · <strong>${escape(r.completion)}</strong></p><p>Stripe: <code>${escape(r.customer_id)}</code> · Email: <code>${escape(r.email_id ?? 'not accepted')}</code></p><details><summary>Stored outcome</summary><pre>${escape(JSON.stringify(r, null, 2))}</pre></details></article>`).join('') || '<p>No customers yet.</p>'}</main></html>`);
26
26
  } catch (error) { response.writeHead(500, { 'Content-Type': 'text/plain' }); response.end(error.message); }
27
27
  });
28
- await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
28
+ await new Promise(resolve => server.listen(Number(process.env.PORT ?? 0), '127.0.0.1', resolve));
29
29
  const url = `http://127.0.0.1:${server.address().port}`;
30
30
  setAppUrl(process.env.VOLTER_WORLD, url, { root: process.cwd() });
31
31
  console.log(`Customer onboarding ${url}`);
@@ -13,7 +13,7 @@ Give two workers the same Stripe baseline, let them change different existing cu
13
13
  Use an empty parent folder. Create the baseline starter there:
14
14
 
15
15
  ```console
16
- $ npx @volter/world@3.0.163 example integration-branches --dir baseline
16
+ $ npx @volter/world@3.0.165 example integration-branches --dir baseline
17
17
  $ cd baseline
18
18
  $ npm install
19
19
  $ ./node_modules/.bin/volter world init --name baseline --twins stripe
@@ -38,9 +38,9 @@ The stopped baseline retains its history and can supply local path remotes. Defa
38
38
  Create three more copies in the same parent folder:
39
39
 
40
40
  ```console
41
- $ npx @volter/world@3.0.163 example integration-branches --dir integration
42
- $ npx @volter/world@3.0.163 example integration-branches --dir worker-a
43
- $ npx @volter/world@3.0.163 example integration-branches --dir worker-b
41
+ $ npx @volter/world@3.0.165 example integration-branches --dir integration
42
+ $ npx @volter/world@3.0.165 example integration-branches --dir worker-a
43
+ $ npx @volter/world@3.0.165 example integration-branches --dir worker-b
44
44
  ```
45
45
 
46
46
  In `integration`, then `worker-a`, then `worker-b`, install dependencies and initialize with the folder's name. Before a first boot or app write, link and pull the baseline:
@@ -9,10 +9,11 @@
9
9
  "stripe": "17.7.0"
10
10
  },
11
11
  "devDependencies": {
12
- "@volter/world": "3.0.163",
13
- "@volter/world-core": "3.0.153",
14
- "@volter/world-runtime": "3.0.161",
15
- "@volter/world-console": "3.0.150",
16
- "@volter/twin-stripe": "3.0.2"
12
+ "@volter/world": "3.0.165",
13
+ "@volter/world-core": "3.0.155",
14
+ "@volter/world-runtime": "3.0.163",
15
+ "@volter/world-console": "3.0.151",
16
+ "@volter/twin-stripe": "3.0.2",
17
+ "@volter/world-ui": "1.0.155"
17
18
  }
18
19
  }
@@ -19,7 +19,7 @@ A catalog page may select a newer default. Keep this example's manifest and inst
19
19
  together; its recorded workflow does not establish behavior for a different release.
20
20
 
21
21
  ```console
22
- $ npx @volter/world@3.0.163 example workplace-agent --dir workplace
22
+ $ npx @volter/world@3.0.165 example workplace-agent --dir workplace
23
23
  $ cd workplace
24
24
  $ npm install
25
25
  $ ./node_modules/.bin/volter world init --name workplace --twins github,linear,slack
@@ -11,12 +11,13 @@
11
11
  "@slack/web-api": "7.19.0"
12
12
  },
13
13
  "devDependencies": {
14
- "@volter/world": "3.0.163",
15
- "@volter/world-core": "3.0.153",
16
- "@volter/world-runtime": "3.0.161",
17
- "@volter/world-console": "3.0.150",
14
+ "@volter/world": "3.0.165",
15
+ "@volter/world-core": "3.0.155",
16
+ "@volter/world-runtime": "3.0.163",
17
+ "@volter/world-console": "3.0.151",
18
18
  "@volter/twin-github": "3.0.9",
19
19
  "@volter/twin-linear": "3.0.5",
20
- "@volter/twin-slack": "3.0.8"
20
+ "@volter/twin-slack": "3.0.8",
21
+ "@volter/world-ui": "1.0.155"
21
22
  }
22
23
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/world",
3
- "version": "3.0.163",
3
+ "version": "3.0.165",
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",
@@ -31,12 +31,12 @@
31
31
  ],
32
32
  "dependencies": {
33
33
  "@modelcontextprotocol/sdk": "^1.30.1",
34
- "@volter/world-core": "3.0.153",
35
- "@volter/world-runtime": "3.0.161",
34
+ "@volter/world-core": "3.0.155",
35
+ "@volter/world-runtime": "3.0.163",
36
36
  "zod": "^4.6.5"
37
37
  },
38
38
  "peerDependencies": {
39
- "@volter/world-console": "3.0.150"
39
+ "@volter/world-console": "3.0.151"
40
40
  },
41
41
  "peerDependenciesMeta": {
42
42
  "@volter/world-console": {
@@ -44,7 +44,7 @@
44
44
  }
45
45
  },
46
46
  "devDependencies": {
47
- "@volter/world-console": "3.0.150"
47
+ "@volter/world-console": "3.0.151"
48
48
  },
49
49
  "bin": {
50
50
  "volter": "dist/src/cli.js"
package/src/agents.ts CHANGED
@@ -31,9 +31,8 @@ export function skillSource(): string {
31
31
  throw new Error('the volter-world skill is not in this installation (skills/volter-world)');
32
32
  }
33
33
 
34
- /** How an agent starts the server: `volter mcp` when the command is on PATH, else this installation by its path. */
34
+ /** Start this installation's server under this runtime; PATH may contain another version of `volter`. */
35
35
  export function mcpCommand(): { command: string; args: string[] } {
36
- if (which('volter')) return { command: 'volter', args: ['mcp'] };
37
36
  const cli = fileURLToPath(new URL(`./cli${import.meta.url.endsWith('.ts') ? '.ts' : '.js'}`, import.meta.url));
38
37
  return { command: process.execPath, args: [cli, 'mcp'] };
39
38
  }
package/src/cli.ts CHANGED
@@ -16,7 +16,7 @@ import { forgetPlatform, forgetToken, platformOrigin, requireToken, storePlatfor
16
16
  import { parseOriginUrl, World } from './world.ts';
17
17
  import { copyExample, examples } from './examples.ts';
18
18
  import { waitForServeShutdown } from './serve-shutdown.ts';
19
- import { startUpdateCheck } from './update-notice.ts';
19
+ import { currentVersion, startUpdateCheck } from './update-notice.ts';
20
20
  import type { ViewConsole } from '@volter/world-runtime';
21
21
  import { withoutVaultSignIn } from '@volter/world-runtime';
22
22
 
@@ -814,11 +814,7 @@ async function offerAgents(): Promise<void> {
814
814
 
815
815
  /** This package's version, for the MCP server's handshake. */
816
816
  function ownVersion(): string {
817
- // src/cli.ts in the repository, dist/src/cli.js in the published package: the package's manifest is one or two up
818
- for (const up of ['../package.json', '../../package.json']) {
819
- try { const m = JSON.parse(readFileSync(fileURLToPath(new URL(up, import.meta.url)), 'utf8')) as { name?: string; version?: string }; if (m.name === '@volter/world' && m.version) return m.version; } catch { /* not there */ }
820
- }
821
- return '0.0.0';
817
+ return currentVersion() ?? '0.0.0';
822
818
  }
823
819
 
824
820
  async function main(): Promise<void> {
@@ -11,9 +11,16 @@ const REGISTRY = 'https://registry.npmjs.org/@volter/world/latest';
11
11
 
12
12
  type Checked = { checkedAt: string; latest: string | null };
13
13
 
14
- /** This package's version, read from its own package.json. */
15
- export function currentVersion(): string {
16
- try { return (JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as { version?: string }).version ?? '0.0.0'; } catch { return '0.0.0'; }
14
+ /** This package's version, read from its own package.json, or null when it cannot be established. */
15
+ export function currentVersion(): string | null {
16
+ // src/update-notice.ts in the repository, dist/src/update-notice.js in the published package
17
+ for (const up of ['../package.json', '../../package.json']) {
18
+ try {
19
+ const m = JSON.parse(readFileSync(new URL(up, import.meta.url), 'utf8')) as { name?: string; version?: string };
20
+ if (m.name === '@volter/world' && typeof m.version === 'string' && m.version) return m.version;
21
+ } catch { /* not there or unreadable */ }
22
+ }
23
+ return null;
17
24
  }
18
25
 
19
26
  /** Whether `a` is a newer release than `b` (major.minor.patch; a prerelease is not offered). */
@@ -28,6 +35,8 @@ export function newer(a: string, b: string): boolean {
28
35
  * done, waits a moment at most for it and prints the notice when there is one. Off, it does nothing and asks nothing. */
29
36
  export function startUpdateCheck(args: string[], env: NodeJS.ProcessEnv = process.env): () => Promise<void> {
30
37
  if (env.VOLTER_NO_UPDATE_NOTIFIER || env.CI || args.includes('--json') || !process.stderr.isTTY) return async () => undefined;
38
+ const here = currentVersion();
39
+ if (!here) return async () => undefined;
31
40
  const path = join(dirname(credentialsPath()), 'update-check.json');
32
41
  let kept: Checked | null = null;
33
42
  try { if (existsSync(path)) kept = JSON.parse(readFileSync(path, 'utf8')) as Checked; } catch { kept = null; }
@@ -38,7 +47,6 @@ export function startUpdateCheck(args: string[], env: NodeJS.ProcessEnv = proces
38
47
  : Promise.resolve(kept?.latest ?? null);
39
48
  return async () => {
40
49
  const latest = await Promise.race([check, new Promise<null>((r) => setTimeout(() => r(null), 800))]);
41
- const here = currentVersion();
42
50
  if (latest && newer(latest, here)) process.stderr.write(`\nvolter ${latest} is out (you have ${here}): npm install -g @volter/world\n`);
43
51
  };
44
52
  }