@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.
Files changed (139) 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/flowMerge.d.ts +35 -0
  77. package/dist/flowMerge.js +74 -0
  78. package/dist/flowMerge.js.map +7 -0
  79. package/dist/run/audit.d.ts +2 -0
  80. package/dist/run/audit.js +10 -0
  81. package/dist/run/audit.js.map +7 -0
  82. package/dist/run/exec.d.ts +14 -0
  83. package/dist/run/exec.js +7 -0
  84. package/dist/run/exec.js.map +7 -0
  85. package/dist/run/facts.d.ts +39 -0
  86. package/dist/run/facts.js +96 -0
  87. package/dist/run/facts.js.map +7 -0
  88. package/dist/run/ids.d.ts +2 -0
  89. package/dist/run/ids.js +8 -0
  90. package/dist/run/ids.js.map +7 -0
  91. package/dist/run/pastRuns.d.ts +9 -0
  92. package/dist/run/pastRuns.js +13 -0
  93. package/dist/run/pastRuns.js.map +7 -0
  94. package/dist/run/runFlow.d.ts +78 -0
  95. package/dist/run/runFlow.js +20 -0
  96. package/dist/run/runFlow.js.map +7 -0
  97. package/dist/run/segments.d.ts +43 -0
  98. package/dist/run/segments.js +157 -0
  99. package/dist/run/segments.js.map +7 -0
  100. package/dist/run/stop.d.ts +3 -0
  101. package/dist/run/stop.js +7 -0
  102. package/dist/run/stop.js.map +7 -0
  103. package/dist/run/storage.d.ts +16 -0
  104. package/dist/run/storage.js +15 -0
  105. package/dist/run/storage.js.map +7 -0
  106. package/dist/run/tail.d.ts +5 -0
  107. package/dist/run/tail.js +7 -0
  108. package/dist/run/tail.js.map +7 -0
  109. package/dist/runner/client.d.ts +149 -0
  110. package/dist/runner/client.js +168 -0
  111. package/dist/runner/client.js.map +7 -0
  112. package/dist/runner/devBundle.d.ts +14 -0
  113. package/dist/runner/devBundle.js +78 -0
  114. package/dist/runner/devBundle.js.map +7 -0
  115. package/dist/scenario/cli.d.ts +39 -0
  116. package/dist/scenario/cli.js +125 -0
  117. package/dist/scenario/cli.js.map +7 -0
  118. package/dist/scenario/runner.d.ts +50 -0
  119. package/dist/scenario/runner.js +185 -0
  120. package/dist/scenario/runner.js.map +7 -0
  121. package/dist/sharedRefs.d.ts +22 -0
  122. package/dist/sharedRefs.js +114 -0
  123. package/dist/sharedRefs.js.map +7 -0
  124. package/dist/ssh/exec.d.ts +39 -0
  125. package/dist/ssh/exec.js +13 -0
  126. package/dist/ssh/exec.js.map +7 -0
  127. package/dist/ssh/remote.d.ts +40 -0
  128. package/dist/ssh/remote.js +25 -0
  129. package/dist/ssh/remote.js.map +7 -0
  130. package/dist/ssh/sshRun.d.ts +1 -0
  131. package/dist/ssh/sshRun.js +24 -0
  132. package/dist/ssh/sshRun.js.map +7 -0
  133. package/dist/ssh/sync.d.ts +22 -0
  134. package/dist/ssh/sync.js +12 -0
  135. package/dist/ssh/sync.js.map +7 -0
  136. package/dist/timeouts.d.ts +38 -0
  137. package/dist/timeouts.js +7 -0
  138. package/dist/timeouts.js.map +7 -0
  139. package/package.json +35 -4
