koppler 0.0.0-stage → 0.0.1

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 +73 -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,365 @@
1
+ // The Test button on a cable, and `koppler doctor --flow`. An app cable asks the callee's local address for any answer;
2
+ // a tunnel link opens its port and, when the app has one stored credential of that kind, logs in with it. Only this
3
+ // Mac's own addresses are ever tried.
4
+ import dns from 'node:dns';
5
+ import fs from 'node:fs';
6
+ import http from 'node:http';
7
+ import https from 'node:https';
8
+ import net from 'node:net';
9
+ import path from 'node:path';
10
+ import tls from 'node:tls';
11
+ import { launchProfile } from "../core/launch.js";
12
+ import { isProtected } from "../core/plan.js";
13
+ import { readClones } from "../data.js";
14
+ import { stateDir } from "../paths.js";
15
+ import { alive, readRecord } from "../engine/processes.js";
16
+ import { declaredSecrets, secretRequest } from "./secrets.js";
17
+ import { appOn } from "../core/stack.js";
18
+ const LOOPBACK = /^(localhost|127(\.\d{1,3}){3})$/;
19
+ const KIND_BY_PORT = { 5432: 'postgres', 3306: 'mysql', 6379: 'redis' };
20
+ const KIND_NAME = { postgres: 'Postgres', mysql: 'MySQL', redis: 'Redis or Valkey' };
21
+ const pause = (ms) => new Promise(resolve => setTimeout(resolve, ms));
22
+ const answers = url => new Promise(resolve => {
23
+ const started = Date.now();
24
+ const done = (value) => {
25
+ clearTimeout(timer);
26
+ resolve(value);
27
+ };
28
+ const get = url.protocol === 'https:' ? https.get : http.get;
29
+ const request = get(url, { rejectUnauthorized: !LOOPBACK.test(url.hostname) }, response => {
30
+ let body = '';
31
+ response.on('data', (chunk) => {
32
+ if (body.length < 2048)
33
+ body += chunk.toString();
34
+ });
35
+ response.on('end', () => {
36
+ done({ status: response.statusCode, ms: Date.now() - started, body });
37
+ });
38
+ });
39
+ const timer = setTimeout(() => {
40
+ request.destroy();
41
+ done(null);
42
+ }, 4000);
43
+ request.once('error', () => {
44
+ done(null);
45
+ });
46
+ });
47
+ const opens = (host, port) => new Promise(resolve => {
48
+ const socket = net.connect({ host, port, timeout: 1500 });
49
+ const done = (open) => {
50
+ socket.destroy();
51
+ resolve(open);
52
+ };
53
+ socket.once('connect', () => {
54
+ done(true);
55
+ });
56
+ socket.once('timeout', () => {
57
+ done(false);
58
+ });
59
+ socket.once('error', () => {
60
+ done(false);
61
+ });
62
+ });
63
+ /** One exchange with whatever listens there: say `hello` (if anything), and judge the first thing it says back. */
64
+ function talk(open, ready, hello, good) {
65
+ return new Promise(resolve => {
66
+ const socket = open();
67
+ const done = (value) => {
68
+ clearTimeout(timer);
69
+ socket.destroy();
70
+ resolve(value);
71
+ };
72
+ const timer = setTimeout(() => {
73
+ done(false);
74
+ }, 6000);
75
+ socket.once(ready, () => {
76
+ if (hello)
77
+ socket.write(hello);
78
+ });
79
+ socket.once('data', (data) => {
80
+ done(good(data));
81
+ });
82
+ socket.once('error', () => {
83
+ done(false);
84
+ });
85
+ socket.once('close', () => {
86
+ done(false);
87
+ });
88
+ });
89
+ }
90
+ /**
91
+ * Whether a real server of that kind answers through a tunnel: the tunnel program listens even when nothing is behind
92
+ * it. Postgres answers an SSL request with one byte, MySQL greets first, Redis and Valkey answer PING (in plain text or
93
+ * over TLS).
94
+ */
95
+ const speaks = (kind, host, port) => {
96
+ const plain = () => net.connect({ host, port });
97
+ const secure = () => tls.connect({ host, port, rejectUnauthorized: false });
98
+ if (kind === 'postgres')
99
+ return talk(plain, 'connect', Buffer.from([0, 0, 0, 8, 4, 210, 22, 47]), data => data[0] === 83 || data[0] === 78);
100
+ if (kind === 'mysql')
101
+ return talk(plain, 'connect', null, data => data.length > 4 && (data[4] === 10 || data[4] === 255));
102
+ const pong = (data) => /^[+-]/.test(data.toString());
103
+ // Plain and TLS are tried at once: a TLS-only cache never answers a plain PING, and waiting for that first would eat
104
+ // the time.
105
+ return Promise.all([talk(plain, 'connect', 'PING\r\n', pong), talk(secure, 'secureConnect', 'PING\r\n', pong)]).then(answered => answered.some(Boolean));
106
+ };
107
+ const leadsHere = name => dns.promises.lookup(name, { all: true }).then(found => found.some(one => one.address === '::1' || LOOPBACK.test(one.address)), () => false);
108
+ /** The -LOCAL launch profile's settings of a .NET caller for an environment, to see where it is pointed. */
109
+ function localProfile(stack, id, env) {
110
+ try {
111
+ const app = stack.apps[id];
112
+ const repo = readClones()[id]?.path;
113
+ if (!app?.launchSettings || !repo)
114
+ return null;
115
+ const file = path.join(repo, app.launchSettings);
116
+ const text = fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, '');
117
+ const settings = JSON.parse(text);
118
+ const profiles = settings.profiles ?? {};
119
+ const name = launchProfile(app, profiles, env).local;
120
+ const profile = profiles[name];
121
+ if (!profile)
122
+ return { name, env: null };
123
+ // When Koppler started the app, to tell a setting written after that.
124
+ // ponytail: the file's time, so any later edit of it asks for a restart
125
+ let started = null;
126
+ try {
127
+ const record = JSON.parse(fs.readFileSync(path.join(stateDir(), 'procs', `${id}.json`), 'utf8'));
128
+ started = Date.parse(record.startedAt);
129
+ }
130
+ catch {
131
+ // Not started by Koppler.
132
+ }
133
+ return { name, env: profile.environmentVariables ?? {}, changed: fs.statSync(file).mtimeMs, started };
134
+ }
135
+ catch {
136
+ return null;
137
+ }
138
+ }
139
+ const cannot = (detail) => ({ ok: false, unsure: true, text: 'Cannot test', detail });
140
+ /** An app's local address, when it is one this Mac answers on. */
141
+ function localUrl(app) {
142
+ let url;
143
+ try {
144
+ url = new URL(app?.url ?? '');
145
+ }
146
+ catch {
147
+ return cannot(app ? `${app.name} has no local address in the stack yet.` : 'That app is not in the stack.');
148
+ }
149
+ if (!/^https?:$/.test(url.protocol) || !LOOPBACK.test(url.hostname))
150
+ return cannot(`${app?.name ?? ''}'s address is not on this Mac.`);
151
+ return url;
152
+ }
153
+ /** An app by itself: it answers on its local address; a Lambda also has its function deployed and its queue trigger on. */
154
+ async function testApp(app, probe) {
155
+ const url = localUrl(app);
156
+ if (!app || !(url instanceof URL))
157
+ return url;
158
+ const down = {
159
+ ok: false,
160
+ text: 'No answer',
161
+ detail: `Nothing answers on ${url.host}. ${app.name} is not running.`,
162
+ };
163
+ if (app.kind !== 'lambda') {
164
+ const answered = await probe.answers(url);
165
+ if (!answered)
166
+ return down;
167
+ const detail = `${app.name} answers on ${url.host} (HTTP ${String(answered.status)}).`;
168
+ return { ok: true, text: `Answers, ${String(answered.ms)} ms`, detail };
169
+ }
170
+ const answered = await probe.answers(new URL('/health', url));
171
+ if (!answered)
172
+ return down;
173
+ let health = null;
174
+ try {
175
+ health = JSON.parse(answered.body ?? '');
176
+ }
177
+ catch {
178
+ // Not the runner's own answer.
179
+ }
180
+ if (health?.koppler !== 'lambda')
181
+ return {
182
+ ok: true,
183
+ text: 'Answers',
184
+ detail: `${app.name} answers on ${url.host}. It was started before the deeper check existed; restart it to have its function and queue checked too.`,
185
+ };
186
+ if (health.ok)
187
+ return {
188
+ ok: true,
189
+ text: 'Function ready',
190
+ detail: `${app.name}: the emulator is up, the function is deployed and its queue trigger is on.`,
191
+ };
192
+ const state = health.error ??
193
+ `its function is ${health.function ?? 'missing'} and its queue trigger ${health.trigger ?? 'missing'}`;
194
+ return { ok: false, text: 'Function not ready', detail: `${app.name} answers on ${url.host}, but ${state}.` };
195
+ }
196
+ const portOf = (value) => {
197
+ try {
198
+ return new URL(value ?? '').port;
199
+ }
200
+ catch {
201
+ return null;
202
+ }
203
+ };
204
+ /** An app cable: the callee answers, and the caller is set to call it. */
205
+ async function testCable(from, to, callee, caller, env, probe) {
206
+ const url = localUrl(callee);
207
+ if (!callee || !(url instanceof URL))
208
+ return url;
209
+ const answered = await probe.answers(url);
210
+ if (!answered)
211
+ return { ok: false, text: 'No answer', detail: `Nothing answers on ${url.host}. Start ${callee.name} first.` };
212
+ const up = `${callee.name} answers on ${url.host} (HTTP ${String(answered.status)})`;
213
+ const call = caller?.consumes?.[to];
214
+ const settings = call?.how === 'env' && caller?.kind === 'dotnet' ? [call.env ?? []].flat() : [];
215
+ const fine = `Answers, ${String(answered.ms)} ms`;
216
+ // Koppler gives the caller the address when it starts it: what it was started with is what it calls.
217
+ const given = settings.length ? probe.started(from) : null;
218
+ const setting = settings.find(one => given?.[one] !== undefined);
219
+ if (caller && given && setting)
220
+ return portOf(given[setting]) === url.port
221
+ ? { ok: true, text: fine, detail: `${up}, and ${caller.name} was started with ${setting} pointing at it.` }
222
+ : {
223
+ ok: false,
224
+ text: 'Not pointed at it',
225
+ detail: `${up}, but ${caller.name} was started with ${setting} pointing elsewhere. Restart ${caller.name} to call it.`,
226
+ };
227
+ const profile = settings.length ? probe.profile(from, env) : null;
228
+ if (!profile || !caller) {
229
+ const unchecked = caller ? ` Whether ${caller.name} is set to call it could not be checked here.` : '';
230
+ return { ok: true, text: fine, detail: `${up}.${unchecked}` };
231
+ }
232
+ const { name } = profile;
233
+ const pointed = profile.env;
234
+ const wrong = !pointed
235
+ ? `it has no ${name} profile yet`
236
+ : settings
237
+ .map(setting => !pointed[setting]
238
+ ? `${setting} is missing from ${name}`
239
+ : portOf(pointed[setting]) !== url.port
240
+ ? `${setting} in ${name} points elsewhere`
241
+ : null)
242
+ .find(Boolean);
243
+ if (wrong)
244
+ return {
245
+ ok: false,
246
+ text: 'Not pointed at it',
247
+ detail: `${up}, but ${caller.name} is not set to call it: ${wrong}.`,
248
+ };
249
+ if (profile.started && (profile.changed ?? 0) > profile.started)
250
+ return {
251
+ ok: false,
252
+ text: 'Restart needed',
253
+ detail: `${up}, and ${caller.name}'s ${name} profile points at it, but ${caller.name} was started before that profile was last changed. Restart ${caller.name} to pick it up.`,
254
+ };
255
+ return { ok: true, text: fine, detail: `${up}, and ${caller.name}'s ${name} profile points at it.` };
256
+ }
257
+ /**
258
+ * A tunnel link: the port is open, a real server answers behind it, the name the app dials leads to it, and its
259
+ * credential logs in.
260
+ */
261
+ async function testTunnel(stack, id, app, env, body, probe) {
262
+ const host = typeof body['host'] === 'string' ? body['host'] : '';
263
+ const port = Number(body['port']);
264
+ if (!app || !LOOPBACK.test(host) || !Number.isInteger(port) || port < 1 || port > 65535)
265
+ return cannot('That tunnel is not on this Mac.');
266
+ const where = `${host}:${String(port)}`;
267
+ if (!(await probe.opens(host, port)))
268
+ return { ok: false, text: 'Port closed', detail: `Nothing listens on ${where}. The tunnel is not running.` };
269
+ const kind = KIND_BY_PORT[Number(body['original']) || port];
270
+ if (!kind)
271
+ return {
272
+ ok: true,
273
+ unsure: true,
274
+ text: 'Port open',
275
+ detail: `The tunnel on ${where} is open. Koppler cannot tell what kind of server is behind it, so only the port was checked.`,
276
+ };
277
+ const server = KIND_NAME[kind];
278
+ // A tunnel sometimes drops one connection while it serves others, so silence is only believed the third time.
279
+ let spoke = false;
280
+ for (let tries = 0; tries < 3 && !spoke; tries++) {
281
+ if (tries)
282
+ await pause(400);
283
+ spoke = await probe.speaks(kind, host, port);
284
+ }
285
+ if (!spoke)
286
+ return {
287
+ ok: false,
288
+ text: 'Nothing behind it',
289
+ detail: `The tunnel listens on ${where}, but no ${server} server answers through it. It may have lost its connection: restart the tunnel.`,
290
+ };
291
+ for (const { hosts: name } of app.prereqs ?? []) {
292
+ if (typeof name !== 'string' || !/^[a-z0-9.-]{1,253}$/i.test(name) || (await probe.leadsHere(name)))
293
+ continue;
294
+ return {
295
+ ok: false,
296
+ text: 'Name not redirected',
297
+ detail: `A ${server} server answers on ${where}, but ${name}, the name ${app.name} dials, does not lead to this Mac. Add its hosts entry (koppler doctor).`,
298
+ };
299
+ }
300
+ const reached = `A ${server} server answers through the tunnel on ${where}`;
301
+ const [mine, ...more] = (declaredSecrets(stack)[id]?.[env] ?? []).filter(secret => secret.type === kind);
302
+ if (!mine || more.length)
303
+ return {
304
+ ok: true,
305
+ text: 'Reachable',
306
+ detail: `${reached}. ${app.name} logs in with its own settings, which Koppler does not hold, so the login itself was not tried.`,
307
+ };
308
+ // `auto` is a check the canvas runs by itself: it never reads a production credential, which would open a Keychain prompt.
309
+ if (body['auto'] && isProtected(stack, env))
310
+ return {
311
+ ok: true,
312
+ text: 'Reachable',
313
+ detail: `${reached}. ${mine.name} was not tried: a production credential is only read when you press the test yourself.`,
314
+ };
315
+ const login = await probe.login(id, env, mine.name);
316
+ if (login.ok)
317
+ return { ok: true, text: 'Logged in', detail: `${reached}, and ${app.name}'s ${mine.name} logs in.` };
318
+ const why = login.message ?? login.error ?? 'the check did not finish';
319
+ if (why.includes('not stored'))
320
+ return {
321
+ ok: false,
322
+ text: 'Not stored',
323
+ detail: `${reached}, but ${mine.name} is not stored yet. Store it under Credentials.`,
324
+ };
325
+ return why.startsWith('cannot test')
326
+ ? {
327
+ ok: true,
328
+ text: 'Reachable',
329
+ detail: `${reached}. ${mine.name} ${why.replace(/^cannot test: /, 'could not be tried: ')}`,
330
+ }
331
+ : {
332
+ ok: false,
333
+ text: 'Login failed',
334
+ detail: `${reached}, but ${mine.name} is rejected: ${why.replace(/^failed: /, '')}`,
335
+ };
336
+ }
337
+ /** Tests one connection: `{ node }` an app by itself, `{ from, to }` an app cable, `{ app, host, port }` a tunnel link. */
338
+ export async function cableTest(loaded, given, probe = {
339
+ answers,
340
+ opens,
341
+ speaks,
342
+ leadsHere,
343
+ profile: (id, env) => (loaded ? localProfile(loaded, id, env) : null),
344
+ started: id => {
345
+ const record = readRecord(id);
346
+ return record && alive(record) ? (record.start?.setEnv ?? null) : null;
347
+ },
348
+ login: (app, env, name) => secretRequest('test', { app, env, name }).then(answer => answer.body),
349
+ }) {
350
+ // With no stack loaded there is no app to find: every test then says so.
351
+ const stack = loaded ?? { envs: [], apps: {} };
352
+ const body = (given ?? {});
353
+ const [first = ''] = stack.envs;
354
+ const env = typeof body['env'] === 'string' && stack.envs.includes(body['env']) ? body['env'] : first;
355
+ const appOf = (id) => {
356
+ return typeof id === 'string' ? (appOn(stack, id, env) ?? null) : null;
357
+ };
358
+ const { node, from, to, app } = body;
359
+ if (typeof node === 'string')
360
+ return testApp(appOf(node), probe);
361
+ if (typeof to === 'string')
362
+ return testCable(String(from), to, appOf(to), appOf(from), env, probe);
363
+ return testTunnel(stack, String(app), appOf(app), env, body, probe);
364
+ }
365
+ //# sourceMappingURL=cable-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cable-test.js","sourceRoot":"","sources":["../../src/server/cable-test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ // What a settings panel offers to pick from on this machine: AWS profile names, and what an app's clone holds
2
+ // (its files, .NET launch profiles, package scripts). Names only: no key, token or file content leaves here.
3
+ import { execFileSync } from 'node:child_process';
4
+ import fs from 'node:fs';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+ import { readJson } from "../json-files.js";
8
+ const MOST_FILES = 4000;
9
+ /** The profile names in the AWS config file. The credentials file is never opened. */
10
+ export function awsProfiles(file = process.env['AWS_CONFIG_FILE'] ?? path.join(os.homedir(), '.aws', 'config')) {
11
+ let text = '';
12
+ try {
13
+ text = fs.readFileSync(file, 'utf8');
14
+ }
15
+ catch {
16
+ return [];
17
+ }
18
+ const names = [...text.matchAll(/^\s*\[\s*(?:profile\s+)?([^\]\s]+)\s*\]/gm)].map(found => found[1] ?? '');
19
+ return [...new Set(names.filter(Boolean))].sort((a, b) => a.localeCompare(b));
20
+ }
21
+ const keysOf = (value, key) => {
22
+ const inner = typeof value === 'object' && value !== null ? value[key] : null;
23
+ return typeof inner === 'object' && inner !== null ? Object.keys(inner) : [];
24
+ };
25
+ /** What a clone holds to pick from; empty when there is none. */
26
+ export function cloneChoices(dir) {
27
+ const none = { files: [], profiles: {}, scripts: {} };
28
+ if (!dir)
29
+ return none;
30
+ let listed = '';
31
+ try {
32
+ listed = execFileSync('git', ['-C', dir, 'ls-files', '-z'], {
33
+ encoding: 'utf8',
34
+ timeout: 5000,
35
+ maxBuffer: 16 * 1024 * 1024,
36
+ stdio: ['ignore', 'pipe', 'ignore'],
37
+ });
38
+ }
39
+ catch {
40
+ return none;
41
+ }
42
+ const all = listed.split('\0').filter(Boolean);
43
+ const named = (name) => all.filter(file => path.posix.basename(file) === name).slice(0, 40);
44
+ const read = (files, key) => Object.fromEntries(files.map(file => [file, keysOf(readJson(path.join(dir, file)), key)]));
45
+ return {
46
+ files: all.slice(0, MOST_FILES),
47
+ profiles: read(named('launchSettings.json'), 'profiles'),
48
+ scripts: read(named('package.json').filter(file => !file.includes('node_modules/')), 'scripts'),
49
+ };
50
+ }
51
+ //# sourceMappingURL=choices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"choices.js","sourceRoot":"","sources":["../../src/server/choices.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,107 @@
1
+ // What the canvas page loads: its own files, its data as JSON, the stack schema, the choices for the settings panels and
2
+ // an app's log.
3
+ import { runLog } from "../engine/run-logs.js";
4
+ import { stackSchema } from "../engine/stack-share.js";
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { readClones, readData } from "../data.js";
8
+ import { APP_ID } from "../ids.js";
9
+ import { CODE, tempDir } from "../paths.js";
10
+ import { awsProfiles, cloneChoices } from "./choices.js";
11
+ import { tail } from "./logs.js";
12
+ import { logFile, SOURCE, statusJson } from "./status.js";
13
+ export const JSON_HEADERS = { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' };
14
+ /** The files the page may load, by the path it asks for; `temp/` ones come from the run folder. */
15
+ const PAGE_FILES = {
16
+ '': 'page/index.html',
17
+ 'theme.js': 'page/theme.js',
18
+ 'temp/debug.jsonl': 'temp/debug.jsonl',
19
+ };
20
+ const CONTENT_TYPES = {
21
+ html: 'text/html; charset=utf-8',
22
+ css: 'text/css; charset=utf-8',
23
+ woff2: 'font/woff2',
24
+ jsonl: 'text/plain; charset=utf-8',
25
+ };
26
+ const DATA_FILE = /^data\/(registry|local|repos|state)\.json$/;
27
+ /** The files the page names: what its build put beside it. */
28
+ const assetFile = (what) => (/^assets\/[\w-][\w.-]*$/.test(what) ? `page/${what}` : undefined);
29
+ /** What the page loads: its own files, its data as scripts or as JSON, and an app's log. */
30
+ export function serveFile(what, url, res) {
31
+ // The open log tab reads its app's log here, much longer than the short tail every status carries.
32
+ if (what === 'log') {
33
+ const id = url.searchParams.get('app') ?? '';
34
+ const run = url.searchParams.get('run');
35
+ if (!APP_ID.test(id)) {
36
+ res.writeHead(400).end();
37
+ return;
38
+ }
39
+ // A past run's log, as the run kept it.
40
+ if (run !== null) {
41
+ const kept = runLog(run, id);
42
+ const text = kept ? tail(kept, 2 * 1024 * 1024, 2000) : null;
43
+ res.writeHead(text === null ? 404 : 200, { 'content-type': 'text/plain; charset=utf-8' }).end(text ?? '');
44
+ return;
45
+ }
46
+ let named;
47
+ try {
48
+ const status = JSON.parse(fs.readFileSync(SOURCE, 'utf8'));
49
+ named = status.apps?.[id]?.log;
50
+ }
51
+ catch {
52
+ // No agent wrote a status: the log is where Koppler keeps it.
53
+ }
54
+ const file = logFile(id, named);
55
+ const text = tail(file, 2 * 1024 * 1024, 2000);
56
+ const headers = { 'content-type': 'text/plain; charset=utf-8', 'cache-control': 'no-store' };
57
+ res.writeHead(text === null ? 404 : 200, headers).end(text ?? '');
58
+ return;
59
+ }
60
+ // The shape of a stack, for the settings panels: every field they show comes from it.
61
+ if (what === 'data/stack-schema.json') {
62
+ res.writeHead(200, JSON_HEADERS).end(JSON.stringify(stackSchema()));
63
+ return;
64
+ }
65
+ // What the settings panels offer to pick from on this machine: names only.
66
+ if (what === 'data/choices.json') {
67
+ const id = url.searchParams.get('app') ?? '';
68
+ const clone = APP_ID.test(id) ? readClones()[id]?.path : undefined;
69
+ res.writeHead(200, JSON_HEADERS).end(JSON.stringify({ awsProfiles: awsProfiles(), ...cloneChoices(clone) }));
70
+ return;
71
+ }
72
+ if (what === 'data/status.json') {
73
+ const status = statusJson();
74
+ res.writeHead(status ? 200 : 404, JSON_HEADERS).end(status ?? 'null');
75
+ return;
76
+ }
77
+ const asked = DATA_FILE.exec(what)?.[1];
78
+ if (asked) {
79
+ // One not written yet (no board saved, no stack) is null, not an error the page's console reports.
80
+ res.writeHead(200, JSON_HEADERS).end(JSON.stringify(readData(asked)));
81
+ return;
82
+ }
83
+ // Where the canvas was while it was built beside the page it replaced.
84
+ if (what === 'next' || what === 'next/') {
85
+ res.writeHead(308, { location: what === 'next' ? './' : '../' }).end();
86
+ return;
87
+ }
88
+ const file = Object.hasOwn(PAGE_FILES, what) ? PAGE_FILES[what] : assetFile(what);
89
+ if (!file) {
90
+ res.writeHead(404).end();
91
+ return;
92
+ }
93
+ const full = file.startsWith('temp/') ? path.join(tempDir(), file.slice(5)) : path.join(CODE, file);
94
+ fs.readFile(full, (error, body) => {
95
+ if (error) {
96
+ res.writeHead(404).end();
97
+ return;
98
+ }
99
+ res
100
+ .writeHead(200, {
101
+ 'content-type': CONTENT_TYPES[path.extname(file).slice(1)] ?? 'text/javascript; charset=utf-8',
102
+ 'cache-control': file.endsWith('.woff2') ? 'private, max-age=604800' : 'no-store',
103
+ })
104
+ .end(body);
105
+ });
106
+ }
107
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/server/files.ts"],"names":[],"mappings":"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"}