@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.
- 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/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,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/node/ai/agents.ts"],
|
|
4
|
+
"sourcesContent": ["// The agents Set Up AI Assistants knows (AFD-20) and how to tell they are on this machine: a\n// program on PATH, their config folder in the home folder, an installed app or their VS Code\n// extension. Agents it does not know get a guide with the skill and a ready MCP config instead.\n// No VS Code import, so detection is tested against a temporary home folder.\nimport { promises as fs } from 'node:fs';\nimport { join } from 'node:path';\nimport { type Agent, claudeCommandLine, type Launch, SERVER_NAME, SKILL_NAME } from './writers';\n\nexport interface AgentInfo {\n id: Agent;\n label: string;\n /** Where the skill and the tools go, for the pick list. */\n detail: string;\n bins: string[];\n /** Paths under the home folder that only this agent creates. */\n markers: string[];\n /** Installed apps: under %LOCALAPPDATA% on Windows, absolute on macOS. */\n apps: { win?: string; mac?: string };\n /** VS Code extension IDs (lowercase). */\n extensions: string[];\n}\n\nexport const AGENT_INFO: AgentInfo[] = [\n {\n id: 'claude',\n label: 'Claude Code',\n detail: 'CLI and VS Code extension: skill in .claude/skills, tools via claude mcp',\n bins: ['claude'],\n markers: ['.claude.json'],\n apps: {},\n extensions: ['anthropic.claude-code'],\n },\n {\n id: 'codex',\n label: 'Codex',\n detail:\n 'CLI, app and VS Code extension: skill in .agents/skills, tools in ~/.codex/config.toml',\n bins: ['codex'],\n markers: ['.codex'],\n apps: {},\n extensions: ['openai.chatgpt'],\n },\n {\n id: 'gemini',\n label: 'Gemini CLI',\n detail:\n 'skill in ~/.gemini/skills (.agents/skills in a repository), tools in ~/.gemini/settings.json',\n bins: ['gemini'],\n markers: ['.gemini'],\n apps: {},\n extensions: ['google.gemini-cli-vscode-ide-companion'],\n },\n {\n id: 'opencode',\n label: 'OpenCode',\n detail: 'CLI and app: skill in .agents/skills, tools in ~/.config/opencode/opencode.json(c)',\n bins: ['opencode'],\n markers: ['.config/opencode', '.local/share/opencode'],\n apps: {},\n extensions: ['sst-dev.opencode'],\n },\n {\n id: 'zcode',\n label: 'ZCode',\n detail: 'app: skill in .agents/skills, tools in ~/.agents/mcp.json',\n bins: ['zcode'],\n markers: ['.zcode'],\n apps: { win: 'Programs/ZCode/ZCode.exe', mac: '/Applications/ZCode.app' },\n extensions: [],\n },\n {\n id: 'copilot',\n label: 'GitHub Copilot CLI',\n detail:\n 'skill in ~/.copilot/skills (.github/skills in a repository), tools in ~/.copilot/mcp-config.json',\n bins: ['copilot'],\n markers: ['.copilot/config.json', '.copilot/mcp-config.json'],\n apps: {},\n extensions: [],\n },\n {\n id: 'cursor',\n label: 'Cursor',\n detail: 'skill in .claude/skills (Cursor reads it), tools in ~/.cursor/mcp.json',\n bins: ['cursor'],\n markers: ['.cursor'],\n apps: { win: 'Programs/cursor/Cursor.exe', mac: '/Applications/Cursor.app' },\n extensions: [],\n },\n // AFD-173: agents built into VS Code forks; paths from what each agent was seen to read (AFD-172).\n {\n id: 'kiro',\n label: 'Kiro',\n detail: 'skill in ~/.kiro/skills, tools in ~/.kiro/settings/mcp.json',\n bins: ['kiro'],\n markers: ['.kiro'],\n apps: { win: 'Programs/Kiro/Kiro.exe', mac: '/Applications/Kiro.app' },\n extensions: [],\n },\n {\n id: 'antigravity',\n label: 'Antigravity IDE',\n detail: 'skill in ~/.gemini/config/skills, tools in ~/.gemini/config/mcp_config.json',\n bins: ['antigravity-ide'],\n markers: ['.antigravity-ide', '.gemini/antigravity-ide'],\n apps: {\n win: 'Programs/Antigravity IDE/Antigravity IDE.exe',\n mac: '/Applications/Antigravity IDE.app',\n },\n extensions: [],\n },\n {\n id: 'devin',\n label: 'Devin Desktop (Windsurf)',\n detail:\n 'skill in ~/.codeium/windsurf/skills, tools in Devin\u2019s mcp_config.json (local agent mode)',\n bins: ['devin-desktop'],\n markers: ['.devin', '.codeium/windsurf'],\n apps: { win: 'Programs/Devin/Devin.exe', mac: '/Applications/Devin.app' },\n extensions: [],\n },\n];\n\nexport const agentLabel = (id: Agent) => AGENT_INFO.find((a) => a.id === id)?.label ?? id;\n\nexport interface Probe {\n home: string;\n /** %LOCALAPPDATA% on Windows. */\n localAppData?: string;\n platform: NodeJS.Platform;\n onPath(name: string): Promise<string | undefined>;\n /** Installed VS Code extension IDs, lowercase. */\n extensions: string[];\n}\n\nconst exists = (path: string) =>\n fs.stat(path).then(\n () => true,\n () => false,\n );\n\n/** Why an agent counts as installed, or undefined when nothing points to it. */\nasync function evidence(info: AgentInfo, probe: Probe): Promise<string | undefined> {\n for (const bin of info.bins) if (await probe.onPath(bin)) return `${bin} on PATH`;\n const ext = info.extensions.find((e) => probe.extensions.includes(e));\n if (ext) return `VS Code extension ${ext}`;\n for (const m of info.markers) if (await exists(join(probe.home, m))) return `~/${m}`;\n const app =\n probe.platform === 'win32' && info.apps.win && probe.localAppData\n ? join(probe.localAppData, info.apps.win)\n : probe.platform === 'darwin'\n ? info.apps.mac\n : undefined;\n if (app && (await exists(app))) return `${info.label} app`;\n return undefined;\n}\n\n/** The known agents on this machine, each with why it was found. */\nexport async function findAgents(probe: Probe): Promise<Map<Agent, string>> {\n const found = new Map<Agent, string>();\n const why = await Promise.all(AGENT_INFO.map((info) => evidence(info, probe)));\n AGENT_INFO.forEach((info, i) => {\n const w = why[i];\n if (w) found.set(info.id, w);\n });\n return found;\n}\n\n/** The MCP entry most agents take (an \"mcpServers\" object). */\nexport function mcpJson(launch: Launch): string {\n const entry = {\n command: launch.command,\n args: launch.args,\n ...(launch.env ? { env: launch.env } : {}),\n };\n return JSON.stringify({ mcpServers: { [SERVER_NAME]: entry } }, null, 2);\n}\n\n/** A guide for agents setup does not know: where the skill and the tools go, with this machine's command. */\nexport function otherAgentGuide(launch: Launch): string {\n const fence = '```';\n return [\n '# Ansible flows in another AI agent',\n '',\n 'Ansible Flow Designer gives AI agents two things. Set Up AI Assistants writes both for Claude Code, Codex, Gemini CLI, OpenCode, ZCode, GitHub Copilot CLI, Cursor, Kiro, Antigravity IDE and Devin Desktop (Windsurf); for any other agent, do it once by hand:',\n '',\n `1. **The skill** \\`${SKILL_NAME}\\`: the rules, the workflow and the format reference. Use _Copy Skill To Folder\u2026_ and pick the folder your agent reads skills from: many read \\`.agents/skills\\` or \\`.claude/skills\\` in the repository or in your home folder.`,\n `2. **The flow tools**: an MCP server (stdio) named \\`${SERVER_NAME}\\`. Most agents take this entry in their MCP config (_Copy MCP Config_):`,\n '',\n `${fence}json`,\n mcpJson(launch),\n fence,\n '',\n ' As a command line:',\n '',\n `${fence}text`,\n `${\n launch.env\n ? `${Object.entries(launch.env)\n .map(([k, v]) => `${k}=${v}`)\n .join(' ')} `\n : ''\n }\"${launch.command}\" ${launch.args.map((a) => `\"${a}\"`).join(' ')}`,\n fence,\n '',\n '3. Restart the agent and ask it, for example, to \"create an Ansible flow that upgrades the firewalls, with checks before and after and a rollback\".',\n '',\n 'Where some agents keep their MCP config (check the agent\u2019s documentation, these move):',\n '',\n '| Agent | MCP config |',\n '| --- | --- |',\n '| Qwen Code | `~/.qwen/settings.json` (`mcpServers`) |',\n '| Cline, Roo Code | MCP Servers panel, Configure (`mcpServers`) |',\n '| Claude Desktop | `claude_desktop_config.json` (`mcpServers`) |',\n '',\n 'The server file lives in VS Code\u2019s storage for this extension and is refreshed with every update; the command stays the same unless VS Code itself moves. Without Node 20+ on PATH the command uses VS Code\u2019s own runtime (`ELECTRON_RUN_AS_NODE=1`).',\n '',\n `Claude Code, for reference: \\`${claudeCommandLine(launch)}\\``,\n '',\n ].join('\\n');\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// src/node/ssh/exec.ts
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
function tarExe() {
|
|
6
|
+
if (process.platform !== "win32") return "tar";
|
|
7
|
+
const own = join(process.env.SystemRoot ?? "C:\\Windows", "System32", "tar.exe");
|
|
8
|
+
return existsSync(own) ? own : "tar";
|
|
9
|
+
}
|
|
10
|
+
function sshWhy(stderr) {
|
|
11
|
+
const lines = stderr.split(/\r?\n/).map((l) => l.trim()).filter((l) => l && !/^(Warning: Permanently added|\*\*)/.test(l));
|
|
12
|
+
return lines.at(-1) ?? "ssh gave no message";
|
|
13
|
+
}
|
|
14
|
+
function run(c, options = {}) {
|
|
15
|
+
return new Promise((resolve) => {
|
|
16
|
+
let proc;
|
|
17
|
+
try {
|
|
18
|
+
proc = spawn(c.command, c.args, { stdio: ["pipe", "pipe", "pipe"], windowsHide: true });
|
|
19
|
+
} catch (e) {
|
|
20
|
+
resolve({ code: null, stdout: "", stderr: String(e), out: Buffer.alloc(0) });
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const out = [];
|
|
24
|
+
const err = [];
|
|
25
|
+
const timer = setTimeout(() => proc.kill(), options.timeoutMs ?? 6e4);
|
|
26
|
+
proc.stdout?.on("data", (d) => out.push(d));
|
|
27
|
+
proc.stderr?.on("data", (d) => err.push(d));
|
|
28
|
+
const end = (code, extra = "") => {
|
|
29
|
+
clearTimeout(timer);
|
|
30
|
+
const all = Buffer.concat(out);
|
|
31
|
+
resolve({
|
|
32
|
+
code,
|
|
33
|
+
stdout: options.binary ? "" : all.toString("utf8"),
|
|
34
|
+
stderr: Buffer.concat(err).toString("utf8") + extra,
|
|
35
|
+
out: all
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
proc.on("error", (e) => end(null, `Could not start ${c.command}: ${e.message}`));
|
|
39
|
+
proc.on("close", (code) => end(code));
|
|
40
|
+
proc.stdin?.on("error", () => void 0);
|
|
41
|
+
proc.stdin?.end(options.input ?? "");
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function pipe(from, to, options = {}) {
|
|
45
|
+
const sources = Array.isArray(from) ? from : [from];
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
const err = [];
|
|
48
|
+
let b;
|
|
49
|
+
try {
|
|
50
|
+
b = spawn(to.command, to.args, { stdio: ["pipe", "pipe", "pipe"], windowsHide: true });
|
|
51
|
+
} catch (e) {
|
|
52
|
+
resolve({ from: null, to: null, stderr: String(e) });
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
let current;
|
|
56
|
+
const timer = setTimeout(() => {
|
|
57
|
+
current?.kill();
|
|
58
|
+
b.kill();
|
|
59
|
+
}, options.timeoutMs ?? 3e5);
|
|
60
|
+
let codeA;
|
|
61
|
+
let codeB;
|
|
62
|
+
const done = () => {
|
|
63
|
+
if (codeA === void 0 || codeB === void 0) return;
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
resolve({ from: codeA, to: codeB, stderr: Buffer.concat(err).toString("utf8") });
|
|
66
|
+
};
|
|
67
|
+
b.stderr?.on("data", (d) => err.push(d));
|
|
68
|
+
b.stdout?.resume();
|
|
69
|
+
b.stdin?.on("error", () => void 0);
|
|
70
|
+
b.on("error", (e) => {
|
|
71
|
+
err.push(
|
|
72
|
+
Buffer.from(`Could not start ${to.command}: ${e.message}
|
|
73
|
+
`)
|
|
74
|
+
);
|
|
75
|
+
codeB = null;
|
|
76
|
+
current?.kill();
|
|
77
|
+
done();
|
|
78
|
+
});
|
|
79
|
+
b.on("close", (code) => {
|
|
80
|
+
codeB = code;
|
|
81
|
+
current?.kill();
|
|
82
|
+
if (codeA === void 0) codeA = null;
|
|
83
|
+
done();
|
|
84
|
+
});
|
|
85
|
+
const next = (i) => {
|
|
86
|
+
const source = sources[i];
|
|
87
|
+
if (!source) {
|
|
88
|
+
codeA = 0;
|
|
89
|
+
b.stdin?.end();
|
|
90
|
+
done();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
let a;
|
|
94
|
+
try {
|
|
95
|
+
a = spawn(source.command, source.args, {
|
|
96
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
97
|
+
windowsHide: true,
|
|
98
|
+
cwd: source.cwd
|
|
99
|
+
});
|
|
100
|
+
} catch (e) {
|
|
101
|
+
err.push(Buffer.from(String(e)));
|
|
102
|
+
codeA = null;
|
|
103
|
+
b.stdin?.end();
|
|
104
|
+
done();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
current = a;
|
|
108
|
+
a.stderr?.on("data", (d) => err.push(d));
|
|
109
|
+
a.stdout?.pipe(b.stdin, { end: false });
|
|
110
|
+
const fail = (code, why = "") => {
|
|
111
|
+
if (why) err.push(Buffer.from(why));
|
|
112
|
+
if (codeA === void 0) codeA = code;
|
|
113
|
+
b.stdin?.end();
|
|
114
|
+
done();
|
|
115
|
+
};
|
|
116
|
+
a.on(
|
|
117
|
+
"error",
|
|
118
|
+
(e) => fail(
|
|
119
|
+
null,
|
|
120
|
+
`Could not start ${source.command}: ${e.message}
|
|
121
|
+
`
|
|
122
|
+
)
|
|
123
|
+
);
|
|
124
|
+
a.on("close", (code) => code === 0 ? next(i + 1) : fail(code));
|
|
125
|
+
};
|
|
126
|
+
next(0);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export {
|
|
131
|
+
tarExe,
|
|
132
|
+
sshWhy,
|
|
133
|
+
run,
|
|
134
|
+
pipe
|
|
135
|
+
};
|
|
136
|
+
//# sourceMappingURL=chunk-FG53MLUX.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/node/ssh/exec.ts"],
|
|
4
|
+
"sourcesContent": ["// AFD-247: runs the commands `core/run/ssh` writes: one `ssh` each, with input when it has some,\n// its output collected, and a limit on how long it may take. Nothing here asks a question: the\n// connection is in batch mode, so a host that wants a password fails with ssh's own message.\nimport { type ChildProcess, spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport interface Command {\n command: string;\n args: string[];\n}\n\nexport interface Outcome {\n code: number | null;\n stdout: string;\n stderr: string;\n}\n\n/**\n * Windows ships bsdtar in System32. A GNU tar earlier on the PATH (Git for Windows) reads\n * `C:\\x` as a remote host, so tar is always that one there.\n */\nexport function tarExe(): string {\n if (process.platform !== 'win32') return 'tar';\n const own = join(process.env.SystemRoot ?? 'C:\\\\Windows', 'System32', 'tar.exe');\n return existsSync(own) ? own : 'tar';\n}\n\n/** ssh's last line that says something, without its banner noise: why a connection failed. */\nexport function sshWhy(stderr: string): string {\n const lines = stderr\n .split(/\\r?\\n/)\n .map((l) => l.trim())\n .filter((l) => l && !/^(Warning: Permanently added|\\*\\*)/.test(l));\n return lines.at(-1) ?? 'ssh gave no message';\n}\n\n/** Runs a command to its end. `input` is written to it and closed; never rejects. */\nexport function run(\n c: Command,\n options: { input?: string | Buffer; timeoutMs?: number; binary?: boolean } = {},\n): Promise<Outcome & { out: Buffer }> {\n return new Promise((resolve) => {\n let proc: ChildProcess;\n try {\n proc = spawn(c.command, c.args, { stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });\n } catch (e) {\n resolve({ code: null, stdout: '', stderr: String(e), out: Buffer.alloc(0) });\n return;\n }\n const out: Buffer[] = [];\n const err: Buffer[] = [];\n const timer = setTimeout(() => proc.kill(), options.timeoutMs ?? 60_000);\n proc.stdout?.on('data', (d: Buffer) => out.push(d));\n proc.stderr?.on('data', (d: Buffer) => err.push(d));\n const end = (code: number | null, extra = '') => {\n clearTimeout(timer);\n const all = Buffer.concat(out);\n resolve({\n code,\n stdout: options.binary ? '' : all.toString('utf8'),\n stderr: Buffer.concat(err).toString('utf8') + extra,\n out: all,\n });\n };\n proc.on('error', (e) => end(null, `Could not start ${c.command}: ${e.message}`));\n proc.on('close', (code) => end(code));\n // A command that ends early closes its input: that is its answer, not an error here.\n proc.stdin?.on('error', () => undefined);\n proc.stdin?.end(options.input ?? '');\n });\n}\n\n/**\n * Pipes the output of one or more commands, one after the other, into another command's input;\n * resolves with the exit codes (`from`: the first that is not 0, else 0).\n */\nexport function pipe(\n from: (Command & { cwd?: string }) | (Command & { cwd?: string })[],\n to: Command,\n options: { timeoutMs?: number } = {},\n): Promise<{ from: number | null; to: number | null; stderr: string }> {\n const sources = Array.isArray(from) ? from : [from];\n return new Promise((resolve) => {\n const err: Buffer[] = [];\n let b: ChildProcess;\n try {\n b = spawn(to.command, to.args, { stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });\n } catch (e) {\n resolve({ from: null, to: null, stderr: String(e) });\n return;\n }\n let current: ChildProcess | undefined;\n const timer = setTimeout(() => {\n current?.kill();\n b.kill();\n }, options.timeoutMs ?? 300_000);\n let codeA: number | null | undefined;\n let codeB: number | null | undefined;\n const done = () => {\n if (codeA === undefined || codeB === undefined) return;\n clearTimeout(timer);\n resolve({ from: codeA, to: codeB, stderr: Buffer.concat(err).toString('utf8') });\n };\n b.stderr?.on('data', (d: Buffer) => err.push(d));\n b.stdout?.resume();\n b.stdin?.on('error', () => undefined);\n b.on('error', (e) => {\n err.push(\n Buffer.from(`Could not start ${to.command}: ${e.message}\n`),\n );\n codeB = null;\n current?.kill();\n done();\n });\n b.on('close', (code) => {\n codeB = code;\n current?.kill();\n if (codeA === undefined) codeA = null;\n done();\n });\n const next = (i: number) => {\n const source = sources[i];\n if (!source) {\n codeA = 0;\n b.stdin?.end();\n done();\n return;\n }\n let a: ChildProcess;\n try {\n a = spawn(source.command, source.args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n windowsHide: true,\n cwd: source.cwd,\n });\n } catch (e) {\n err.push(Buffer.from(String(e)));\n codeA = null;\n b.stdin?.end();\n done();\n return;\n }\n current = a;\n a.stderr?.on('data', (d: Buffer) => err.push(d));\n a.stdout?.pipe(b.stdin as NodeJS.WritableStream, { end: false });\n const fail = (code: number | null, why = '') => {\n if (why) err.push(Buffer.from(why));\n if (codeA === undefined) codeA = code;\n b.stdin?.end();\n done();\n };\n a.on('error', (e) =>\n fail(\n null,\n `Could not start ${source.command}: ${e.message}\n`,\n ),\n );\n a.on('close', (code) => (code === 0 ? next(i + 1) : fail(code)));\n };\n next(0);\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TIMEOUTS
|
|
3
|
+
} from "./chunk-IIFP6UQR.js";
|
|
4
|
+
|
|
5
|
+
// src/node/awx/client.ts
|
|
6
|
+
import * as http from "node:http";
|
|
7
|
+
import * as https from "node:https";
|
|
8
|
+
var PAGE = 200;
|
|
9
|
+
var AwxError = class extends Error {
|
|
10
|
+
constructor(message, status, code) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.status = status;
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.name = "AwxError";
|
|
15
|
+
}
|
|
16
|
+
status;
|
|
17
|
+
code;
|
|
18
|
+
};
|
|
19
|
+
var STALE_SOCKET = /* @__PURE__ */ new Set(["ECONNRESET", "EPIPE", "ECONNABORTED"]);
|
|
20
|
+
var MAX_BODY_BYTES = 64 * 1024 * 1024;
|
|
21
|
+
var MAX_PAGES = 500;
|
|
22
|
+
var LOOPBACK = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
23
|
+
var cleartextTo = (url) => url.protocol === "http:" && !LOOPBACK.has(url.hostname);
|
|
24
|
+
function describe(body) {
|
|
25
|
+
if (!body || typeof body !== "object") return String(body ?? "").slice(0, 300);
|
|
26
|
+
const o = body;
|
|
27
|
+
if (typeof o.detail === "string") return o.detail;
|
|
28
|
+
return Object.entries(o).map(([k, v]) => `${k}: ${Array.isArray(v) ? v.join(" ") : String(v)}`).join("; ").slice(0, 300);
|
|
29
|
+
}
|
|
30
|
+
var AwxClient = class {
|
|
31
|
+
constructor(url, token, options = {}) {
|
|
32
|
+
this.token = token;
|
|
33
|
+
this.options = options;
|
|
34
|
+
this.base = url.replace(/\/+$/, "");
|
|
35
|
+
this.origin = new URL(this.base).origin;
|
|
36
|
+
}
|
|
37
|
+
token;
|
|
38
|
+
options;
|
|
39
|
+
base;
|
|
40
|
+
origin;
|
|
41
|
+
/** The AWX UI page of a job's output. */
|
|
42
|
+
jobUrl(id) {
|
|
43
|
+
return `${this.base}/#/jobs/playbook/${id}/output`;
|
|
44
|
+
}
|
|
45
|
+
request(method, path, body) {
|
|
46
|
+
const url = new URL(path.startsWith("http") ? path : `${this.base}${path}`);
|
|
47
|
+
if (url.origin !== this.origin)
|
|
48
|
+
return Promise.reject(new AwxError(`Not sending the AWX token to ${url.origin}`));
|
|
49
|
+
if (cleartextTo(url) && !this.options.allowHttp)
|
|
50
|
+
return Promise.reject(
|
|
51
|
+
new AwxError(
|
|
52
|
+
`Not sending the AWX token unencrypted to ${url.origin}: use an https address, or turn on afd.awx.allowHttp in your user settings (a lab AWX only)`
|
|
53
|
+
)
|
|
54
|
+
);
|
|
55
|
+
const lib = url.protocol === "https:" ? https : http;
|
|
56
|
+
const data = body === void 0 ? void 0 : JSON.stringify(body);
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const req = lib.request(
|
|
59
|
+
url,
|
|
60
|
+
{
|
|
61
|
+
method,
|
|
62
|
+
headers: {
|
|
63
|
+
Authorization: `Bearer ${this.token}`,
|
|
64
|
+
Accept: "application/json",
|
|
65
|
+
...data ? { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(data) } : {}
|
|
66
|
+
},
|
|
67
|
+
timeout: this.options.timeoutMs ?? TIMEOUTS.awxRequest,
|
|
68
|
+
...url.protocol === "https:" ? { rejectUnauthorized: this.options.verifyTls !== false } : {}
|
|
69
|
+
},
|
|
70
|
+
(res) => {
|
|
71
|
+
const chunks = [];
|
|
72
|
+
let size = 0;
|
|
73
|
+
const max = this.options.maxBodyBytes ?? MAX_BODY_BYTES;
|
|
74
|
+
res.on("data", (c) => {
|
|
75
|
+
if (size > max) return;
|
|
76
|
+
size += c.length;
|
|
77
|
+
if (size > max) {
|
|
78
|
+
clearTimeout(deadline);
|
|
79
|
+
reject(
|
|
80
|
+
new AwxError(
|
|
81
|
+
`AWX answered ${url.pathname} with more than ${max} bytes: stopped reading`
|
|
82
|
+
)
|
|
83
|
+
);
|
|
84
|
+
req.destroy();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
chunks.push(c);
|
|
88
|
+
});
|
|
89
|
+
res.on("end", () => {
|
|
90
|
+
clearTimeout(deadline);
|
|
91
|
+
if (size > max) return;
|
|
92
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
93
|
+
let parsed = text;
|
|
94
|
+
try {
|
|
95
|
+
parsed = text ? JSON.parse(text) : void 0;
|
|
96
|
+
} catch {
|
|
97
|
+
}
|
|
98
|
+
const status = res.statusCode ?? 0;
|
|
99
|
+
if (status >= 200 && status < 300) resolve(parsed);
|
|
100
|
+
else if (status === 401)
|
|
101
|
+
reject(
|
|
102
|
+
new AwxError(
|
|
103
|
+
'AWX refused the token (401): set a new one with "Set AWX Token"',
|
|
104
|
+
status
|
|
105
|
+
)
|
|
106
|
+
);
|
|
107
|
+
else
|
|
108
|
+
reject(
|
|
109
|
+
new AwxError(
|
|
110
|
+
`AWX ${method} ${url.pathname} failed (${status}): ${describe(parsed)}`,
|
|
111
|
+
status
|
|
112
|
+
)
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
req.on(
|
|
118
|
+
"timeout",
|
|
119
|
+
() => req.destroy(new AwxError(`AWX did not answer ${url.pathname} in time`))
|
|
120
|
+
);
|
|
121
|
+
const deadline = setTimeout(
|
|
122
|
+
() => req.destroy(new AwxError(`AWX did not finish ${url.pathname} in time`)),
|
|
123
|
+
this.options.deadlineMs ?? TIMEOUTS.awxRequestTotal
|
|
124
|
+
);
|
|
125
|
+
req.on("error", (e) => {
|
|
126
|
+
clearTimeout(deadline);
|
|
127
|
+
reject(
|
|
128
|
+
e instanceof AwxError ? e : new AwxError(`Cannot reach AWX at ${this.base}: ${e.message}`, void 0, e.code)
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
if (data) req.write(data);
|
|
132
|
+
req.end();
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/** A GET is safe to repeat, so a stale kept-alive socket gets one more try. */
|
|
136
|
+
async get(path) {
|
|
137
|
+
try {
|
|
138
|
+
return await this.request("GET", path);
|
|
139
|
+
} catch (e) {
|
|
140
|
+
if (!(e instanceof AwxError) || !e.code || !STALE_SOCKET.has(e.code)) throw e;
|
|
141
|
+
return this.request("GET", path);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
post(path, body) {
|
|
145
|
+
return this.request("POST", path, body);
|
|
146
|
+
}
|
|
147
|
+
patch(path, body) {
|
|
148
|
+
return this.request("PATCH", path, body);
|
|
149
|
+
}
|
|
150
|
+
/** Every page of a list endpoint. */
|
|
151
|
+
async all(path) {
|
|
152
|
+
const out = [];
|
|
153
|
+
let next = `${path}${path.includes("?") ? "&" : "?"}page_size=${PAGE}`;
|
|
154
|
+
const seen = /* @__PURE__ */ new Set();
|
|
155
|
+
while (next) {
|
|
156
|
+
if (seen.has(next))
|
|
157
|
+
throw new AwxError(`AWX's pages of ${path} lead back to ${next}: stopped reading`);
|
|
158
|
+
if (seen.size >= MAX_PAGES)
|
|
159
|
+
throw new AwxError(`AWX's list ${path} has more than ${MAX_PAGES} pages: stopped reading`);
|
|
160
|
+
seen.add(next);
|
|
161
|
+
const page = await this.get(next);
|
|
162
|
+
out.push(...page.results);
|
|
163
|
+
if (!page.next) next = null;
|
|
164
|
+
else {
|
|
165
|
+
const url = new URL(page.next, `${this.base}/`);
|
|
166
|
+
next = `${this.origin}${url.pathname}${url.search}`;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
async ping() {
|
|
172
|
+
return this.get("/api/v2/ping/");
|
|
173
|
+
}
|
|
174
|
+
async named(kind, name) {
|
|
175
|
+
const page = await this.get(`/api/v2/${kind}/?name=${encodeURIComponent(name)}`);
|
|
176
|
+
return page.results.find((r) => r.name === name);
|
|
177
|
+
}
|
|
178
|
+
list(kind) {
|
|
179
|
+
return this.all(`/api/v2/${kind}/?order_by=name`);
|
|
180
|
+
}
|
|
181
|
+
/** Creates the job template or updates the one of the same name; credentials are set to the list. */
|
|
182
|
+
async upsertTemplate(spec) {
|
|
183
|
+
const body = {
|
|
184
|
+
name: spec.name,
|
|
185
|
+
description: spec.description,
|
|
186
|
+
job_type: "run",
|
|
187
|
+
project: spec.project,
|
|
188
|
+
playbook: spec.playbook,
|
|
189
|
+
inventory: spec.inventory,
|
|
190
|
+
execution_environment: spec.execution_environment ?? null,
|
|
191
|
+
ask_variables_on_launch: true,
|
|
192
|
+
ask_limit_on_launch: true
|
|
193
|
+
};
|
|
194
|
+
const existing = await this.named("job_templates", spec.name);
|
|
195
|
+
const jt = existing ? await this.patch(`/api/v2/job_templates/${existing.id}/`, body) : await this.post("/api/v2/job_templates/", body);
|
|
196
|
+
const current = await this.all(`/api/v2/job_templates/${jt.id}/credentials/`);
|
|
197
|
+
for (const c of current)
|
|
198
|
+
if (!spec.credentials.includes(c.id))
|
|
199
|
+
await this.post(`/api/v2/job_templates/${jt.id}/credentials/`, {
|
|
200
|
+
id: c.id,
|
|
201
|
+
disassociate: true
|
|
202
|
+
});
|
|
203
|
+
for (const id of spec.credentials)
|
|
204
|
+
if (!current.some((c) => c.id === id))
|
|
205
|
+
await this.post(`/api/v2/job_templates/${jt.id}/credentials/`, { id });
|
|
206
|
+
return jt;
|
|
207
|
+
}
|
|
208
|
+
/** A template's survey (AFD-59); `kind` is job_templates or workflow_job_templates. */
|
|
209
|
+
async survey(kind, id) {
|
|
210
|
+
const spec = await this.get(`/api/v2/${kind}/${id}/survey_spec/`);
|
|
211
|
+
return spec && Object.keys(spec).length > 0 ? spec : void 0;
|
|
212
|
+
}
|
|
213
|
+
/** Writes the survey and turns it on: launches then ask its questions. */
|
|
214
|
+
async setSurvey(kind, id, spec) {
|
|
215
|
+
await this.post(`/api/v2/${kind}/${id}/survey_spec/`, spec);
|
|
216
|
+
await this.patch(`/api/v2/${kind}/${id}/`, { survey_enabled: true });
|
|
217
|
+
}
|
|
218
|
+
async launch(templateId, extraVars) {
|
|
219
|
+
const r = await this.post(`/api/v2/job_templates/${templateId}/launch/`, {
|
|
220
|
+
extra_vars: extraVars
|
|
221
|
+
});
|
|
222
|
+
return this.job(r.job);
|
|
223
|
+
}
|
|
224
|
+
job(id) {
|
|
225
|
+
return this.get(`/api/v2/jobs/${id}/`);
|
|
226
|
+
}
|
|
227
|
+
/** Events after `counter`, in order (incremental polling while a job runs). */
|
|
228
|
+
events(jobId, counter) {
|
|
229
|
+
return this.all(
|
|
230
|
+
`/api/v2/jobs/${jobId}/job_events/?order_by=counter&counter__gt=${counter}`
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
async cancel(jobId) {
|
|
234
|
+
await this.post(`/api/v2/jobs/${jobId}/cancel/`, {});
|
|
235
|
+
}
|
|
236
|
+
async cancelWorkflow(workflowJobId) {
|
|
237
|
+
await this.post(`/api/v2/workflow_jobs/${workflowJobId}/cancel/`, {});
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
var StoppedFollowing = class extends Error {
|
|
241
|
+
constructor(kind, id, url, timedOut) {
|
|
242
|
+
super(
|
|
243
|
+
timedOut ? `Stopped following AWX ${kind} ${id} after ${FOLLOW_HOURS} hours: it keeps running in AWX` : `Stopped following AWX ${kind} ${id}: it keeps running in AWX`
|
|
244
|
+
);
|
|
245
|
+
this.kind = kind;
|
|
246
|
+
this.id = id;
|
|
247
|
+
this.url = url;
|
|
248
|
+
this.name = "StoppedFollowing";
|
|
249
|
+
}
|
|
250
|
+
kind;
|
|
251
|
+
id;
|
|
252
|
+
url;
|
|
253
|
+
};
|
|
254
|
+
var FOLLOW_HOURS = 12;
|
|
255
|
+
function followLimit(cancelled, start = Date.now()) {
|
|
256
|
+
return () => cancelled() ? "cancelled" : Date.now() - start > FOLLOW_HOURS * 36e5 ? "timeout" : void 0;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export {
|
|
260
|
+
AwxError,
|
|
261
|
+
MAX_BODY_BYTES,
|
|
262
|
+
MAX_PAGES,
|
|
263
|
+
cleartextTo,
|
|
264
|
+
AwxClient,
|
|
265
|
+
StoppedFollowing,
|
|
266
|
+
FOLLOW_HOURS,
|
|
267
|
+
followLimit
|
|
268
|
+
};
|
|
269
|
+
//# sourceMappingURL=chunk-GAL5LW6Y.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/node/awx/client.ts"],
|
|
4
|
+
"sourcesContent": ["// AWX REST client (M7, ADR-0013): bearer token, JSON, pagination, per-client TLS verification.\n// node:http(s) rather than fetch so verification can be turned off for a lab without a CA.\n// The token only ever travels in the Authorization header: errors never echo request headers.\nimport * as http from 'node:http';\nimport * as https from 'node:https';\nimport type { AwxSurveySpec } from '../../core/awx/survey';\nimport type {\n AwxJob,\n AwxJobEvent,\n AwxJobTemplate,\n AwxNamed,\n AwxPage,\n TemplateSpec,\n} from '../../core/awx/types';\nimport { TIMEOUTS } from '../timeouts';\n\nconst PAGE = 200;\n\nexport class AwxError extends Error {\n constructor(\n message: string,\n readonly status?: number,\n /** The socket error code when AWX could not be reached (ECONNRESET, \u2026). */\n readonly code?: string,\n ) {\n super(message);\n this.name = 'AwxError';\n }\n}\n\n/**\n * Errors a second try fixes: a kept-alive socket the server (or the tunnel in front of it) closed\n * while idle, which the next request then hits (AFD-32).\n */\nconst STALE_SOCKET = new Set(['ECONNRESET', 'EPIPE', 'ECONNABORTED']);\n\nexport interface AwxOptions {\n verifyTls?: boolean;\n /** Idle time without data before a request fails. */\n timeoutMs?: number;\n /** AFD-161: the whole request, however slowly the data comes. */\n deadlineMs?: number;\n /**\n * AFD-161: send the token over plain http. Refused by default, except to this machine\n * (localhost, 127.0.0.1, ::1), where nothing crosses a network.\n */\n allowHttp?: boolean;\n /** The largest answer read (default MAX_BODY_BYTES). */\n maxBodyBytes?: number;\n}\n\n/** AFD-161: no AWX answer is larger than this; a bigger one is a misbehaving proxy or server. */\nexport const MAX_BODY_BYTES = 64 * 1024 * 1024;\n\n/** AFD-161: 500 pages of 200 is 100,000 items, more than any AWX list Ansible Flow reads. */\nexport const MAX_PAGES = 500;\n\nconst LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]']);\n\n/** Whether a URL would carry the token over plain http to another machine. */\nexport const cleartextTo = (url: URL) => url.protocol === 'http:' && !LOOPBACK.has(url.hostname);\n\n/** AWX's validation errors are `{field: [\"message\"]}` or `{detail: \"message\"}`. */\nfunction describe(body: unknown): string {\n if (!body || typeof body !== 'object') return String(body ?? '').slice(0, 300);\n const o = body as Record<string, unknown>;\n if (typeof o.detail === 'string') return o.detail;\n return Object.entries(o)\n .map(([k, v]) => `${k}: ${Array.isArray(v) ? v.join(' ') : String(v)}`)\n .join('; ')\n .slice(0, 300);\n}\n\nexport class AwxClient {\n readonly base: string;\n private readonly origin: string;\n\n constructor(\n url: string,\n private readonly token: string,\n private readonly options: AwxOptions = {},\n ) {\n this.base = url.replace(/\\/+$/, '');\n this.origin = new URL(this.base).origin;\n }\n\n /** The AWX UI page of a job's output. */\n jobUrl(id: number): string {\n return `${this.base}/#/jobs/playbook/${id}/output`;\n }\n\n request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const url = new URL(path.startsWith('http') ? path : `${this.base}${path}`);\n // The token goes to the configured AWX only (AFD-29), whatever a response points to.\n if (url.origin !== this.origin)\n return Promise.reject(new AwxError(`Not sending the AWX token to ${url.origin}`));\n if (cleartextTo(url) && !this.options.allowHttp)\n return Promise.reject(\n new AwxError(\n `Not sending the AWX token unencrypted to ${url.origin}: use an https address, or turn on afd.awx.allowHttp in your user settings (a lab AWX only)`,\n ),\n );\n const lib = url.protocol === 'https:' ? https : http;\n const data = body === undefined ? undefined : JSON.stringify(body);\n return new Promise<T>((resolve, reject) => {\n const req = lib.request(\n url,\n {\n method,\n headers: {\n Authorization: `Bearer ${this.token}`,\n Accept: 'application/json',\n ...(data\n ? { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) }\n : {}),\n },\n timeout: this.options.timeoutMs ?? TIMEOUTS.awxRequest,\n ...(url.protocol === 'https:'\n ? { rejectUnauthorized: this.options.verifyTls !== false }\n : {}),\n },\n (res) => {\n const chunks: Buffer[] = [];\n let size = 0;\n const max = this.options.maxBodyBytes ?? MAX_BODY_BYTES;\n res.on('data', (c: Buffer) => {\n if (size > max) return;\n size += c.length;\n if (size > max) {\n // The error reaches the caller here: a body read whole in one chunk still ends.\n clearTimeout(deadline);\n reject(\n new AwxError(\n `AWX answered ${url.pathname} with more than ${max} bytes: stopped reading`,\n ),\n );\n req.destroy();\n return;\n }\n chunks.push(c);\n });\n res.on('end', () => {\n clearTimeout(deadline);\n if (size > max) return;\n const text = Buffer.concat(chunks).toString('utf8');\n let parsed: unknown = text;\n try {\n parsed = text ? JSON.parse(text) : undefined;\n } catch {\n // Not JSON (a proxy error page): reported as text.\n }\n const status = res.statusCode ?? 0;\n if (status >= 200 && status < 300) resolve(parsed as T);\n else if (status === 401)\n reject(\n new AwxError(\n 'AWX refused the token (401): set a new one with \"Set AWX Token\"',\n status,\n ),\n );\n else\n reject(\n new AwxError(\n `AWX ${method} ${url.pathname} failed (${status}): ${describe(parsed)}`,\n status,\n ),\n );\n });\n },\n );\n req.on('timeout', () =>\n req.destroy(new AwxError(`AWX did not answer ${url.pathname} in time`)),\n );\n // AFD-161: the idle timeout above restarts with every chunk; this ends a slow trickle too.\n const deadline = setTimeout(\n () => req.destroy(new AwxError(`AWX did not finish ${url.pathname} in time`)),\n this.options.deadlineMs ?? TIMEOUTS.awxRequestTotal,\n );\n req.on('error', (e: NodeJS.ErrnoException) => {\n clearTimeout(deadline);\n reject(\n e instanceof AwxError\n ? e\n : new AwxError(`Cannot reach AWX at ${this.base}: ${e.message}`, undefined, e.code),\n );\n });\n if (data) req.write(data);\n req.end();\n });\n }\n\n /** A GET is safe to repeat, so a stale kept-alive socket gets one more try. */\n async get<T>(path: string): Promise<T> {\n try {\n return await this.request<T>('GET', path);\n } catch (e) {\n if (!(e instanceof AwxError) || !e.code || !STALE_SOCKET.has(e.code)) throw e;\n return this.request<T>('GET', path);\n }\n }\n\n post<T>(path: string, body: unknown): Promise<T> {\n return this.request<T>('POST', path, body);\n }\n\n patch<T>(path: string, body: unknown): Promise<T> {\n return this.request<T>('PATCH', path, body);\n }\n\n /** Every page of a list endpoint. */\n async all<T>(path: string): Promise<T[]> {\n const out: T[] = [];\n let next: string | null = `${path}${path.includes('?') ? '&' : '?'}page_size=${PAGE}`;\n // AFD-161: a `next` that comes round again, or no end at all, would loop for good.\n const seen = new Set<string>();\n while (next) {\n if (seen.has(next))\n throw new AwxError(`AWX's pages of ${path} lead back to ${next}: stopped reading`);\n if (seen.size >= MAX_PAGES)\n throw new AwxError(`AWX's list ${path} has more than ${MAX_PAGES} pages: stopped reading`);\n seen.add(next);\n const page: AwxPage<T> = await this.get<AwxPage<T>>(next);\n out.push(...page.results);\n // AWX answers with a path; an absolute `next` (a proxy rewriting hosts) is followed on the\n // configured AWX, so the token never leaves it.\n if (!page.next) next = null;\n else {\n const url = new URL(page.next, `${this.base}/`);\n next = `${this.origin}${url.pathname}${url.search}`;\n }\n }\n return out;\n }\n\n async ping(): Promise<{ version: string }> {\n return this.get('/api/v2/ping/');\n }\n\n async named<T extends AwxNamed>(kind: string, name: string): Promise<T | undefined> {\n const page = await this.get<AwxPage<T>>(`/api/v2/${kind}/?name=${encodeURIComponent(name)}`);\n return page.results.find((r) => r.name === name);\n }\n\n list<T extends AwxNamed>(kind: string): Promise<T[]> {\n return this.all<T>(`/api/v2/${kind}/?order_by=name`);\n }\n\n /** Creates the job template or updates the one of the same name; credentials are set to the list. */\n async upsertTemplate(spec: TemplateSpec): Promise<AwxJobTemplate> {\n const body = {\n name: spec.name,\n description: spec.description,\n job_type: 'run',\n project: spec.project,\n playbook: spec.playbook,\n inventory: spec.inventory,\n execution_environment: spec.execution_environment ?? null,\n ask_variables_on_launch: true,\n ask_limit_on_launch: true,\n };\n const existing = await this.named<AwxJobTemplate>('job_templates', spec.name);\n const jt = existing\n ? await this.patch<AwxJobTemplate>(`/api/v2/job_templates/${existing.id}/`, body)\n : await this.post<AwxJobTemplate>('/api/v2/job_templates/', body);\n const current = await this.all<AwxNamed>(`/api/v2/job_templates/${jt.id}/credentials/`);\n for (const c of current)\n if (!spec.credentials.includes(c.id))\n await this.post(`/api/v2/job_templates/${jt.id}/credentials/`, {\n id: c.id,\n disassociate: true,\n });\n for (const id of spec.credentials)\n if (!current.some((c) => c.id === id))\n await this.post(`/api/v2/job_templates/${jt.id}/credentials/`, { id });\n return jt;\n }\n\n /** A template's survey (AFD-59); `kind` is job_templates or workflow_job_templates. */\n async survey(kind: string, id: number): Promise<Partial<AwxSurveySpec> | undefined> {\n const spec = await this.get<Partial<AwxSurveySpec>>(`/api/v2/${kind}/${id}/survey_spec/`);\n return spec && Object.keys(spec).length > 0 ? spec : undefined;\n }\n\n /** Writes the survey and turns it on: launches then ask its questions. */\n async setSurvey(kind: string, id: number, spec: AwxSurveySpec): Promise<void> {\n await this.post(`/api/v2/${kind}/${id}/survey_spec/`, spec);\n await this.patch(`/api/v2/${kind}/${id}/`, { survey_enabled: true });\n }\n\n async launch(templateId: number, extraVars: Record<string, unknown>): Promise<AwxJob> {\n const r = await this.post<{ job: number }>(`/api/v2/job_templates/${templateId}/launch/`, {\n extra_vars: extraVars,\n });\n return this.job(r.job);\n }\n\n job(id: number): Promise<AwxJob> {\n return this.get<AwxJob>(`/api/v2/jobs/${id}/`);\n }\n\n /** Events after `counter`, in order (incremental polling while a job runs). */\n events(jobId: number, counter: number): Promise<AwxJobEvent[]> {\n return this.all<AwxJobEvent>(\n `/api/v2/jobs/${jobId}/job_events/?order_by=counter&counter__gt=${counter}`,\n );\n }\n\n async cancel(jobId: number): Promise<void> {\n await this.post(`/api/v2/jobs/${jobId}/cancel/`, {});\n }\n\n async cancelWorkflow(workflowJobId: number): Promise<void> {\n await this.post(`/api/v2/workflow_jobs/${workflowJobId}/cancel/`, {});\n }\n}\n\n/**\n * Following an AWX job stopped before it finished (AFD-29): the user cancelled the progress, or it\n * ran past the follow limit. The job itself keeps running in AWX.\n */\nexport class StoppedFollowing extends Error {\n constructor(\n readonly kind: 'job' | 'workflow',\n readonly id: number,\n readonly url: string,\n timedOut: boolean,\n ) {\n super(\n timedOut\n ? `Stopped following AWX ${kind} ${id} after ${FOLLOW_HOURS} hours: it keeps running in AWX`\n : `Stopped following AWX ${kind} ${id}: it keeps running in AWX`,\n );\n this.name = 'StoppedFollowing';\n }\n}\n\n/** How long a launch follows a job or workflow before it stops polling (it can be stopped sooner). */\nexport const FOLLOW_HOURS = 12;\n\n/** A follow loop's stop check: cancelled by the user, or past the follow limit. */\nexport function followLimit(cancelled: () => boolean, start = Date.now()) {\n return (): 'cancelled' | 'timeout' | undefined =>\n cancelled()\n ? 'cancelled'\n : Date.now() - start > FOLLOW_HOURS * 3600_000\n ? 'timeout'\n : undefined;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|