koppler 0.0.0-stage → 0.0.2

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 (225) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +101 -2
  3. package/bin/run-logged +16 -0
  4. package/bin/secret-list +7 -0
  5. package/bin/secret-psql +41 -0
  6. package/bin/secret-set +17 -0
  7. package/bin/secret-store +82 -0
  8. package/bin/secret-test +10 -0
  9. package/bin/with-secrets +25 -0
  10. package/dist/.built +1 -0
  11. package/dist/cli/agent.js +144 -0
  12. package/dist/cli/agent.js.map +1 -0
  13. package/dist/cli/doctor.js +173 -0
  14. package/dist/cli/doctor.js.map +1 -0
  15. package/dist/cli/mcp.js +204 -0
  16. package/dist/cli/mcp.js.map +1 -0
  17. package/dist/cli/output.js +37 -0
  18. package/dist/cli/output.js.map +1 -0
  19. package/dist/cli/records.js +121 -0
  20. package/dist/cli/records.js.map +1 -0
  21. package/dist/cli/running.js +270 -0
  22. package/dist/cli/running.js.map +1 -0
  23. package/dist/cli/shell.js +43 -0
  24. package/dist/cli/shell.js.map +1 -0
  25. package/dist/cli/wait.js +59 -0
  26. package/dist/cli/wait.js.map +1 -0
  27. package/dist/core/addresses.js +55 -0
  28. package/dist/core/addresses.js.map +1 -0
  29. package/dist/core/arrange.js +329 -0
  30. package/dist/core/arrange.js.map +1 -0
  31. package/dist/core/cables.js +158 -0
  32. package/dist/core/cables.js.map +1 -0
  33. package/dist/core/expressions.js +72 -0
  34. package/dist/core/expressions.js.map +1 -0
  35. package/dist/core/frameworks.js +13 -0
  36. package/dist/core/frameworks.js.map +1 -0
  37. package/dist/core/kinds.js +244 -0
  38. package/dist/core/kinds.js.map +1 -0
  39. package/dist/core/launch.js +181 -0
  40. package/dist/core/launch.js.map +1 -0
  41. package/dist/core/logfmt.js +144 -0
  42. package/dist/core/logfmt.js.map +1 -0
  43. package/dist/core/mock.js +27 -0
  44. package/dist/core/mock.js.map +1 -0
  45. package/dist/core/plan.js +147 -0
  46. package/dist/core/plan.js.map +1 -0
  47. package/dist/core/ports.js +155 -0
  48. package/dist/core/ports.js.map +1 -0
  49. package/dist/core/presets.js +186 -0
  50. package/dist/core/presets.js.map +1 -0
  51. package/dist/core/secret-types.js +3 -0
  52. package/dist/core/secret-types.js.map +1 -0
  53. package/dist/core/sign-in.js +30 -0
  54. package/dist/core/sign-in.js.map +1 -0
  55. package/dist/core/stack-path.js +64 -0
  56. package/dist/core/stack-path.js.map +1 -0
  57. package/dist/core/stack.js +509 -0
  58. package/dist/core/stack.js.map +1 -0
  59. package/dist/core/terraform.js +393 -0
  60. package/dist/core/terraform.js.map +1 -0
  61. package/dist/data.js +37 -0
  62. package/dist/data.js.map +1 -0
  63. package/dist/engine/actions.js +99 -0
  64. package/dist/engine/actions.js.map +1 -0
  65. package/dist/engine/app-commands.js +106 -0
  66. package/dist/engine/app-commands.js.map +1 -0
  67. package/dist/engine/app-start.js +167 -0
  68. package/dist/engine/app-start.js.map +1 -0
  69. package/dist/engine/app-state.js +43 -0
  70. package/dist/engine/app-state.js.map +1 -0
  71. package/dist/engine/boards.js +219 -0
  72. package/dist/engine/boards.js.map +1 -0
  73. package/dist/engine/branch.js +194 -0
  74. package/dist/engine/branch.js.map +1 -0
  75. package/dist/engine/browser-console.js +45 -0
  76. package/dist/engine/browser-console.js.map +1 -0
  77. package/dist/engine/calls.js +97 -0
  78. package/dist/engine/calls.js.map +1 -0
  79. package/dist/engine/checks.js +270 -0
  80. package/dist/engine/checks.js.map +1 -0
  81. package/dist/engine/clones.js +67 -0
  82. package/dist/engine/clones.js.map +1 -0
  83. package/dist/engine/config.js +26 -0
  84. package/dist/engine/config.js.map +1 -0
  85. package/dist/engine/deployed.js +488 -0
  86. package/dist/engine/deployed.js.map +1 -0
  87. package/dist/engine/devices.js +81 -0
  88. package/dist/engine/devices.js.map +1 -0
  89. package/dist/engine/discover.js +236 -0
  90. package/dist/engine/discover.js.map +1 -0
  91. package/dist/engine/down.js +253 -0
  92. package/dist/engine/down.js.map +1 -0
  93. package/dist/engine/history.js +215 -0
  94. package/dist/engine/history.js.map +1 -0
  95. package/dist/engine/investigate.js +278 -0
  96. package/dist/engine/investigate.js.map +1 -0
  97. package/dist/engine/libraries.js +129 -0
  98. package/dist/engine/libraries.js.map +1 -0
  99. package/dist/engine/linked.js +146 -0
  100. package/dist/engine/linked.js.map +1 -0
  101. package/dist/engine/listen.js +223 -0
  102. package/dist/engine/listen.js.map +1 -0
  103. package/dist/engine/locks.js +38 -0
  104. package/dist/engine/locks.js.map +1 -0
  105. package/dist/engine/messages.js +177 -0
  106. package/dist/engine/messages.js.map +1 -0
  107. package/dist/engine/needs.js +363 -0
  108. package/dist/engine/needs.js.map +1 -0
  109. package/dist/engine/processes.js +350 -0
  110. package/dist/engine/processes.js.map +1 -0
  111. package/dist/engine/query.js +241 -0
  112. package/dist/engine/query.js.map +1 -0
  113. package/dist/engine/queues.js +100 -0
  114. package/dist/engine/queues.js.map +1 -0
  115. package/dist/engine/recipes.js +248 -0
  116. package/dist/engine/recipes.js.map +1 -0
  117. package/dist/engine/reload.js +32 -0
  118. package/dist/engine/reload.js.map +1 -0
  119. package/dist/engine/repos.js +214 -0
  120. package/dist/engine/repos.js.map +1 -0
  121. package/dist/engine/research.js +134 -0
  122. package/dist/engine/research.js.map +1 -0
  123. package/dist/engine/run-logs.js +79 -0
  124. package/dist/engine/run-logs.js.map +1 -0
  125. package/dist/engine/run.js +54 -0
  126. package/dist/engine/run.js.map +1 -0
  127. package/dist/engine/runs.js +223 -0
  128. package/dist/engine/runs.js.map +1 -0
  129. package/dist/engine/session.js +130 -0
  130. package/dist/engine/session.js.map +1 -0
  131. package/dist/engine/share.js +124 -0
  132. package/dist/engine/share.js.map +1 -0
  133. package/dist/engine/stack-file.js +206 -0
  134. package/dist/engine/stack-file.js.map +1 -0
  135. package/dist/engine/stack-share.js +86 -0
  136. package/dist/engine/stack-share.js.map +1 -0
  137. package/dist/engine/stacks.js +143 -0
  138. package/dist/engine/stacks.js.map +1 -0
  139. package/dist/engine/sync.js +214 -0
  140. package/dist/engine/sync.js.map +1 -0
  141. package/dist/engine/traces.js +65 -0
  142. package/dist/engine/traces.js.map +1 -0
  143. package/dist/engine/up.js +621 -0
  144. package/dist/engine/up.js.map +1 -0
  145. package/dist/engine/watch.js +71 -0
  146. package/dist/engine/watch.js.map +1 -0
  147. package/dist/engine/wire.js +120 -0
  148. package/dist/engine/wire.js.map +1 -0
  149. package/dist/failure.js +16 -0
  150. package/dist/failure.js.map +1 -0
  151. package/dist/github.js +95 -0
  152. package/dist/github.js.map +1 -0
  153. package/dist/ids.js +6 -0
  154. package/dist/ids.js.map +1 -0
  155. package/dist/json-files.js +36 -0
  156. package/dist/json-files.js.map +1 -0
  157. package/dist/koppler.mjs +840 -0
  158. package/dist/koppler.mjs.map +1 -0
  159. package/dist/lambda-local.mjs +250 -0
  160. package/dist/lambda-local.mjs.map +1 -0
  161. package/dist/lambda-secrets-proxy.go +31 -0
  162. package/dist/mock-server.mjs +23 -0
  163. package/dist/mock-server.mjs.map +1 -0
  164. package/dist/page/assets/JetBrainsMono-cyrillic-D2jMJ_bk.woff2 +0 -0
  165. package/dist/page/assets/JetBrainsMono-greek-D2nYnAec.woff2 +0 -0
  166. package/dist/page/assets/JetBrainsMono-latin-Db4Uuiha.woff2 +0 -0
  167. package/dist/page/assets/JetBrainsMono-latin-ext-Bnd2h4Br.woff2 +0 -0
  168. package/dist/page/assets/JetBrainsMono-vietnamese-CNlsqbWb.woff2 +0 -0
  169. package/dist/page/assets/SchibstedGrotesk-latin-DxefFeRw.woff2 +0 -0
  170. package/dist/page/assets/SchibstedGrotesk-latin-ext-CnpJE18X.woff2 +0 -0
  171. package/dist/page/assets/index-Bkj_pX35.js +42 -0
  172. package/dist/page/assets/index-Dq45jswH.css +1 -0
  173. package/dist/page/index.html +14 -0
  174. package/dist/page/theme.js +8 -0
  175. package/dist/paths.js +19 -0
  176. package/dist/paths.js.map +1 -0
  177. package/dist/safe-env.js +73 -0
  178. package/dist/safe-env.js.map +1 -0
  179. package/dist/sam-local.mjs +135 -0
  180. package/dist/sam-local.mjs.map +1 -0
  181. package/dist/scrub.js +87 -0
  182. package/dist/scrub.js.map +1 -0
  183. package/dist/secret-check.mjs +262 -0
  184. package/dist/secret-check.mjs.map +1 -0
  185. package/dist/server/browser-console.js +117 -0
  186. package/dist/server/browser-console.js.map +1 -0
  187. package/dist/server/cable-test.js +365 -0
  188. package/dist/server/cable-test.js.map +1 -0
  189. package/dist/server/choices.js +51 -0
  190. package/dist/server/choices.js.map +1 -0
  191. package/dist/server/files.js +107 -0
  192. package/dist/server/files.js.map +1 -0
  193. package/dist/server/gate.js +128 -0
  194. package/dist/server/gate.js.map +1 -0
  195. package/dist/server/logs.js +76 -0
  196. package/dist/server/logs.js.map +1 -0
  197. package/dist/server/mobile.js +113 -0
  198. package/dist/server/mobile.js.map +1 -0
  199. package/dist/server/routes.js +326 -0
  200. package/dist/server/routes.js.map +1 -0
  201. package/dist/server/runs.js +111 -0
  202. package/dist/server/runs.js.map +1 -0
  203. package/dist/server/sanitize.js +242 -0
  204. package/dist/server/sanitize.js.map +1 -0
  205. package/dist/server/secrets.js +111 -0
  206. package/dist/server/secrets.js.map +1 -0
  207. package/dist/server/status.js +321 -0
  208. package/dist/server/status.js.map +1 -0
  209. package/dist/server/token.js +38 -0
  210. package/dist/server/token.js.map +1 -0
  211. package/dist/server/waiting.js +79 -0
  212. package/dist/server/waiting.js.map +1 -0
  213. package/dist/status-watch.mjs +63 -0
  214. package/dist/status-watch.mjs.map +1 -0
  215. package/package.json +74 -4
  216. package/skills/koppler-run/SKILL.md +169 -0
  217. package/skills/koppler-run/VERSION +1 -0
  218. package/skills/koppler-run/references/action-log.md +50 -0
  219. package/skills/koppler-run/references/permissions.md +25 -0
  220. package/skills/koppler-run/references/preparing-nodes.md +62 -0
  221. package/skills/koppler-run/references/reporting-bugs.md +76 -0
  222. package/skills/koppler-run/references/research.md +40 -0
  223. package/skills/koppler-run/references/secrets.md +56 -0
  224. package/skills/koppler-run/references/stack.md +197 -0
  225. package/skills/koppler-run/references/status.md +92 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checks.js","sourceRoot":"","sources":["../../src/engine/checks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ // Brings each clone up to date: fetches, and fast-forwards a clean branch that is only behind.
