@procedures/tailscale 0.34.0 → 0.35.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/dist/argv.d.ts CHANGED
@@ -1,21 +1,7 @@
1
1
  /**
2
2
  * The argv of a CLI command from a command path, named arguments and positional arguments. The
3
- * procedures start the program with argv, never through a shell.
3
+ * procedures start the program with argv, never through a shell. The helpers are the shared ones
4
+ * of `@procedures/shell/cli` (dedupe review F2): this file keeps the exports of this package.
4
5
  */
5
- export type ArgValue = string | number | boolean | readonly string[];
6
- /** A flag name: lowercase letters, digits and hyphens. */
7
- export declare const FLAG_NAME: RegExp;
8
- /**
9
- * This function gives the argv: the path, then each named argument, then `--` and the positional
10
- * arguments. `true` gives `--key`, `false` gives nothing, a list gives `--key=value` for each
11
- * value, and another value gives `--key=value`.
12
- */
13
- export declare function buildArgv(input: {
14
- readonly path: readonly string[];
15
- readonly args?: Readonly<Record<string, ArgValue>> | undefined;
16
- readonly positional?: readonly string[] | undefined;
17
- readonly flags?: readonly string[] | undefined;
18
- }): string[];
19
- /** This function reads JSON output: one document, or one document on each line (NDJSON). */
20
- export declare function parseJsonOutput(stdout: string): unknown;
6
+ export { buildArgv, parseJsonOutput, FLAG_NAME, type ArgValue } from "@procedures/shell/cli";
21
7
  //# sourceMappingURL=argv.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"argv.d.ts","sourceRoot":"","sources":["../src/argv.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,MAAM,EAAE,CAAC;AAErE,0DAA0D;AAC1D,eAAO,MAAM,SAAS,EAAE,MAA4B,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE;IAC/B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,GAAG,SAAS,CAAC;IAC/D,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACpD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CAChD,GAAG,MAAM,EAAE,CAWX;AAED,4FAA4F;AAC5F,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAavD"}
