@ansible-flow/node 0.0.0-stage → 0.85.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.
Files changed (136) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +9 -2
  3. package/dist/ai/agents.d.ts +34 -0
  4. package/dist/ai/agents.js +16 -0
  5. package/dist/ai/agents.js.map +7 -0
  6. package/dist/ai/hostConfig.d.ts +44 -0
  7. package/dist/ai/hostConfig.js +120 -0
  8. package/dist/ai/hostConfig.js.map +7 -0
  9. package/dist/ai/hosts.d.ts +28 -0
  10. package/dist/ai/hosts.js +9 -0
  11. package/dist/ai/hosts.js.map +7 -0
  12. package/dist/ai/writers.d.ts +124 -0
  13. package/dist/ai/writers.js +75 -0
  14. package/dist/ai/writers.js.map +7 -0
  15. package/dist/autoLayout.d.ts +38 -0
  16. package/dist/autoLayout.js +145 -0
  17. package/dist/autoLayout.js.map +7 -0
  18. package/dist/awx/awxWorkflow.d.ts +47 -0
  19. package/dist/awx/awxWorkflow.js +17 -0
  20. package/dist/awx/awxWorkflow.js.map +7 -0
  21. package/dist/awx/client.d.ts +77 -0
  22. package/dist/awx/client.js +22 -0
  23. package/dist/awx/client.js.map +7 -0
  24. package/dist/catalog/bindingCheck.d.ts +4 -0
  25. package/dist/catalog/bindingCheck.js +69 -0
  26. package/dist/catalog/bindingCheck.js.map +7 -0
  27. package/dist/catalog/catalogIndex.d.ts +52 -0
  28. package/dist/catalog/catalogIndex.js +13 -0
  29. package/dist/catalog/catalogIndex.js.map +7 -0
  30. package/dist/catalog/catalogWorker.d.ts +17 -0
  31. package/dist/catalog/catalogWorker.js +38 -0
  32. package/dist/catalog/catalogWorker.js.map +7 -0
  33. package/dist/catalog/dumpFiles.d.ts +3 -0
  34. package/dist/catalog/dumpFiles.js +19 -0
  35. package/dist/catalog/dumpFiles.js.map +7 -0
  36. package/dist/chunks/chunk-2MZQHIXP.js +614 -0
  37. package/dist/chunks/chunk-2MZQHIXP.js.map +7 -0
  38. package/dist/chunks/chunk-4FHPTGI4.js +52 -0
  39. package/dist/chunks/chunk-4FHPTGI4.js.map +7 -0
  40. package/dist/chunks/chunk-A2NMQGQE.js +189 -0
  41. package/dist/chunks/chunk-A2NMQGQE.js.map +7 -0
  42. package/dist/chunks/chunk-EPJKJBNW.js +181 -0
  43. package/dist/chunks/chunk-EPJKJBNW.js.map +7 -0
  44. package/dist/chunks/chunk-FG53MLUX.js +136 -0
  45. package/dist/chunks/chunk-FG53MLUX.js.map +7 -0
  46. package/dist/chunks/chunk-GAL5LW6Y.js +269 -0
  47. package/dist/chunks/chunk-GAL5LW6Y.js.map +7 -0
  48. package/dist/chunks/chunk-HPVT6I36.js +371 -0
  49. package/dist/chunks/chunk-HPVT6I36.js.map +7 -0
  50. package/dist/chunks/chunk-I6FIOP7G.js +48 -0
  51. package/dist/chunks/chunk-I6FIOP7G.js.map +7 -0
  52. package/dist/chunks/chunk-IIFP6UQR.js +44 -0
  53. package/dist/chunks/chunk-IIFP6UQR.js.map +7 -0
  54. package/dist/chunks/chunk-JDPN3LTT.js +18 -0
  55. package/dist/chunks/chunk-JDPN3LTT.js.map +7 -0
  56. package/dist/chunks/chunk-LGUG2Q7Z.js +191 -0
  57. package/dist/chunks/chunk-LGUG2Q7Z.js.map +7 -0
  58. package/dist/chunks/chunk-NTZRNEZM.js +25 -0
  59. package/dist/chunks/chunk-NTZRNEZM.js.map +7 -0
  60. package/dist/chunks/chunk-RETJPSKL.js +123 -0
  61. package/dist/chunks/chunk-RETJPSKL.js.map +7 -0
  62. package/dist/chunks/chunk-SSZNBJZQ.js +52 -0
  63. package/dist/chunks/chunk-SSZNBJZQ.js.map +7 -0
  64. package/dist/chunks/chunk-SUV3VHKR.js +52 -0
  65. package/dist/chunks/chunk-SUV3VHKR.js.map +7 -0
  66. package/dist/chunks/chunk-THNSGAKN.js +133 -0
  67. package/dist/chunks/chunk-THNSGAKN.js.map +7 -0
  68. package/dist/chunks/chunk-UNCR5JOZ.js +85 -0
  69. package/dist/chunks/chunk-UNCR5JOZ.js.map +7 -0
  70. package/dist/env/probeClient.d.ts +77 -0
  71. package/dist/env/probeClient.js +244 -0
  72. package/dist/env/probeClient.js.map +7 -0
  73. package/dist/env/userSettings.d.ts +33 -0
  74. package/dist/env/userSettings.js +101 -0
  75. package/dist/env/userSettings.js.map +7 -0
  76. package/dist/run/audit.d.ts +2 -0
  77. package/dist/run/audit.js +10 -0
  78. package/dist/run/audit.js.map +7 -0
  79. package/dist/run/exec.d.ts +14 -0
  80. package/dist/run/exec.js +7 -0
  81. package/dist/run/exec.js.map +7 -0
  82. package/dist/run/facts.d.ts +39 -0
  83. package/dist/run/facts.js +96 -0
  84. package/dist/run/facts.js.map +7 -0
  85. package/dist/run/ids.d.ts +2 -0
  86. package/dist/run/ids.js +8 -0
  87. package/dist/run/ids.js.map +7 -0
  88. package/dist/run/pastRuns.d.ts +9 -0
  89. package/dist/run/pastRuns.js +13 -0
  90. package/dist/run/pastRuns.js.map +7 -0
  91. package/dist/run/runFlow.d.ts +78 -0
  92. package/dist/run/runFlow.js +20 -0
  93. package/dist/run/runFlow.js.map +7 -0
  94. package/dist/run/segments.d.ts +43 -0
  95. package/dist/run/segments.js +157 -0
  96. package/dist/run/segments.js.map +7 -0
  97. package/dist/run/stop.d.ts +3 -0
  98. package/dist/run/stop.js +7 -0
  99. package/dist/run/stop.js.map +7 -0
  100. package/dist/run/storage.d.ts +16 -0
  101. package/dist/run/storage.js +15 -0
  102. package/dist/run/storage.js.map +7 -0
  103. package/dist/run/tail.d.ts +5 -0
  104. package/dist/run/tail.js +7 -0
  105. package/dist/run/tail.js.map +7 -0
  106. package/dist/runner/client.d.ts +149 -0
  107. package/dist/runner/client.js +168 -0
  108. package/dist/runner/client.js.map +7 -0
  109. package/dist/runner/devBundle.d.ts +14 -0
  110. package/dist/runner/devBundle.js +78 -0
  111. package/dist/runner/devBundle.js.map +7 -0
  112. package/dist/scenario/cli.d.ts +39 -0
  113. package/dist/scenario/cli.js +125 -0
  114. package/dist/scenario/cli.js.map +7 -0
  115. package/dist/scenario/runner.d.ts +50 -0
  116. package/dist/scenario/runner.js +185 -0
  117. package/dist/scenario/runner.js.map +7 -0
  118. package/dist/sharedRefs.d.ts +22 -0
  119. package/dist/sharedRefs.js +114 -0
  120. package/dist/sharedRefs.js.map +7 -0
  121. package/dist/ssh/exec.d.ts +39 -0
  122. package/dist/ssh/exec.js +13 -0
  123. package/dist/ssh/exec.js.map +7 -0
  124. package/dist/ssh/remote.d.ts +40 -0
  125. package/dist/ssh/remote.js +25 -0
  126. package/dist/ssh/remote.js.map +7 -0
  127. package/dist/ssh/sshRun.d.ts +1 -0
  128. package/dist/ssh/sshRun.js +24 -0
  129. package/dist/ssh/sshRun.js.map +7 -0
  130. package/dist/ssh/sync.d.ts +22 -0
  131. package/dist/ssh/sync.js +12 -0
  132. package/dist/ssh/sync.js.map +7 -0
  133. package/dist/timeouts.d.ts +38 -0
  134. package/dist/timeouts.js +7 -0
  135. package/dist/timeouts.js.map +7 -0
  136. package/package.json +35 -4