@@ -0,0 +1,371 @@
1
+ // src/node/catalog/catalogIndex.ts
2
+ import {
3
+ badgesOf,
4
+ metaOf,
5
+ nameCandidates,
6
+ planQuery,
7
+ rerank,
8
+ stem
9
+ } from "@ansible-flow/core/catalog/rank";
10
+ var INDEX_SCHEMA = 8;
11
+ var WEIGHTS = [10, 6, 3, 1, 2];
12
+ var CANDIDATES = 200;
13
+ var COMMON = 0.5;
14
+ var BROAD = 0.12;
15
+ var TOKENIZE = "tokenize='porter unicode61'";
16
+ var COLUMNS = "p.id, p.fqcn, p.type, p.collection, p.name, p.short, p.deprecated, p.meta";
17
+ var sqlite;
18
+ function loadSqlite() {
19
+ if (sqlite === void 0) {
20
+ const get = process.getBuiltinModule;
21
+ try {
22
+ sqlite = get?.call(process, "node:sqlite") ?? null;
23
+ } catch {
24
+ sqlite = null;
25
+ }
26
+ }
27
+ return sqlite;
28
+ }
29
+ function sqliteAvailable() {
30
+ return loadSqlite() !== null;
31
+ }
32
+ function sqliteMissing() {
33
+ return `The module catalog needs an editor running Node.js 22.13 or later (VS Code 1.101+); this one runs Node.js ${process.versions.node}`;
34
+ }
35
+ function database(path, options) {
36
+ const s = loadSqlite();
37
+ if (!s) throw new Error(sqliteMissing());
38
+ return options ? new s.DatabaseSync(path, options) : new s.DatabaseSync(path);
39
+ }
40
+ function hits(column, words2) {
41
+ return Array.from({ length: words2 }, (_, i) => `((instr(p.${column}, ?) > 0) << ${i})`).join(" | ") || "0";
42
+ }
43
+ function words(text) {
44
+ return text.replaceAll(/[._-]+/g, " ");
45
+ }
46
+ var CatalogIndex = class _CatalogIndex {
47
+ constructor(db) {
48
+ this.db = db;
49
+ this.total = db.prepare("SELECT count(*) AS n FROM plugins").get().n;
50
+ this.df = db.prepare("SELECT n FROM df WHERE term = ?");
51
+ this.named = db.prepare("SELECT 1 FROM plugins WHERE name = ? LIMIT 1");
52
+ this.docOf = db.prepare(
53
+ "SELECT d.doc FROM plugins p JOIN docs d ON d.id = p.id WHERE p.type = ? AND p.fqcn = ?"
54
+ );
55
+ this.routeOf = db.prepare("SELECT entry FROM routing WHERE type = ? AND name = ?");
56
+ this.textOf = db.prepare("SELECT opts, text FROM plugins WHERE id = ?");
57
+ this.stemmer.exec(`CREATE VIRTUAL TABLE t USING fts5(x, ${TOKENIZE});
58
+ CREATE VIRTUAL TABLE v USING fts5vocab(t, 'instance');`);
59
+ this.stemSql = {
60
+ clear: this.stemmer.prepare("DELETE FROM t"),
61
+ add: this.stemmer.prepare("INSERT INTO t (x) VALUES (?)"),
62
+ read: this.stemmer.prepare("SELECT term, offset FROM v")
63
+ };
64
+ }
65
+ db;
66
+ total;
67
+ df;
68
+ named;
69
+ docOf;
70
+ routeOf;
71
+ textOf;
72
+ searches = /* @__PURE__ */ new Map();
73
+ /** A scratch FTS table whose vocabulary gives the Porter stems the index uses. */
74
+ stemmer = database(":memory:");
75
+ stemSql;
76
+ /** Builds a fresh index at `path` (':memory:' in tests) from the probe's collection files. */
77
+ static build(path, files) {
78
+ const db = database(path);
79
+ db.exec(`
80
+ PRAGMA journal_mode = OFF;
81
+ PRAGMA synchronous = OFF;
82
+ PRAGMA page_size = 16384;
83
+ DROP TABLE IF EXISTS plugins; DROP TABLE IF EXISTS fts; DROP TABLE IF EXISTS routing;
84
+ DROP TABLE IF EXISTS meta; DROP TABLE IF EXISTS vocab; DROP TABLE IF EXISTS docs;
85
+ DROP TABLE IF EXISTS df;
86
+ CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT);
87
+ CREATE TABLE plugins (
88
+ id INTEGER PRIMARY KEY, type TEXT, fqcn TEXT, collection TEXT, name TEXT, short TEXT,
89
+ deprecated INTEGER, opts TEXT, strong TEXT, text TEXT, meta TEXT, UNIQUE (type, fqcn));
90
+ CREATE INDEX plugins_name ON plugins (name);
91
+ -- Full docs apart, so searches never page through them.
92
+ CREATE TABLE docs (id INTEGER PRIMARY KEY, doc TEXT);
93
+ CREATE VIRTUAL TABLE fts USING fts5(name, short, opts, body, coll, content='', ${TOKENIZE});
94
+ CREATE VIRTUAL TABLE vocab USING fts5vocab(fts, 'row');
95
+ -- Document frequency per stemmed term; reading it from vocab walks the term's doclist.
96
+ CREATE TABLE df (term TEXT PRIMARY KEY, n INTEGER) WITHOUT ROWID;
97
+ INSERT INTO fts (fts, rank) VALUES ('rank', 'bm25(${WEIGHTS.join(", ")})');
98
+ CREATE TABLE routing (type TEXT, name TEXT, entry TEXT, PRIMARY KEY (type, name));
99
+ `);
100
+ const addPlugin = db.prepare(
101
+ "INSERT OR IGNORE INTO plugins (type, fqcn, collection, name, short, deprecated, opts, strong, text, meta) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
102
+ );
103
+ const addDoc = db.prepare("INSERT INTO docs (id, doc) VALUES (?, ?)");
104
+ const addText = db.prepare(
105
+ "INSERT INTO fts (rowid, name, short, opts, body, coll) VALUES (?, ?, ?, ?, ?, ?)"
106
+ );
107
+ const addRoute = db.prepare(
108
+ "INSERT OR REPLACE INTO routing (type, name, entry) VALUES (?, ?, ?)"
109
+ );
110
+ db.exec("BEGIN");
111
+ for (const file of files) {
112
+ for (const p of file.plugins) {
113
+ const options = Object.entries(p.options ?? {});
114
+ const strong = [
115
+ words(p.name),
116
+ p.short_description,
117
+ options.map(([k]) => words(k)).join(" ")
118
+ ].join(" ").toLowerCase();
119
+ const text = [
120
+ p.description.slice(0, 1500),
121
+ options.map(([, o]) => o.description).join(" ").slice(0, 2500)
122
+ ].join(" ").toLowerCase();
123
+ const r = addPlugin.run(
124
+ p.type,
125
+ p.fqcn,
126
+ p.collection,
127
+ p.name,
128
+ p.short_description,
129
+ p.deprecated ? 1 : 0,
130
+ options.map(([k]) => k).join(" "),
131
+ strong,
132
+ text,
133
+ JSON.stringify(metaOf(p))
134
+ );
135
+ if (!r.changes) continue;
136
+ addDoc.run(r.lastInsertRowid, JSON.stringify(p));
137
+ addText.run(
138
+ r.lastInsertRowid,
139
+ // The local name only: "open"* must not match every openstack.* module.
140
+ words(p.name),
141
+ p.short_description,
142
+ options.flatMap(([k, o]) => [words(k), ...(o.aliases ?? []).map(words)]).join(" "),
143
+ [p.description, p.notes ?? "", ...options.map(([, o]) => o.description)].join("\n").slice(0, 8e3),
144
+ words(p.collection)
145
+ );
146
+ }
147
+ const prefix = file.collection === "ansible.builtin" ? "" : `${file.collection}.`;
148
+ for (const [type, entries] of Object.entries(file.routing ?? {})) {
149
+ for (const [name, entry] of Object.entries(entries ?? {})) {
150
+ addRoute.run(type, `${prefix}${name}`, JSON.stringify(entry));
151
+ if (!prefix) addRoute.run(type, `ansible.builtin.${name}`, JSON.stringify(entry));
152
+ }
153
+ }
154
+ }
155
+ db.exec("INSERT INTO df (term, n) SELECT term, doc FROM vocab");
156
+ const set = db.prepare("INSERT INTO meta (key, value) VALUES (?, ?)");
157
+ set.run("schema", String(INDEX_SCHEMA));
158
+ set.run("collections", JSON.stringify(files.map((f) => `${f.collection}@${f.version}`)));
159
+ db.exec("COMMIT");
160
+ return new _CatalogIndex(db);
161
+ }
162
+ /** Opens an index built before, or undefined when it is missing or of an older schema. */
163
+ static open(path) {
164
+ try {
165
+ const db = database(path, { readOnly: true });
166
+ const row = db.prepare("SELECT value FROM meta WHERE key = 'schema'").get();
167
+ if (row?.value !== String(INDEX_SCHEMA)) {
168
+ db.close();
169
+ return void 0;
170
+ }
171
+ return new _CatalogIndex(db);
172
+ } catch {
173
+ return void 0;
174
+ }
175
+ }
176
+ close() {
177
+ this.db.close();
178
+ this.stemmer.close();
179
+ }
180
+ stats() {
181
+ const count = (sql) => this.db.prepare(sql).get().n;
182
+ return {
183
+ plugins: count("SELECT count(*) AS n FROM plugins"),
184
+ modules: count("SELECT count(*) AS n FROM plugins WHERE type = 'module'"),
185
+ collections: count("SELECT count(DISTINCT collection) AS n FROM plugins")
186
+ };
187
+ }
188
+ versions;
189
+ /** Every indexed collection and its version. */
190
+ collections() {
191
+ this.collectionVersion("");
192
+ return Object.fromEntries(this.versions ?? []);
193
+ }
194
+ /** The indexed version of a collection (`ansible.builtin` is ansible-core's). */
195
+ collectionVersion(name) {
196
+ if (!this.versions) {
197
+ const row = this.db.prepare("SELECT value FROM meta WHERE key = 'collections'").get();
198
+ const list = row ? JSON.parse(row.value) : [];
199
+ this.versions = new Map(
200
+ list.map((c) => [c.slice(0, c.indexOf("@")), c.slice(c.indexOf("@") + 1)])
201
+ );
202
+ }
203
+ return this.versions.get(name);
204
+ }
205
+ /**
206
+ * AFD-225: the filters, tests or lookups the environment has, for the Jinja completion list:
207
+ * ansible.builtin's by short name, a collection's by FQCN (ansible.legacy repeats builtin).
208
+ */
209
+ names(type) {
210
+ const rows = this.db.prepare(
211
+ "SELECT fqcn, collection, name, short FROM plugins WHERE type = ? AND collection <> 'ansible.legacy' ORDER BY collection <> 'ansible.builtin', fqcn"
212
+ ).all(type);
213
+ return rows.map((r) => ({
214
+ name: r.collection === "ansible.builtin" ? r.name : r.fqcn,
215
+ fqcn: r.fqcn,
216
+ ...r.short ? { doc: r.short } : {}
217
+ }));
218
+ }
219
+ plugin(fqcn, type = "module") {
220
+ const row = this.docOf.get(type, fqcn);
221
+ return row ? JSON.parse(row.doc) : void 0;
222
+ }
223
+ /** The index's own Porter stems of query words (create → creat, query → queri). */
224
+ porter(list) {
225
+ this.stemSql.clear.run();
226
+ this.stemSql.add.run(list.join(" "));
227
+ const out = [...list];
228
+ for (const r of this.stemSql.read.all())
229
+ out[r.offset] = r.term;
230
+ return out;
231
+ }
232
+ /** Parameters in text order: (match | names), stems for in_strong, stems for in_text, types. */
233
+ statement(kind, words2, types) {
234
+ const key = `${kind}:${words2}:${types}`;
235
+ let st = this.searches.get(key);
236
+ if (!st) {
237
+ const typed = types ? `p.type IN (${Array.from({ length: types }, () => "?").join(", ")})` : "";
238
+ const select = `${COLUMNS}, ${hits("strong", words2)} AS in_strong, ${hits("text", words2)} AS in_text`;
239
+ st = this.db.prepare(
240
+ kind === "fts" ? (
241
+ // FTS5 sorts by its configured rank (weighted bm25) fastest when nothing else is joined in.
242
+ `WITH top AS (SELECT rowid, rank FROM fts WHERE fts MATCH ? ORDER BY rank LIMIT ${CANDIDATES})
243
+ SELECT ${select}, top.rank FROM top JOIN plugins p ON p.id = top.rowid ${typed ? `WHERE ${typed}` : ""}`
244
+ ) : `WITH wanted AS (SELECT value FROM json_each(?))
245
+ SELECT ${select}, NULL AS rank FROM plugins p
246
+ WHERE p.name IN (SELECT value FROM wanted) ${typed ? `AND ${typed}` : ""}`
247
+ );
248
+ this.searches.set(key, st);
249
+ }
250
+ return st;
251
+ }
252
+ search(query, ctx = {}, limit = 20) {
253
+ const plan = planQuery(query);
254
+ if (plan.words.length === 0) return [];
255
+ let share = this.porter(plan.words).map(
256
+ (s) => (this.df.get(s)?.n ?? 0) / this.total
257
+ );
258
+ const keep = plan.words.map((w, i) => (share[i] ?? 0) <= COMMON || !!this.named.get(w));
259
+ if (plan.words.length > 1 && keep.some(Boolean)) {
260
+ plan.words = plan.words.filter((_, i) => keep[i]);
261
+ share = share.filter((_, i) => keep[i]);
262
+ }
263
+ const terms = plan.words;
264
+ const match = terms.map((t, i) => {
265
+ const column = (share[i] ?? 0) > BROAD ? "{name short opts} : " : "";
266
+ return t.length >= 3 ? `${column}"${t}" OR name : "${t}"*` : `${column}"${t}"`;
267
+ }).join(" OR ");
268
+ plan.names = nameCandidates(terms);
269
+ const types = ctx.types?.length ? ctx.types : [];
270
+ const crude = terms.map(stem);
271
+ const args = [...crude, ...crude, ...types];
272
+ const rows = [
273
+ ...this.statement("fts", terms.length, types.length).all(match, ...args),
274
+ ...this.statement("names", terms.length, types.length).all(
275
+ JSON.stringify([...plan.names.keys()]),
276
+ ...args
277
+ )
278
+ ];
279
+ const best = rows.reduce((m, r) => Math.max(m, -(r.rank ?? 0)), 0);
280
+ const seen = /* @__PURE__ */ new Set();
281
+ const scored = rows.filter((r) => {
282
+ const key = `${r.type}:${r.fqcn}`;
283
+ if (seen.has(key)) return false;
284
+ seen.add(key);
285
+ return true;
286
+ }).map((r) => {
287
+ const candidate = {
288
+ type: r.type,
289
+ name: r.name,
290
+ collection: r.collection,
291
+ inStrong: r.in_strong,
292
+ inText: r.in_text,
293
+ deprecated: r.deprecated === 1,
294
+ bm25: -(r.rank ?? 0)
295
+ };
296
+ return { r, score: rerank(candidate, plan, ctx, best) };
297
+ });
298
+ scored.sort((a, b) => b.score - a.score);
299
+ return scored.slice(0, limit).map(({ r, score }) => {
300
+ const badges = badgesOf(r, JSON.parse(r.meta), ctx);
301
+ return {
302
+ fqcn: r.fqcn,
303
+ type: r.type,
304
+ collection: r.collection,
305
+ short_description: r.short,
306
+ score: Math.round(score * 1e3) / 1e3,
307
+ match: matchText(r, this.textOf.get(r.id), terms),
308
+ badges,
309
+ dimmed: badges.includes("other-platform")
310
+ };
311
+ });
312
+ }
313
+ route(type, name) {
314
+ const row = this.routeOf.get(type, name);
315
+ return row ? JSON.parse(row.entry) : void 0;
316
+ }
317
+ /**
318
+ * What Ansible runs for a module name (07 §2): the play's collections, then ansible.builtin, then
319
+ * redirects (at most five hops); tombstones mean removed.
320
+ */
321
+ resolve(name, collections = [], type = "module") {
322
+ const candidates = name.split(".").length >= 3 ? [name] : [...collections.map((c) => `${c}.${name}`), `ansible.builtin.${name}`];
323
+ let fallback;
324
+ for (const start of candidates) {
325
+ const hops = [start];
326
+ let current = start;
327
+ let deprecated;
328
+ for (let i = 0; i <= 5; i++) {
329
+ if (this.docOf.get(type, current)) return { fqcn: current, hops, deprecated, found: true };
330
+ const r = this.route(type, current);
331
+ if (!r) break;
332
+ if (r.deprecation)
333
+ deprecated = r.deprecation.warning_text || `deprecated (${r.deprecation.removal_version})`;
334
+ if (r.tombstone) {
335
+ fallback ??= {
336
+ hops,
337
+ removed: r.tombstone.warning_text || `removed in ${r.tombstone.removal_version}`,
338
+ found: false
339
+ };
340
+ break;
341
+ }
342
+ if (!r.redirect) break;
343
+ current = r.redirect;
344
+ hops.push(current);
345
+ }
346
+ }
347
+ return fallback ?? { hops: [name], found: false };
348
+ }
349
+ };
350
+ function matchText(r, more, terms) {
351
+ const where = [];
352
+ for (const t of terms) {
353
+ const s = stem(t);
354
+ if (r.name.toLowerCase().includes(s)) where.push(`name: ${t}`);
355
+ else if (r.short.toLowerCase().includes(s)) where.push(`summary: ${t}`);
356
+ else {
357
+ const opt = more.opts.split(" ").find((k) => k.toLowerCase().includes(s));
358
+ if (opt) where.push(`option \`${opt}\``);
359
+ else if (more.text.includes(s)) where.push(`description: ${t}`);
360
+ }
361
+ }
362
+ return [...new Set(where)].join(" \xB7 ");
363
+ }
364
+
365
+ export {
366
+ INDEX_SCHEMA,
367
+ sqliteAvailable,
368
+ sqliteMissing,
369
+ CatalogIndex
370
+ };
371
+ //# sourceMappingURL=chunk-HPVT6I36.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/node/catalog/catalogIndex.ts"],
4
+ "sourcesContent": ["// The module catalog index (M5, ADR-0011): SQLite FTS5 through node:sqlite in the extension host\n// (verified on VS Code 1.140 and 1.144, desktop and Remote server). Built from the probe's\n// per-collection files; one database per environment manifest.\nimport type { DatabaseSync, StatementSync } from 'node:sqlite';\nimport {\n badgesOf,\n type Candidate,\n metaOf,\n nameCandidates,\n type PluginMeta,\n planQuery,\n rerank,\n stem,\n} from '../../core/catalog/rank';\nimport type {\n CatalogPlugin,\n CollectionFile,\n PluginType,\n Resolution,\n RoutingEntry,\n SearchContext,\n SearchHit,\n} from '../../core/catalog/types';\nimport type { PluginName } from '../../core/view/jinjaComplete';\n\nexport const INDEX_SCHEMA = 8;\n/** bm25 column weights: plugin name, short description, option names, long text, collection. */\nconst WEIGHTS = [10, 6, 3, 1, 2];\nconst CANDIDATES = 200;\n/** Words in more than this share of the docs say nothing about the query (07 \u00A72). */\nconst COMMON = 0.5;\n/**\n * Words in more than this share are matched in names, summaries and option names only: bm25 runs\n * over every matching doc, and \"value\" in every option description is the whole catalog (p95 gate).\n */\nconst BROAD = 0.12;\nconst TOKENIZE = \"tokenize='porter unicode61'\";\n\ntype Row = {\n id: number;\n fqcn: string;\n type: PluginType;\n collection: string;\n name: string;\n short: string;\n deprecated: number;\n meta: string;\n /** Bit i: query word i is in the name, summary or option names. */\n in_strong: number;\n /** Bit i: query word i is in the description or the option descriptions. */\n in_text: number;\n rank: number | null;\n};\n\nconst COLUMNS = 'p.id, p.fqcn, p.type, p.collection, p.name, p.short, p.deprecated, p.meta';\n\ntype Sqlite = typeof import('node:sqlite');\nlet sqlite: Sqlite | null | undefined;\n\n/**\n * node:sqlite, loaded on first use (AFD-171). It needs Node 22.13 (VS Code 1.101); editors built\n * on an older Electron (Kiro 0.1, older forks) run the extension without the module catalog\n * instead of failing to load it. getBuiltinModule, not require: the same code runs bundled and\n * under Vitest; Node before 20.16 has no getBuiltinModule and no SQLite either.\n */\nfunction loadSqlite(): Sqlite | null {\n if (sqlite === undefined) {\n const get = (process as { getBuiltinModule?: (id: string) => unknown }).getBuiltinModule;\n try {\n sqlite = (get?.call(process, 'node:sqlite') as Sqlite | undefined) ?? null;\n } catch {\n sqlite = null;\n }\n }\n return sqlite;\n}\n\nexport function sqliteAvailable(): boolean {\n return loadSqlite() !== null;\n}\n\n/** Why the catalog is off in this editor, for the status line and errors. */\nexport function sqliteMissing(): string {\n return `The module catalog needs an editor running Node.js 22.13 or later (VS Code 1.101+); this one runs Node.js ${process.versions.node}`;\n}\n\nfunction database(path: string, options?: { readOnly?: boolean }): DatabaseSync {\n const s = loadSqlite();\n if (!s) throw new Error(sqliteMissing());\n // An explicit undefined options argument is refused (\"must be an object\").\n return options ? new s.DatabaseSync(path, options) : new s.DatabaseSync(path);\n}\n\n/** `(instr(col, ?) > 0) << 0 | \u2026` per query word: coverage computed in SQLite, not by shipping text. */\nfunction hits(column: string, words: number): string {\n return (\n Array.from({ length: words }, (_, i) => `((instr(p.${column}, ?) > 0) << ${i})`).join(' | ') ||\n '0'\n );\n}\n\nfunction words(text: string): string {\n return text.replaceAll(/[._-]+/g, ' ');\n}\n\nexport class CatalogIndex {\n private readonly total: number;\n private readonly df: StatementSync;\n private readonly named: StatementSync;\n private readonly docOf: StatementSync;\n private readonly routeOf: StatementSync;\n private readonly textOf: StatementSync;\n private readonly searches = new Map<string, StatementSync>();\n /** A scratch FTS table whose vocabulary gives the Porter stems the index uses. */\n private readonly stemmer = database(':memory:');\n private readonly stemSql: { clear: StatementSync; add: StatementSync; read: StatementSync };\n\n private constructor(private readonly db: DatabaseSync) {\n this.total = (db.prepare('SELECT count(*) AS n FROM plugins').get() as { n: number }).n;\n this.df = db.prepare('SELECT n FROM df WHERE term = ?');\n this.named = db.prepare('SELECT 1 FROM plugins WHERE name = ? LIMIT 1');\n this.docOf = db.prepare(\n 'SELECT d.doc FROM plugins p JOIN docs d ON d.id = p.id WHERE p.type = ? AND p.fqcn = ?',\n );\n this.routeOf = db.prepare('SELECT entry FROM routing WHERE type = ? AND name = ?');\n this.textOf = db.prepare('SELECT opts, text FROM plugins WHERE id = ?');\n this.stemmer.exec(`CREATE VIRTUAL TABLE t USING fts5(x, ${TOKENIZE});\n CREATE VIRTUAL TABLE v USING fts5vocab(t, 'instance');`);\n this.stemSql = {\n clear: this.stemmer.prepare('DELETE FROM t'),\n add: this.stemmer.prepare('INSERT INTO t (x) VALUES (?)'),\n read: this.stemmer.prepare('SELECT term, offset FROM v'),\n };\n }\n\n /** Builds a fresh index at `path` (':memory:' in tests) from the probe's collection files. */\n static build(path: string, files: CollectionFile[]): CatalogIndex {\n const db = database(path);\n db.exec(`\n PRAGMA journal_mode = OFF;\n PRAGMA synchronous = OFF;\n PRAGMA page_size = 16384;\n DROP TABLE IF EXISTS plugins; DROP TABLE IF EXISTS fts; DROP TABLE IF EXISTS routing;\n DROP TABLE IF EXISTS meta; DROP TABLE IF EXISTS vocab; DROP TABLE IF EXISTS docs;\n DROP TABLE IF EXISTS df;\n CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT);\n CREATE TABLE plugins (\n id INTEGER PRIMARY KEY, type TEXT, fqcn TEXT, collection TEXT, name TEXT, short TEXT,\n deprecated INTEGER, opts TEXT, strong TEXT, text TEXT, meta TEXT, UNIQUE (type, fqcn));\n CREATE INDEX plugins_name ON plugins (name);\n -- Full docs apart, so searches never page through them.\n CREATE TABLE docs (id INTEGER PRIMARY KEY, doc TEXT);\n CREATE VIRTUAL TABLE fts USING fts5(name, short, opts, body, coll, content='', ${TOKENIZE});\n CREATE VIRTUAL TABLE vocab USING fts5vocab(fts, 'row');\n -- Document frequency per stemmed term; reading it from vocab walks the term's doclist.\n CREATE TABLE df (term TEXT PRIMARY KEY, n INTEGER) WITHOUT ROWID;\n INSERT INTO fts (fts, rank) VALUES ('rank', 'bm25(${WEIGHTS.join(', ')})');\n CREATE TABLE routing (type TEXT, name TEXT, entry TEXT, PRIMARY KEY (type, name));\n `);\n const addPlugin = db.prepare(\n 'INSERT OR IGNORE INTO plugins (type, fqcn, collection, name, short, deprecated, opts, strong, text, meta) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',\n );\n const addDoc = db.prepare('INSERT INTO docs (id, doc) VALUES (?, ?)');\n const addText = db.prepare(\n 'INSERT INTO fts (rowid, name, short, opts, body, coll) VALUES (?, ?, ?, ?, ?, ?)',\n );\n const addRoute = db.prepare(\n 'INSERT OR REPLACE INTO routing (type, name, entry) VALUES (?, ?, ?)',\n );\n db.exec('BEGIN');\n for (const file of files) {\n for (const p of file.plugins) {\n const options = Object.entries(p.options ?? {});\n // What the re-rank checks query words against, without parsing the whole doc per search:\n // name, summary and option names, then the prose.\n const strong = [\n words(p.name),\n p.short_description,\n options.map(([k]) => words(k)).join(' '),\n ]\n .join(' ')\n .toLowerCase();\n const text = [\n p.description.slice(0, 1500),\n options\n .map(([, o]) => o.description)\n .join(' ')\n .slice(0, 2500),\n ]\n .join(' ')\n .toLowerCase();\n const r = addPlugin.run(\n p.type,\n p.fqcn,\n p.collection,\n p.name,\n p.short_description,\n p.deprecated ? 1 : 0,\n options.map(([k]) => k).join(' '),\n strong,\n text,\n JSON.stringify(metaOf(p)),\n );\n if (!r.changes) continue;\n addDoc.run(r.lastInsertRowid, JSON.stringify(p));\n addText.run(\n r.lastInsertRowid,\n // The local name only: \"open\"* must not match every openstack.* module.\n words(p.name),\n p.short_description,\n options.flatMap(([k, o]) => [words(k), ...(o.aliases ?? []).map(words)]).join(' '),\n [p.description, p.notes ?? '', ...options.map(([, o]) => o.description)]\n .join('\\n')\n .slice(0, 8000),\n words(p.collection),\n );\n }\n // Builtin runtime entries are short names (ios_config); collection entries are local names.\n const prefix = file.collection === 'ansible.builtin' ? '' : `${file.collection}.`;\n for (const [type, entries] of Object.entries(file.routing ?? {})) {\n for (const [name, entry] of Object.entries(entries ?? {})) {\n addRoute.run(type, `${prefix}${name}`, JSON.stringify(entry));\n if (!prefix) addRoute.run(type, `ansible.builtin.${name}`, JSON.stringify(entry));\n }\n }\n }\n db.exec('INSERT INTO df (term, n) SELECT term, doc FROM vocab');\n const set = db.prepare('INSERT INTO meta (key, value) VALUES (?, ?)');\n set.run('schema', String(INDEX_SCHEMA));\n set.run('collections', JSON.stringify(files.map((f) => `${f.collection}@${f.version}`)));\n db.exec('COMMIT');\n return new CatalogIndex(db);\n }\n\n /** Opens an index built before, or undefined when it is missing or of an older schema. */\n static open(path: string): CatalogIndex | undefined {\n try {\n const db = database(path, { readOnly: true });\n const row = db.prepare(\"SELECT value FROM meta WHERE key = 'schema'\").get() as\n | { value: string }\n | undefined;\n if (row?.value !== String(INDEX_SCHEMA)) {\n db.close();\n return undefined;\n }\n return new CatalogIndex(db);\n } catch {\n return undefined;\n }\n }\n\n close(): void {\n this.db.close();\n this.stemmer.close();\n }\n\n stats(): { plugins: number; modules: number; collections: number } {\n const count = (sql: string) => (this.db.prepare(sql).get() as { n: number }).n;\n return {\n plugins: count('SELECT count(*) AS n FROM plugins'),\n modules: count(\"SELECT count(*) AS n FROM plugins WHERE type = 'module'\"),\n collections: count('SELECT count(DISTINCT collection) AS n FROM plugins'),\n };\n }\n\n private versions?: Map<string, string>;\n\n /** Every indexed collection and its version. */\n collections(): Record<string, string> {\n this.collectionVersion('');\n return Object.fromEntries(this.versions ?? []);\n }\n\n /** The indexed version of a collection (`ansible.builtin` is ansible-core's). */\n collectionVersion(name: string): string | undefined {\n if (!this.versions) {\n const row = this.db.prepare(\"SELECT value FROM meta WHERE key = 'collections'\").get() as\n | { value: string }\n | undefined;\n const list = row ? (JSON.parse(row.value) as string[]) : [];\n this.versions = new Map(\n list.map((c) => [c.slice(0, c.indexOf('@')), c.slice(c.indexOf('@') + 1)]),\n );\n }\n return this.versions.get(name);\n }\n\n /**\n * AFD-225: the filters, tests or lookups the environment has, for the Jinja completion list:\n * ansible.builtin's by short name, a collection's by FQCN (ansible.legacy repeats builtin).\n */\n names(type: 'filter' | 'test' | 'lookup'): PluginName[] {\n const rows = this.db\n .prepare(\n \"SELECT fqcn, collection, name, short FROM plugins WHERE type = ? AND collection <> 'ansible.legacy' ORDER BY collection <> 'ansible.builtin', fqcn\",\n )\n .all(type) as { fqcn: string; collection: string; name: string; short: string | null }[];\n return rows.map((r) => ({\n name: r.collection === 'ansible.builtin' ? r.name : r.fqcn,\n fqcn: r.fqcn,\n ...(r.short ? { doc: r.short } : {}),\n }));\n }\n\n plugin(fqcn: string, type: PluginType = 'module'): CatalogPlugin | undefined {\n const row = this.docOf.get(type, fqcn) as { doc: string } | undefined;\n return row ? (JSON.parse(row.doc) as CatalogPlugin) : undefined;\n }\n\n /** The index's own Porter stems of query words (create \u2192 creat, query \u2192 queri). */\n private porter(list: string[]): string[] {\n this.stemSql.clear.run();\n this.stemSql.add.run(list.join(' '));\n const out = [...list];\n for (const r of this.stemSql.read.all() as { term: string; offset: number }[])\n out[r.offset] = r.term;\n return out;\n }\n\n /** Parameters in text order: (match | names), stems for in_strong, stems for in_text, types. */\n private statement(kind: 'fts' | 'names', words: number, types: number): StatementSync {\n const key = `${kind}:${words}:${types}`;\n let st = this.searches.get(key);\n if (!st) {\n const typed = types\n ? `p.type IN (${Array.from({ length: types }, () => '?').join(', ')})`\n : '';\n const select = `${COLUMNS}, ${hits('strong', words)} AS in_strong, ${hits('text', words)} AS in_text`;\n st = this.db.prepare(\n kind === 'fts'\n ? // FTS5 sorts by its configured rank (weighted bm25) fastest when nothing else is joined in.\n `WITH top AS (SELECT rowid, rank FROM fts WHERE fts MATCH ? ORDER BY rank LIMIT ${CANDIDATES})\n SELECT ${select}, top.rank FROM top JOIN plugins p ON p.id = top.rowid ${typed ? `WHERE ${typed}` : ''}`\n : `WITH wanted AS (SELECT value FROM json_each(?))\n SELECT ${select}, NULL AS rank FROM plugins p\n WHERE p.name IN (SELECT value FROM wanted) ${typed ? `AND ${typed}` : ''}`,\n );\n this.searches.set(key, st);\n }\n return st;\n }\n\n search(query: string, ctx: SearchContext = {}, limit = 20): SearchHit[] {\n const plan = planQuery(query);\n if (plan.words.length === 0) return [];\n // Share of plugins using each word. Common words go, except plugin names (\"create user\").\n let share = this.porter(plan.words).map(\n (s) => ((this.df.get(s) as { n: number } | undefined)?.n ?? 0) / this.total,\n );\n const keep = plan.words.map((w, i) => (share[i] ?? 0) <= COMMON || !!this.named.get(w));\n if (plan.words.length > 1 && keep.some(Boolean)) {\n plan.words = plan.words.filter((_, i) => keep[i]);\n share = share.filter((_, i) => keep[i]);\n }\n const terms = plan.words;\n const match = terms\n .map((t, i) => {\n const column = (share[i] ?? 0) > BROAD ? '{name short opts} : ' : '';\n return t.length >= 3 ? `${column}\"${t}\" OR name : \"${t}\"*` : `${column}\"${t}\"`;\n })\n .join(' OR ');\n // Plugins named by the words themselves (user, json_query, ipaddr for \"ip address\") are\n // candidates even when bm25 can't tell them apart from thousands of others.\n plan.names = nameCandidates(terms);\n const types = ctx.types?.length ? ctx.types : [];\n const crude = terms.map(stem);\n const args = [...crude, ...crude, ...types];\n const rows = [\n ...(this.statement('fts', terms.length, types.length).all(match, ...args) as Row[]),\n ...(this.statement('names', terms.length, types.length).all(\n JSON.stringify([...plan.names.keys()]),\n ...args,\n ) as Row[]),\n ];\n const best = rows.reduce((m, r) => Math.max(m, -(r.rank ?? 0)), 0);\n const seen = new Set<string>();\n const scored = rows\n .filter((r) => {\n const key = `${r.type}:${r.fqcn}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n })\n .map((r) => {\n const candidate: Candidate = {\n type: r.type,\n name: r.name,\n collection: r.collection,\n inStrong: r.in_strong,\n inText: r.in_text,\n deprecated: r.deprecated === 1,\n bm25: -(r.rank ?? 0),\n };\n return { r, score: rerank(candidate, plan, ctx, best) };\n });\n scored.sort((a, b) => b.score - a.score);\n return scored.slice(0, limit).map(({ r, score }) => {\n const badges = badgesOf(r, JSON.parse(r.meta) as PluginMeta, ctx);\n return {\n fqcn: r.fqcn,\n type: r.type,\n collection: r.collection,\n short_description: r.short,\n score: Math.round(score * 1000) / 1000,\n match: matchText(r, this.textOf.get(r.id) as { opts: string; text: string }, terms),\n badges,\n dimmed: badges.includes('other-platform'),\n };\n });\n }\n\n private route(type: string, name: string): RoutingEntry | undefined {\n const row = this.routeOf.get(type, name) as { entry: string } | undefined;\n return row ? (JSON.parse(row.entry) as RoutingEntry) : undefined;\n }\n\n /**\n * What Ansible runs for a module name (07 \u00A72): the play's collections, then ansible.builtin, then\n * redirects (at most five hops); tombstones mean removed.\n */\n resolve(name: string, collections: string[] = [], type: PluginType = 'module'): Resolution {\n const candidates =\n name.split('.').length >= 3\n ? [name]\n : [...collections.map((c) => `${c}.${name}`), `ansible.builtin.${name}`];\n let fallback: Resolution | undefined;\n for (const start of candidates) {\n const hops = [start];\n let current = start;\n let deprecated: string | undefined;\n for (let i = 0; i <= 5; i++) {\n if (this.docOf.get(type, current)) return { fqcn: current, hops, deprecated, found: true };\n const r = this.route(type, current);\n if (!r) break;\n if (r.deprecation)\n deprecated =\n r.deprecation.warning_text || `deprecated (${r.deprecation.removal_version})`;\n if (r.tombstone) {\n fallback ??= {\n hops,\n removed: r.tombstone.warning_text || `removed in ${r.tombstone.removal_version}`,\n found: false,\n };\n break;\n }\n if (!r.redirect) break;\n current = r.redirect;\n hops.push(current);\n }\n }\n return fallback ?? { hops: [name], found: false };\n }\n}\n\n/** \"name: vlans \u00B7 option `vlan_id`\": where the query words were found, for the result list. */\nfunction matchText(\n r: Pick<Row, 'name' | 'short'>,\n more: { opts: string; text: string },\n terms: string[],\n): string {\n const where: string[] = [];\n for (const t of terms) {\n const s = stem(t);\n if (r.name.toLowerCase().includes(s)) where.push(`name: ${t}`);\n else if (r.short.toLowerCase().includes(s)) where.push(`summary: ${t}`);\n else {\n const opt = more.opts.split(' ').find((k) => k.toLowerCase().includes(s));\n if (opt) where.push(`option \\`${opt}\\``);\n else if (more.text.includes(s)) where.push(`description: ${t}`);\n }\n }\n return [...new Set(where)].join(' \u00B7 ');\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["words"]
7
+ }
@@ -0,0 +1,48 @@
1
+ // src/node/run/exec.ts
2
+ import { spawn } from "node:child_process";
3
+ function startProcess(spec, env, log) {
4
+ const group = process.platform !== "win32";
5
+ const feed = spec.input?.length ? spec.input : void 0;
6
+ const child = spawn(spec.command, spec.args, {
7
+ cwd: spec.cwd,
8
+ env: { ...process.env, ...env },
9
+ stdio: [feed ? "pipe" : "ignore", "pipe", "pipe"],
10
+ windowsHide: true,
11
+ detached: group
12
+ });
13
+ if (feed && child.stdin) {
14
+ child.stdin.on("error", () => void 0);
15
+ for (const name of feed)
16
+ child.stdin.write(`${Buffer.from(env[name] ?? "", "utf8").toString("base64")}
17
+ `);
18
+ child.stdin.end();
19
+ }
20
+ const lines = (chunk) => {
21
+ for (const line of chunk.toString().split(/\r?\n/)) if (line.trim()) log(line);
22
+ };
23
+ child.stdout?.on("data", lines);
24
+ child.stderr?.on("data", lines);
25
+ const exit = new Promise((resolve) => {
26
+ child.on("error", (e) => {
27
+ log(e.message);
28
+ resolve(void 0);
29
+ });
30
+ child.on("close", (code) => resolve(code ?? void 0));
31
+ });
32
+ return {
33
+ exit,
34
+ terminate() {
35
+ try {
36
+ if (group && child.pid) process.kill(-child.pid, "SIGTERM");
37
+ else child.kill();
38
+ } catch {
39
+ child.kill();
40
+ }
41
+ }
42
+ };
43
+ }
44
+
45
+ export {
46
+ startProcess
47
+ };
48
+ //# sourceMappingURL=chunk-I6FIOP7G.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/node/run/exec.ts"],
4
+ "sourcesContent": ["// Starts the process of a run, a scenario or a check (AFD-271: one copy for the scenario runner\n// and for hosts that run flows without a terminal).\nimport { spawn } from 'node:child_process';\nimport type { ProcessSpec } from '../../core/run/command';\n\n/** A process that was started: how it ended, and how to end it. */\nexport interface Execution {\n /** The exit code; undefined when the process could not start or ended without one. */\n exit: Promise<number | undefined>;\n /** Ends the process. What it started beyond itself (a container) is ended with `stopStarted`. */\n terminate(): void;\n}\n\n/**\n * Runs `spec` with `env` on top of this process's environment, feeding its output to `log` line\n * by line. AFD-247: a spec with `input` names the variables whose values go to the process's\n * input, one base64 line each, instead of its arguments (what afd-ssh-run does for a terminal).\n */\nexport function startProcess(\n spec: ProcessSpec,\n env: Record<string, string>,\n log: (line: string) => void,\n): Execution {\n // AFD-131: on Linux and macOS the run gets a process group of its own, so a stop ends\n // ansible-playbook too, not only the shell that started it.\n const group = process.platform !== 'win32';\n const feed = spec.input?.length ? spec.input : undefined;\n const child = spawn(spec.command, spec.args, {\n cwd: spec.cwd,\n env: { ...process.env, ...env },\n stdio: [feed ? 'pipe' : 'ignore', 'pipe', 'pipe'],\n windowsHide: true,\n detached: group,\n });\n if (feed && child.stdin) {\n child.stdin.on('error', () => undefined);\n for (const name of feed)\n child.stdin.write(`${Buffer.from(env[name] ?? '', 'utf8').toString('base64')}\\n`);\n child.stdin.end();\n }\n const lines = (chunk: Buffer) => {\n for (const line of chunk.toString().split(/\\r?\\n/)) if (line.trim()) log(line);\n };\n child.stdout?.on('data', lines);\n child.stderr?.on('data', lines);\n const exit = new Promise<number | undefined>((resolve) => {\n child.on('error', (e) => {\n log(e.message);\n resolve(undefined);\n });\n child.on('close', (code) => resolve(code ?? undefined));\n });\n return {\n exit,\n terminate() {\n try {\n if (group && child.pid) process.kill(-child.pid, 'SIGTERM');\n else child.kill();\n } catch {\n child.kill();\n }\n },\n };\n}\n"],
5
+ "mappings": ";AAEA,SAAS,aAAa;AAgBf,SAAS,aACd,MACA,KACA,KACW;AAGX,QAAM,QAAQ,QAAQ,aAAa;AACnC,QAAM,OAAO,KAAK,OAAO,SAAS,KAAK,QAAQ;AAC/C,QAAM,QAAQ,MAAM,KAAK,SAAS,KAAK,MAAM;AAAA,IAC3C,KAAK,KAAK;AAAA,IACV,KAAK,EAAE,GAAG,QAAQ,KAAK,GAAG,IAAI;AAAA,IAC9B,OAAO,CAAC,OAAO,SAAS,UAAU,QAAQ,MAAM;AAAA,IAChD,aAAa;AAAA,IACb,UAAU;AAAA,EACZ,CAAC;AACD,MAAI,QAAQ,MAAM,OAAO;AACvB,UAAM,MAAM,GAAG,SAAS,MAAM,MAAS;AACvC,eAAW,QAAQ;AACjB,YAAM,MAAM,MAAM,GAAG,OAAO,KAAK,IAAI,IAAI,KAAK,IAAI,MAAM,EAAE,SAAS,QAAQ,CAAC;AAAA,CAAI;AAClF,UAAM,MAAM,IAAI;AAAA,EAClB;AACA,QAAM,QAAQ,CAAC,UAAkB;AAC/B,eAAW,QAAQ,MAAM,SAAS,EAAE,MAAM,OAAO,EAAG,KAAI,KAAK,KAAK,EAAG,KAAI,IAAI;AAAA,EAC/E;AACA,QAAM,QAAQ,GAAG,QAAQ,KAAK;AAC9B,QAAM,QAAQ,GAAG,QAAQ,KAAK;AAC9B,QAAM,OAAO,IAAI,QAA4B,CAAC,YAAY;AACxD,UAAM,GAAG,SAAS,CAAC,MAAM;AACvB,UAAI,EAAE,OAAO;AACb,cAAQ,MAAS;AAAA,IACnB,CAAC;AACD,UAAM,GAAG,SAAS,CAAC,SAAS,QAAQ,QAAQ,MAAS,CAAC;AAAA,EACxD,CAAC;AACD,SAAO;AAAA,IACL;AAAA,IACA,YAAY;AACV,UAAI;AACF,YAAI,SAAS,MAAM,IAAK,SAAQ,KAAK,CAAC,MAAM,KAAK,SAAS;AAAA,YACrD,OAAM,KAAK;AAAA,MAClB,QAAQ;AACN,cAAM,KAAK;AAAA,MACb;AAAA,IACF;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,44 @@
1
+ // src/node/timeouts.ts
2
+ var TIMEOUTS = {
3
+ /** The probe's first answer: the first start may pull the image. */
4
+ probeReady: 18e4,
5
+ /** A probe call without a timeout of its own. */
6
+ probeCall: 6e4,
7
+ /** The inventory read for a run's confirmation or the Inventory lens. */
8
+ probeInventory: 12e4,
9
+ /** The roles of a repository (catalog, Code mode). */
10
+ probeRoles: 12e4,
11
+ /** Compiling the generated code and roles (Code mode, the syntax check). */
12
+ probeCompile: 18e4,
13
+ /** Reading every collection's docs for the module catalog. */
14
+ catalogDump: 15 * 6e4,
15
+ /** Indexing the collection docs in a worker (seconds for a full environment; AFD-163). */
16
+ catalogIndex: 5 * 6e4,
17
+ /** One AWX REST request without data (idle). */
18
+ awxRequest: 3e4,
19
+ /** One AWX REST request from start to end, however slowly the data comes (AFD-161). */
20
+ awxRequestTotal: 12e4,
21
+ /** Between two polls of a running AWX job, workflow or project update. */
22
+ awxPoll: 2e3,
23
+ /** A finished AWX job's events keep arriving for a while. */
24
+ awxEventsGrace: 2e4,
25
+ /** An AWX project update before a template is set up. */
26
+ awxProjectSync: 18e4,
27
+ /** AFD-276: one Ansible Flow Server request without data. */
28
+ runnerRequest: 3e4,
29
+ /** AFD-276: between two looks at a run on the server. */
30
+ runnerPoll: 2e3,
31
+ /** A designer panel loading (PNG export opens one). */
32
+ designerReady: 3e4,
33
+ /** A designer panel drawing a PNG. */
34
+ designerPng: 6e4,
35
+ /** Test mode: a designer panel's DOM summary. */
36
+ designerInspect: 1e4,
37
+ /** Typing in a text editor settles before the designer parses again. */
38
+ typingSettles: 150
39
+ };
40
+
41
+ export {
42
+ TIMEOUTS
43
+ };
44
+ //# sourceMappingURL=chunk-IIFP6UQR.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/node/timeouts.ts"],
4
+ "sourcesContent": ["// How long the extension waits for things outside it (AFD-39: one place instead of constants in\n// a dozen files). Values in milliseconds.\n\nexport const TIMEOUTS = {\n /** The probe's first answer: the first start may pull the image. */\n probeReady: 180_000,\n /** A probe call without a timeout of its own. */\n probeCall: 60_000,\n /** The inventory read for a run's confirmation or the Inventory lens. */\n probeInventory: 120_000,\n /** The roles of a repository (catalog, Code mode). */\n probeRoles: 120_000,\n /** Compiling the generated code and roles (Code mode, the syntax check). */\n probeCompile: 180_000,\n /** Reading every collection's docs for the module catalog. */\n catalogDump: 15 * 60_000,\n /** Indexing the collection docs in a worker (seconds for a full environment; AFD-163). */\n catalogIndex: 5 * 60_000,\n /** One AWX REST request without data (idle). */\n awxRequest: 30_000,\n /** One AWX REST request from start to end, however slowly the data comes (AFD-161). */\n awxRequestTotal: 120_000,\n /** Between two polls of a running AWX job, workflow or project update. */\n awxPoll: 2_000,\n /** A finished AWX job's events keep arriving for a while. */\n awxEventsGrace: 20_000,\n /** An AWX project update before a template is set up. */\n awxProjectSync: 180_000,\n /** AFD-276: one Ansible Flow Server request without data. */\n runnerRequest: 30_000,\n /** AFD-276: between two looks at a run on the server. */\n runnerPoll: 2_000,\n /** A designer panel loading (PNG export opens one). */\n designerReady: 30_000,\n /** A designer panel drawing a PNG. */\n designerPng: 60_000,\n /** Test mode: a designer panel's DOM summary. */\n designerInspect: 10_000,\n /** Typing in a text editor settles before the designer parses again. */\n typingSettles: 150,\n} as const;\n"],
5
+ "mappings": ";AAGO,IAAM,WAAW;AAAA;AAAA,EAEtB,YAAY;AAAA;AAAA,EAEZ,WAAW;AAAA;AAAA,EAEX,gBAAgB;AAAA;AAAA,EAEhB,YAAY;AAAA;AAAA,EAEZ,cAAc;AAAA;AAAA,EAEd,aAAa,KAAK;AAAA;AAAA,EAElB,cAAc,IAAI;AAAA;AAAA,EAElB,YAAY;AAAA;AAAA,EAEZ,iBAAiB;AAAA;AAAA,EAEjB,SAAS;AAAA;AAAA,EAET,gBAAgB;AAAA;AAAA,EAEhB,gBAAgB;AAAA;AAAA,EAEhB,eAAe;AAAA;AAAA,EAEf,YAAY;AAAA;AAAA,EAEZ,eAAe;AAAA;AAAA,EAEf,aAAa;AAAA;AAAA,EAEb,iBAAiB;AAAA;AAAA,EAEjB,eAAe;AACjB;",
6
+ "names": []
7
+ }
@@ -0,0 +1,18 @@
1
+ // src/node/run/stop.ts
2
+ import { execFile } from "node:child_process";
3
+ function stopStarted(spec, timeoutMs = 15e3) {
4
+ const stop = spec.stop;
5
+ if (!stop) return Promise.resolve();
6
+ return new Promise((done) => {
7
+ try {
8
+ execFile(stop.command, stop.args, { timeout: timeoutMs, windowsHide: true }, () => done());
9
+ } catch {
10
+ done();
11
+ }
12
+ });
13
+ }
14
+
15
+ export {
16
+ stopStarted
17
+ };
18
+ //# sourceMappingURL=chunk-JDPN3LTT.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/node/run/stop.ts"],
4
+ "sourcesContent": ["// Ends what a run, scenario or probe process started beyond itself (AFD-131): its container. The\n// client process can be killed, but its container keeps running the playbook against the hosts.\nimport { execFile } from 'node:child_process';\nimport type { ProcessSpec } from '../../core/run/command';\n\n/** Runs `spec.stop` when it has one. Resolves once it ended or after `timeoutMs`; never rejects. */\nexport function stopStarted(spec: ProcessSpec, timeoutMs = 15_000): Promise<void> {\n const stop = spec.stop;\n if (!stop) return Promise.resolve();\n return new Promise((done) => {\n try {\n execFile(stop.command, stop.args, { timeout: timeoutMs, windowsHide: true }, () => done());\n } catch {\n done();\n }\n });\n}\n"],
5
+ "mappings": ";AAEA,SAAS,gBAAgB;AAIlB,SAAS,YAAY,MAAmB,YAAY,MAAuB;AAChF,QAAM,OAAO,KAAK;AAClB,MAAI,CAAC,KAAM,QAAO,QAAQ,QAAQ;AAClC,SAAO,IAAI,QAAQ,CAAC,SAAS;AAC3B,QAAI;AACF,eAAS,KAAK,SAAS,KAAK,MAAM,EAAE,SAAS,WAAW,aAAa,KAAK,GAAG,MAAM,KAAK,CAAC;AAAA,IAC3F,QAAQ;AACN,WAAK;AAAA,IACP;AAAA,EACF,CAAC;AACH;",
6
+ "names": []
7
+ }