1
+ {"version":3,"file":"argv.d.ts","sourceRoot":"","sources":["../src/argv.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC"}
package/dist/argv.js CHANGED
@@ -1,49 +1,7 @@
1
1
  /**
2
2
  * The argv of a CLI command from a command path, named arguments and positional arguments. The
3
- * procedures start the program with argv, never through a shell.
3
+ * procedures start the program with argv, never through a shell. The helpers are the shared ones
4
+ * of `@procedures/shell/cli` (dedupe review F2): this file keeps the exports of this package.
4
5
  */
5
- /** A flag name: lowercase letters, digits and hyphens. */
6
- export const FLAG_NAME = /^[a-z][a-z0-9-]*$/;
7
- /**
8
- * This function gives the argv: the path, then each named argument, then `--` and the positional
9
- * arguments. `true` gives `--key`, `false` gives nothing, a list gives `--key=value` for each
10
- * value, and another value gives `--key=value`.
11
- */
12
- export function buildArgv(input) {
13
- const argv = [...input.path, ...(input.flags ?? [])];
14
- for (const [key, value] of Object.entries(input.args ?? {})) {
15
- if (!FLAG_NAME.test(key))
16
- throw new Error(`The argument name "${key}" is not a flag name (lowercase letters, digits, hyphens)`);
17
- if (value === false)
18
- continue;
19
- if (value === true)
20
- argv.push(`--${key}`);
21
- else if (Array.isArray(value))
22
- for (const item of value)
23
- argv.push(`--${key}=${item}`);
24
- else
25
- argv.push(`--${key}=${String(value)}`);
26
- }
27
- if (input.positional && input.positional.length > 0)
28
- argv.push("--", ...input.positional);
29
- return argv;
30
- }
31
- /** This function reads JSON output: one document, or one document on each line (NDJSON). */
32
- export function parseJsonOutput(stdout) {
33
- const text = stdout.trim();
34
- if (text.length === 0)
35
- return undefined;
36
- try {
37
- return JSON.parse(text);
38
- }
39
- catch {
40
- const lines = text.split(/\r?\n/).filter((line) => line.trim().length > 0);
41
- try {
42
- return lines.map((line) => JSON.parse(line));
43
- }
44
- catch {
45
- return undefined;
46
- }
47
- }
48
- }
6
+ export { buildArgv, parseJsonOutput, FLAG_NAME } from "@procedures/shell/cli";
49
7
  //# sourceMappingURL=argv.js.map
package/dist/argv.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"argv.js","sourceRoot":"","sources":["../src/argv.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,0DAA0D;AAC1D,MAAM,CAAC,MAAM,SAAS,GAAW,mBAAmB,CAAC;AAErD;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,KAKzB;IACC,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;IACrD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;QAC5D,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,GAAG,2DAA2D,CAAC,CAAC;QAChI,IAAI,KAAK,KAAK,KAAK;YAAE,SAAS;QAC9B,IAAI,KAAK,KAAK,IAAI;YAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;aACrC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,KAAK,MAAM,IAAI,IAAI,KAAK;gBAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC;;YAClF,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;IAC1F,OAAO,IAAI,CAAC;AACd,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC3E,IAAI,CAAC;YACH,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC,CAAC;QAC1D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"argv.js","sourceRoot":"","sources":["../src/argv.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,SAAS,EAAiB,MAAM,uBAAuB,CAAC"}
package/dist/module.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  /**
2
- * The procedures of @procedures/tailscale as data. Importing this file registers nothing.
2
+ * The procedures of @procedures/tailscale as data. Importing this file registers nothing. The
3
+ * module requires the module of `@procedures/shell`: `tailscale.run` calls `shell.run`.
3
4
  */
4
5
  import { type ProcedureModule, type ProcedureRegistry } from "@procedures/client";
5
6
  import { type TailscaleProceduresOptions } from "./procedures.js";
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAqD,KAAK,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACrI,OAAO,EAAkD,KAAK,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAElH,eAAO,MAAM,eAAe,EAAE,eAA4E,CAAC;eAE5F,eAAe;AAE9B;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,0BAA+B,GAAG,eAAe,CAG/F;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,GAAE,iBAAsC,GAAG,IAAI,CAElG"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAqD,KAAK,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAErI,OAAO,EAAkD,KAAK,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAElH,eAAO,MAAM,eAAe,EAAE,eAA2F,CAAC;eAE3G,eAAe;AAE9B;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,0BAA+B,GAAG,eAAe,CAG/F;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,GAAE,iBAAsC,GAAG,IAAI,CAElG"}
package/dist/module.js CHANGED
@@ -1,9 +1,11 @@
1
1
  /**
2
- * The procedures of @procedures/tailscale as data. Importing this file registers nothing.
2
+ * The procedures of @procedures/tailscale as data. Importing this file registers nothing. The
3
+ * module requires the module of `@procedures/shell`: `tailscale.run` calls `shell.run`.
3
4
  */
4
5
  import { defineModule, registerModules, PROCEDURE_REGISTRY } from "@procedures/client";
6
+ import shellModule from "@procedures/shell/module";
5
7
  import { createTailscaleProcedures, tailscaleProcedures } from "./procedures.js";
6
- export const tailscaleModule = defineModule("@procedures/tailscale", tailscaleProcedures);
8
+ export const tailscaleModule = defineModule("@procedures/tailscale", tailscaleProcedures, [shellModule]);
7
9
  export default tailscaleModule;
8
10
  /**
9
11
  * This function gives the module with the settings of the host: the tailscale program. Without
@@ -16,7 +18,7 @@ export default tailscaleModule;
16
18
  export function createTailscaleModule(options = {}) {
17
19
  if (options.executable === undefined)
18
20
  return tailscaleModule;
19
- return defineModule("@procedures/tailscale", createTailscaleProcedures(options));
21
+ return defineModule("@procedures/tailscale", createTailscaleProcedures(options), [shellModule]);
20
22
  }
21
23
  /**
22
24
  * This function registers the procedures of @procedures/tailscale in a registry. The default is the
@@ -1 +1 @@
1
- {"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAgD,MAAM,oBAAoB,CAAC;AACrI,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAmC,MAAM,iBAAiB,CAAC;AAElH,MAAM,CAAC,MAAM,eAAe,GAAoB,YAAY,CAAC,uBAAuB,EAAE,mBAAmB,CAAC,CAAC;AAE3G,eAAe,eAAe,CAAC;AAE/B;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAO,GAA+B,EAAE;IAC5E,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS;QAAE,OAAO,eAAe,CAAC;IAC7D,OAAO,YAAY,CAAC,uBAAuB,EAAE,yBAAyB,CAAC,OAAO,CAAC,CAAC,CAAC;AACnF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CAAC,QAAQ,GAAsB,kBAAkB;IAC1F,eAAe,CAAC,QAAQ,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;AAC/C,CAAC"}
1
+ {"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAgD,MAAM,oBAAoB,CAAC;AACrI,OAAO,WAAW,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAmC,MAAM,iBAAiB,CAAC;AAElH,MAAM,CAAC,MAAM,eAAe,GAAoB,YAAY,CAAC,uBAAuB,EAAE,mBAAmB,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;AAE1H,eAAe,eAAe,CAAC;AAE/B;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAO,GAA+B,EAAE;IAC5E,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS;QAAE,OAAO,eAAe,CAAC;IAC7D,OAAO,YAAY,CAAC,uBAAuB,EAAE,yBAAyB,CAAC,OAAO,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;AAClG,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CAAC,QAAQ,GAAsB,kBAAkB;IAC1F,eAAe,CAAC,QAAQ,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;AAC/C,CAAC"}
@@ -5,6 +5,10 @@
5
5
  * The program comes from the host, never from the input (audit pass 2, O55): the `executable`
6
6
  * option of `createTailscaleModule()`, then `TAILSCALE_EXE`, then the install folder, then
7
7
  * `tailscale`. Before, an input `executable` let the caller start any program.
8
+ *
9
+ * The procedure starts tailscale with argv through a nested `shell.run` call, the runner of
10
+ * `defineCliTool()` (tracker item O64). The engine applies its rules to that call, and a cancel
11
+ * stops the program.
8
12
  */
9
13
  import { type AnyProcedure } from "@procedures/client";
10
14
  /** This function gives the tailscale program: the explicit value, TAILSCALE_EXE, the Windows install folder, then tailscale. */
@@ -1 +1 @@
1
- {"version":3,"file":"procedures.d.ts","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAmB,KAAK,YAAY,EAAyB,MAAM,oBAAoB,CAAC;AAQ/F,gIAAgI;AAChI,wBAAgB,mBAAmB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAK7D;AAuCD,2GAA2G;AAC3G,MAAM,WAAW,0BAA0B;IACzC,gHAAgH;IAChH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,0BAA+B,GAAG,YAAY,EAAE,CAoClG;AAED,eAAO,MAAM,mBAAmB,EAAE,YAAY,EAAgC,CAAC;AAE/E,eAAO,MAAM,qBAAqB,EAAE,YAAsC,CAAC"}
1
+ {"version":3,"file":"procedures.d.ts","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAmB,KAAK,YAAY,EAAyB,MAAM,oBAAoB,CAAC;AAQ/F,gIAAgI;AAChI,wBAAgB,mBAAmB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAK7D;AA+CD,2GAA2G;AAC3G,MAAM,WAAW,0BAA0B;IACzC,gHAAgH;IAChH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,0BAA+B,GAAG,YAAY,EAAE,CAsClG;AAED,eAAO,MAAM,mBAAmB,EAAE,YAAY,EAAgC,CAAC;AAE/E,eAAO,MAAM,qBAAqB,EAAE,YAAsC,CAAC"}
@@ -5,10 +5,14 @@
5
5
  * The program comes from the host, never from the input (audit pass 2, O55): the `executable`
6
6
  * option of `createTailscaleModule()`, then `TAILSCALE_EXE`, then the install folder, then
7
7
  * `tailscale`. Before, an input `executable` let the caller start any program.
8
+ *
9
+ * The procedure starts tailscale with argv through a nested `shell.run` call, the runner of
10
+ * `defineCliTool()` (tracker item O64). The engine applies its rules to that call, and a cancel
11
+ * stops the program.
8
12
  */
9
13
  import { existsSync } from "node:fs";
10
14
  import { createProcedure } from "@procedures/client";
11
- import { runCommand } from "@procedures/shell/command";
15
+ import { defineCliTool } from "@procedures/shell/cli";
12
16
  import { z } from "zod";
13
17
  import { buildArgv, parseJsonOutput } from "./argv.js";
14
18
  /** The Windows installer does not put tailscale on the PATH. */
@@ -47,11 +51,19 @@ const RunOutputSchema = z.object({
47
51
  json: z.unknown().optional(),
48
52
  actionUrl: z.string().optional(),
49
53
  });
54
+ /**
55
+ * True when the output of `shell.run` tells that the program did not start. `shell.run` gives
56
+ * exit code 1 and the error of the start as the standard error, for example `spawn tailscale ENOENT`.
57
+ */
58
+ function didNotStart(result) {
59
+ return result.exitCode === 1 && result.stdout === "" && /^spawn .+ E[A-Z0-9]+$/.test(result.stderr);
60
+ }
50
61
  /**
51
62
  * This function gives the procedures of this package with the settings of the host. The program
52
63
  * of `tailscale.run` is `executable`, or the default of `tailscaleExecutable()` at each call.
53
64
  */
54
65
  export function createTailscaleProcedures(options = {}) {
66
+ const tailscale = defineCliTool({ name: "tailscale", resolve: () => tailscaleExecutable(options.executable) });
55
67
  const run = createProcedure()
56
68
  .path(["tailscale", "run"])
57
69
  .input(RunInputSchema)
@@ -61,6 +73,7 @@ export function createTailscaleProcedures(options = {}) {
61
73
  args: ["path"],
62
74
  shorts: { timeout: "t" },
63
75
  output: "json",
76
+ calls: ["shell.run"],
64
77
  // up, down and serve change the network of the host. ssh starts a program on another host.
65
78
  effects: { reads: ["network"], writes: ["network", "process"], destructive: true },
66
79
  })
@@ -71,9 +84,9 @@ export function createTailscaleProcedures(options = {}) {
71
84
  args: input.args,
72
85
  positional: input.positional,
73
86
  });