@@ -0,0 +1,33 @@
1
+ /** The settings keys that choose the environment (package.json's restrictedConfigurations). */
2
+ export declare const ENVIRONMENT_KEYS: readonly ['afd.environment.kind', 'afd.environment.image', 'afd.environment.containerEngine', 'afd.environment.containerOptions', 'afd.environment.python', 'afd.environment.wslDistro', 'afd.environment.sshHost', 'afd.environment.sshDir', 'afd.environment.sshImage', 'ansible.executionEnvironment.enabled', 'ansible.executionEnvironment.image', 'ansible.executionEnvironment.containerEngine', 'ansible.executionEnvironment.containerOptions', 'ansible.executionEnvironment.volumeMounts', 'ansible.python.interpreterPath'];
3
+ export interface SettingsHost {
4
+ platform: NodeJS.Platform;
5
+ env: NodeJS.ProcessEnv;
6
+ home: string;
7
+ }
8
+ /** User settings.json files to try; AFD_USER_SETTINGS names one explicitly. */
9
+ export declare function userSettingsCandidates(host: SettingsHost): string[];
10
+ export declare const workspaceEnvironmentAllowed: (env: NodeJS.ProcessEnv) => boolean;
11
+ export interface EnvironmentSettings {
12
+ /** The environment keys, the workspace's over the user's when the workspace is allowed. */
13
+ json: Record<string, unknown>;
14
+ /** The settings files the keys came from. */
15
+ sources: string[];
16
+ /** The workspace sets environment keys that were not used (no opt-in). */
17
+ ignoredWorkspace: boolean;
18
+ }
19
+ /** The environment settings for a root: the first user settings.json found, then the workspace's. */
20
+ export declare function environmentSettings(root: string, host?: SettingsHost): Promise<EnvironmentSettings>;
21
+ /**
22
+ * `afd.canvas.direction` for a root (AFD-192), so the MCP tools lay out unplaced nodes as the
23
+ * canvas does: the workspace's .vscode/settings.json, else the first user settings.json found
24
+ * (VS Code's order). Not a restricted key: it only places boxes. TB when neither sets it.
25
+ */
26
+ export declare function canvasDirection(root: string, host?: SettingsHost): Promise<'TB' | 'LR'>;
27
+ /**
28
+ * `afd.run.inventory` for a root (AFD-261), so the agents' inventory tool works on the inventory
29
+ * the runs use: the workspace's .vscode/settings.json, else the first user settings.json found.
30
+ * Not a restricted key: it names a file of the repository, which the tool reads inside the root
31
+ * only. Empty when neither sets it (the default of ansible.cfg).
32
+ */
33
+ export declare function runInventory(root: string, host?: SettingsHost): Promise<string>;
@@ -0,0 +1,101 @@
1
+ // src/node/env/userSettings.ts
2
+ import { promises as fs } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { getNodeValue, parseTree } from "jsonc-parser";
6
+ var PRODUCTS = ["Code", "Code - Insiders", "VSCodium", "Cursor", "Windsurf"];
7
+ var ENVIRONMENT_KEYS = [
8
+ "afd.environment.kind",
9
+ "afd.environment.image",
10
+ "afd.environment.containerEngine",
11
+ "afd.environment.containerOptions",
12
+ "afd.environment.python",
13
+ "afd.environment.wslDistro",
14
+ // AFD-252: without these an SSH environment had a kind and no host.
15
+ "afd.environment.sshHost",
16
+ "afd.environment.sshDir",
17
+ "afd.environment.sshImage",
18
+ "ansible.executionEnvironment.enabled",
19
+ "ansible.executionEnvironment.image",
20
+ "ansible.executionEnvironment.containerEngine",
21
+ "ansible.executionEnvironment.containerOptions",
22
+ "ansible.executionEnvironment.volumeMounts",
23
+ "ansible.python.interpreterPath"
24
+ ];
25
+ var thisHost = () => ({
26
+ platform: process.platform,
27
+ env: process.env,
28
+ home: homedir()
29
+ });
30
+ function userSettingsCandidates(host) {
31
+ if (host.env.AFD_USER_SETTINGS) return [host.env.AFD_USER_SETTINGS];
32
+ const base = host.platform === "win32" ? host.env.APPDATA ?? join(host.home, "AppData", "Roaming") : host.platform === "darwin" ? join(host.home, "Library", "Application Support") : host.env.XDG_CONFIG_HOME ?? join(host.home, ".config");
33
+ return PRODUCTS.map((p) => join(base, p, "User", "settings.json"));
34
+ }
35
+ var workspaceEnvironmentAllowed = (env) => /^(1|true|yes)$/i.test(env.AFD_MCP_WORKSPACE_ENVIRONMENT ?? "");
36
+ async function readSettings(path) {
37
+ try {
38
+ const tree = parseTree(await fs.readFile(path, "utf8"));
39
+ const value = tree ? getNodeValue(tree) : void 0;
40
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
41
+ } catch {
42
+ return void 0;
43
+ }
44
+ }
45
+ var environmentOnly = (json) => Object.fromEntries(ENVIRONMENT_KEYS.filter((k) => k in json).map((k) => [k, json[k]]));
46
+ async function environmentSettings(root, host = thisHost()) {
47
+ const sources = [];
48
+ let json = {};
49
+ for (const path of userSettingsCandidates(host)) {
50
+ const user = await readSettings(path);
51
+ if (!user) continue;
52
+ json = environmentOnly(user);
53
+ sources.push(path);
54
+ break;
55
+ }
56
+ const workspacePath = join(root, ".vscode", "settings.json");
57
+ const workspace = environmentOnly(await readSettings(workspacePath) ?? {});
58
+ const used = Object.keys(workspace).length > 0 && workspaceEnvironmentAllowed(host.env);
59
+ if (used) {
60
+ json = { ...json, ...workspace };
61
+ sources.push(workspacePath);
62
+ }
63
+ return {
64
+ json,
65
+ sources,
66
+ ignoredWorkspace: Object.keys(workspace).length > 0 && !used
67
+ };
68
+ }
69
+ async function canvasDirection(root, host = thisHost()) {
70
+ const workspace = await readSettings(join(root, ".vscode", "settings.json"));
71
+ let value = workspace?.["afd.canvas.direction"];
72
+ if (value === void 0)
73
+ for (const path of userSettingsCandidates(host)) {
74
+ const user = await readSettings(path);
75
+ if (!user) continue;
76
+ value = user["afd.canvas.direction"];
77
+ break;
78
+ }
79
+ return value === "LR" ? "LR" : "TB";
80
+ }
81
+ async function runInventory(root, host = thisHost()) {
82
+ const workspace = await readSettings(join(root, ".vscode", "settings.json"));
83
+ let value = workspace?.["afd.run.inventory"];
84
+ if (value === void 0)
85
+ for (const path of userSettingsCandidates(host)) {
86
+ const user = await readSettings(path);
87
+ if (!user) continue;
88
+ value = user["afd.run.inventory"];
89
+ break;
90
+ }
91
+ return typeof value === "string" ? value.trim() : "";
92
+ }
93
+ export {
94
+ ENVIRONMENT_KEYS,
95
+ canvasDirection,
96
+ environmentSettings,
97
+ runInventory,
98
+ userSettingsCandidates,
99
+ workspaceEnvironmentAllowed
100
+ };
101
+ //# sourceMappingURL=userSettings.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/node/env/userSettings.ts"],
4
+ "sourcesContent": ["// Where the MCP server takes its Ansible environment from (AFD-126). The image, container options,\n// mounts and interpreter decide what runs on this machine, so they come from the user's own VS Code\n// settings. A workspace's .vscode/settings.json is repository content: VS Code ignores these keys\n// there until the workspace is trusted, and the server has no trust prompt, so it reads them only\n// when the user starts it with AFD_MCP_WORKSPACE_ENVIRONMENT=1.\n\nimport { promises as fs } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { getNodeValue, parseTree } from 'jsonc-parser';\n\n/** VS Code and the forks the setup registers the server for, in the order they are tried. */\nconst PRODUCTS = ['Code', 'Code - Insiders', 'VSCodium', 'Cursor', 'Windsurf'];\n\n/** The settings keys that choose the environment (package.json's restrictedConfigurations). */\nexport const ENVIRONMENT_KEYS = [\n 'afd.environment.kind',\n 'afd.environment.image',\n 'afd.environment.containerEngine',\n 'afd.environment.containerOptions',\n 'afd.environment.python',\n 'afd.environment.wslDistro',\n // AFD-252: without these an SSH environment had a kind and no host.\n 'afd.environment.sshHost',\n 'afd.environment.sshDir',\n 'afd.environment.sshImage',\n 'ansible.executionEnvironment.enabled',\n 'ansible.executionEnvironment.image',\n 'ansible.executionEnvironment.containerEngine',\n 'ansible.executionEnvironment.containerOptions',\n 'ansible.executionEnvironment.volumeMounts',\n 'ansible.python.interpreterPath',\n] as const;\n\nexport interface SettingsHost {\n platform: NodeJS.Platform;\n env: NodeJS.ProcessEnv;\n home: string;\n}\n\nconst thisHost = (): SettingsHost => ({\n platform: process.platform,\n env: process.env,\n home: homedir(),\n});\n\n/** User settings.json files to try; AFD_USER_SETTINGS names one explicitly. */\nexport function userSettingsCandidates(host: SettingsHost): string[] {\n if (host.env.AFD_USER_SETTINGS) return [host.env.AFD_USER_SETTINGS];\n const base =\n host.platform === 'win32'\n ? (host.env.APPDATA ?? join(host.home, 'AppData', 'Roaming'))\n : host.platform === 'darwin'\n ? join(host.home, 'Library', 'Application Support')\n : (host.env.XDG_CONFIG_HOME ?? join(host.home, '.config'));\n return PRODUCTS.map((p) => join(base, p, 'User', 'settings.json'));\n}\n\nexport const workspaceEnvironmentAllowed = (env: NodeJS.ProcessEnv): boolean =>\n /^(1|true|yes)$/i.test(env.AFD_MCP_WORKSPACE_ENVIRONMENT ?? '');\n\nasync function readSettings(path: string): Promise<Record<string, unknown> | undefined> {\n try {\n const tree = parseTree(await fs.readFile(path, 'utf8'));\n const value = tree ? getNodeValue(tree) : undefined;\n return value && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n } catch {\n return undefined;\n }\n}\n\nconst environmentOnly = (json: Record<string, unknown>) =>\n Object.fromEntries(ENVIRONMENT_KEYS.filter((k) => k in json).map((k) => [k, json[k]]));\n\nexport interface EnvironmentSettings {\n /** The environment keys, the workspace's over the user's when the workspace is allowed. */\n json: Record<string, unknown>;\n /** The settings files the keys came from. */\n sources: string[];\n /** The workspace sets environment keys that were not used (no opt-in). */\n ignoredWorkspace: boolean;\n}\n\n/** The environment settings for a root: the first user settings.json found, then the workspace's. */\nexport async function environmentSettings(\n root: string,\n host: SettingsHost = thisHost(),\n): Promise<EnvironmentSettings> {\n const sources: string[] = [];\n let json: Record<string, unknown> = {};\n for (const path of userSettingsCandidates(host)) {\n const user = await readSettings(path);\n if (!user) continue;\n json = environmentOnly(user);\n sources.push(path);\n break;\n }\n const workspacePath = join(root, '.vscode', 'settings.json');\n const workspace = environmentOnly((await readSettings(workspacePath)) ?? {});\n const used = Object.keys(workspace).length > 0 && workspaceEnvironmentAllowed(host.env);\n if (used) {\n json = { ...json, ...workspace };\n sources.push(workspacePath);\n }\n return {\n json,\n sources,\n ignoredWorkspace: Object.keys(workspace).length > 0 && !used,\n };\n}\n\n/**\n * `afd.canvas.direction` for a root (AFD-192), so the MCP tools lay out unplaced nodes as the\n * canvas does: the workspace's .vscode/settings.json, else the first user settings.json found\n * (VS Code's order). Not a restricted key: it only places boxes. TB when neither sets it.\n */\nexport async function canvasDirection(\n root: string,\n host: SettingsHost = thisHost(),\n): Promise<'TB' | 'LR'> {\n const workspace = await readSettings(join(root, '.vscode', 'settings.json'));\n let value = workspace?.['afd.canvas.direction'];\n if (value === undefined)\n for (const path of userSettingsCandidates(host)) {\n const user = await readSettings(path);\n if (!user) continue;\n value = user['afd.canvas.direction'];\n break;\n }\n return value === 'LR' ? 'LR' : 'TB';\n}\n\n/**\n * `afd.run.inventory` for a root (AFD-261), so the agents' inventory tool works on the inventory\n * the runs use: the workspace's .vscode/settings.json, else the first user settings.json found.\n * Not a restricted key: it names a file of the repository, which the tool reads inside the root\n * only. Empty when neither sets it (the default of ansible.cfg).\n */\nexport async function runInventory(root: string, host: SettingsHost = thisHost()): Promise<string> {\n const workspace = await readSettings(join(root, '.vscode', 'settings.json'));\n let value = workspace?.['afd.run.inventory'];\n if (value === undefined)\n for (const path of userSettingsCandidates(host)) {\n const user = await readSettings(path);\n if (!user) continue;\n value = user['afd.run.inventory'];\n break;\n }\n return typeof value === 'string' ? value.trim() : '';\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,2 @@
1
+ export declare function auditFile(storage: string): string;
2
+ export declare function appendAudit(storage: string, entry: Record<string, unknown>): Promise<void>;
@@ -0,0 +1,10 @@
1
+ import {
2
+ appendAudit,
3
+ auditFile
4
+ } from "../chunks/chunk-NTZRNEZM.js";
5
+ import "../chunks/chunk-SUV3VHKR.js";
6
+ export {
7
+ appendAudit,
8
+ auditFile
9
+ };
10
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,14 @@
1
+ import type { ProcessSpec } from '@ansible-flow/core/run/command';
2
+ /** A process that was started: how it ended, and how to end it. */
3
+ export interface Execution {
4
+ /** The exit code; undefined when the process could not start or ended without one. */
5
+ exit: Promise<number | undefined>;
6
+ /** Ends the process. What it started beyond itself (a container) is ended with `stopStarted`. */
7
+ terminate(): void;
8
+ }
9
+ /**
10
+ * Runs `spec` with `env` on top of this process's environment, feeding its output to `log` line
11
+ * by line. AFD-247: a spec with `input` names the variables whose values go to the process's
12
+ * input, one base64 line each, instead of its arguments (what afd-ssh-run does for a terminal).
13
+ */
14
+ export declare function startProcess(spec: ProcessSpec, env: Record<string, string>, log: (line: string) => void): Execution;
@@ -0,0 +1,7 @@
1
+ import {
2
+ startProcess
3
+ } from "../chunks/chunk-I6FIOP7G.js";
4
+ export {
5
+ startProcess
6
+ };
7
+ //# sourceMappingURL=exec.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,39 @@
1
+ import type { FlowDocument } from '@ansible-flow/core/flow/types';
2
+ import { type LaunchFacts } from '@ansible-flow/core/run/launch';
3
+ /** The probe of the Ansible environment a run uses, and the name people know it by. */
4
+ export interface RunProbe {
5
+ client: {
6
+ call<T>(method: string, params?: Record<string, unknown>, timeoutMs?: number): Promise<T>;
7
+ };
8
+ label: string;
9
+ }
10
+ /**
11
+ * What a run with these inputs would touch: the hosts the targets match in the inventory, the
12
+ * inventory_targets of the flow's steps, what looks like production.
13
+ */
14
+ export declare function launchFacts(doc: FlowDocument, inputs: Record<string, unknown>, probe: RunProbe, inventory: string | undefined): Promise<{
15
+ ok: true;
16
+ facts: LaunchFacts;
17
+ } | {
18
+ ok: false;
19
+ error: string;
20
+ }>;
21
+ /**
22
+ * The inventory's groups, for the dialog's host list suggestions (AFD-82), with each group's hosts
23
+ * (AFD-95, from the probe's inventory_tree, one inventory parse as before); none when it fails.
24
+ */
25
+ export declare function inventoryGroups(probe: RunProbe, inventory: string | undefined): Promise<{
26
+ groups: string[];
27
+ hosts: Record<string, {
28
+ count: number;
29
+ hosts: string[];
30
+ }>;
31
+ }>;
32
+ /**
33
+ * AFD-141: the callbacks the repository's ansible.cfg enables, so they keep running next to the
34
+ * run's own; undefined when the environment does not answer (the run's callback alone, then).
35
+ */
36
+ export declare function repositoryCallbacks(probe: RunProbe): Promise<{
37
+ enabled: string[];
38
+ paths: string[];
39
+ } | undefined>;
@@ -0,0 +1,96 @@
1
+ import {
2
+ TIMEOUTS
3
+ } from "../chunks/chunk-IIFP6UQR.js";
4
+
5
+ // src/node/run/facts.ts
6
+ import { resolvePattern, targetUses } from "@ansible-flow/core/engine/targets";
7
+ import { buildModel } from "@ansible-flow/core/flow/model";
8
+ import { targetsOf } from "@ansible-flow/core/run/launch";
9
+ import { groupHosts } from "@ansible-flow/core/view/inventoryTree";
10
+ async function targetFacts(doc, inputs, count) {
11
+ return Promise.all(
12
+ targetUses(buildModel(doc)).map(async (u) => {
13
+ const pattern = resolvePattern(u.target, inputs);
14
+ const use = { pattern, declared: u.target, nodes: u.nodes, prod: [] };
15
+ if (!pattern) return use;
16
+ try {
17
+ const f = await count(pattern);
18
+ use.count = f.count;
19
+ use.prod = [...f.prod_groups, ...f.prod_hosts];
20
+ } catch {
21
+ }
22
+ return use;
23
+ })
24
+ );
25
+ }
26
+ async function launchFacts(doc, inputs, probe, inventory) {
27
+ const targets = targetsOf(doc, inputs);
28
+ if (targets.length === 0)
29
+ return { ok: false, error: "No target hosts: the targets input is empty" };
30
+ let found;
31
+ try {
32
+ found = await probe.client.call(
33
+ "inventory",
34
+ { inventory, targets },
35
+ TIMEOUTS.probeInventory
36
+ );
37
+ } catch (e) {
38
+ return {
39
+ ok: false,
40
+ error: `The Ansible environment (${probe.label}) did not answer: ${e.message}`
41
+ };
42
+ }
43
+ if (found.count === 0)
44
+ return { ok: false, error: `None of the targets (${targets.join(", ")}) is in the inventory` };
45
+ const uses = await targetFacts(
46
+ doc,
47
+ inputs,
48
+ (pattern) => probe.client.call(
49
+ "inventory",
50
+ { inventory, targets: [pattern] },
51
+ TIMEOUTS.probeInventory
52
+ )
53
+ );
54
+ const prod = [
55
+ .../* @__PURE__ */ new Set([...found.prod_groups, ...found.prod_hosts, ...uses.flatMap((u) => u.prod)])
56
+ ];
57
+ return {
58
+ ok: true,
59
+ facts: {
60
+ sources: found.sources,
61
+ count: found.count,
62
+ hosts: found.hosts,
63
+ unknown: found.unknown,
64
+ uses,
65
+ prod,
66
+ vault: found.vault,
67
+ environment: probe.label
68
+ }
69
+ };
70
+ }
71
+ async function inventoryGroups(probe, inventory) {
72
+ try {
73
+ const tree = await probe.client.call(
74
+ "inventory_tree",
75
+ { inventory },
76
+ TIMEOUTS.probeInventory
77
+ );
78
+ const hosts = groupHosts(tree);
79
+ return { groups: Object.keys(hosts).sort((a, b) => a.localeCompare(b)), hosts };
80
+ } catch {
81
+ return { groups: [], hosts: {} };
82
+ }
83
+ }
84
+ async function repositoryCallbacks(probe) {
85
+ return probe.client.call(
86
+ "run_config",
87
+ {},
88
+ TIMEOUTS.probeInventory
89
+ ).then((c) => ({ enabled: c.callbacks_enabled ?? [], paths: c.callback_plugins ?? [] })).catch(() => void 0);
90
+ }
91
+ export {
92
+ inventoryGroups,
93
+ launchFacts,
94
+ repositoryCallbacks
95
+ };
96
+ //# sourceMappingURL=facts.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/node/run/facts.ts"],
4
+ "sourcesContent": ["// What a run would touch, asked of the inventory through the probe (the real-run confirmation and\n// the launch dialog, AFD-82). AFD-271: without VS Code, for every host that runs flows.\nimport { resolvePattern, targetUses } from '../../core/engine/targets';\nimport { buildModel } from '../../core/flow/model';\nimport type { FlowDocument } from '../../core/flow/types';\nimport { type LaunchFacts, type TargetUse, targetsOf } from '../../core/run/launch';\nimport { groupHosts, type InventoryTree } from '../../core/view/inventoryTree';\nimport { TIMEOUTS } from '../timeouts';\n\n/** The probe of the Ansible environment a run uses, and the name people know it by. */\nexport interface RunProbe {\n client: {\n call<T>(method: string, params?: Record<string, unknown>, timeoutMs?: number): Promise<T>;\n };\n label: string;\n}\n\ninterface InventoryFacts {\n sources: string[];\n count: number;\n hosts: string[];\n unknown: string[];\n prod_groups: string[];\n prod_hosts: string[];\n vault: boolean;\n /** Per group, how many of the matched hosts it has. */\n groups?: Record<string, number>;\n}\n\nasync function targetFacts(\n doc: FlowDocument,\n inputs: Record<string, unknown>,\n count: (pattern: string) => Promise<InventoryFacts>,\n): Promise<TargetUse[]> {\n return Promise.all(\n targetUses(buildModel(doc)).map(async (u): Promise<TargetUse> => {\n const pattern = resolvePattern(u.target, inputs);\n const use: TargetUse = { pattern, declared: u.target, nodes: u.nodes, prod: [] };\n if (!pattern) return use;\n try {\n const f = await count(pattern);\n use.count = f.count;\n use.prod = [...f.prod_groups, ...f.prod_hosts];\n } catch {\n // Counted at run time only; the confirmation says so.\n }\n return use;\n }),\n );\n}\n\n/**\n * What a run with these inputs would touch: the hosts the targets match in the inventory, the\n * inventory_targets of the flow's steps, what looks like production.\n */\nexport async function launchFacts(\n doc: FlowDocument,\n inputs: Record<string, unknown>,\n probe: RunProbe,\n inventory: string | undefined,\n): Promise<{ ok: true; facts: LaunchFacts } | { ok: false; error: string }> {\n const targets = targetsOf(doc, inputs);\n if (targets.length === 0)\n return { ok: false, error: 'No target hosts: the targets input is empty' };\n let found: InventoryFacts;\n try {\n found = await probe.client.call<InventoryFacts>(\n 'inventory',\n { inventory, targets },\n TIMEOUTS.probeInventory,\n );\n } catch (e) {\n return {\n ok: false,\n error: `The Ansible environment (${probe.label}) did not answer: ${(e as Error).message}`,\n };\n }\n if (found.count === 0)\n return { ok: false, error: `None of the targets (${targets.join(', ')}) is in the inventory` };\n const uses = await targetFacts(doc, inputs, (pattern) =>\n probe.client.call<InventoryFacts>(\n 'inventory',\n { inventory, targets: [pattern] },\n TIMEOUTS.probeInventory,\n ),\n );\n const prod = [\n ...new Set([...found.prod_groups, ...found.prod_hosts, ...uses.flatMap((u) => u.prod)]),\n ];\n return {\n ok: true,\n facts: {\n sources: found.sources,\n count: found.count,\n hosts: found.hosts,\n unknown: found.unknown,\n uses,\n prod,\n vault: found.vault,\n environment: probe.label,\n },\n };\n}\n\n/**\n * The inventory's groups, for the dialog's host list suggestions (AFD-82), with each group's hosts\n * (AFD-95, from the probe's inventory_tree, one inventory parse as before); none when it fails.\n */\nexport async function inventoryGroups(\n probe: RunProbe,\n inventory: string | undefined,\n): Promise<{ groups: string[]; hosts: Record<string, { count: number; hosts: string[] }> }> {\n try {\n const tree = await probe.client.call<InventoryTree>(\n 'inventory_tree',\n { inventory },\n TIMEOUTS.probeInventory,\n );\n const hosts = groupHosts(tree);\n return { groups: Object.keys(hosts).sort((a, b) => a.localeCompare(b)), hosts };\n } catch {\n return { groups: [], hosts: {} };\n }\n}\n\n/**\n * AFD-141: the callbacks the repository's ansible.cfg enables, so they keep running next to the\n * run's own; undefined when the environment does not answer (the run's callback alone, then).\n */\nexport async function repositoryCallbacks(\n probe: RunProbe,\n): Promise<{ enabled: string[]; paths: string[] } | undefined> {\n return probe.client\n .call<{ callbacks_enabled: string[]; callback_plugins: string[] }>(\n 'run_config',\n {},\n TIMEOUTS.probeInventory,\n )\n .then((c) => ({ enabled: c.callbacks_enabled ?? [], paths: c.callback_plugins ?? [] }))\n .catch(() => undefined);\n}\n"],
5
+ "mappings": ";;;;;AAEA,SAAS,gBAAgB,kBAAkB;AAC3C,SAAS,kBAAkB;AAE3B,SAA2C,iBAAiB;AAC5D,SAAS,kBAAsC;AAuB/C,eAAe,YACb,KACA,QACA,OACsB;AACtB,SAAO,QAAQ;AAAA,IACb,WAAW,WAAW,GAAG,CAAC,EAAE,IAAI,OAAO,MAA0B;AAC/D,YAAM,UAAU,eAAe,EAAE,QAAQ,MAAM;AAC/C,YAAM,MAAiB,EAAE,SAAS,UAAU,EAAE,QAAQ,OAAO,EAAE,OAAO,MAAM,CAAC,EAAE;AAC/E,UAAI,CAAC,QAAS,QAAO;AACrB,UAAI;AACF,cAAM,IAAI,MAAM,MAAM,OAAO;AAC7B,YAAI,QAAQ,EAAE;AACd,YAAI,OAAO,CAAC,GAAG,EAAE,aAAa,GAAG,EAAE,UAAU;AAAA,MAC/C,QAAQ;AAAA,MAER;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AACF;AAMA,eAAsB,YACpB,KACA,QACA,OACA,WAC0E;AAC1E,QAAM,UAAU,UAAU,KAAK,MAAM;AACrC,MAAI,QAAQ,WAAW;AACrB,WAAO,EAAE,IAAI,OAAO,OAAO,8CAA8C;AAC3E,MAAI;AACJ,MAAI;AACF,YAAQ,MAAM,MAAM,OAAO;AAAA,MACzB;AAAA,MACA,EAAE,WAAW,QAAQ;AAAA,MACrB,SAAS;AAAA,IACX;AAAA,EACF,SAAS,GAAG;AACV,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,OAAO,4BAA4B,MAAM,KAAK,qBAAsB,EAAY,OAAO;AAAA,IACzF;AAAA,EACF;AACA,MAAI,MAAM,UAAU;AAClB,WAAO,EAAE,IAAI,OAAO,OAAO,wBAAwB,QAAQ,KAAK,IAAI,CAAC,wBAAwB;AAC/F,QAAM,OAAO,MAAM;AAAA,IAAY;AAAA,IAAK;AAAA,IAAQ,CAAC,YAC3C,MAAM,OAAO;AAAA,MACX;AAAA,MACA,EAAE,WAAW,SAAS,CAAC,OAAO,EAAE;AAAA,MAChC,SAAS;AAAA,IACX;AAAA,EACF;AACA,QAAM,OAAO;AAAA,IACX,GAAG,oBAAI,IAAI,CAAC,GAAG,MAAM,aAAa,GAAG,MAAM,YAAY,GAAG,KAAK,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAAA,EACxF;AACA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,OAAO;AAAA,MACL,SAAS,MAAM;AAAA,MACf,OAAO,MAAM;AAAA,MACb,OAAO,MAAM;AAAA,MACb,SAAS,MAAM;AAAA,MACf;AAAA,MACA;AAAA,MACA,OAAO,MAAM;AAAA,MACb,aAAa,MAAM;AAAA,IACrB;AAAA,EACF;AACF;AAMA,eAAsB,gBACpB,OACA,WAC0F;AAC1F,MAAI;AACF,UAAM,OAAO,MAAM,MAAM,OAAO;AAAA,MAC9B;AAAA,MACA,EAAE,UAAU;AAAA,MACZ,SAAS;AAAA,IACX;AACA,UAAM,QAAQ,WAAW,IAAI;AAC7B,WAAO,EAAE,QAAQ,OAAO,KAAK,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC,GAAG,MAAM;AAAA,EAChF,QAAQ;AACN,WAAO,EAAE,QAAQ,CAAC,GAAG,OAAO,CAAC,EAAE;AAAA,EACjC;AACF;AAMA,eAAsB,oBACpB,OAC6D;AAC7D,SAAO,MAAM,OACV;AAAA,IACC;AAAA,IACA,CAAC;AAAA,IACD,SAAS;AAAA,EACX,EACC,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,qBAAqB,CAAC,GAAG,OAAO,EAAE,oBAAoB,CAAC,EAAE,EAAE,EACrF,MAAM,MAAM,MAAS;AAC1B;",
6
+ "names": []
7
+ }
@@ -0,0 +1,2 @@
1
+ /** A run id: the start time, sortable (run folders are pruned by it, AFD-29). */
2
+ export declare function stamp(d?: Date): string;
@@ -0,0 +1,8 @@
1
+ // src/node/run/ids.ts
2
+ function stamp(d = /* @__PURE__ */ new Date()) {
3
+ return d.toISOString().replaceAll(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
4
+ }
5
+ export {
6
+ stamp
7
+ };
8
+ //# sourceMappingURL=ids.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/node/run/ids.ts"],
4
+ "sourcesContent": ["/** A run id: the start time, sortable (run folders are pruned by it, AFD-29). */\nexport function stamp(d = new Date()): string {\n return d\n .toISOString()\n .replaceAll(/[-:]/g, '')\n .replace(/\\.\\d+Z$/, 'Z');\n}\n"],
5
+ "mappings": ";AACO,SAAS,MAAM,IAAI,oBAAI,KAAK,GAAW;AAC5C,SAAO,EACJ,YAAY,EACZ,WAAW,SAAS,EAAE,EACtB,QAAQ,WAAW,GAAG;AAC3B;",
6
+ "names": []
7
+ }
@@ -0,0 +1,9 @@
1
+ import type { RunView } from '@ansible-flow/core/protocol';
2
+ import { type PastRun } from '@ansible-flow/core/run/pastRuns';
3
+ /** Where the runs of a flow are kept. */
4
+ export declare function runsDir(storage: string, flowId: string): string;
5
+ /** Keeps a finished run with its events. The view holds host names and results, no input values. */
6
+ export declare function keepRun(runDir: string, run: RunView): Promise<void>;
7
+ /** The kept runs of a flow, the newest first. */
8
+ export declare function listRuns(storage: string, flowId: string): Promise<PastRun[]>;
9
+ export declare function loadRun(storage: string, flowId: string, id: string): Promise<RunView | undefined>;
@@ -0,0 +1,13 @@
1
+ import {
2
+ keepRun,
3
+ listRuns,
4
+ loadRun,
5
+ runsDir
6
+ } from "../chunks/chunk-4FHPTGI4.js";
7
+ export {
8
+ keepRun,
9
+ listRuns,
10
+ loadRun,
11
+ runsDir
12
+ };
13
+ //# sourceMappingURL=pastRuns.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,78 @@
1
+ import type { GenerateResult } from '@ansible-flow/core/engine/types';
2
+ import type { FlowDocument } from '@ansible-flow/core/flow/types';
3
+ import type { RunView } from '@ansible-flow/core/protocol';
4
+ import { type ProcessSpec } from '@ansible-flow/core/run/command';
5
+ import type { Environment } from '@ansible-flow/core/run/environment';
6
+ import { type RunSummary } from '@ansible-flow/core/run/events';
7
+ import { type Execution } from './exec';
8
+ /** How often a live run updates its view, at most (AFD-81). */
9
+ export declare const LIVE_MS = 500;
10
+ /** No new events for this long: the run may wait for input. */
11
+ export declare const STALL_MS = 20000;
12
+ export interface RunFlowRequest {
13
+ /** The run's id (`stamp()`), which names its folder. */
14
+ id: string;
15
+ startedAt: string;
16
+ mode: 'check' | 'real';
17
+ doc: FlowDocument;
18
+ /** The code that runs: its routes map the events back to nodes. */
19
+ code: GenerateResult;
20
+ env: Environment;
21
+ /** The environment's name for people ("afd-ee:2.19 (docker)"). */
22
+ environment: string;
23
+ /** Repository root on this machine. */
24
+ root: string;
25
+ /** Folder with afd_probe/ and callback_plugins/afd_events.py. */
26
+ probeDir: string;
27
+ /** The playbook to run, relative to `root`, with forward slashes. */
28
+ playbook: string;
29
+ inventory?: string;
30
+ /** Where runs are kept: `<storage>/runs/<flow id>/<run id>/`, and the audit log. */
31
+ storage: string;
32
+ /** How many runs of the flow to keep (default `KEEP_RUNS`). */
33
+ keepRuns?: number;
34
+ /** Flow input values. Secret ones never reach a file. */
35
+ inputs: Record<string, unknown>;
36
+ vaultPassword?: string;
37
+ /** The repository's own callbacks, kept running beside the run's (`repositoryCallbacks`). */
38
+ callbacks?: {
39
+ enabled: string[];
40
+ paths: string[];
41
+ };
42
+ /** AFD-244: the run is of a part of the flow; its code is in a folder of its own. */
43
+ partial?: RunView['partial'];
44
+ /** AFD-247: paths the SSH copy needs that Git does not list (a partial run's folder). */
45
+ syncAlso?: string[];
46
+ /** What the audit log says about the launch, besides the run's id. Never input values. */
47
+ audit?: Record<string, unknown>;
48
+ /** Stops the run when it aborts. */
49
+ signal?: AbortSignal;
50
+ }
51
+ export interface RunFlowHooks {
52
+ /** The run's view: when it starts, as it goes (at most every `LIVE_MS`), and when it ends. */
53
+ report(run: RunView): void;
54
+ /**
55
+ * Starts the process. Default: a child process whose output goes to `log`. `stopping` says the
56
+ * run is being stopped, for a host whose process may end without an exit code then.
57
+ */
58
+ execute?(spec: ProcessSpec, env: Record<string, string>, stopping: () => boolean): Execution | Promise<Execution>;
59
+ /** The default process's output, line by line. */
60
+ log?(line: string): void;
61
+ /** The run started; `stop` ends it. Returns what to call when the run has ended. */
62
+ started?(stop: () => Promise<void>): (() => void) | undefined;
63
+ /** Whether to watch for a run gone quiet (no event for `STALL_MS`). */
64
+ watchQuiet?: boolean;
65
+ /** The run went quiet. */
66
+ quiet?(): void;
67
+ /** The process ended; before its events are read. */
68
+ ended?(): Promise<void>;
69
+ /** How the run ended, before it is kept and reported. */
70
+ finished?(end: {
71
+ stopped: boolean;
72
+ rc: number | undefined;
73
+ events: boolean;
74
+ summary: RunSummary;
75
+ }): void;
76
+ }
77
+ /** Runs the flow's code and resolves with the finished run (also reported). Never rejects. */
78
+ export declare function runFlow(request: RunFlowRequest, hooks: RunFlowHooks): Promise<RunView>;
@@ -0,0 +1,20 @@
1
+ import {
2
+ LIVE_MS,
3
+ STALL_MS,
4
+ runFlow
5
+ } from "../chunks/chunk-LGUG2Q7Z.js";
6
+ import "../chunks/chunk-RETJPSKL.js";
7
+ import "../chunks/chunk-THNSGAKN.js";
8
+ import "../chunks/chunk-FG53MLUX.js";
9
+ import "../chunks/chunk-4FHPTGI4.js";
10
+ import "../chunks/chunk-SSZNBJZQ.js";
11
+ import "../chunks/chunk-JDPN3LTT.js";
12
+ import "../chunks/chunk-NTZRNEZM.js";
13
+ import "../chunks/chunk-SUV3VHKR.js";
14
+ import "../chunks/chunk-I6FIOP7G.js";
15
+ export {
16
+ LIVE_MS,
17
+ STALL_MS,
18
+ runFlow
19
+ };
20
+ //# sourceMappingURL=runFlow.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,43 @@
1
+ import type { SegmentFile } from '@ansible-flow/core/engine/types';
2
+ import type { Ulid } from '@ansible-flow/core/flow/types';
3
+ import type { RunApproval, RunView } from '@ansible-flow/core/protocol';
4
+ import { type RunSummary, summarizeRun } from '@ansible-flow/core/run/events';
5
+ import { type RunFlowHooks, type RunFlowRequest } from './runFlow';
6
+ /** How a gate was decided. `timeout` takes the approval's timeout port (else rejected's). */
7
+ export type GateDecision = 'approved' | 'rejected' | 'timeout';
8
+ /** What a segment's run left for the next one. */
9
+ export interface SegmentOutcome {
10
+ /** The set_stats data the segment carried (`afd_hostsets`, `afd_gate_empty`); empty if none. */
11
+ carried: Record<string, unknown>;
12
+ /** Hosts wait at the segment's gate: a decision is needed. */
13
+ waiting: boolean;
14
+ /** The gate had no host: the flow goes on with `finish`. */
15
+ empty: boolean;
16
+ }
17
+ /** The carried data of a segment's run, from its events (the last stats event's `custom`). */
18
+ export declare function segmentOutcome(segment: SegmentFile, events: string): SegmentOutcome;
19
+ /**
20
+ * The segment after `from`: its approved or rejected continuation for a decision (a timeout runs
21
+ * the rejected one, which then takes the timeout port), `finish` when nobody reached its gate.
22
+ * Undefined when there is none: the run has ended.
23
+ */
24
+ export declare function nextSegment(segments: SegmentFile[], from: string, decision: GateDecision | 'empty'): SegmentFile | undefined;
25
+ /** The inputs of the segment after a decision: the run's, the carried host sets, the decision. */
26
+ export declare function segmentInputs(inputs: Record<string, unknown>, carried: Record<string, unknown>, decision?: GateDecision): Record<string, unknown>;
27
+ /** The run as one: the events of its segments in order, summarized with the flow's routes. */
28
+ export declare function segmentsSummary(events: string[], routes: Parameters<typeof summarizeRun>[1]): RunSummary;
29
+ export interface SegmentsHooks extends RunFlowHooks {
30
+ /**
31
+ * The decision for the approval `gate`, when hosts wait there. Called with the run as it is
32
+ * then (state `running`, the gate's approval `waiting`). Rejects to stop the run.
33
+ */
34
+ decide(gate: Ulid, run: RunView): Promise<{
35
+ decision: GateDecision;
36
+ } & Partial<RunApproval>>;
37
+ }
38
+ /**
39
+ * Runs a flow with approval gates segment by segment in one call: each segment is a `runFlow` of
40
+ * its playbook (folder `<id>-<n>`), each gate waits for `decide`. Reports the run as one (id
41
+ * `request.id`), with its approvals; resolves with the finished run. Never rejects.
42
+ */
43
+ export declare function runSegments(request: RunFlowRequest, segments: SegmentFile[], hooks: SegmentsHooks): Promise<RunView>;