2
+ import { execFile } from 'node:child_process';
3
+ import { readData, writeData } from "../data.js";
4
+ import { lines } from "./calls.js";
5
+ import { branchesOf } from "./repos.js";
6
+ /** What to do with a clone after fetching: fast-forward only a clean branch that is simply behind its upstream. */
7
+ export function updateAction({ upstream, dirty, ahead, behind, }) {
8
+ if (!upstream)
9
+ return 'no-upstream';
10
+ if (!behind)
11
+ return 'current';
12
+ if (ahead)
13
+ return 'diverged';
14
+ return dirty ? 'dirty' : 'fast-forward';
15
+ }
16
+ // No password prompts from a background fetch; a slow remote gives up after a minute.
17
+ const gitQuietly = (cwd, ...args) => new Promise(resolve => {
18
+ const options = {
19
+ cwd,
20
+ encoding: 'utf8',
21
+ timeout: 60000,
22
+ env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
23
+ };
24
+ execFile('git', args, options, (error, out, errOut) => {
25
+ resolve({ ok: !error, out: out.trim(), err: (errOut || error?.message || '').trim().split('\n').pop() });
26
+ });
27
+ });
28
+ /**
29
+ * Fetches every clone of the stack (all at once) and fast-forwards the checked-out branch where that loses nothing.
30
+ * Never commits, stashes, discards or switches branches; uncommitted or diverged work is only reported.
31
+ */
32
+ export async function updateClones() {
33
+ const local = readData('local');
34
+ const all = (local?.['apps'] ?? {});
35
+ const apps = Object.entries(all).flatMap(([id, clone]) => (clone?.path ? [[id, clone]] : []));
36
+ if (!apps.length)
37
+ return;
38
+ console.log(`FETCH ${String(apps.length)} clones`);
39
+ await Promise.all(apps.map(async ([id, clone]) => {
40
+ const run = (...args) => gitQuietly(clone.path, ...args);
41
+ const fetched = await run('fetch', '--prune', '--quiet');
42
+ if (!fetched.ok) {
43
+ console.log(`NOFETCH ${id}: ${fetched.err || 'git fetch failed'} (working from what is already on this Mac)`);
44
+ return;
45
+ }
46
+ const upstream = (await run('rev-parse', '--abbrev-ref', '@{u}')).out || null;
47
+ const counted = upstream ? (await run('rev-list', '--left-right', '--count', 'HEAD...@{u}')).out : '0 0';
48
+ const [ahead = 0, behind = 0] = counted.split(/\s+/).map(Number);
49
+ const dirty = lines((await run('status', '--porcelain')).out).length;
50
+ const action = updateAction({ upstream, dirty, ahead, behind });
51
+ const lag = `${String(clone.branch)} is ${String(behind)} behind`;
52
+ if (action === 'fast-forward') {
53
+ const merged = await run('merge', '--ff-only', '--quiet', '@{u}');
54
+ console.log(merged.ok
55
+ ? `UPDATED ${id}: ${String(clone.branch)} +${String(behind)} from ${String(upstream)}`
56
+ : `BEHIND ${id}: ${lag} ${String(upstream)}, fast-forward failed: ${String(merged.err)}`);
57
+ }
58
+ else if (action === 'dirty')
59
+ console.log(`BEHIND ${id}: ${lag} ${String(upstream)}; not updated because of uncommitted changes`);
60
+ else if (action === 'diverged')
61
+ console.log(`BEHIND ${id}: ${lag} and ${String(ahead)} ahead of ${String(upstream)}; not updated (diverged)`);
62
+ clone.branches = branchesOf(clone.path);
63
+ clone.dirty = dirty;
64
+ }));
65
+ writeData('local', { ...local, at: new Date().toISOString() });
66
+ }
67
+ //# sourceMappingURL=clones.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clones.js","sourceRoot":"","sources":["../../src/engine/clones.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ // Koppler's own config (which stack, where to look for repos) and the index of clones the last scan found.
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { readJson, writeJson } from "../json-files.js";
5
+ import { Failure } from "../failure.js";
6
+ import { configDir } from "../paths.js";
7
+ export const CONFIG = path.join(configDir(), 'config.json');
8
+ export const INDEX = path.join(configDir(), 'repos.json');
9
+ export const expand = (given) => path.resolve(given.replace(/^~/, os.homedir()));
10
+ export const tilde = (full) => (full.startsWith(os.homedir()) ? '~' + full.slice(os.homedir().length) : full);
11
+ // Every failure here exits 1, a usage line too.
12
+ export function fail(message) {
13
+ throw new Failure(message, 1);
14
+ }
15
+ export const readIndex = () => readJson(INDEX);
16
+ export const saveConfig = config => {
17
+ writeJson(CONFIG, config);
18
+ };
19
+ export function loadConfig() {
20
+ const config = readJson(CONFIG) ?? {};
21
+ if (config.root && !config.roots)
22
+ config.roots = [config.root];
23
+ delete config.root;
24
+ return config;
25
+ }
26
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/engine/config.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAC3G,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAiDxC,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,aAAa,CAAC,CAAC;AAE5D,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,YAAY,CAAC,CAAC;AAE1D,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AAEzF,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAEtH,gDAAgD;AAChD,MAAM,UAAU,IAAI,CAAC,OAAe;IAClC,MAAM,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAiB,CAAC;AAE/D,MAAM,CAAC,MAAM,UAAU,GAAS,MAAM,CAAC,EAAE;IACvC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAC5B,CAAC,CAAC;AAEF,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAI,QAAQ,CAAC,MAAM,CAAmB,IAAI,EAAE,CAAC;IACzD,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK;QAAE,MAAM,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/D,OAAO,MAAM,CAAC,IAAI,CAAC;IACnB,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,488 @@
1
+ // Reads the Terraform in every stack app's clone and drafts what stack.json needs: each app's deployed address,
2
+ // the names of its settings and secrets, and the apps it calls. Only names and plain addresses are read out, never other values.
3
+ // The draft goes to temp/stack.deployed.json; the shared stack is never changed.
4
+ // Usage: koppler discover --deployed [app...]
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { fail } from "../failure.js";
8
+ import { readClones, readStack } from "../data.js";
9
+ import { tempDir } from "../paths.js";
10
+ import { findFiles } from "./investigate.js";
11
+ import { hostsByApp } from "./calls.js";
12
+ import { writeJson } from "../json-files.js";
13
+ import { appOn } from "../core/stack.js";
14
+ import { ENV_TOKEN, hostOf, readModule, samDomains, stackEnv, tokenIn, } from "../core/terraform.js";
15
+ /** Every Terraform folder of a clone, read per environment; a SAM app's samconfig.toml adds its domains. */
16
+ export function readRepo(repo, workspaces = []) {
17
+ const byDir = new Map();
18
+ for (const file of findFiles(repo, name => /\.(tf|tfvars)$/.test(name), 7)) {
19
+ let text;
20
+ try {
21
+ text = fs.readFileSync(file, 'utf8');
22
+ }
23
+ catch {
24
+ continue;
25
+ }
26
+ const dir = path.dirname(file);
27
+ byDir.set(dir, [...(byDir.get(dir) ?? []), { name: path.basename(file), text }]);
28
+ }
29
+ const terraform = [...byDir]
30
+ .filter(([, files]) => files.some(file => file.name.endsWith('.tf')))
31
+ .flatMap(([dir, files]) => readModule(files, workspaces).map(module => ({ ...module, dir: path.relative(repo, dir) })));
32
+ let sam = [];
33
+ try {
34
+ sam = samDomains(fs.readFileSync(path.join(repo, 'samconfig.toml'), 'utf8')).map(({ token, domains }) => ({
35
+ token,
36
+ account: null,
37
+ domains,
38
+ settings: [],
39
+ secrets: [],
40
+ queues: [],
41
+ dir: 'samconfig.toml',
42
+ }));
43
+ }
44
+ catch {
45
+ // Not a SAM app.
46
+ }
47
+ return [...terraform, ...sam];
48
+ }
49
+ const RANK = { api: 0, cdn: 1, dns: 2 };
50
+ const deployedUrlOf = (stack, id, env) => appOn(stack, id, env)?.deployedUrl;
51
+ const union = (a, b) => [...new Set([...(a ?? []), ...b])];
52
+ /** The draft for one app: per stack environment its address, account, settings, secrets and calls. */
53
+ export function draftApp(id, modules, stack, owners) {
54
+ const gathered = {};
55
+ // What a repo says under the environment's own name wins over what it says under an alias.
56
+ const own = (module) => stack.envs.includes(module.token ?? '');
57
+ for (const module of [...modules].sort((a, b) => Number(own(a)) - Number(own(b)))) {
58
+ const env = stackEnv(module.token, stack);
59
+ if (!env)
60
+ continue;
61
+ const into = (gathered[env] ??= {
62
+ domains: [],
63
+ settings: new Map(),
64
+ secrets: new Map(),
65
+ accounts: new Set(),
66
+ queues: new Map(),
67
+ });
68
+ if (own(module) && module.domains.length)
69
+ into.domains = into.domains.filter(domain => domain.own);
70
+ into.domains.push(...module.domains.map(domain => ({ ...domain, own: own(module) })));
71
+ if (module.account)
72
+ into.accounts.add(module.account);
73
+ for (const setting of module.settings) {
74
+ const had = into.settings.get(setting.name);
75
+ into.settings.set(setting.name, { ...had, ...setting, address: setting.address ?? had?.address ?? null });
76
+ }
77
+ for (const secret of module.secrets)
78
+ into.secrets.set(`${secret.store}:${secret.name ?? secret.raw}`, secret);
79
+ for (const queue of module.queues ?? []) {
80
+ const key = queue.name ?? `${module.dir ?? ''}:${queue.address}`;
81
+ const had = into.queues.get(key);
82
+ into.queues.set(key, {
83
+ type: queue.type,
84
+ name: queue.name,
85
+ address: queue.address,
86
+ reads: had?.reads || queue.reads,
87
+ sends: had?.sends || queue.sends,
88
+ env: union(had?.env, queue.env).sort(),
89
+ fedBy: union(had?.fedBy, queue.fedBy),
90
+ });
91
+ }
92
+ }
93
+ return Object.fromEntries(Object.entries(gathered).map(([env, found]) => {
94
+ const ranked = found.domains.sort((a, b) => RANK[a.from] - RANK[b.from]);
95
+ const domains = [...new Map(ranked.map(domain => [domain.host, domain])).values()];
96
+ const settings = [...found.settings.values()];
97
+ const calls = settings.flatMap(({ name, address }) => {
98
+ const callee = address && owners.get(hostOf(address));
99
+ return callee && callee !== id ? [{ to: callee, setting: name, deployed: address }] : [];
100
+ });
101
+ // The address the stack already has, when Terraform names it too, stays the app's address.
102
+ const have = hostOf(deployedUrlOf(stack, id, env) ?? '');
103
+ domains.sort((a, b) => Number(b.host === have) - Number(a.host === have));
104
+ const [first, ...others] = domains;
105
+ const secrets = [...found.secrets.values()];
106
+ return [
107
+ env,
108
+ {
109
+ deployedUrl: first ? `https://${first.host}/` : null,
110
+ otherAddresses: others.map(domain => domain.host),
111
+ accounts: [...found.accounts],
112
+ settingsWithAddress: settings.flatMap(({ name, address, secret }) => !secret && address ? [{ name, address }] : []),
113
+ settings: settings
114
+ .filter(setting => !setting.secret)
115
+ .map(setting => setting.name)
116
+ .sort(),
117
+ secretSettings: settings
118
+ .filter(setting => setting.secret)
119
+ .map(setting => setting.name)
120
+ .sort(),
121
+ secrets: secrets.flatMap(({ store, name }) => (name ? [{ store, name }] : [])),
122
+ unnamedSecrets: secrets.filter(secret => !secret.name).length,
123
+ calls,
124
+ queues: [...found.queues.values()],
125
+ },
126
+ ];
127
+ }));
128
+ }
129
+ /**
130
+ * One entry per queue across the apps, by its name in the first environment: who sends, who reads, what feeds it.
131
+ * A queue only one app touches, with nothing outside feeding it, is internal to that app and not a link.
132
+ */
133
+ export function linkQueues(drafts, stack) {
134
+ const [first = ''] = stack.envs;
135
+ const byName = new Map();
136
+ const all = Object.entries(drafts).flatMap(([app, envs]) => Object.entries(envs).flatMap(([env, draft]) => (draft.queues ?? []).map(queue => ({ app, env, queue }))));
137
+ for (const { app, env, queue } of all) {
138
+ if (env !== first)
139
+ continue;
140
+ const key = queue.name ?? `${app}:${queue.address}`;
141
+ const entry = byName.get(key) ?? {
142
+ queue: queue.name,
143
+ address: queue.address,
144
+ type: queue.type,
145
+ users: new Map(),
146
+ fedBy: new Set(),
147
+ envs: {},
148
+ };
149
+ const user = entry.users.get(app) ?? { reads: false, sends: false, env: new Set() };
150
+ entry.users.set(app, {
151
+ reads: user.reads || queue.reads,
152
+ sends: user.sends || queue.sends,
153
+ env: new Set([...user.env, ...queue.env]),
154
+ });
155
+ for (const feeder of queue.fedBy)
156
+ entry.fedBy.add(feeder);
157
+ byName.set(key, entry);
158
+ }
159
+ // The same queue's name in the other environments, from the app that declares it.
160
+ for (const { app, env, queue } of all) {
161
+ if (env === first)
162
+ continue;
163
+ const name = drafts[app]?.[first]?.queues?.find(other => other.address === queue.address)?.name;
164
+ const entry = name ? byName.get(name) : undefined;
165
+ if (entry && queue.name && queue.name !== name)
166
+ entry.envs[env] = queue.name;
167
+ }
168
+ // A queue or topic no app sends to or reads, and nothing feeds (an alarm topic, a spare dead-letter queue), is not a link.
169
+ const used = (user) => user.reads || user.sends || user.env.size > 0;
170
+ return [...byName.values()]
171
+ .filter(entry => entry.fedBy.size || [...entry.users.values()].some(used))
172
+ .map(entry => {
173
+ const users = [...entry.users];
174
+ const owner = users[0]?.[0] ?? '';
175
+ const resourceName = (entry.address.split('.').pop() ?? '').replace(/_/g, '-');
176
+ const reads = users.filter(([, user]) => user.reads).map(([app]) => app);
177
+ // An app that only carries the address and neither sends nor reads by itself is taken to send.
178
+ const sends = users.filter(([, user]) => user.sends || (!user.reads && user.env.size)).map(([app]) => app);
179
+ // Internal: one app both sends and reads, and nothing outside feeds it. One side unknown is a link still to find.
180
+ const internal = users.length === 1 && !entry.fedBy.size && reads.length && sends.length;
181
+ return {
182
+ id: entry.queue ? queueId(entry.queue) : `${owner}-${resourceName}`,
183
+ queue: entry.queue,
184
+ type: entry.type,
185
+ envs: entry.envs,
186
+ reads,
187
+ sends,
188
+ fedBy: [...entry.fedBy],
189
+ env: Object.fromEntries(users.map(([app, user]) => [app, [...user.env].sort()])),
190
+ inside: internal ? owner : null,
191
+ address: entry.address,
192
+ owner,
193
+ };
194
+ });
195
+ }
196
+ const QUEUE_WORDS = ['sqs', 'sns', 'queue', 'topic'];
197
+ /** "bookings-sqs-queue-dev" and "user-media-thumbnail-queue" become "bookings" and "user-media-thumbnail". */
198
+ export const queueId = (name) => name
199
+ .toLowerCase()
200
+ .replace(/\.fifo$/, '')
201
+ .split(/[^a-z0-9]+/)
202
+ .filter(word => word && !ENV_TOKEN.test(`-${word}-`) && !QUEUE_WORDS.includes(word))
203
+ .join('-') || 'queue';
204
+ /** A queue the stack already has, by its id or its real name. */
205
+ export const inStack = (queue, stack) => !!stack.queues?.[queue.id] || Object.values(stack.queues ?? {}).some(own => own.queue && own.queue === queue.queue);
206
+ const titled = (id) => id
207
+ .split('-')
208
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
209
+ .join(' ');
210
+ /** What stack.json would need for the queues it does not list yet: the queue, and publishes / reads on each app. */
211
+ export function queueDraft(queues, stack) {
212
+ const out = { queues: {}, apps: {} };
213
+ for (const queue of queues) {
214
+ if (queue.inside || inStack(queue, stack))
215
+ continue;
216
+ const perEnv = Object.entries(queue.envs).map(([env, name]) => [env, { queue: name }]);
217
+ out.queues[queue.id] = {
218
+ name: titled(queue.id),
219
+ type: queue.type,
220
+ ...(queue.queue && { queue: queue.queue }),
221
+ ...(perEnv.length && { envs: Object.fromEntries(perEnv) }),
222
+ ...(queue.fedBy.length && { note: `Fed by ${queue.fedBy.join(' and ')}` }),
223
+ };
224
+ for (const [key, apps] of [
225
+ ['publishes', queue.sends],
226
+ ['reads', queue.reads],
227
+ ])
228
+ for (const app of apps) {
229
+ const env = queue.env[app];
230
+ ((out.apps[app] ??= {})[key] ??= {})[queue.id] = env?.length ? { env } : {};
231
+ }
232
+ }
233
+ return out;
234
+ }
235
+ /** Outside systems recognised by their addresses: never run locally, each environment uses its own (a sandbox below PROD). */
236
+ export const OUTSIDE = [
237
+ {
238
+ id: 'salesforce',
239
+ name: 'Salesforce',
240
+ test: /(^|\.)(my\.salesforce\.com|my\.site\.com|force\.com|salesforce-scrt\.com|my\.salesforce-sites\.com)$/i,
241
+ why: 'the Salesforce org: never runs locally; each environment uses its own org, a sandbox below PROD',
242
+ },
243
+ ];
244
+ /** Each outside system an app reaches, per stack environment: from its Terraform settings and its appsettings.<Env>.json files. */
245
+ export function outsideCalls(repo, drafts, stack) {
246
+ const found = {};
247
+ const add = (env, address, setting) => {
248
+ const host = hostOf(address);
249
+ const provider = host && OUTSIDE.find(outside => outside.test.test(host));
250
+ if (!host || !provider)
251
+ return;
252
+ const reached = ((found[provider.id] ??= {})[env] ??= { hosts: new Set(), settings: new Set() });
253
+ reached.hosts.add(host);
254
+ if (setting)
255
+ reached.settings.add(setting);
256
+ };
257
+ for (const [env, draft] of Object.entries(drafts))
258
+ for (const setting of draft.settingsWithAddress ?? [])
259
+ add(env, setting.address, setting.name);
260
+ for (const file of findFiles(repo, name => /^appsettings\.[\w-]+\.json$/i.test(name), 6)) {
261
+ const env = stackEnv(tokenIn(path.basename(file)), stack);
262
+ if (!env)
263
+ continue;
264
+ let text;
265
+ try {
266
+ text = fs.readFileSync(file, 'utf8');
267
+ }
268
+ catch {
269
+ continue;
270
+ }
271
+ for (const [, key = '', address = ''] of text.matchAll(/"([\w.:-]+)"\s*:\s*"(https?:\/\/[^"\s]+)"/g))
272
+ add(env, address, key.replace(/:/g, '__'));
273
+ }
274
+ return found;
275
+ }
276
+ const isEmpty = (value) => !Object.keys(value).length;
277
+ /** Ready to copy into stack.json: only what it does not have yet. */
278
+ function suggestionsFor(stack, drafts) {
279
+ const suggestions = {};
280
+ for (const [id, envs] of Object.entries(drafts))
281
+ for (const [env, draft] of Object.entries(envs)) {
282
+ const whole = (suggestions[id] ??= {});
283
+ const target = env === stack.envs[0] ? whole : ((whole.envs ??= {})[env] ??= {});
284
+ if (draft.deployedUrl && !deployedUrlOf(stack, id, env))
285
+ target.deployedUrl = draft.deployedUrl;
286
+ for (const call of draft.calls)
287
+ if (!stack.apps[id]?.consumes?.[call.to])
288
+ (target.consumes ??= {})[call.to] = { how: 'env', env: [call.setting], deployed: call.deployed };
289
+ }
290
+ // An environment, and then an app, with nothing new is left out; the rest keeps its order.
291
+ const withSomething = (from) => Object.fromEntries(Object.entries(from).filter(([, value]) => !isEmpty(value)));
292
+ for (const [id, whole] of Object.entries(suggestions)) {
293
+ const envs = withSomething(whole.envs ?? {});
294
+ const kept = Object.entries(whole).flatMap(([key, value]) => key !== 'envs' ? [[key, value]] : isEmpty(envs) ? [] : [[key, envs]]);
295
+ suggestions[id] = Object.fromEntries(kept);
296
+ }
297
+ return withSomething(suggestions);
298
+ }
299
+ export function deployed(args = []) {
300
+ const stack = readStack() ?? fail('no stack loaded (koppler init)');
301
+ const clones = readClones();
302
+ const ids = args.filter(arg => !arg.startsWith('--'));
303
+ for (const id of ids)
304
+ if (!stack.apps[id])
305
+ fail(`${id} is not an app in the stack`);
306
+ const nameOf = (id) => String(stack.apps[id]?.name);
307
+ const cloned = Object.keys(stack.apps).flatMap((id) => {
308
+ const repo = clones[id]?.path;
309
+ return repo ? [[id, repo]] : [];
310
+ });
311
+ const workspaces = [...new Set(stack.envs.flatMap(env => [env, ...(stack.envAliases?.[env] ?? [])]))];
312
+ const repos = Object.fromEntries(cloned.map(([id, repo]) => [id, readRepo(repo, workspaces)]));
313
+ // Hosts the stack already knows, plus the addresses drafted here, so calls to apps without an address yet are found too.
314
+ const owners = new Map(Object.entries(hostsByApp(stack)).flatMap(([id, hosts]) => [...hosts].map((host) => [host, id])));
315
+ const draftAll = () => Object.fromEntries(Object.entries(repos).map(([id, modules]) => [id, draftApp(id, modules, stack, owners)]));
316
+ for (const [id, envs] of Object.entries(draftAll()))
317
+ for (const draft of Object.values(envs)) {
318
+ const host = draft.deployedUrl && hostOf(draft.deployedUrl);
319
+ if (host && !owners.has(host))
320
+ owners.set(host, id);
321
+ }
322
+ const drafts = draftAll();
323
+ for (const id of ids.length ? ids : Object.keys(stack.apps)) {
324
+ const app = stack.apps[id];
325
+ const envs = drafts[id] ?? {};
326
+ const modules = repos[id];
327
+ console.log(`\n${nameOf(id)} (${id})`);
328
+ if (!modules) {
329
+ console.log(' not cloned');
330
+ continue;
331
+ }
332
+ if (!modules.length) {
333
+ console.log(' no Terraform in its clone');
334
+ continue;
335
+ }
336
+ if (isEmpty(envs)) {
337
+ const theirs = [...new Set(modules.map(module => module.token ?? 'unnamed'))].join(', ');
338
+ console.log(` Terraform found, but none of its environments (${theirs}) matches the stack's (${stack.envs.join(', ')}); list them in envAliases`);
339
+ continue;
340
+ }
341
+ const lines = Object.entries(envs).map(([env, draft]) => {
342
+ const have = deployedUrlOf(stack, id, env);
343
+ const same = have && draft.deployedUrl && hostOf(have) === hostOf(draft.deployedUrl);
344
+ const against = !draft.deployedUrl
345
+ ? have
346
+ ? ` (stack.json has ${have})`
347
+ : ''
348
+ : same
349
+ ? ' (matches stack.json)'
350
+ : have
351
+ ? ` OTHER than stack.json: ${have}`
352
+ : ' NEW';
353
+ const said = [
354
+ `address ${draft.deployedUrl ?? 'not found'}${against}`,
355
+ draft.otherAddresses.length && `also ${draft.otherAddresses.join(', ')}`,
356
+ draft.accounts.length && `account ${draft.accounts.join(', ')}`,
357
+ ...draft.calls.map(call => `calls ${call.to} through ${call.setting}${app?.consumes?.[call.to] ? ' (in stack.json)' : ' NEW'}`),
358
+ draft.settings.length && `settings ${draft.settings.join(', ')}`,
359
+ draft.secretSettings.length && `settings that hold or point to a secret ${draft.secretSettings.join(', ')}`,
360
+ ...draft.secrets.map(secret => `${secret.store === 'Secrets Manager' ? 'secret ' : 'parameter'} ${secret.name}`),
361
+ draft.unnamedSecrets && `secrets ${String(draft.unnamedSecrets)} more, named when deployed`,
362
+ ];
363
+ return [env, said.filter(line => typeof line === 'string')];
364
+ });
365
+ // What every environment shares is printed once.
366
+ const [first, ...others] = lines.map(([, said]) => said);
367
+ const common = first && others.length ? first.filter(line => others.every(said => said.includes(line))) : [];
368
+ for (const [env, said] of lines) {
369
+ const own = said.filter(line => !common.includes(line));
370
+ if (own.length)
371
+ console.log(` ${env.toUpperCase()}`);
372
+ for (const line of own)
373
+ console.log(` ${line}`);
374
+ }
375
+ if (common.length) {
376
+ console.log(' EVERY ENVIRONMENT');
377
+ for (const line of common)
378
+ console.log(` ${line}`);
379
+ }
380
+ }
381
+ const suggestions = suggestionsFor(stack, drafts);
382
+ const queues = linkQueues(drafts, stack);
383
+ // Other apps whose code or settings name a queue: likely its other side, for the user to confirm.
384
+ const namesOf = (queue) => [queue.queue, ...Object.values(queue.envs)].flatMap(name => (name ? [name.replace(/\.fifo$/, '')] : []));
385
+ const isCode = (name) => /\.(json|cs|ts|js|py|go|java|kt|dart|yaml|yml|env)$/.test(name) && !/lock|\.min\./.test(name);
386
+ const textOf = (file) => {
387
+ try {
388
+ return fs.statSync(file).size < 512 * 1024 ? fs.readFileSync(file, 'utf8') : '';
389
+ }
390
+ catch {
391
+ return '';
392
+ }
393
+ };
394
+ const texts = Object.fromEntries(cloned.map(([id, repo]) => [id, findFiles(repo, isCode, 6).map(textOf).join('\n')]));
395
+ for (const queue of queues)
396
+ queue.namedIn = Object.entries(texts)
397
+ .filter(([id, text]) => ![...queue.sends, ...queue.reads].includes(id) && namesOf(queue).some(name => text.includes(name)))
398
+ .map(([id]) => id);
399
+ if (queues.length) {
400
+ console.log('\nQueues and topics');
401
+ for (const queue of queues) {
402
+ const users = [
403
+ ...queue.sends.map(app => `${nameOf(app)} sends`),
404
+ ...queue.reads.map(app => `${nameOf(app)} reads`),
405
+ ...queue.fedBy.map(feeder => `fed by ${feeder}`),
406
+ ];
407
+ const oneSided = !queue.inside && !queue.fedBy.length;
408
+ const missing = !oneSided
409
+ ? ''
410
+ : !queue.reads.length
411
+ ? ', reader not found'
412
+ : !queue.sends.length
413
+ ? ', sender not found'
414
+ : '';
415
+ if (queue.namedIn?.length)
416
+ users.push(`named in ${queue.namedIn.map(nameOf).join(', ')}'s code`);
417
+ const what = queue.queue ?? `${queue.address} in ${nameOf(queue.owner)}, named when deployed`;
418
+ const where = queue.inside
419
+ ? ` (inside ${nameOf(queue.inside)} only)`
420
+ : inStack(queue, stack)
421
+ ? ' (in stack.json)'
422
+ : ' NEW';
423
+ console.log(` ${what} (${queue.type.toUpperCase()}) ${users.join(', ') || 'no use found'}${missing}${where}`);
424
+ }
425
+ }
426
+ // Outside systems the apps reach, with the address each environment uses.
427
+ const outside = {};
428
+ for (const [id, envs] of Object.entries(drafts))
429
+ for (const [provider, byEnv] of Object.entries(outsideCalls(clones[id]?.path ?? '', envs, stack)))
430
+ for (const [env, reached] of Object.entries(byEnv)) {
431
+ const known = ((outside[provider] ??= {})[env] ??= { hosts: new Set(), apps: {} });
432
+ for (const host of reached.hosts)
433
+ known.hosts.add(host);
434
+ known.apps[id] = union(known.apps[id], [...reached.settings]);
435
+ }
436
+ const outsideSuggestions = { apps: {} };
437
+ for (const [provider, byEnv] of Object.entries(outside)) {
438
+ const known = stack.apps[provider];
439
+ const system = OUTSIDE.find(one => one.id === provider);
440
+ if (!system)
441
+ continue;
442
+ console.log(`\n${system.name} (outside)${known ? ' (in stack.json)' : ' NEW'}`);
443
+ for (const env of stack.envs) {
444
+ const reached = byEnv[env];
445
+ if (!reached)
446
+ continue;
447
+ const users = Object.keys(reached.apps).map(nameOf).join(', ');
448
+ console.log(` ${env.toUpperCase()} ${[...reached.hosts].join(', ')} used by ${users}`);
449
+ }
450
+ // The address each environment uses: the one most apps name.
451
+ const pick = (env) => {
452
+ const hosts = [...(byEnv[env]?.hosts ?? [])].sort((a, b) => b.length - a.length);
453
+ const host = hosts.find(one => /\.my\.site\.com$|\.my\.salesforce\.com$/.test(one)) ?? hosts[0];
454
+ return host ? `https://${host}` : null;
455
+ };
456
+ if (!known) {
457
+ const [firstEnv = '', ...laterEnvs] = stack.envs;
458
+ const first = pick(firstEnv);
459
+ const more = laterEnvs.flatMap((env) => {
460
+ const url = pick(env);
461
+ return url ? [[env, { deployedUrl: url }]] : [];
462
+ });
463
+ outsideSuggestions.apps[provider] = {
464
+ name: system.name,
465
+ kind: 'other',
466
+ outside: system.why,
467
+ ...(first && { deployedUrl: first }),
468
+ ...(more.length && { envs: Object.fromEntries(more) }),
469
+ };
470
+ }
471
+ for (const reached of Object.values(byEnv))
472
+ for (const [app, settings] of Object.entries(reached.apps)) {
473
+ if (stack.apps[app]?.consumes?.[provider])
474
+ continue;
475
+ const consumes = ((outsideSuggestions.apps[app] ??= {})['consumes'] ??= {});
476
+ consumes[provider] = { how: 'env', env: settings.length ? settings : undefined };
477
+ }
478
+ }
479
+ const queueSuggestions = queueDraft(queues, stack);
480
+ const file = path.join(tempDir(), 'stack.deployed.json');
481
+ const draft = { at: new Date().toISOString(), suggestions, queueSuggestions, outsideSuggestions, apps: drafts };
482
+ writeJson(file, draft);
483
+ const newQueues = Object.keys(queueSuggestions.queues).length;
484
+ if (newQueues)
485
+ console.log(`${String(newQueues)} queues are new for stack.json (queueSuggestions in the draft).`);
486
+ console.log(`\n${String(Object.keys(suggestions).length)} apps have something new for stack.json. Draft for review: ${file}`);
487
+ }
488
+ //# sourceMappingURL=deployed.js.map