74
- const result = await runCommand(tailscaleExecutable(options.executable), { args: argv, timeout: input.timeout, signal: ctx?.signal });
75
- if (result.error)
76
- throw new Error(`tailscale did not start: ${result.error}`);
87
+ const result = await tailscale.call(argv, { timeout: input.timeout }, ctx);
88
+ if (didNotStart(result))
89
+ throw new Error(`tailscale did not start: ${result.stderr}`);
77
90
  const json = input.json ? parseJsonOutput(result.stdout) : undefined;
78
91
  // tailscale prints an address to open when a login or a feature needs the owner
79
92
  const actionUrl = /https:\/\/login\.tailscale\.com\/\S+/.exec(`${result.stdout}\n${result.stderr}`)?.[0];
@@ -1 +1 @@
1
- {"version":3,"file":"procedures.js","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"procedures.js","sourceRoot":"","sources":["../src/procedures.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/tailscale",
4
- "version": "0.34.0",
4
+ "version": "0.35.0",
5
5
  "description": "The tailscale CLI as one procedure: status, ip, serve, up and down, with JSON output.",
6
6
  "keywords": [
7
7
  "tailscale",
@@ -56,9 +56,9 @@
56
56
  "provenance": true
57
57
  },
58
58
  "dependencies": {
59
- "@procedures/client": "0.34.0",
59
+ "@procedures/client": "0.35.0",
60
60
  "zod": "^4.6.5",
61
- "@procedures/shell": "0.34.0"
61
+ "@procedures/shell": "0.35.0"
62
62
  },
63
63
  "devDependencies": {
64
64
  "typescript": "^7.0.2",
package/src/argv.ts CHANGED
@@ -1,48 +1,7 @@
1
1
  /**
2
2
  * The argv of a CLI command from a command path, named arguments and positional arguments. The
3
- * procedures start the program with argv, never through a shell.
3
+ * procedures start the program with argv, never through a shell. The helpers are the shared ones
4
+ * of `@procedures/shell/cli` (dedupe review F2): this file keeps the exports of this package.
4
5
  */
5
6
 
6
- export type ArgValue = string | number | boolean | readonly string[];
7
-
8
- /** A flag name: lowercase letters, digits and hyphens. */
9
- export const FLAG_NAME: RegExp = /^[a-z][a-z0-9-]*$/;
10
-
11
- /**
12
- * This function gives the argv: the path, then each named argument, then `--` and the positional
13
- * arguments. `true` gives `--key`, `false` gives nothing, a list gives `--key=value` for each
14
- * value, and another value gives `--key=value`.
15
- */
16
- export function buildArgv(input: {
17
- readonly path: readonly string[];
18
- readonly args?: Readonly<Record<string, ArgValue>> | undefined;
19
- readonly positional?: readonly string[] | undefined;
20
- readonly flags?: readonly string[] | undefined;
21
- }): string[] {
22
- const argv = [...input.path, ...(input.flags ?? [])];
23
- for (const [key, value] of Object.entries(input.args ?? {})) {
24
- if (!FLAG_NAME.test(key)) throw new Error(`The argument name "${key}" is not a flag name (lowercase letters, digits, hyphens)`);
25
- if (value === false) continue;
26
- if (value === true) argv.push(`--${key}`);
27
- else if (Array.isArray(value)) for (const item of value) argv.push(`--${key}=${item}`);
28
- else argv.push(`--${key}=${String(value)}`);
29
- }
30
- if (input.positional && input.positional.length > 0) argv.push("--", ...input.positional);
31
- return argv;
32
- }
33
-
34
- /** This function reads JSON output: one document, or one document on each line (NDJSON). */
35
- export function parseJsonOutput(stdout: string): unknown {
36
- const text = stdout.trim();
37
- if (text.length === 0) return undefined;
38
- try {
39
- return JSON.parse(text);
40
- } catch {
41
- const lines = text.split(/\r?\n/).filter((line) => line.trim().length > 0);
42
- try {
43
- return lines.map((line) => JSON.parse(line) as unknown);
44
- } catch {
45
- return undefined;
46
- }
47
- }
48
- }
7
+ export { buildArgv, parseJsonOutput, FLAG_NAME, type ArgValue } from "@procedures/shell/cli";
package/src/module.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  /**
2
- * The procedures of @procedures/tailscale as data. Importing this file registers nothing.
2
+ * The procedures of @procedures/tailscale as data. Importing this file registers nothing. The
3
+ * module requires the module of `@procedures/shell`: `tailscale.run` calls `shell.run`.
3
4
  */
4
5
 
5
6
  import { defineModule, registerModules, PROCEDURE_REGISTRY, type ProcedureModule, type ProcedureRegistry } from "@procedures/client";
7
+ import shellModule from "@procedures/shell/module";
6
8
  import { createTailscaleProcedures, tailscaleProcedures, type TailscaleProceduresOptions } from "./procedures.js";
7
9
 
8
- export const tailscaleModule: ProcedureModule = defineModule("@procedures/tailscale", tailscaleProcedures);
10
+ export const tailscaleModule: ProcedureModule = defineModule("@procedures/tailscale", tailscaleProcedures, [shellModule]);
9
11
 
10
12
  export default tailscaleModule;
11
13
 
@@ -19,7 +21,7 @@ export default tailscaleModule;
19
21
  */
20
22
  export function createTailscaleModule(options: TailscaleProceduresOptions = {}): ProcedureModule {
21
23
  if (options.executable === undefined) return tailscaleModule;
22
- return defineModule("@procedures/tailscale", createTailscaleProcedures(options));
24
+ return defineModule("@procedures/tailscale", createTailscaleProcedures(options), [shellModule]);
23
25
  }
24
26
 
25
27
  /**
package/src/procedures.ts CHANGED
@@ -5,11 +5,15 @@
5
5
  * The program comes from the host, never from the input (audit pass 2, O55): the `executable`
6
6
  * option of `createTailscaleModule()`, then `TAILSCALE_EXE`, then the install folder, then
7
7
  * `tailscale`. Before, an input `executable` let the caller start any program.
8
+ *
9
+ * The procedure starts tailscale with argv through a nested `shell.run` call, the runner of
10
+ * `defineCliTool()` (tracker item O64). The engine applies its rules to that call, and a cancel
11
+ * stops the program.
8
12
  */
9
13
 
10
14
  import { existsSync } from "node:fs";
11
15
  import { createProcedure, type AnyProcedure, type ProcedureContext } from "@procedures/client";
12
- import { runCommand } from "@procedures/shell/command";
16
+ import { defineCliTool } from "@procedures/shell/cli";
13
17
  import { z } from "zod";
14
18
  import { buildArgv, parseJsonOutput, type ArgValue } from "./argv.js";
15
19
 
@@ -61,6 +65,14 @@ const RunOutputSchema = z.object({
61
65
  actionUrl: z.string().optional(),
62
66
  });
63
67
 
68
+ /**
69
+ * True when the output of `shell.run` tells that the program did not start. `shell.run` gives
70
+ * exit code 1 and the error of the start as the standard error, for example `spawn tailscale ENOENT`.
71
+ */
72
+ function didNotStart(result: { exitCode: number; stdout: string; stderr: string }): boolean {
73
+ return result.exitCode === 1 && result.stdout === "" && /^spawn .+ E[A-Z0-9]+$/.test(result.stderr);
74
+ }
75
+
64
76
  /** The settings of the host for the procedures of this package. The procedure input cannot change them. */
65
77
  export interface TailscaleProceduresOptions {
66
78
  /** The tailscale program. The default is `TAILSCALE_EXE`, then the Windows install folder, then `tailscale`. */
@@ -72,6 +84,7 @@ export interface TailscaleProceduresOptions {
72
84
  * of `tailscale.run` is `executable`, or the default of `tailscaleExecutable()` at each call.
73
85
  */
74
86
  export function createTailscaleProcedures(options: TailscaleProceduresOptions = {}): AnyProcedure[] {
87
+ const tailscale = defineCliTool({ name: "tailscale", resolve: () => tailscaleExecutable(options.executable) });
75
88
  const run = createProcedure()
76
89
  .path(["tailscale", "run"])
77
90
  .input(RunInputSchema)
@@ -81,18 +94,19 @@ export function createTailscaleProcedures(options: TailscaleProceduresOptions =
81
94
  args: ["path"],
82
95
  shorts: { timeout: "t" },
83
96
  output: "json",
97
+ calls: ["shell.run"],
84
98
  // up, down and serve change the network of the host. ssh starts a program on another host.
85
99
  effects: { reads: ["network"], writes: ["network", "process"], destructive: true },
86
100
  })
87
- .handler(async (input: RunInput, ctx?: ProcedureContext): Promise<RunOutput> => {
101
+ .handler(async (input: RunInput, ctx: ProcedureContext): Promise<RunOutput> => {
88
102
  const argv = buildArgv({
89
103
  path: input.path,
90
104
  flags: input.json ? ["--json"] : [],
91
105
  args: input.args as Record<string, ArgValue> | undefined,
92
106
  positional: input.positional,
93
107
  });
94
- const result = await runCommand(tailscaleExecutable(options.executable), { args: argv, timeout: input.timeout, signal: ctx?.signal });
95
- if (result.error) throw new Error(`tailscale did not start: ${result.error}`);
108
+ const result = await tailscale.call(argv, { timeout: input.timeout }, ctx);
109
+ if (didNotStart(result)) throw new Error(`tailscale did not start: ${result.stderr}`);
96
110
  const json = input.json ? parseJsonOutput(result.stdout) : undefined;
97
111
  // tailscale prints an address to open when a login or a feature needs the owner
98
112
  const actionUrl = /https:\/\/login\.tailscale\.com\/\S+/.exec(`${result.stdout}\n${result.stderr}`)?.[0];