@ansible-flow/node 0.0.0-stage → 0.86.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/LICENSE +21 -0
- package/README.md +9 -2
- package/dist/ai/agents.d.ts +34 -0
- package/dist/ai/agents.js +16 -0
- package/dist/ai/agents.js.map +7 -0
- package/dist/ai/hostConfig.d.ts +44 -0
- package/dist/ai/hostConfig.js +120 -0
- package/dist/ai/hostConfig.js.map +7 -0
- package/dist/ai/hosts.d.ts +28 -0
- package/dist/ai/hosts.js +9 -0
- package/dist/ai/hosts.js.map +7 -0
- package/dist/ai/writers.d.ts +124 -0
- package/dist/ai/writers.js +75 -0
- package/dist/ai/writers.js.map +7 -0
- package/dist/autoLayout.d.ts +38 -0
- package/dist/autoLayout.js +145 -0
- package/dist/autoLayout.js.map +7 -0
- package/dist/awx/awxWorkflow.d.ts +47 -0
- package/dist/awx/awxWorkflow.js +17 -0
- package/dist/awx/awxWorkflow.js.map +7 -0
- package/dist/awx/client.d.ts +77 -0
- package/dist/awx/client.js +22 -0
- package/dist/awx/client.js.map +7 -0
- package/dist/catalog/bindingCheck.d.ts +4 -0
- package/dist/catalog/bindingCheck.js +69 -0
- package/dist/catalog/bindingCheck.js.map +7 -0
- package/dist/catalog/catalogIndex.d.ts +52 -0
- package/dist/catalog/catalogIndex.js +13 -0
- package/dist/catalog/catalogIndex.js.map +7 -0
- package/dist/catalog/catalogWorker.d.ts +17 -0
- package/dist/catalog/catalogWorker.js +38 -0
- package/dist/catalog/catalogWorker.js.map +7 -0
- package/dist/catalog/dumpFiles.d.ts +3 -0
- package/dist/catalog/dumpFiles.js +19 -0
- package/dist/catalog/dumpFiles.js.map +7 -0
- package/dist/chunks/chunk-2MZQHIXP.js +614 -0
- package/dist/chunks/chunk-2MZQHIXP.js.map +7 -0
- package/dist/chunks/chunk-4FHPTGI4.js +52 -0
- package/dist/chunks/chunk-4FHPTGI4.js.map +7 -0
- package/dist/chunks/chunk-A2NMQGQE.js +189 -0
- package/dist/chunks/chunk-A2NMQGQE.js.map +7 -0
- package/dist/chunks/chunk-EPJKJBNW.js +181 -0
- package/dist/chunks/chunk-EPJKJBNW.js.map +7 -0
- package/dist/chunks/chunk-FG53MLUX.js +136 -0
- package/dist/chunks/chunk-FG53MLUX.js.map +7 -0
- package/dist/chunks/chunk-GAL5LW6Y.js +269 -0
- package/dist/chunks/chunk-GAL5LW6Y.js.map +7 -0
- package/dist/chunks/chunk-HPVT6I36.js +371 -0
- package/dist/chunks/chunk-HPVT6I36.js.map +7 -0
- package/dist/chunks/chunk-I6FIOP7G.js +48 -0
- package/dist/chunks/chunk-I6FIOP7G.js.map +7 -0
- package/dist/chunks/chunk-IIFP6UQR.js +44 -0
- package/dist/chunks/chunk-IIFP6UQR.js.map +7 -0
- package/dist/chunks/chunk-JDPN3LTT.js +18 -0
- package/dist/chunks/chunk-JDPN3LTT.js.map +7 -0
- package/dist/chunks/chunk-LGUG2Q7Z.js +191 -0
- package/dist/chunks/chunk-LGUG2Q7Z.js.map +7 -0
- package/dist/chunks/chunk-NTZRNEZM.js +25 -0
- package/dist/chunks/chunk-NTZRNEZM.js.map +7 -0
- package/dist/chunks/chunk-RETJPSKL.js +123 -0
- package/dist/chunks/chunk-RETJPSKL.js.map +7 -0
- package/dist/chunks/chunk-SSZNBJZQ.js +52 -0
- package/dist/chunks/chunk-SSZNBJZQ.js.map +7 -0
- package/dist/chunks/chunk-SUV3VHKR.js +52 -0
- package/dist/chunks/chunk-SUV3VHKR.js.map +7 -0
- package/dist/chunks/chunk-THNSGAKN.js +133 -0
- package/dist/chunks/chunk-THNSGAKN.js.map +7 -0
- package/dist/chunks/chunk-UNCR5JOZ.js +85 -0
- package/dist/chunks/chunk-UNCR5JOZ.js.map +7 -0
- package/dist/env/probeClient.d.ts +77 -0
- package/dist/env/probeClient.js +244 -0
- package/dist/env/probeClient.js.map +7 -0
- package/dist/env/userSettings.d.ts +33 -0
- package/dist/env/userSettings.js +101 -0
- package/dist/env/userSettings.js.map +7 -0
- package/dist/flowMerge.d.ts +35 -0
- package/dist/flowMerge.js +74 -0
- package/dist/flowMerge.js.map +7 -0
- package/dist/run/audit.d.ts +2 -0
- package/dist/run/audit.js +10 -0
- package/dist/run/audit.js.map +7 -0
- package/dist/run/exec.d.ts +14 -0
- package/dist/run/exec.js +7 -0
- package/dist/run/exec.js.map +7 -0
- package/dist/run/facts.d.ts +39 -0
- package/dist/run/facts.js +96 -0
- package/dist/run/facts.js.map +7 -0
- package/dist/run/ids.d.ts +2 -0
- package/dist/run/ids.js +8 -0
- package/dist/run/ids.js.map +7 -0
- package/dist/run/pastRuns.d.ts +9 -0
- package/dist/run/pastRuns.js +13 -0
- package/dist/run/pastRuns.js.map +7 -0
- package/dist/run/runFlow.d.ts +78 -0
- package/dist/run/runFlow.js +20 -0
- package/dist/run/runFlow.js.map +7 -0
- package/dist/run/segments.d.ts +43 -0
- package/dist/run/segments.js +157 -0
- package/dist/run/segments.js.map +7 -0
- package/dist/run/stop.d.ts +3 -0
- package/dist/run/stop.js +7 -0
- package/dist/run/stop.js.map +7 -0
- package/dist/run/storage.d.ts +16 -0
- package/dist/run/storage.js +15 -0
- package/dist/run/storage.js.map +7 -0
- package/dist/run/tail.d.ts +5 -0
- package/dist/run/tail.js +7 -0
- package/dist/run/tail.js.map +7 -0
- package/dist/runner/client.d.ts +149 -0
- package/dist/runner/client.js +168 -0
- package/dist/runner/client.js.map +7 -0
- package/dist/runner/devBundle.d.ts +14 -0
- package/dist/runner/devBundle.js +78 -0
- package/dist/runner/devBundle.js.map +7 -0
- package/dist/scenario/cli.d.ts +39 -0
- package/dist/scenario/cli.js +125 -0
- package/dist/scenario/cli.js.map +7 -0
- package/dist/scenario/runner.d.ts +50 -0
- package/dist/scenario/runner.js +185 -0
- package/dist/scenario/runner.js.map +7 -0
- package/dist/sharedRefs.d.ts +22 -0
- package/dist/sharedRefs.js +114 -0
- package/dist/sharedRefs.js.map +7 -0
- package/dist/ssh/exec.d.ts +39 -0
- package/dist/ssh/exec.js +13 -0
- package/dist/ssh/exec.js.map +7 -0
- package/dist/ssh/remote.d.ts +40 -0
- package/dist/ssh/remote.js +25 -0
- package/dist/ssh/remote.js.map +7 -0
- package/dist/ssh/sshRun.d.ts +1 -0
- package/dist/ssh/sshRun.js +24 -0
- package/dist/ssh/sshRun.js.map +7 -0
- package/dist/ssh/sync.d.ts +22 -0
- package/dist/ssh/sync.js +12 -0
- package/dist/ssh/sync.js.map +7 -0
- package/dist/timeouts.d.ts +38 -0
- package/dist/timeouts.js +7 -0
- package/dist/timeouts.js.map +7 -0
- package/package.json +35 -4
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { FlowDocument, FlowNode } from '@ansible-flow/core/flow/types';
|
|
2
|
+
import type { Finding } from '@ansible-flow/core/flow/validate';
|
|
3
|
+
export interface SharedSource {
|
|
4
|
+
/** The source flow's version and the subflow at the key path. */
|
|
5
|
+
version?: number;
|
|
6
|
+
node?: FlowNode;
|
|
7
|
+
error?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The file a shared subflow's `ref.from` names, checked before anything on disk is touched
|
|
11
|
+
* (AFD-128): a UNC path would make Windows connect to that host and offer the user's credentials,
|
|
12
|
+
* and a device or a huge file would be read whole. It must be a relative path to a .flow.yaml that
|
|
13
|
+
* stays, links resolved, inside `root`: the workspace folder, else the folder above the flow's own.
|
|
14
|
+
*/
|
|
15
|
+
export declare function sharedFile(flowFile: string, path: string, root?: string): {
|
|
16
|
+
file?: string;
|
|
17
|
+
error?: string;
|
|
18
|
+
};
|
|
19
|
+
/** The source of `from`, for the flow at `flowFile` in the workspace folder `root`. */
|
|
20
|
+
export declare function sharedSource(flowFile: string, from: string, root?: string): SharedSource;
|
|
21
|
+
/** Problems for the flow's shared subflows: a source that is gone, or a newer version of it. */
|
|
22
|
+
export declare function sharedFindings(flowFile: string, doc: FlowDocument, root?: string): Finding[];
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// src/node/sharedRefs.ts
|
|
2
|
+
import { readFileSync, realpathSync, statSync } from "node:fs";
|
|
3
|
+
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
4
|
+
import { parse } from "yaml";
|
|
5
|
+
import { planCommand } from "@ansible-flow/core/flow/commands";
|
|
6
|
+
import { parseFlow } from "@ansible-flow/core/flow/document";
|
|
7
|
+
import { formatOf } from "@ansible-flow/core/flow/format";
|
|
8
|
+
import { createIdFactory } from "@ansible-flow/core/flow/ids";
|
|
9
|
+
import { modelOf } from "@ansible-flow/core/flow/model";
|
|
10
|
+
import { parseRefFrom, subflowsOf } from "@ansible-flow/core/flow/refs";
|
|
11
|
+
var cache = /* @__PURE__ */ new Map();
|
|
12
|
+
var MAX_BYTES = 2 * 1024 * 1024;
|
|
13
|
+
var within = (root, path) => {
|
|
14
|
+
const rel = relative(root, path);
|
|
15
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
16
|
+
};
|
|
17
|
+
function realOr(path) {
|
|
18
|
+
try {
|
|
19
|
+
return realpathSync.native(path);
|
|
20
|
+
} catch {
|
|
21
|
+
return path;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function sharedFile(flowFile, path, root = dirname(dirname(flowFile))) {
|
|
25
|
+
if (!path.endsWith(".flow.yaml")) return { error: "not a .flow.yaml file" };
|
|
26
|
+
if (isAbsolute(path) || /^[\\/]|^[A-Za-z]:|\0/.test(path))
|
|
27
|
+
return { error: "not a path relative to this flow" };
|
|
28
|
+
const file = resolve(dirname(flowFile), path);
|
|
29
|
+
if (!within(resolve(root), file)) return { error: "outside the workspace folder" };
|
|
30
|
+
const real = realOr(file);
|
|
31
|
+
if (!within(realOr(resolve(root)), real)) return { error: "outside the workspace folder" };
|
|
32
|
+
return { file: real };
|
|
33
|
+
}
|
|
34
|
+
function format2(text, doc) {
|
|
35
|
+
if (formatOf(doc) >= 2) return doc;
|
|
36
|
+
const upgraded = planCommand(text, { kind: "upgradeFormat" }, { newId: createIdFactory() });
|
|
37
|
+
return parse(upgraded.text);
|
|
38
|
+
}
|
|
39
|
+
function readFlow(file) {
|
|
40
|
+
let mtime;
|
|
41
|
+
try {
|
|
42
|
+
const stat = statSync(file);
|
|
43
|
+
if (!stat.isFile()) return { error: "not a file" };
|
|
44
|
+
if (stat.size > MAX_BYTES) return { error: "larger than 2 MB" };
|
|
45
|
+
mtime = stat.mtimeMs;
|
|
46
|
+
} catch {
|
|
47
|
+
return { error: "no such file" };
|
|
48
|
+
}
|
|
49
|
+
const hit = cache.get(file);
|
|
50
|
+
if (hit && hit.mtime === mtime) return hit;
|
|
51
|
+
let entry;
|
|
52
|
+
try {
|
|
53
|
+
const text = readFileSync(file, "utf8");
|
|
54
|
+
const parsed = parseFlow(text);
|
|
55
|
+
const doc = parsed.doc;
|
|
56
|
+
if (!doc) entry = { mtime, error: parsed.errors[0]?.message ?? "does not parse" };
|
|
57
|
+
else if (!doc.flow) entry = { mtime, error: "not a flow" };
|
|
58
|
+
else
|
|
59
|
+
try {
|
|
60
|
+
entry = { mtime, doc: format2(text, doc) };
|
|
61
|
+
} catch (e) {
|
|
62
|
+
entry = {
|
|
63
|
+
mtime,
|
|
64
|
+
error: `format 1, and it cannot be upgraded here (${e.message}): run Upgrade Flow Format on it`
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
} catch (e) {
|
|
68
|
+
entry = { mtime, error: `does not parse: ${e.message}` };
|
|
69
|
+
}
|
|
70
|
+
cache.set(file, entry);
|
|
71
|
+
return entry;
|
|
72
|
+
}
|
|
73
|
+
function sharedSource(flowFile, from, root) {
|
|
74
|
+
const ref = parseRefFrom(from);
|
|
75
|
+
if (!ref) return { error: `"${from}" is not <flow file>#<subflow key>` };
|
|
76
|
+
const where = sharedFile(flowFile, ref.path, root);
|
|
77
|
+
if (!where.file) return { error: `${ref.path}: ${where.error}` };
|
|
78
|
+
const { doc, error } = readFlow(where.file);
|
|
79
|
+
if (!doc) return { error: `${ref.path}: ${error}` };
|
|
80
|
+
const node = subflowsOf(doc).find((s) => s.path === ref.key)?.node;
|
|
81
|
+
if (!node) return { error: `${ref.path} has no subflow ${ref.key}` };
|
|
82
|
+
return { version: Number(doc.flow.version) || 1, node };
|
|
83
|
+
}
|
|
84
|
+
function sharedFindings(flowFile, doc, root) {
|
|
85
|
+
const out = [];
|
|
86
|
+
for (const { node, path } of modelOf(doc).nodes.values()) {
|
|
87
|
+
if (!node.ref) continue;
|
|
88
|
+
const label = `"${node.title}"`;
|
|
89
|
+
const src = sharedSource(flowFile, node.ref.from, root);
|
|
90
|
+
if (src.error)
|
|
91
|
+
out.push({
|
|
92
|
+
code: "shared-source",
|
|
93
|
+
severity: "error",
|
|
94
|
+
message: `${label} is a copy of ${node.ref.from}, which is gone (${src.error}): fix ref.from, or Unlink to keep it as this flow's own`,
|
|
95
|
+
path: [...path, "ref", "from"],
|
|
96
|
+
node: node.id
|
|
97
|
+
});
|
|
98
|
+
else if ((src.version ?? 0) > node.ref.version)
|
|
99
|
+
out.push({
|
|
100
|
+
code: "shared-source",
|
|
101
|
+
severity: "info",
|
|
102
|
+
message: `${label}: ${node.ref.from} is at version ${src.version}, this copy at ${node.ref.version}: Update Shared Subflow copies it again`,
|
|
103
|
+
path: [...path, "ref", "version"],
|
|
104
|
+
node: node.id
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
sharedFile,
|
|
111
|
+
sharedFindings,
|
|
112
|
+
sharedSource
|
|
113
|
+
};
|
|
114
|
+
//# sourceMappingURL=sharedRefs.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/node/sharedRefs.ts"],
|
|
4
|
+
"sourcesContent": ["// Shared subflows against their sources (AFD-60): the file a copy names, read from disk (cached by\n// its modification time), its version and the subflow at the key path. The designer turns what is\n// missing or newer into problems with Update and Unlink quick fixes. AFD-128: `ref.from` comes from\n// the flow file, so it may name only a .flow.yaml inside the workspace folder, by relative path.\nimport { readFileSync, realpathSync, statSync } from 'node:fs';\nimport { dirname, isAbsolute, relative, resolve } from 'node:path';\nimport { parse } from 'yaml';\nimport { planCommand } from '../core/flow/commands';\nimport { parseFlow } from '../core/flow/document';\nimport { formatOf } from '../core/flow/format';\nimport { createIdFactory } from '../core/flow/ids';\nimport { modelOf } from '../core/flow/model';\nimport { parseRefFrom, subflowsOf } from '../core/flow/refs';\nimport type { FlowDocument, FlowNode } from '../core/flow/types';\nimport type { Finding } from '../core/flow/validate';\n\nexport interface SharedSource {\n /** The source flow's version and the subflow at the key path. */\n version?: number;\n node?: FlowNode;\n error?: string;\n}\n\nconst cache = new Map<string, { mtime: number; doc?: FlowDocument; error?: string }>();\n\n/** A flow is read whole on every check; a source bigger than this is not a flow (AFD-128). */\nconst MAX_BYTES = 2 * 1024 * 1024;\n\nconst within = (root: string, path: string) => {\n const rel = relative(root, path);\n return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));\n};\n\nfunction realOr(path: string): string {\n try {\n return realpathSync.native(path);\n } catch {\n return path;\n }\n}\n\n/**\n * The file a shared subflow's `ref.from` names, checked before anything on disk is touched\n * (AFD-128): a UNC path would make Windows connect to that host and offer the user's credentials,\n * and a device or a huge file would be read whole. It must be a relative path to a .flow.yaml that\n * stays, links resolved, inside `root`: the workspace folder, else the folder above the flow's own.\n */\nexport function sharedFile(\n flowFile: string,\n path: string,\n root = dirname(dirname(flowFile)),\n): { file?: string; error?: string } {\n if (!path.endsWith('.flow.yaml')) return { error: 'not a .flow.yaml file' };\n if (isAbsolute(path) || /^[\\\\/]|^[A-Za-z]:|\\0/.test(path))\n return { error: 'not a path relative to this flow' };\n const file = resolve(dirname(flowFile), path);\n if (!within(resolve(root), file)) return { error: 'outside the workspace folder' };\n const real = realOr(file);\n if (!within(realOr(resolve(root)), real)) return { error: 'outside the workspace folder' };\n return { file: real };\n}\n\n/**\n * Copies go into format 2 flows only, so a format 1 source is upgraded in memory first (AFD-62):\n * its legacy ports, `handler` and the like would otherwise land in a level the target cannot edit.\n * The file is left as it is.\n */\nfunction format2(text: string, doc: FlowDocument): FlowDocument {\n if (formatOf(doc) >= 2) return doc;\n const upgraded = planCommand(text, { kind: 'upgradeFormat' }, { newId: createIdFactory() });\n return parse(upgraded.text) as FlowDocument;\n}\n\nfunction readFlow(file: string): { doc?: FlowDocument; error?: string } {\n let mtime: number;\n try {\n const stat = statSync(file);\n if (!stat.isFile()) return { error: 'not a file' };\n if (stat.size > MAX_BYTES) return { error: 'larger than 2 MB' };\n mtime = stat.mtimeMs;\n } catch {\n return { error: 'no such file' };\n }\n const hit = cache.get(file);\n if (hit && hit.mtime === mtime) return hit;\n let entry: { mtime: number; doc?: FlowDocument; error?: string };\n try {\n const text = readFileSync(file, 'utf8');\n // AFD-147: parseFlow refuses an alias inside what it names (the copy would never end).\n const parsed = parseFlow(text);\n const doc = parsed.doc;\n if (!doc) entry = { mtime, error: parsed.errors[0]?.message ?? 'does not parse' };\n else if (!doc.flow) entry = { mtime, error: 'not a flow' };\n else\n try {\n entry = { mtime, doc: format2(text, doc) };\n } catch (e) {\n entry = {\n mtime,\n error: `format 1, and it cannot be upgraded here (${(e as Error).message}): run Upgrade Flow Format on it`,\n };\n }\n } catch (e) {\n entry = { mtime, error: `does not parse: ${(e as Error).message}` };\n }\n cache.set(file, entry);\n return entry;\n}\n\n/** The source of `from`, for the flow at `flowFile` in the workspace folder `root`. */\nexport function sharedSource(flowFile: string, from: string, root?: string): SharedSource {\n const ref = parseRefFrom(from);\n if (!ref) return { error: `\"${from}\" is not <flow file>#<subflow key>` };\n const where = sharedFile(flowFile, ref.path, root);\n if (!where.file) return { error: `${ref.path}: ${where.error}` };\n const { doc, error } = readFlow(where.file);\n if (!doc) return { error: `${ref.path}: ${error}` };\n const node = subflowsOf(doc).find((s) => s.path === ref.key)?.node;\n if (!node) return { error: `${ref.path} has no subflow ${ref.key}` };\n return { version: Number(doc.flow.version) || 1, node };\n}\n\n/** Problems for the flow's shared subflows: a source that is gone, or a newer version of it. */\nexport function sharedFindings(flowFile: string, doc: FlowDocument, root?: string): Finding[] {\n const out: Finding[] = [];\n for (const { node, path } of modelOf(doc).nodes.values()) {\n if (!node.ref) continue;\n const label = `\"${node.title}\"`;\n const src = sharedSource(flowFile, node.ref.from, root);\n if (src.error)\n out.push({\n code: 'shared-source',\n severity: 'error',\n message: `${label} is a copy of ${node.ref.from}, which is gone (${src.error}): fix ref.from, or Unlink to keep it as this flow's own`,\n path: [...path, 'ref', 'from'],\n node: node.id,\n });\n else if ((src.version ?? 0) > node.ref.version)\n out.push({\n code: 'shared-source',\n severity: 'info',\n message: `${label}: ${node.ref.from} is at version ${src.version}, this copy at ${node.ref.version}: Update Shared Subflow copies it again`,\n path: [...path, 'ref', 'version'],\n node: node.id,\n });\n }\n return out;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface Command {
|
|
2
|
+
command: string;
|
|
3
|
+
args: string[];
|
|
4
|
+
}
|
|
5
|
+
export interface Outcome {
|
|
6
|
+
code: number | null;
|
|
7
|
+
stdout: string;
|
|
8
|
+
stderr: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Windows ships bsdtar in System32. A GNU tar earlier on the PATH (Git for Windows) reads
|
|
12
|
+
* `C:\x` as a remote host, so tar is always that one there.
|
|
13
|
+
*/
|
|
14
|
+
export declare function tarExe(): string;
|
|
15
|
+
/** ssh's last line that says something, without its banner noise: why a connection failed. */
|
|
16
|
+
export declare function sshWhy(stderr: string): string;
|
|
17
|
+
/** Runs a command to its end. `input` is written to it and closed; never rejects. */
|
|
18
|
+
export declare function run(c: Command, options?: {
|
|
19
|
+
input?: string | Buffer;
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
binary?: boolean;
|
|
22
|
+
}): Promise<Outcome & {
|
|
23
|
+
out: Buffer;
|
|
24
|
+
}>;
|
|
25
|
+
/**
|
|
26
|
+
* Pipes the output of one or more commands, one after the other, into another command's input;
|
|
27
|
+
* resolves with the exit codes (`from`: the first that is not 0, else 0).
|
|
28
|
+
*/
|
|
29
|
+
export declare function pipe(from: (Command & {
|
|
30
|
+
cwd?: string;
|
|
31
|
+
}) | (Command & {
|
|
32
|
+
cwd?: string;
|
|
33
|
+
})[], to: Command, options?: {
|
|
34
|
+
timeoutMs?: number;
|
|
35
|
+
}): Promise<{
|
|
36
|
+
from: number | null;
|
|
37
|
+
to: number | null;
|
|
38
|
+
stderr: string;
|
|
39
|
+
}>;
|
package/dist/ssh/exec.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Environment } from '@ansible-flow/core/run/environment';
|
|
2
|
+
import { type HostFacts } from '@ansible-flow/core/run/ssh';
|
|
3
|
+
export type SshEnv = Extract<Environment, {
|
|
4
|
+
kind: 'ssh';
|
|
5
|
+
}>;
|
|
6
|
+
/** The repository and the probe on the host, as they are here now. */
|
|
7
|
+
export declare function syncWorkspace(env: SshEnv, paths: {
|
|
8
|
+
root: string;
|
|
9
|
+
probe: string;
|
|
10
|
+
},
|
|
11
|
+
/** Folders of the repository copied whatever .gitignore says. */
|
|
12
|
+
include?: readonly string[]): Promise<string | undefined>;
|
|
13
|
+
/** Puts a file of the run folder on the host (its inputs, a generated config). */
|
|
14
|
+
export declare function putRunFile(env: SshEnv, runDir: string, name: string, text: string): Promise<string | undefined>;
|
|
15
|
+
/**
|
|
16
|
+
* Follows a file of the run folder on the host into the same file here, so what reads the local
|
|
17
|
+
* one (the live summary on the canvas) sees the run as it goes. `stop` ends the following and
|
|
18
|
+
* then writes the whole file once more: nothing the last moments wrote is lost.
|
|
19
|
+
*/
|
|
20
|
+
export declare function followRunFile(env: SshEnv, runDir: string, name: string, local: string): {
|
|
21
|
+
stop(): Promise<void>;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* AFD-247: a scenario's folder (its build of the flow, inventory and vars) on the host, as the
|
|
25
|
+
* run folder `runDir`. `events`: a folder in it for an image's own user to write the events to.
|
|
26
|
+
*/
|
|
27
|
+
export declare function putRunFolder(env: SshEnv, runDir: string, events?: string): Promise<string | undefined>;
|
|
28
|
+
/** A file of the run folder on the host, read whole into the same file here. */
|
|
29
|
+
export declare function fetchRunFile(env: SshEnv, runDir: string, name: string, local: string): Promise<void>;
|
|
30
|
+
/** Removes the run's folder on the host (its inputs are not kept there). */
|
|
31
|
+
export declare function removeRunDir(env: SshEnv, runDir: string): Promise<void>;
|
|
32
|
+
/** Brings the catalog dumps the probe wrote on the host into the cache here. */
|
|
33
|
+
export declare function fetchCache(env: SshEnv, localCache: string): Promise<string | undefined>;
|
|
34
|
+
/** What the host has; an error when it cannot be reached. */
|
|
35
|
+
export declare function hostFacts(host: string): Promise<{
|
|
36
|
+
facts?: HostFacts;
|
|
37
|
+
error?: string;
|
|
38
|
+
}>;
|
|
39
|
+
/** Copies one file to the host, readable by the account alone (a vault password file). */
|
|
40
|
+
export declare function copyFile(env: Pick<SshEnv, 'host' | 'dir'>, content: Buffer, remotePath: string): Promise<string | undefined>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
copyFile,
|
|
3
|
+
fetchCache,
|
|
4
|
+
fetchRunFile,
|
|
5
|
+
followRunFile,
|
|
6
|
+
hostFacts,
|
|
7
|
+
putRunFile,
|
|
8
|
+
putRunFolder,
|
|
9
|
+
removeRunDir,
|
|
10
|
+
syncWorkspace
|
|
11
|
+
} from "../chunks/chunk-RETJPSKL.js";
|
|
12
|
+
import "../chunks/chunk-THNSGAKN.js";
|
|
13
|
+
import "../chunks/chunk-FG53MLUX.js";
|
|
14
|
+
export {
|
|
15
|
+
copyFile,
|
|
16
|
+
fetchCache,
|
|
17
|
+
fetchRunFile,
|
|
18
|
+
followRunFile,
|
|
19
|
+
hostFacts,
|
|
20
|
+
putRunFile,
|
|
21
|
+
putRunFolder,
|
|
22
|
+
removeRunDir,
|
|
23
|
+
syncWorkspace
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=remote.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// src/node/ssh/sshRun.ts
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
var [names = "", command, ...args] = process.argv.slice(2);
|
|
4
|
+
if (!command) {
|
|
5
|
+
process.stderr.write("afd-ssh-run: nothing to run\n");
|
|
6
|
+
process.exit(2);
|
|
7
|
+
}
|
|
8
|
+
var child = spawn(command, args, { stdio: ["pipe", "inherit", "inherit"], windowsHide: true });
|
|
9
|
+
child.on("error", (e) => {
|
|
10
|
+
process.stderr.write(`Could not start ${command}: ${e.message}
|
|
11
|
+
`);
|
|
12
|
+
process.exit(127);
|
|
13
|
+
});
|
|
14
|
+
child.stdin?.on("error", () => void 0);
|
|
15
|
+
for (const name of names.split(",").filter(Boolean)) {
|
|
16
|
+
const value = process.env[name] ?? "";
|
|
17
|
+
child.stdin?.write(`${Buffer.from(value, "utf8").toString("base64")}
|
|
18
|
+
`);
|
|
19
|
+
}
|
|
20
|
+
child.stdin?.end();
|
|
21
|
+
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"])
|
|
22
|
+
process.on(signal, () => child.kill(signal));
|
|
23
|
+
child.on("close", (code, signal) => process.exit(code ?? (signal ? 143 : 1)));
|
|
24
|
+
//# sourceMappingURL=sshRun.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/node/ssh/sshRun.ts"],
|
|
4
|
+
"sourcesContent": ["// AFD-247: starts a run's `ssh` with the run's secrets on its input. A run is a VS Code task, and\n// a task's process gets its input from the terminal; ssh passes no environment to the host. So\n// the task runs this instead: it takes the secret values from its own environment, writes each as\n// one base64 line to ssh's input, and from then on is ssh (its output, its exit code, its\n// signals). The values are never arguments, on this machine or on the host.\n//\n// Usage: afd-ssh-run <NAME>[,<NAME>\u2026] <ssh> <ssh arguments\u2026>\nimport { spawn } from 'node:child_process';\n\nconst [names = '', command, ...args] = process.argv.slice(2);\nif (!command) {\n process.stderr.write('afd-ssh-run: nothing to run\\n');\n process.exit(2);\n}\nconst child = spawn(command, args, { stdio: ['pipe', 'inherit', 'inherit'], windowsHide: true });\nchild.on('error', (e) => {\n process.stderr.write(`Could not start ${command}: ${e.message}\\n`);\n process.exit(127);\n});\nchild.stdin?.on('error', () => undefined);\nfor (const name of names.split(',').filter(Boolean)) {\n const value = process.env[name] ?? '';\n child.stdin?.write(`${Buffer.from(value, 'utf8').toString('base64')}\\n`);\n}\nchild.stdin?.end();\nfor (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP'] as const)\n process.on(signal, () => child.kill(signal));\nchild.on('close', (code, signal) => process.exit(code ?? (signal ? 143 : 1)));\n"],
|
|
5
|
+
"mappings": ";AAOA,SAAS,aAAa;AAEtB,IAAM,CAAC,QAAQ,IAAI,SAAS,GAAG,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC;AAC3D,IAAI,CAAC,SAAS;AACZ,UAAQ,OAAO,MAAM,+BAA+B;AACpD,UAAQ,KAAK,CAAC;AAChB;AACA,IAAM,QAAQ,MAAM,SAAS,MAAM,EAAE,OAAO,CAAC,QAAQ,WAAW,SAAS,GAAG,aAAa,KAAK,CAAC;AAC/F,MAAM,GAAG,SAAS,CAAC,MAAM;AACvB,UAAQ,OAAO,MAAM,mBAAmB,OAAO,KAAK,EAAE,OAAO;AAAA,CAAI;AACjE,UAAQ,KAAK,GAAG;AAClB,CAAC;AACD,MAAM,OAAO,GAAG,SAAS,MAAM,MAAS;AACxC,WAAW,QAAQ,MAAM,MAAM,GAAG,EAAE,OAAO,OAAO,GAAG;AACnD,QAAM,QAAQ,QAAQ,IAAI,IAAI,KAAK;AACnC,QAAM,OAAO,MAAM,GAAG,OAAO,KAAK,OAAO,MAAM,EAAE,SAAS,QAAQ,CAAC;AAAA,CAAI;AACzE;AACA,MAAM,OAAO,IAAI;AACjB,WAAW,UAAU,CAAC,UAAU,WAAW,QAAQ;AACjD,UAAQ,GAAG,QAAQ,MAAM,MAAM,KAAK,MAAM,CAAC;AAC7C,MAAM,GAAG,SAAS,CAAC,MAAM,WAAW,QAAQ,KAAK,SAAS,SAAS,MAAM,EAAE,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type SshTarget } from '@ansible-flow/core/run/ssh';
|
|
2
|
+
/** Forgets what was sent: the next sync copies everything again (tests, a changed host). */
|
|
3
|
+
export declare function forgetSynced(): void;
|
|
4
|
+
/** The files to copy, relative with forward slashes, and which of them Git marks executable. */
|
|
5
|
+
export declare function listFiles(root: string, include?: readonly string[]): Promise<{
|
|
6
|
+
files: string[];
|
|
7
|
+
exec: Set<string>;
|
|
8
|
+
}>;
|
|
9
|
+
export interface SyncResult {
|
|
10
|
+
ok: boolean;
|
|
11
|
+
/** Files sent and files deleted on the host. */
|
|
12
|
+
sent: number;
|
|
13
|
+
removed: number;
|
|
14
|
+
error?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Makes `<target.dir>/<into>` on the host hold the files of `root`. Nothing is run when nothing
|
|
18
|
+
* changed since the last sync of this session.
|
|
19
|
+
*/
|
|
20
|
+
export declare function syncFolder(target: SshTarget, root: string, into: string,
|
|
21
|
+
/** Folders of `root` copied whatever .gitignore says. */
|
|
22
|
+
include?: readonly string[]): Promise<SyncResult>;
|
package/dist/ssh/sync.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export declare const TIMEOUTS: {
|
|
2
|
+
/** The probe's first answer: the first start may pull the image. */
|
|
3
|
+
readonly probeReady: 180000;
|
|
4
|
+
/** A probe call without a timeout of its own. */
|
|
5
|
+
readonly probeCall: 60000;
|
|
6
|
+
/** The inventory read for a run's confirmation or the Inventory lens. */
|
|
7
|
+
readonly probeInventory: 120000;
|
|
8
|
+
/** The roles of a repository (catalog, Code mode). */
|
|
9
|
+
readonly probeRoles: 120000;
|
|
10
|
+
/** Compiling the generated code and roles (Code mode, the syntax check). */
|
|
11
|
+
readonly probeCompile: 180000;
|
|
12
|
+
/** Reading every collection's docs for the module catalog. */
|
|
13
|
+
readonly catalogDump: number;
|
|
14
|
+
/** Indexing the collection docs in a worker (seconds for a full environment; AFD-163). */
|
|
15
|
+
readonly catalogIndex: number;
|
|
16
|
+
/** One AWX REST request without data (idle). */
|
|
17
|
+
readonly awxRequest: 30000;
|
|
18
|
+
/** One AWX REST request from start to end, however slowly the data comes (AFD-161). */
|
|
19
|
+
readonly awxRequestTotal: 120000;
|
|
20
|
+
/** Between two polls of a running AWX job, workflow or project update. */
|
|
21
|
+
readonly awxPoll: 2000;
|
|
22
|
+
/** A finished AWX job's events keep arriving for a while. */
|
|
23
|
+
readonly awxEventsGrace: 20000;
|
|
24
|
+
/** An AWX project update before a template is set up. */
|
|
25
|
+
readonly awxProjectSync: 180000;
|
|
26
|
+
/** AFD-276: one Ansible Flow Server request without data. */
|
|
27
|
+
readonly runnerRequest: 30000;
|
|
28
|
+
/** AFD-276: between two looks at a run on the server. */
|
|
29
|
+
readonly runnerPoll: 2000;
|
|
30
|
+
/** A designer panel loading (PNG export opens one). */
|
|
31
|
+
readonly designerReady: 30000;
|
|
32
|
+
/** A designer panel drawing a PNG. */
|
|
33
|
+
readonly designerPng: 60000;
|
|
34
|
+
/** Test mode: a designer panel's DOM summary. */
|
|
35
|
+
readonly designerInspect: 10000;
|
|
36
|
+
/** Typing in a text editor settles before the designer parses again. */
|
|
37
|
+
readonly typingSettles: 150;
|
|
38
|
+
};
|
package/dist/timeouts.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ansible-flow/node",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.86.0",
|
|
4
|
+
"description": "Ansible Flow Designer for Node hosts: layout, the probe client, runs, the AWX client, the module catalog and SSH.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://github.com/ChistokhinSV/ansible-flow-designer/blob/main/docs/packages.md",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/ChistokhinSV/ansible-flow-designer.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/ChistokhinSV/ansible-flow-designer/issues"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"exports": {
|
|
16
|
+
"./package.json": "./package.json",
|
|
17
|
+
"./*": {
|
|
18
|
+
"types": "./dist/*.d.ts",
|
|
19
|
+
"default": "./dist/*.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=22.13"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@ansible-flow/core": "0.86.0",
|
|
30
|
+
"elkjs": "0.12.0",
|
|
31
|
+
"jsonc-parser": "3.3.1",
|
|
32
|
+
"yaml": "2.9.1"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
}
|
|
37
|
+
}
|