@procedures/mark 0.7.0 → 0.8.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 +34 -0
- package/README.md +10 -7
- package/dist/.tsbuildinfo +1 -1
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +3 -1
- package/dist/args.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -4
- package/dist/cli.js.map +1 -1
- package/dist/ecosystem.d.ts +7 -5
- package/dist/ecosystem.d.ts.map +1 -1
- package/dist/ecosystem.js +14 -13
- package/dist/ecosystem.js.map +1 -1
- package/dist/manifest.json +41 -5
- package/dist/server-mode.d.ts +1 -1
- package/dist/server-mode.d.ts.map +1 -1
- package/dist/server-mode.js +4 -3
- package/dist/server-mode.js.map +1 -1
- package/package.json +49 -49
- package/src/args.ts +3 -1
- package/src/cli.ts +6 -4
- package/src/ecosystem.ts +24 -17
- package/src/server-mode.ts +4 -3
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.8.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.8.0",
|
|
56
|
+
"@procedures/client": "0.8.0",
|
|
57
|
+
"@procedures/lib": "0.8.0",
|
|
58
|
+
"@procedures/mcp-dev-tools": "0.8.0",
|
|
59
|
+
"@procedures/peer": "0.8.0",
|
|
60
|
+
"@procedures/pnpm": "0.8.0",
|
|
61
|
+
"@procedures/procedure": "0.8.0",
|
|
62
|
+
"@procedures/server": "0.8.0",
|
|
63
|
+
"@procedures/shell": "0.8.0",
|
|
64
|
+
"@procedures/vitest": "0.8.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.8.0",
|
|
74
|
+
"@procedures/bundle-assistant": "0.8.0",
|
|
75
|
+
"@procedures/bundle-dev": "0.8.0",
|
|
76
|
+
"@procedures/bundle-mcp": "0.8.0",
|
|
77
|
+
"@procedures/claude-cli": "0.8.0",
|
|
78
|
+
"@procedures/compiler": "0.8.0",
|
|
79
|
+
"@procedures/cue": "0.8.0",
|
|
80
|
+
"@procedures/dag": "0.8.0",
|
|
81
|
+
"@procedures/docker": "0.8.0",
|
|
82
|
+
"@procedures/env": "0.8.0",
|
|
83
|
+
"@procedures/exif": "0.8.0",
|
|
84
|
+
"@procedures/fs": "0.8.0",
|
|
85
|
+
"@procedures/geo": "0.8.0",
|
|
86
|
+
"@procedures/gh": "0.8.0",
|
|
87
|
+
"@procedures/git": "0.8.0",
|
|
88
|
+
"@procedures/http": "0.8.0",
|
|
89
|
+
"@procedures/ics": "0.8.0",
|
|
90
|
+
"@procedures/ios-backup": "0.8.0",
|
|
91
|
+
"@procedures/json": "0.8.0",
|
|
92
|
+
"@procedures/llm": "0.8.0",
|
|
93
|
+
"@procedures/mail": "0.8.0",
|
|
94
|
+
"@procedures/mongo": "0.8.0",
|
|
95
|
+
"@procedures/node": "0.8.0",
|
|
96
|
+
"@procedures/notify": "0.8.0",
|
|
97
|
+
"@procedures/npm": "0.8.0",
|
|
98
|
+
"@procedures/pdf": "0.8.0",
|
|
99
|
+
"@procedures/privacy-scan": "0.8.0",
|
|
100
|
+
"@procedures/restic": "0.8.0",
|
|
101
|
+
"@procedures/s3": "0.8.0",
|
|
102
|
+
"@procedures/schedule": "0.8.0",
|
|
103
|
+
"@procedures/snapshot": "0.8.0",
|
|
104
|
+
"@procedures/splay": "0.8.0",
|
|
105
|
+
"@procedures/sqlite": "0.8.0",
|
|
106
|
+
"@procedures/store": "0.8.0",
|
|
107
|
+
"@procedures/systems": "0.8.0",
|
|
108
|
+
"@procedures/tailscale": "0.8.0",
|
|
109
|
+
"@procedures/vite": "0.8.0",
|
|
110
|
+
"@procedures/weather": "0.8.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
|
@@ -413,7 +413,9 @@ export function parseCommandLine<T extends CommandLike>(argv: readonly string[],
|
|
|
413
413
|
return;
|
|
414
414
|
}
|
|
415
415
|
let value: unknown;
|
|
416
|
-
|
|
416
|
+
// Each argument of a last array field is one item, also when there is only one. Before, one
|
|
417
|
+
// argument was converted as the whole array, so `[]` gave no item and `["a","b"]` two.
|
|
418
|
+
if (many) {
|
|
417
419
|
const items: unknown[] = [];
|
|
418
420
|
for (const text of texts) {
|
|
419
421
|
const converted = convertValue(text, type.item ?? ANY, field);
|
package/src/cli.ts
CHANGED
|
@@ -142,7 +142,8 @@ function showRootHelp(procedures: readonly CommandLike[]): void {
|
|
|
142
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
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>'
|
|
145
|
+
print.info("Root: 'mark --fs-root <folder> <command>' keeps each path of each procedure in the folder, also in each mode and host.");
|
|
146
|
+
print.info(" It refuses the procedures that it cannot confine: git, pnpm, docker run, a program (FORBIDDEN, outside-root).");
|
|
146
147
|
}
|
|
147
148
|
|
|
148
149
|
/** The global flags that only `--server` takes. */
|
|
@@ -207,7 +208,7 @@ function interruptSignal(): { signal: AbortSignal; release: () => void } {
|
|
|
207
208
|
/**
|
|
208
209
|
* Initialize the CLI context (load ecosystem, create client)
|
|
209
210
|
* This is the expensive part (~4s). Call once, reuse many times. With `fsRoot` (`--fs-root`),
|
|
210
|
-
*
|
|
211
|
+
* each procedure keeps its paths in the root, also in the nested calls, or the root refuses it.
|
|
211
212
|
*/
|
|
212
213
|
export async function initCli(verbose = false, fsRoot?: string): Promise<CliContext> {
|
|
213
214
|
const clientModule = await import("@procedures/client");
|
|
@@ -485,8 +486,9 @@ Options:
|
|
|
485
486
|
--http <port> Serve over Streamable HTTP on this port, not over stdio (MCP_HTTP_PORT)
|
|
486
487
|
--host <host> The address of --http (default: 127.0.0.1, only this computer)
|
|
487
488
|
--token <token> A secret that each HTTP request must send as a bearer token
|
|
488
|
-
--fs-root <folder>
|
|
489
|
-
|
|
489
|
+
--fs-root <folder> Keep each path of each tool in this folder, also in the nested calls,
|
|
490
|
+
and refuse each tool that the folder cannot confine (default:
|
|
491
|
+
MCP_FS_ROOT, or no root). The list of the tools stays the same.
|
|
490
492
|
--debug Write debug lines to stderr
|
|
491
493
|
-h, --help Show this help
|
|
492
494
|
|
package/src/ecosystem.ts
CHANGED
|
@@ -209,36 +209,43 @@ 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
|
|
212
|
+
/** The absolute path of the root of the files (`--fs-root`), or undefined. */
|
|
213
213
|
fsRoot?: string | undefined;
|
|
214
|
+
/** The keys of the procedures that the root refuses, or undefined without a root. */
|
|
215
|
+
refusedUnderRoot?: string[] | undefined;
|
|
214
216
|
}
|
|
215
217
|
|
|
216
218
|
/** The options of `loadEcosystemRegistry()`. */
|
|
217
219
|
export interface EcosystemOptions {
|
|
218
220
|
/**
|
|
219
|
-
* The
|
|
220
|
-
* nested
|
|
221
|
+
* The root of the files (`--fs-root`, roadmap R33). Each procedure keeps its paths in the root,
|
|
222
|
+
* also in the nested calls, or the root refuses it (`confineToRoot()` of `@procedures/fs`).
|
|
221
223
|
*/
|
|
222
224
|
fsRoot?: string | undefined;
|
|
223
225
|
}
|
|
224
226
|
|
|
225
|
-
/** The function of `@procedures/fs` that confines
|
|
226
|
-
type
|
|
227
|
+
/** The function of `@procedures/fs` that confines each procedure of a registry to a root. */
|
|
228
|
+
type ConfineToRoot = (registry: ProcedureRegistry, root: string) => { root: string; refused: string[] };
|
|
227
229
|
|
|
228
230
|
/**
|
|
229
|
-
* This function confines
|
|
230
|
-
*
|
|
231
|
-
*
|
|
231
|
+
* This function confines each procedure of the registry to a root, with `confineToRoot()` of the
|
|
232
|
+
* workspace package `@procedures/fs`. Without that package, it throws: a root that does not apply
|
|
233
|
+
* is an error, not a silent no-op.
|
|
232
234
|
*/
|
|
233
|
-
async function
|
|
235
|
+
async function confineRoot(
|
|
236
|
+
registry: ProcedureRegistry,
|
|
237
|
+
root: string,
|
|
238
|
+
discovered: readonly DiscoveredPackage[],
|
|
239
|
+
): Promise<{ root: string; refused: string[] }> {
|
|
234
240
|
const fsPackage = discovered.find((pkg) => pkg.name === "@procedures/fs");
|
|
235
241
|
const file = fsPackage?.modulePath ? path.join(fsPackage.path, fsPackage.modulePath) : undefined;
|
|
236
242
|
if (!file || !fs.existsSync(file)) {
|
|
237
243
|
throw new Error("--fs-root needs the package @procedures/fs of the workspace: build packages/client-fs");
|
|
238
244
|
}
|
|
239
|
-
const {
|
|
240
|
-
if (typeof
|
|
241
|
-
|
|
245
|
+
const { confineToRoot } = (await import(pathToFileURL(file).href)) as { confineToRoot?: ConfineToRoot };
|
|
246
|
+
if (typeof confineToRoot !== "function") throw new Error("--fs-root needs a build of @procedures/fs with confineToRoot");
|
|
247
|
+
const { root: folder, refused } = confineToRoot(registry, root);
|
|
248
|
+
return { root: folder, refused };
|
|
242
249
|
}
|
|
243
250
|
|
|
244
251
|
/**
|
|
@@ -265,8 +272,8 @@ async function moduleOfPackage(pkg: DiscoveredPackage): Promise<ProcedureModule
|
|
|
265
272
|
* The registry of `mark` (roadmap 3.1): a registry of its own, built from the module of each
|
|
266
273
|
* workspace package that declares `client.procedures`, with all the core procedures. The global
|
|
267
274
|
* registry does not change for a package with a module entry. A package that fails to load gives
|
|
268
|
-
* one warning line on stderr, as with `loadEcosystemProcedures()`. With `options.fsRoot`,
|
|
269
|
-
*
|
|
275
|
+
* one warning line on stderr, as with `loadEcosystemProcedures()`. With `options.fsRoot`, each
|
|
276
|
+
* procedure keeps its paths in the root, or the root refuses it (`confineToRoot()`).
|
|
270
277
|
*/
|
|
271
278
|
export async function loadEcosystemRegistry(verbose = false, warn = true, options: EcosystemOptions = {}): Promise<EcosystemRegistry> {
|
|
272
279
|
const registry = createRegistry(allCoreModule);
|
|
@@ -289,7 +296,7 @@ export async function loadEcosystemRegistry(verbose = false, warn = true, option
|
|
|
289
296
|
}
|
|
290
297
|
}
|
|
291
298
|
if (options.fsRoot === undefined) return { registry, loaded };
|
|
292
|
-
// After the last package: each
|
|
293
|
-
const fsRoot = await
|
|
294
|
-
return { registry, loaded, fsRoot };
|
|
299
|
+
// After the last package: each procedure of the registry gets the rules of the root
|
|
300
|
+
const { root: fsRoot, refused } = await confineRoot(registry, options.fsRoot, discovered);
|
|
301
|
+
return { registry, loaded, fsRoot, refusedUnderRoot: refused };
|
|
295
302
|
}
|
package/src/server-mode.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
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
6
|
* the host policy of the core (roadmap 2.3): the token in the lockfile, and the topics of
|
|
7
|
-
* `--events`. With `--fs-root`,
|
|
7
|
+
* `--events`. With `--fs-root`, each procedure keeps its paths in the root, or the root refuses it.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { print } from "./print.js";
|
|
@@ -83,7 +83,7 @@ export async function startServerMode(options: ServerModeOptions): Promise<void>
|
|
|
83
83
|
if (verbose) {
|
|
84
84
|
print.info("Loading ecosystem procedures...");
|
|
85
85
|
}
|
|
86
|
-
const { registry, fsRoot } = await loadEcosystemRegistry(verbose, true, { fsRoot: options.fsRoot });
|
|
86
|
+
const { registry, fsRoot, refusedUnderRoot } = await loadEcosystemRegistry(verbose, true, { fsRoot: options.fsRoot });
|
|
87
87
|
|
|
88
88
|
// Build transport config
|
|
89
89
|
const transports = buildTransports(options);
|
|
@@ -145,7 +145,8 @@ export async function startServerMode(options: ServerModeOptions): Promise<void>
|
|
|
145
145
|
}
|
|
146
146
|
print.info(events.length > 0 ? `\nEvent topics (events.subscribe): ${events.join(", ")}` : "\nEvent topics: none (--events adds them)");
|
|
147
147
|
if (fsRoot !== undefined) {
|
|
148
|
-
print.info(`File root: ${fsRoot} (
|
|
148
|
+
print.info(`File root: ${fsRoot} (each procedure refuses a path outside it)`);
|
|
149
|
+
print.info(` Refused under the root: ${refusedUnderRoot?.length ?? 0} procedures that the root cannot confine`);
|
|
149
150
|
}
|
|
150
151
|
print.info("\nPress Ctrl+C to stop.");
|
|
151
152
|
|