my-durable 0.0.3 → 0.0.4

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/README.md CHANGED
@@ -8,18 +8,20 @@
8
8
 
9
9
  📺 Watch this video to get the full breakdown of this codebase: **[Pi Durable PROACTIVE Agents](https://youtu.be/DNDYYHsypZg)**
10
10
 
11
- ## Quick start (`md`)
11
+ ## Quick start (`dpi`)
12
12
 
13
13
  ```sh
14
14
  npm install -g my-durable
15
15
 
16
- md hub # web UI + device tunnel server → http://127.0.0.1:4781
17
- md device # device daemon (pairs to a hub, serves this machine)
18
- md tui # terminal admin client
19
- md --help # commands, flags and environment variables
16
+ dpi hub # web UI + device tunnel server → http://127.0.0.1:4781
17
+ dpi device # device daemon (pairs to a hub, serves this machine)
18
+ dpi tui # terminal admin client
19
+ dpi --help # commands, flags and environment variables
20
+ dpi update --check # is a newer published version available?
21
+ dpi update # install it (npm i -g my-durable@latest)
20
22
  ```
21
23
 
22
- `md hub` is the only component that other machines talk to, so it is also the one
24
+ `dpi hub` is the only component that other machines talk to, so it is also the one
23
25
  to put behind a TLS proxy or tunnel (it refuses anything that did not arrive over
24
26
  HTTPS). Pair a device by opening the hub, pressing **Pair a device**, and entering
25
27
  the code — or open the pairing link directly on the machine being added.
package/bin/dpi.mjs ADDED
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env node
2
+ // `dpi` — one command for the Durable Pi stack.
3
+ // dpi hub [--port N] [--host H] [--public-host H] web UI + device tunnel server
4
+ // dpi device device-side daemon
5
+ // dpi tui terminal admin client
6
+ // Every subcommand runs the TypeScript entry through tsx, so no build step is needed.
7
+ import { access } from "node:fs/promises";
8
+ import { fileURLToPath } from "node:url";
9
+
10
+ /** Subcommand → TypeScript entry, relative to this file. */
11
+ const COMMANDS = {
12
+ hub: "../apps/dpi-playground/apps/dpi-playground-hub/src/main.ts",
13
+ device: "../apps/dpi-backend/src/main.ts",
14
+ tui: "../apps/dpi-tui/src/main.ts",
15
+ };
16
+
17
+ const HELP = `dpi — Durable Pi
18
+
19
+ Usage:
20
+ dpi hub [--port 4781] [--host 127.0.0.1] [--public-host <host>]
21
+ Web UI + device tunnel server (devices dial this).
22
+ dpi device
23
+ Device-side daemon (pairs to a hub, serves one machine's agents).
24
+ dpi tui
25
+ Terminal admin client.
26
+ dpi help | dpi --help | dpi -h
27
+ dpi version | dpi --version | dpi -v
28
+ dpi update [--check]
29
+ --check compare the installed version with the registry, change nothing
30
+ (no flag) install the latest published version globally
31
+
32
+ Environment (hub): DPI_HOME, DPI_HUB_PORT, DPI_HUB_HOST, DPI_HUB_PUBLIC_HOST,
33
+ DPI_HUB_ORIGIN, DPI_HUB_REQUIRE_TLS
34
+ Environment (device): DPI_HUB_URL, DPI_HOME
35
+ `;
36
+
37
+ /**
38
+ * tsx is a dependency of the published package, but a bare repository checkout has it only
39
+ * inside the per-app node_modules. Try the normal resolution first, then the known local
40
+ * copies, so `dpi` works in both places without a root install.
41
+ */
42
+ async function loadTsx() {
43
+ try {
44
+ return await import("tsx/esm/api");
45
+ } catch (error) {
46
+ if (error?.code !== "ERR_MODULE_NOT_FOUND") throw error;
47
+ }
48
+ const candidates = [
49
+ "../apps/dpi-playground/node_modules/tsx/dist/esm/api/index.mjs",
50
+ "../apps/dpi-backend/node_modules/tsx/dist/esm/api/index.mjs",
51
+ "../apps/dpi-sdk/node_modules/tsx/dist/esm/api/index.mjs",
52
+ "../apps/dpi-tui/node_modules/tsx/dist/esm/api/index.mjs",
53
+ ];
54
+ for (const relative of candidates) {
55
+ const url = new URL(relative, import.meta.url);
56
+ try {
57
+ await access(fileURLToPath(url));
58
+ return await import(url.href);
59
+ } catch {
60
+ // try the next one
61
+ }
62
+ }
63
+ throw new Error(
64
+ "dpi: cannot find tsx. Install the package (npm install -g my-durable) or run npm install in an app directory.",
65
+ );
66
+ }
67
+
68
+ const [first, ...rest] = process.argv.slice(2);
69
+
70
+ if (!first || first === "help" || first === "--help" || first === "-h") {
71
+ process.stdout.write(HELP);
72
+ process.exit(0);
73
+ }
74
+
75
+ if (first === "version" || first === "--version" || first === "-v") {
76
+ const { readFileSync } = await import("node:fs");
77
+ const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
78
+ process.stdout.write(`dpi (my-durable) ${pkg.version}\n`);
79
+ process.exit(0);
80
+ }
81
+
82
+ if (first === "update" || first === "upgrade") {
83
+ const { readFileSync } = await import("node:fs");
84
+ const { spawn } = await import("node:child_process");
85
+ const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
86
+ const current = pkg.version;
87
+ const checkOnly = rest.includes("--check") || rest.includes("-c");
88
+
89
+ let latest;
90
+ try {
91
+ const res = await fetch("https://registry.npmjs.org/my-durable/latest", {
92
+ headers: { accept: "application/json" },
93
+ });
94
+ if (!res.ok) throw new Error(`registry responded ${res.status}`);
95
+ latest = (await res.json()).version;
96
+ } catch (error) {
97
+ process.stderr.write(`dpi update: cannot reach the npm registry (${error.message})\n`);
98
+ process.exit(1);
99
+ }
100
+
101
+ // Numeric triple compare; a prerelease tag on either side counts as older.
102
+ const rank = (v) => {
103
+ const [core, ...pre] = String(v).split("-");
104
+ const nums = core.split(".").map((n) => Number.parseInt(n, 10) || 0);
105
+ return { nums: [nums[0] ?? 0, nums[1] ?? 0, nums[2] ?? 0], pre: pre.length > 0 };
106
+ };
107
+ const a = rank(current);
108
+ const b = rank(latest);
109
+ let order = 0;
110
+ for (let i = 0; i < 3; i += 1) {
111
+ if (a.nums[i] !== b.nums[i]) {
112
+ order = a.nums[i] < b.nums[i] ? -1 : 1;
113
+ break;
114
+ }
115
+ }
116
+ if (order === 0 && a.pre !== b.pre) order = a.pre ? -1 : 1;
117
+
118
+ if (order >= 0) {
119
+ process.stdout.write(`dpi ${current} is up to date (latest ${latest}).\n`);
120
+ process.exit(0);
121
+ }
122
+ process.stdout.write(`dpi ${current} -> ${latest} available.\n`);
123
+ if (checkOnly) {
124
+ process.stdout.write("Run `dpi update` to install it.\n");
125
+ process.exit(0);
126
+ }
127
+
128
+ const child = spawn("npm", ["install", "-g", `my-durable@${latest}`], { stdio: "inherit" });
129
+ child.on("error", (error) => {
130
+ process.stderr.write(`dpi update: cannot run npm (${error.message})\n`);
131
+ process.exit(1);
132
+ });
133
+ child.on("exit", (code) => {
134
+ if (code !== 0) {
135
+ process.stderr.write(`dpi update: npm exited ${code}; still on ${current}.\n`);
136
+ process.exit(code ?? 1);
137
+ }
138
+ process.stdout.write(`dpi updated ${current} -> ${latest}.\n`);
139
+ process.exit(0);
140
+ });
141
+ } else if (!Object.hasOwn(COMMANDS, first)) {
142
+ process.stderr.write(`dpi: unknown command '${first}'\n\n${HELP}`);
143
+ process.exit(2);
144
+ } else {
145
+ // The entries parse process.argv.slice(2) themselves: hand them their own args.
146
+ process.argv = [process.argv[0], process.argv[1], ...rest];
147
+ const { register } = await loadTsx();
148
+ register();
149
+ await import(COMMANDS[first]);
150
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "my-durable",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Durable Pi — the durable agent network: hub web UI, device backend daemon, TUI admin and the shared SDK, packaged as one runnable monorepo install",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -15,7 +15,7 @@
15
15
  "node": ">=22.0.0"
16
16
  },
17
17
  "bin": {
18
- "md": "bin/md.mjs"
18
+ "dpi": "bin/dpi.mjs"
19
19
  },
20
20
  "scripts": {
21
21
  "hub": "tsx apps/dpi-playground/apps/dpi-playground-hub/src/main.ts",
package/bin/md.mjs DELETED
@@ -1,88 +0,0 @@
1
- #!/usr/bin/env node
2
- // `md` — one command for the Durable Pi stack.
3
- // md hub [--port N] [--host H] [--public-host H] web UI + device tunnel server
4
- // md device device-side daemon
5
- // md tui terminal admin client
6
- // Every subcommand runs the TypeScript entry through tsx, so no build step is needed.
7
- import { access } from "node:fs/promises";
8
- import { fileURLToPath } from "node:url";
9
-
10
- /** Subcommand → TypeScript entry, relative to this file. */
11
- const COMMANDS = {
12
- hub: "../apps/dpi-playground/apps/dpi-playground-hub/src/main.ts",
13
- device: "../apps/dpi-backend/src/main.ts",
14
- tui: "../apps/dpi-tui/src/main.ts",
15
- };
16
-
17
- const HELP = `md — Durable Pi
18
-
19
- Usage:
20
- md hub [--port 4781] [--host 127.0.0.1] [--public-host <host>]
21
- Web UI + device tunnel server (devices dial this).
22
- md device
23
- Device-side daemon (pairs to a hub, serves one machine's agents).
24
- md tui
25
- Terminal admin client.
26
- md help | md --help | md -h
27
- md version | md --version | md -v
28
-
29
- Environment (hub): DPI_HOME, DPI_HUB_PORT, DPI_HUB_HOST, DPI_HUB_PUBLIC_HOST,
30
- DPI_HUB_ORIGIN, DPI_HUB_REQUIRE_TLS
31
- Environment (device): DPI_HUB_URL, DPI_HOME
32
- `;
33
-
34
- /**
35
- * tsx is a dependency of the published package, but a bare repository checkout has it only
36
- * inside the per-app node_modules. Try the normal resolution first, then the known local
37
- * copies, so `md` works in both places without a root install.
38
- */
39
- async function loadTsx() {
40
- try {
41
- return await import("tsx/esm/api");
42
- } catch (error) {
43
- if (error?.code !== "ERR_MODULE_NOT_FOUND") throw error;
44
- }
45
- const candidates = [
46
- "../apps/dpi-playground/node_modules/tsx/dist/esm/api/index.mjs",
47
- "../apps/dpi-backend/node_modules/tsx/dist/esm/api/index.mjs",
48
- "../apps/dpi-sdk/node_modules/tsx/dist/esm/api/index.mjs",
49
- "../apps/dpi-tui/node_modules/tsx/dist/esm/api/index.mjs",
50
- ];
51
- for (const relative of candidates) {
52
- const url = new URL(relative, import.meta.url);
53
- try {
54
- await access(fileURLToPath(url));
55
- return await import(url.href);
56
- } catch {
57
- // try the next one
58
- }
59
- }
60
- throw new Error(
61
- "md: cannot find tsx. Install the package (npm install -g my-durable) or run npm install in an app directory.",
62
- );
63
- }
64
-
65
- const [first, ...rest] = process.argv.slice(2);
66
-
67
- if (!first || first === "help" || first === "--help" || first === "-h") {
68
- process.stdout.write(HELP);
69
- process.exit(0);
70
- }
71
-
72
- if (first === "version" || first === "--version" || first === "-v") {
73
- const { readFileSync } = await import("node:fs");
74
- const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
75
- process.stdout.write(`md (my-durable) ${pkg.version}\n`);
76
- process.exit(0);
77
- }
78
-
79
- if (!Object.hasOwn(COMMANDS, first)) {
80
- process.stderr.write(`md: unknown command '${first}'\n\n${HELP}`);
81
- process.exit(2);
82
- }
83
-
84
- // The entries parse process.argv.slice(2) themselves: hand them their own args.
85
- process.argv = [process.argv[0], process.argv[1], ...rest];
86
- const { register } = await loadTsx();
87
- register();
88
- await import(COMMANDS[first]);