@procedures/mark 0.4.0 → 0.6.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/CHANGELOG.md +58 -0
- package/README.md +38 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/args.d.ts +3 -1
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +5 -1
- package/dist/args.js.map +1 -1
- package/dist/cli.d.ts +7 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +57 -17
- package/dist/cli.js.map +1 -1
- package/dist/client-mode.d.ts +1 -1
- package/dist/client-mode.d.ts.map +1 -1
- package/dist/client-mode.js +3 -2
- package/dist/client-mode.js.map +1 -1
- package/dist/ecosystem.d.ts +13 -2
- package/dist/ecosystem.d.ts.map +1 -1
- package/dist/ecosystem.js +26 -4
- package/dist/ecosystem.js.map +1 -1
- package/dist/manifest.json +1080 -934
- package/dist/repl.d.ts +2 -0
- package/dist/repl.d.ts.map +1 -1
- package/dist/repl.js +1 -1
- package/dist/repl.js.map +1 -1
- package/dist/server-mode.d.ts +9 -1
- package/dist/server-mode.d.ts.map +1 -1
- package/dist/server-mode.js +14 -4
- package/dist/server-mode.js.map +1 -1
- package/package.json +49 -49
- package/src/args.ts +8 -2
- package/src/cli.ts +65 -17
- package/src/client-mode.ts +4 -2
- package/src/ecosystem.ts +39 -4
- package/src/repl.ts +3 -1
- package/src/server-mode.ts +21 -4
package/dist/repl.d.ts
CHANGED
package/dist/repl.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repl.d.ts","sourceRoot":"","sources":["../src/repl.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,WAAW;IAC1B,qBAAqB;IACrB,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"repl.d.ts","sourceRoot":"","sources":["../src/repl.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,WAAW;IAC1B,qBAAqB;IACrB,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBnE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,CAAC,cAAc,EAC5B,MAAM,EAAE,MAAM,CAAC,cAAc,GAC5B;IAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,IAAI,CAAA;CAAE,CActD;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,EAC5B,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,EAC1C,MAAM,GAAE,MAAM,IAAe,GAC5B,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAmChD"}
|
package/dist/repl.js
CHANGED
|
@@ -14,7 +14,7 @@ export async function startRepl(options) {
|
|
|
14
14
|
const { verbose = false } = options;
|
|
15
15
|
// One-time initialization (same as CLI startup)
|
|
16
16
|
const spinner = print.spin("Loading procedures...");
|
|
17
|
-
const ctx = await initCli(verbose);
|
|
17
|
+
const ctx = await initCli(verbose, options.fsRoot);
|
|
18
18
|
spinner.succeed(`Ready (${ctx.procedures.length} procedures)`);
|
|
19
19
|
print.info("Type any command (without 'mark' prefix). Ctrl+C to exit.\n");
|
|
20
20
|
const reader = lineReader(process.stdin, process.stdout);
|
package/dist/repl.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repl.js","sourceRoot":"","sources":["../src/repl.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAmB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"repl.js","sourceRoot":"","sources":["../src/repl.ts"],"names":[],"mappings":"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"}
|
package/dist/server-mode.d.ts
CHANGED
|
@@ -3,13 +3,21 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Runs the CLI as a persistent server, exposing all ecosystem procedures
|
|
5
5
|
* via HTTP. The server has the registry of mark (the module of each package, roadmap 3.1) and
|
|
6
|
-
* the host policy of the core (roadmap 2.3): the token in the lockfile
|
|
6
|
+
* the host policy of the core (roadmap 2.3): the token in the lockfile, and the topics of
|
|
7
|
+
* `--events`. With `--fs-root`, the `fs.*` procedures confine each path to the root.
|
|
7
8
|
*/
|
|
8
9
|
export interface ServerModeOptions {
|
|
9
10
|
port: number;
|
|
10
11
|
host?: string;
|
|
11
12
|
transport?: "http" | "websocket" | "both";
|
|
12
13
|
verbose?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* The topic patterns of the remote events (`--events`, `documentation/EVENTS.md`). Empty or no
|
|
16
|
+
* value: the server serves no topic, and each subscription gets `TOPIC_NOT_FOUND`.
|
|
17
|
+
*/
|
|
18
|
+
events?: readonly string[] | undefined;
|
|
19
|
+
/** The folder that confines each path of the `fs.*` procedures (`--fs-root`). */
|
|
20
|
+
fsRoot?: string | undefined;
|
|
13
21
|
}
|
|
14
22
|
/**
|
|
15
23
|
* Start CLI in server mode
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-mode.d.ts","sourceRoot":"","sources":["../src/server-mode.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"server-mode.d.ts","sourceRoot":"","sources":["../src/server-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IAC1C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACvC,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AA6CD;;GAEG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwG/E;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM/C;AAED,2CAA2C;AAC3C,eAAO,MAAM,iBAAiB,YAAI,MAAM,EAAE,WAAW,EAAE,MAAM,CAAU,CAAC;AAExE;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAM3E;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAUzD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAWzD"}
|
package/dist/server-mode.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Runs the CLI as a persistent server, exposing all ecosystem procedures
|
|
5
5
|
* via HTTP. The server has the registry of mark (the module of each package, roadmap 3.1) and
|
|
6
|
-
* the host policy of the core (roadmap 2.3): the token in the lockfile
|
|
6
|
+
* the host policy of the core (roadmap 2.3): the token in the lockfile, and the topics of
|
|
7
|
+
* `--events`. With `--fs-root`, the `fs.*` procedures confine each path to the root.
|
|
7
8
|
*/
|
|
8
9
|
import { print } from "./print.js";
|
|
9
10
|
import { loadEcosystemRegistry } from "./ecosystem.js";
|
|
@@ -47,7 +48,7 @@ export async function startServerMode(options) {
|
|
|
47
48
|
if (verbose) {
|
|
48
49
|
print.info("Loading ecosystem procedures...");
|
|
49
50
|
}
|
|
50
|
-
const { registry } = await loadEcosystemRegistry(verbose);
|
|
51
|
+
const { registry, fsRoot } = await loadEcosystemRegistry(verbose, true, { fsRoot: options.fsRoot });
|
|
51
52
|
// Build transport config
|
|
52
53
|
const transports = buildTransports(options);
|
|
53
54
|
// Every request must send this token. Only the lockfile (readable by this user) holds it.
|
|
@@ -60,14 +61,17 @@ export async function startServerMode(options) {
|
|
|
60
61
|
print.warning("A WebSocket-only server takes no `mark` commands: `mark` sends its commands over HTTP.");
|
|
61
62
|
}
|
|
62
63
|
// The host policy of the core (roadmap 2.3): the token of each transport. The server of mark
|
|
63
|
-
// serves every procedure of the workspace to its one user, so it has no expose list.
|
|
64
|
+
// serves every procedure of the workspace to its one user, so it has no expose list. It always
|
|
65
|
+
// serves events.subscribe (EVENTS.md): the topic rule is the --events patterns, else empty.
|
|
66
|
+
// The bus is the global bus, which is also the ctx.bus of the procedures.
|
|
67
|
+
const events = [...(options.events ?? [])];
|
|
64
68
|
const { hostPolicy } = await import("@procedures/client");
|
|
65
69
|
const { createPeer } = await import("@procedures/peer");
|
|
66
70
|
const peer = await createPeer({
|
|
67
71
|
id: `peer-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
68
72
|
transports,
|
|
69
73
|
registry,
|
|
70
|
-
policy: hostPolicy({ token }),
|
|
74
|
+
policy: hostPolicy({ token, events: { topics: events } }),
|
|
71
75
|
});
|
|
72
76
|
await peer.start();
|
|
73
77
|
const result = {
|
|
@@ -87,6 +91,8 @@ export async function startServerMode(options) {
|
|
|
87
91
|
cwd: process.cwd(),
|
|
88
92
|
build: currentBuild(),
|
|
89
93
|
peerId: result.serverId,
|
|
94
|
+
// A client uses this server only with the same --fs-root (or none)
|
|
95
|
+
...(fsRoot !== undefined ? { fsRoot } : {}),
|
|
90
96
|
});
|
|
91
97
|
print.success("\nCLI Server running!");
|
|
92
98
|
print.info(` PID: ${process.pid}`);
|
|
@@ -97,6 +103,10 @@ export async function startServerMode(options) {
|
|
|
97
103
|
for (const endpoint of result.endpoints) {
|
|
98
104
|
print.info(` [${endpoint.type}] ${endpoint.address}`);
|
|
99
105
|
}
|
|
106
|
+
print.info(events.length > 0 ? `\nEvent topics (events.subscribe): ${events.join(", ")}` : "\nEvent topics: none (--events adds them)");
|
|
107
|
+
if (fsRoot !== undefined) {
|
|
108
|
+
print.info(`File root: ${fsRoot} (fs.* refuses each path outside it)`);
|
|
109
|
+
}
|
|
100
110
|
print.info("\nPress Ctrl+C to stop.");
|
|
101
111
|
// Handle shutdown: the peer stops (its requests end and its ports close) before the process
|
|
102
112
|
// exits, with a limit of 10 seconds. A second signal exits at once. Before, the process exited
|
package/dist/server-mode.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-mode.js","sourceRoot":"","sources":["../src/server-mode.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"server-mode.js","sourceRoot":"","sources":["../src/server-mode.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@procedures/mark",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.0",
|
|
5
5
|
"description": "mark: one CLI for every procedure package. It finds the procedures, parses flags by their Zod types, runs local or through a fast warm server, and asks for missing arguments.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"cli",
|
|
@@ -52,16 +52,16 @@
|
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@clack/prompts": "^0.7.0",
|
|
55
|
-
"@procedures/cli-run": "0.
|
|
56
|
-
"@procedures/client": "0.
|
|
57
|
-
"@procedures/lib": "0.
|
|
58
|
-
"@procedures/mcp-dev-tools": "0.
|
|
59
|
-
"@procedures/peer": "0.
|
|
60
|
-
"@procedures/pnpm": "0.
|
|
61
|
-
"@procedures/procedure": "0.
|
|
62
|
-
"@procedures/server": "0.
|
|
63
|
-
"@procedures/shell": "0.
|
|
64
|
-
"@procedures/vitest": "0.
|
|
55
|
+
"@procedures/cli-run": "0.6.0",
|
|
56
|
+
"@procedures/client": "0.6.0",
|
|
57
|
+
"@procedures/lib": "0.6.0",
|
|
58
|
+
"@procedures/mcp-dev-tools": "0.6.0",
|
|
59
|
+
"@procedures/peer": "0.6.0",
|
|
60
|
+
"@procedures/pnpm": "0.6.0",
|
|
61
|
+
"@procedures/procedure": "0.6.0",
|
|
62
|
+
"@procedures/server": "0.6.0",
|
|
63
|
+
"@procedures/shell": "0.6.0",
|
|
64
|
+
"@procedures/vitest": "0.6.0",
|
|
65
65
|
"chalk": "^5.3.0",
|
|
66
66
|
"cli-table3": "^0.6.5",
|
|
67
67
|
"ora": "^8.0.0",
|
|
@@ -70,44 +70,44 @@
|
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@mark1russell7/cue": "^1.0.0",
|
|
73
|
-
"@procedures/archive": "0.
|
|
74
|
-
"@procedures/bundle-assistant": "0.
|
|
75
|
-
"@procedures/bundle-dev": "0.
|
|
76
|
-
"@procedures/bundle-mcp": "0.
|
|
77
|
-
"@procedures/claude-cli": "0.
|
|
78
|
-
"@procedures/compiler": "0.
|
|
79
|
-
"@procedures/cue": "0.
|
|
80
|
-
"@procedures/dag": "0.
|
|
81
|
-
"@procedures/docker": "0.
|
|
82
|
-
"@procedures/env": "0.
|
|
83
|
-
"@procedures/exif": "0.
|
|
84
|
-
"@procedures/fs": "0.
|
|
85
|
-
"@procedures/geo": "0.
|
|
86
|
-
"@procedures/gh": "0.
|
|
87
|
-
"@procedures/git": "0.
|
|
88
|
-
"@procedures/http": "0.
|
|
89
|
-
"@procedures/ics": "0.
|
|
90
|
-
"@procedures/ios-backup": "0.
|
|
91
|
-
"@procedures/json": "0.
|
|
92
|
-
"@procedures/llm": "0.
|
|
93
|
-
"@procedures/mail": "0.
|
|
94
|
-
"@procedures/mongo": "0.
|
|
95
|
-
"@procedures/node": "0.
|
|
96
|
-
"@procedures/notify": "0.
|
|
97
|
-
"@procedures/npm": "0.
|
|
98
|
-
"@procedures/pdf": "0.
|
|
99
|
-
"@procedures/privacy-scan": "0.
|
|
100
|
-
"@procedures/restic": "0.
|
|
101
|
-
"@procedures/s3": "0.
|
|
102
|
-
"@procedures/schedule": "0.
|
|
103
|
-
"@procedures/snapshot": "0.
|
|
104
|
-
"@procedures/splay": "0.
|
|
105
|
-
"@procedures/sqlite": "0.
|
|
106
|
-
"@procedures/store": "0.
|
|
107
|
-
"@procedures/systems": "0.
|
|
108
|
-
"@procedures/tailscale": "0.
|
|
109
|
-
"@procedures/vite": "0.
|
|
110
|
-
"@procedures/weather": "0.
|
|
73
|
+
"@procedures/archive": "0.6.0",
|
|
74
|
+
"@procedures/bundle-assistant": "0.6.0",
|
|
75
|
+
"@procedures/bundle-dev": "0.6.0",
|
|
76
|
+
"@procedures/bundle-mcp": "0.6.0",
|
|
77
|
+
"@procedures/claude-cli": "0.6.0",
|
|
78
|
+
"@procedures/compiler": "0.6.0",
|
|
79
|
+
"@procedures/cue": "0.6.0",
|
|
80
|
+
"@procedures/dag": "0.6.0",
|
|
81
|
+
"@procedures/docker": "0.6.0",
|
|
82
|
+
"@procedures/env": "0.6.0",
|
|
83
|
+
"@procedures/exif": "0.6.0",
|
|
84
|
+
"@procedures/fs": "0.6.0",
|
|
85
|
+
"@procedures/geo": "0.6.0",
|
|
86
|
+
"@procedures/gh": "0.6.0",
|
|
87
|
+
"@procedures/git": "0.6.0",
|
|
88
|
+
"@procedures/http": "0.6.0",
|
|
89
|
+
"@procedures/ics": "0.6.0",
|
|
90
|
+
"@procedures/ios-backup": "0.6.0",
|
|
91
|
+
"@procedures/json": "0.6.0",
|
|
92
|
+
"@procedures/llm": "0.6.0",
|
|
93
|
+
"@procedures/mail": "0.6.0",
|
|
94
|
+
"@procedures/mongo": "0.6.0",
|
|
95
|
+
"@procedures/node": "0.6.0",
|
|
96
|
+
"@procedures/notify": "0.6.0",
|
|
97
|
+
"@procedures/npm": "0.6.0",
|
|
98
|
+
"@procedures/pdf": "0.6.0",
|
|
99
|
+
"@procedures/privacy-scan": "0.6.0",
|
|
100
|
+
"@procedures/restic": "0.6.0",
|
|
101
|
+
"@procedures/s3": "0.6.0",
|
|
102
|
+
"@procedures/schedule": "0.6.0",
|
|
103
|
+
"@procedures/snapshot": "0.6.0",
|
|
104
|
+
"@procedures/splay": "0.6.0",
|
|
105
|
+
"@procedures/sqlite": "0.6.0",
|
|
106
|
+
"@procedures/store": "0.6.0",
|
|
107
|
+
"@procedures/systems": "0.6.0",
|
|
108
|
+
"@procedures/tailscale": "0.6.0",
|
|
109
|
+
"@procedures/vite": "0.6.0",
|
|
110
|
+
"@procedures/weather": "0.6.0",
|
|
111
111
|
"@types/node": "^22.0.0",
|
|
112
112
|
"fast-check": "^4.10.2",
|
|
113
113
|
"typescript": "^7.0.2",
|
package/src/args.ts
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* - Each value is converted by the type of its field: a string field keeps its text.
|
|
12
12
|
* - A repeated array flag collects its values. A repeated record flag (`--env A=1 --env B=2`) merges.
|
|
13
13
|
* - A boolean flag takes `true`/`false` after it, or `--flag=false`, or `--no-flag`.
|
|
14
|
+
* - A repeated global flag with a value keeps the last value. A repeatable one (`--events`)
|
|
15
|
+
* collects its values, separated by commas.
|
|
14
16
|
* - A negative number is a value, not a flag. `--` ends the flags.
|
|
15
17
|
* - The positional fields take the arguments in order. A last array field takes the rest.
|
|
16
18
|
* An argument that no field takes is an error.
|
|
@@ -37,11 +39,13 @@ export type GlobalName =
|
|
|
37
39
|
| "local"
|
|
38
40
|
| "json"
|
|
39
41
|
| "no-input"
|
|
42
|
+
| "fs-root"
|
|
40
43
|
| "interactive"
|
|
41
44
|
| "server"
|
|
42
45
|
| "port"
|
|
43
46
|
| "host"
|
|
44
|
-
| "transport"
|
|
47
|
+
| "transport"
|
|
48
|
+
| "events";
|
|
45
49
|
|
|
46
50
|
/** The global flags that the command line gives: true for a flag, text for a flag with a value */
|
|
47
51
|
export type Globals = Partial<Record<GlobalName, string | boolean>>;
|
|
@@ -183,7 +187,9 @@ function readGlobal(
|
|
|
183
187
|
if (value === undefined) {
|
|
184
188
|
return `${label} needs a value`;
|
|
185
189
|
}
|
|
186
|
-
|
|
190
|
+
// A repeatable flag (--events) collects its values, separated by commas
|
|
191
|
+
const previous = globals[name];
|
|
192
|
+
globals[name] = flag.repeatable && typeof previous === "string" ? `${previous},${value}` : value;
|
|
187
193
|
return undefined;
|
|
188
194
|
}
|
|
189
195
|
if (inline !== undefined && inline !== "true" && inline !== "false") {
|
package/src/cli.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { print } from "./print.js";
|
|
10
10
|
import type { Method, AnyProcedure } from "@procedures/client";
|
|
11
11
|
import { readFileSync, realpathSync } from "node:fs";
|
|
12
|
-
import { dirname } from "node:path";
|
|
12
|
+
import { dirname, resolve } from "node:path";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
14
|
import { fieldsOf, generateHelp, resultFieldsOf, type CLIMeta, type CommandLike } from "./parse.js";
|
|
15
15
|
import { readFreshManifest, writeManifest, type ManifestCommand } from "./manifest.js";
|
|
@@ -139,9 +139,25 @@ function showRootHelp(procedures: readonly CommandLike[]): void {
|
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
print.info("Run 'mark <command> --help' for more information.");
|
|
142
|
-
print.info("Hosts: 'mark serve [--procedures pkgs] [--port N] [--expose patterns]' (the procedure server),");
|
|
142
|
+
print.info("Hosts: 'mark serve [--procedures pkgs] [--port N] [--expose patterns] [--events topics]' (the procedure server),");
|
|
143
143
|
print.info(" 'mark mcp' (the dev-tools MCP server over stdio).");
|
|
144
|
-
print.info("Modes: 'mark --server [--port N] [--host H] [--transport http|websocket|both]' and 'mark -i' (the REPL).");
|
|
144
|
+
print.info("Modes: 'mark --server [--port N] [--host H] [--transport http|websocket|both] [--events topics]' and 'mark -i' (the REPL).");
|
|
145
|
+
print.info("Root: 'mark --fs-root <folder> <command>' confines each path of fs.* to the folder, also in each mode and host.");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** The global flags that only `--server` takes. */
|
|
149
|
+
const SERVER_OPTIONS = ["port", "host", "transport", "events"] as const;
|
|
150
|
+
|
|
151
|
+
/** True when the global flags give an option of `--server`. */
|
|
152
|
+
function hasServerOption(globals: Readonly<Record<string, unknown>>): boolean {
|
|
153
|
+
return SERVER_OPTIONS.some((name) => globals[name] !== undefined);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** The topic patterns of `--events`, checked as `mark serve` checks them. */
|
|
157
|
+
async function eventPatterns(value: string | boolean | undefined): Promise<string[]> {
|
|
158
|
+
if (value === undefined) return [];
|
|
159
|
+
const { parseTopicPatterns } = await import("@procedures/server");
|
|
160
|
+
return parseTopicPatterns(String(value));
|
|
145
161
|
}
|
|
146
162
|
|
|
147
163
|
/**
|
|
@@ -151,6 +167,8 @@ export interface CliContext {
|
|
|
151
167
|
client: InstanceType<typeof import("@procedures/client").Client>;
|
|
152
168
|
procedures: AnyProcedure[];
|
|
153
169
|
verbose: boolean;
|
|
170
|
+
/** The absolute path of the root of the `fs.*` procedures (`--fs-root`), or undefined. */
|
|
171
|
+
fsRoot?: string | undefined;
|
|
154
172
|
}
|
|
155
173
|
|
|
156
174
|
/**
|
|
@@ -160,6 +178,8 @@ export interface CliContext {
|
|
|
160
178
|
export interface ManifestContext {
|
|
161
179
|
commands: readonly ManifestCommand[];
|
|
162
180
|
verbose: boolean;
|
|
181
|
+
/** The absolute path of the root that `load()` gives to the `fs.*` procedures, or undefined. */
|
|
182
|
+
fsRoot?: string | undefined;
|
|
163
183
|
load: () => Promise<CliContext>;
|
|
164
184
|
}
|
|
165
185
|
|
|
@@ -186,14 +206,16 @@ function interruptSignal(): { signal: AbortSignal; release: () => void } {
|
|
|
186
206
|
|
|
187
207
|
/**
|
|
188
208
|
* Initialize the CLI context (load ecosystem, create client)
|
|
189
|
-
* This is the expensive part (~4s). Call once, reuse many times.
|
|
209
|
+
* This is the expensive part (~4s). Call once, reuse many times. With `fsRoot` (`--fs-root`),
|
|
210
|
+
* the `fs.*` procedures confine each path to the root, also in the nested calls.
|
|
190
211
|
*/
|
|
191
|
-
export async function initCli(verbose = false): Promise<CliContext> {
|
|
212
|
+
export async function initCli(verbose = false, fsRoot?: string): Promise<CliContext> {
|
|
192
213
|
const clientModule = await import("@procedures/client");
|
|
193
214
|
const { Client, LocalTransport } = clientModule;
|
|
194
215
|
|
|
195
216
|
// A registry of its own, from the module of each package (roadmap 3.1): not the global one
|
|
196
|
-
const
|
|
217
|
+
const ecosystem = await loadEcosystemRegistry(verbose, true, { fsRoot });
|
|
218
|
+
const { registry } = ecosystem;
|
|
197
219
|
|
|
198
220
|
// The transport runs each procedure of the registry through invokeProcedure() (ARCHITECTURE-PROPOSALS P1).
|
|
199
221
|
// useRegistry(): exec() of a program (--json) uses the same registry.
|
|
@@ -202,7 +224,7 @@ export async function initCli(verbose = false): Promise<CliContext> {
|
|
|
202
224
|
|
|
203
225
|
const procedures = registry.getAll();
|
|
204
226
|
|
|
205
|
-
return { client, procedures, verbose };
|
|
227
|
+
return { client, procedures, verbose, ...(ecosystem.fsRoot !== undefined ? { fsRoot: ecosystem.fsRoot } : {}) };
|
|
206
228
|
}
|
|
207
229
|
|
|
208
230
|
/**
|
|
@@ -254,8 +276,16 @@ export async function executeArgs(
|
|
|
254
276
|
fail("--server and -i start a mode of mark: give them alone, before any command.");
|
|
255
277
|
return;
|
|
256
278
|
}
|
|
257
|
-
if (globals
|
|
258
|
-
fail("--port, --host and --
|
|
279
|
+
if (hasServerOption(globals)) {
|
|
280
|
+
fail("--port, --host, --transport and --events are options of --server.");
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
// The root applies to the whole run: the procedures load with it, and a warm server must have it
|
|
284
|
+
if (globals["fs-root"] !== undefined && resolve(String(globals["fs-root"])) !== ctx.fsRoot) {
|
|
285
|
+
fail(
|
|
286
|
+
`--fs-root ${String(globals["fs-root"])} does not apply here: give it before the command (mark --fs-root <folder> <command>).`,
|
|
287
|
+
ctx.fsRoot === undefined ? undefined : `This run has the root ${ctx.fsRoot}.`
|
|
288
|
+
);
|
|
259
289
|
return;
|
|
260
290
|
}
|
|
261
291
|
|
|
@@ -371,7 +401,7 @@ export async function executeArgs(
|
|
|
371
401
|
|
|
372
402
|
// Try client mode: connect to running server if available (unless --local)
|
|
373
403
|
if (!globals.local) {
|
|
374
|
-
const clientResult = await tryClientMode(path, validated, printItem);
|
|
404
|
+
const clientResult = await tryClientMode(path, validated, printItem, ctx.fsRoot);
|
|
375
405
|
if (clientResult !== null) {
|
|
376
406
|
if (clientResult.success) {
|
|
377
407
|
if (!clientResult.printed) {
|
|
@@ -455,18 +485,23 @@ Options:
|
|
|
455
485
|
--http <port> Serve over Streamable HTTP on this port, not over stdio (MCP_HTTP_PORT)
|
|
456
486
|
--host <host> The address of --http (default: 127.0.0.1, only this computer)
|
|
457
487
|
--token <token> A secret that each HTTP request must send as a bearer token
|
|
488
|
+
--fs-root <folder> Confine each path of fs.* to this folder, also the nested fs calls
|
|
489
|
+
of the tools (default: MCP_FS_ROOT, or no root). The tools stay the same.
|
|
458
490
|
--debug Write debug lines to stderr
|
|
459
491
|
-h, --help Show this help
|
|
460
492
|
|
|
493
|
+
The MCP server serves no remote events: MCP carries only the tool calls.
|
|
461
494
|
\`mark mcp <procedure>\` runs an mcp.* procedure instead (mark mcp serve, mark mcp listTools).`;
|
|
462
495
|
|
|
463
496
|
/**
|
|
464
497
|
* Start the dev-tools MCP server (`mark mcp`). stdout carries the protocol: this function
|
|
465
498
|
* writes nothing to stdout, and the server sends console.log to stderr.
|
|
466
499
|
*/
|
|
467
|
-
async function startMcp(args: string[]): Promise<void> {
|
|
500
|
+
async function startMcp(args: string[], fsRoot?: string): Promise<void> {
|
|
468
501
|
const { loadConfig, startDevToolsServer } = await import("@procedures/mcp-dev-tools");
|
|
469
502
|
const config = loadConfig();
|
|
503
|
+
// `mark --fs-root <folder> mcp` replaces MCP_FS_ROOT, and `mark mcp --fs-root` replaces both
|
|
504
|
+
if (fsRoot !== undefined) config.fsRoot = fsRoot;
|
|
470
505
|
for (let i = 0; i < args.length; i++) {
|
|
471
506
|
const arg = args[i]!;
|
|
472
507
|
const eq = arg.indexOf("=");
|
|
@@ -496,6 +531,8 @@ async function startMcp(args: string[]): Promise<void> {
|
|
|
496
531
|
config.http = { port: config.http?.port ?? 3003, ...config.http, host: value() };
|
|
497
532
|
} else if (name === "--token") {
|
|
498
533
|
config.http = { port: config.http?.port ?? 3003, ...config.http, token: value() };
|
|
534
|
+
} else if (name === "--fs-root") {
|
|
535
|
+
config.fsRoot = value();
|
|
499
536
|
} else if (name === "--debug") {
|
|
500
537
|
config.debug = true;
|
|
501
538
|
} else {
|
|
@@ -529,6 +566,9 @@ async function run(argv: string[]): Promise<void> {
|
|
|
529
566
|
return;
|
|
530
567
|
}
|
|
531
568
|
|
|
569
|
+
// --fs-root (roadmap R33): the root of the fs.* procedures for each mode and host of this run
|
|
570
|
+
const fsRoot = globals["fs-root"] === undefined ? undefined : resolve(String(globals["fs-root"]));
|
|
571
|
+
|
|
532
572
|
// Handle --server flag: start server mode
|
|
533
573
|
if (globals.server) {
|
|
534
574
|
if (leading.rest.length > 0) {
|
|
@@ -539,7 +579,13 @@ async function run(argv: string[]): Promise<void> {
|
|
|
539
579
|
// Loopback unless --host says otherwise (deep dive CLI-1)
|
|
540
580
|
const host = globals.host === undefined ? "127.0.0.1" : String(globals.host);
|
|
541
581
|
const transport = globals.transport === undefined ? "http" : parseTransport(String(globals.transport));
|
|
542
|
-
|
|
582
|
+
const events = await eventPatterns(globals.events);
|
|
583
|
+
await startServerMode({ port, host, transport, verbose, events, ...(fsRoot !== undefined ? { fsRoot } : {}) });
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
// Before, `mark --port 4000 serve` ignored --port without a word
|
|
587
|
+
if (hasServerOption(globals)) {
|
|
588
|
+
fail("--port, --host, --transport and --events are options of --server.", "For mark serve, give them after serve.");
|
|
543
589
|
return;
|
|
544
590
|
}
|
|
545
591
|
|
|
@@ -549,7 +595,7 @@ async function run(argv: string[]): Promise<void> {
|
|
|
549
595
|
fail(`-i takes no command: ${leading.rest.join(" ")}`);
|
|
550
596
|
return;
|
|
551
597
|
}
|
|
552
|
-
await startRepl({ verbose });
|
|
598
|
+
await startRepl({ verbose, fsRoot });
|
|
553
599
|
return;
|
|
554
600
|
}
|
|
555
601
|
|
|
@@ -562,12 +608,14 @@ async function run(argv: string[]): Promise<void> {
|
|
|
562
608
|
// A --procedures package resolves from the current folder, then from mark, which depends on
|
|
563
609
|
// every procedure package of the workspace
|
|
564
610
|
const markDir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
565
|
-
|
|
611
|
+
// `mark --fs-root <folder> serve`: the root goes first, so a --fs-root after serve wins
|
|
612
|
+
const serveArgs = fsRoot === undefined ? commandArgs : ["--fs-root", fsRoot, ...commandArgs];
|
|
613
|
+
await runServer(serveArgs, { resolveFrom: [process.cwd(), markDir] });
|
|
566
614
|
return;
|
|
567
615
|
}
|
|
568
616
|
// `mark mcp` and `mark mcp --option value`: the server. `mark mcp <procedure>`: an mcp.* command.
|
|
569
617
|
if (command === "mcp" && (commandArgs.length === 0 || commandArgs[0]!.startsWith("-"))) {
|
|
570
|
-
await startMcp(commandArgs);
|
|
618
|
+
await startMcp(commandArgs, fsRoot);
|
|
571
619
|
return;
|
|
572
620
|
}
|
|
573
621
|
|
|
@@ -575,13 +623,13 @@ async function run(argv: string[]): Promise<void> {
|
|
|
575
623
|
// server do not load them (roadmap 1.3)
|
|
576
624
|
const manifest = readFreshManifest();
|
|
577
625
|
if (manifest) {
|
|
578
|
-
await executeArgs(argv, { commands: manifest.commands, verbose, load: () => initCli(verbose) });
|
|
626
|
+
await executeArgs(argv, { commands: manifest.commands, verbose, fsRoot, load: () => initCli(verbose, fsRoot) });
|
|
579
627
|
return;
|
|
580
628
|
}
|
|
581
629
|
|
|
582
630
|
// No manifest, or a stale one (a package was built again): load the procedures as before, and
|
|
583
631
|
// write the manifest for the next run. A failure to write costs only time.
|
|
584
|
-
const ctx = await initCli(verbose);
|
|
632
|
+
const ctx = await initCli(verbose, fsRoot);
|
|
585
633
|
try {
|
|
586
634
|
await writeManifest(ctx.procedures);
|
|
587
635
|
} catch (error) {
|
package/src/client-mode.ts
CHANGED
|
@@ -23,7 +23,8 @@ interface ClientModeResult {
|
|
|
23
23
|
export async function tryClientMode(
|
|
24
24
|
path: string[],
|
|
25
25
|
input: unknown,
|
|
26
|
-
onItem?: (item: unknown) => void
|
|
26
|
+
onItem?: (item: unknown) => void,
|
|
27
|
+
fsRoot?: string
|
|
27
28
|
): Promise<ClientModeResult | null> {
|
|
28
29
|
// A running server of this build, started in this folder, that answers as the peer of its
|
|
29
30
|
// lockfile and has a token. Otherwise the CLI runs the command itself. (Before, any server
|
|
@@ -34,7 +35,8 @@ export async function tryClientMode(
|
|
|
34
35
|
if (path[0] === "server") {
|
|
35
36
|
return null;
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
+
// The server must have the same fs root (--fs-root), or none when this run has none
|
|
39
|
+
const lockfile = await findServer({ cwd: process.cwd(), build: currentBuild(), fsRoot });
|
|
38
40
|
// mark sends its commands over HTTP: a WebSocket-only server cannot take them
|
|
39
41
|
if (!lockfile?.token || lockfile.transport === "websocket") {
|
|
40
42
|
return null;
|
package/src/ecosystem.ts
CHANGED
|
@@ -209,6 +209,36 @@ const allCoreModule: ProcedureModule = defineModule("@procedures/client (all cor
|
|
|
209
209
|
export interface EcosystemRegistry {
|
|
210
210
|
registry: ProcedureRegistry;
|
|
211
211
|
loaded: string[];
|
|
212
|
+
/** The absolute path of the root of the `fs.*` procedures (`--fs-root`), or undefined. */
|
|
213
|
+
fsRoot?: string | undefined;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** The options of `loadEcosystemRegistry()`. */
|
|
217
|
+
export interface EcosystemOptions {
|
|
218
|
+
/**
|
|
219
|
+
* The folder that confines each path of the `fs.*` procedures (`--fs-root`, roadmap R33). The
|
|
220
|
+
* nested `fs.*` calls of the other procedures are confined too.
|
|
221
|
+
*/
|
|
222
|
+
fsRoot?: string | undefined;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** The function of `@procedures/fs` that confines the `fs.*` procedures of a registry to a root. */
|
|
226
|
+
type ConfineFs = (registry: ProcedureRegistry, root: string) => { root: string; procedures: string[] };
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* This function confines the `fs.*` procedures of the registry to a root, with
|
|
230
|
+
* `confineFsProcedures()` of the workspace package `@procedures/fs`. Without that package, it
|
|
231
|
+
* throws: a root that does not apply is an error, not a silent no-op.
|
|
232
|
+
*/
|
|
233
|
+
async function confineFs(registry: ProcedureRegistry, root: string, discovered: readonly DiscoveredPackage[]): Promise<string> {
|
|
234
|
+
const fsPackage = discovered.find((pkg) => pkg.name === "@procedures/fs");
|
|
235
|
+
const file = fsPackage?.modulePath ? path.join(fsPackage.path, fsPackage.modulePath) : undefined;
|
|
236
|
+
if (!file || !fs.existsSync(file)) {
|
|
237
|
+
throw new Error("--fs-root needs the package @procedures/fs of the workspace: build packages/client-fs");
|
|
238
|
+
}
|
|
239
|
+
const { confineFsProcedures } = (await import(pathToFileURL(file).href)) as { confineFsProcedures?: ConfineFs };
|
|
240
|
+
if (typeof confineFsProcedures !== "function") throw new Error("--fs-root needs a build of @procedures/fs with confineFsProcedures");
|
|
241
|
+
return confineFsProcedures(registry, root).root;
|
|
212
242
|
}
|
|
213
243
|
|
|
214
244
|
/**
|
|
@@ -235,12 +265,14 @@ async function moduleOfPackage(pkg: DiscoveredPackage): Promise<ProcedureModule
|
|
|
235
265
|
* The registry of `mark` (roadmap 3.1): a registry of its own, built from the module of each
|
|
236
266
|
* workspace package that declares `client.procedures`, with all the core procedures. The global
|
|
237
267
|
* registry does not change for a package with a module entry. A package that fails to load gives
|
|
238
|
-
* one warning line on stderr, as with `loadEcosystemProcedures()`.
|
|
268
|
+
* one warning line on stderr, as with `loadEcosystemProcedures()`. With `options.fsRoot`, the
|
|
269
|
+
* `fs.*` procedures confine each path to the root.
|
|
239
270
|
*/
|
|
240
|
-
export async function loadEcosystemRegistry(verbose = false, warn = true): Promise<EcosystemRegistry> {
|
|
271
|
+
export async function loadEcosystemRegistry(verbose = false, warn = true, options: EcosystemOptions = {}): Promise<EcosystemRegistry> {
|
|
241
272
|
const registry = createRegistry(allCoreModule);
|
|
242
273
|
const loaded: string[] = [];
|
|
243
|
-
|
|
274
|
+
const discovered = discoverFromEcosystem();
|
|
275
|
+
for (const pkg of discovered) {
|
|
244
276
|
try {
|
|
245
277
|
const module = await moduleOfPackage(pkg);
|
|
246
278
|
if (!module) {
|
|
@@ -256,5 +288,8 @@ export async function loadEcosystemRegistry(verbose = false, warn = true): Promi
|
|
|
256
288
|
if (verbose && error instanceof Error && error.stack) console.warn(error.stack);
|
|
257
289
|
}
|
|
258
290
|
}
|
|
259
|
-
return { registry, loaded };
|
|
291
|
+
if (options.fsRoot === undefined) return { registry, loaded };
|
|
292
|
+
// After the last package: each fs.* procedure of the registry gets the handler of the root
|
|
293
|
+
const fsRoot = await confineFs(registry, options.fsRoot, discovered);
|
|
294
|
+
return { registry, loaded, fsRoot };
|
|
260
295
|
}
|
package/src/repl.ts
CHANGED
|
@@ -12,6 +12,8 @@ import { initCli, executeArgs, type CliContext } from "./cli.js";
|
|
|
12
12
|
export interface ReplOptions {
|
|
13
13
|
/** Verbose output */
|
|
14
14
|
verbose?: boolean | undefined;
|
|
15
|
+
/** The folder that confines each path of the `fs.*` procedures (`--fs-root`). */
|
|
16
|
+
fsRoot?: string | undefined;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
/**
|
|
@@ -22,7 +24,7 @@ export async function startRepl(options: ReplOptions): Promise<void> {
|
|
|
22
24
|
|
|
23
25
|
// One-time initialization (same as CLI startup)
|
|
24
26
|
const spinner = print.spin("Loading procedures...");
|
|
25
|
-
const ctx: CliContext = await initCli(verbose);
|
|
27
|
+
const ctx: CliContext = await initCli(verbose, options.fsRoot);
|
|
26
28
|
spinner.succeed(`Ready (${ctx.procedures.length} procedures)`);
|
|
27
29
|
|
|
28
30
|
print.info("Type any command (without 'mark' prefix). Ctrl+C to exit.\n");
|