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,130 @@
1
+ // Session review, while Koppler itself is worked on (run from its source folder): after a full Stop and revert, everything
2
+ // the session left behind is gathered in one folder, and the agent is asked to find and fix the problems the user ran into.
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { CODE } from "../paths.js";
6
+ import { redact } from "../scrub.js";
7
+ export const isDev = () => !fs.realpathSync(CODE).split(path.sep).join('/').includes('/plugins/cache/');
8
+ const read = (file) => {
9
+ try {
10
+ return fs.readFileSync(file, 'utf8');
11
+ }
12
+ catch {
13
+ return '';
14
+ }
15
+ };
16
+ const lastLines = (text, count) => text.trimEnd().split('\n').slice(-count).join('\n');
17
+ const jsonl = (text) => text.split('\n').flatMap(line => {
18
+ try {
19
+ const entry = JSON.parse(line);
20
+ return entry ? [entry] : [];
21
+ }
22
+ catch {
23
+ return [];
24
+ }
25
+ });
26
+ const PROBLEM = /\b(error|exception|failed|fatal|unhandled|refused|denied|timed? ?out)\b/i;
27
+ const STATES = ['done', 'failed', 'replaced', 'applying', 'waiting'];
28
+ const indented = (text) => text
29
+ .trim()
30
+ .split('\n')
31
+ .map(line => ` ${line.trim()}`)
32
+ .join('\n');
33
+ /** Gathers the session in a folder and returns it. The session is every run after the previous finished Stop, up to and with this one, or up to now. */
34
+ export function collectSession({ temp, logsDir, stop, runs, }) {
35
+ const at = stop ? runs.findIndex(run => run.id === stop.id) : runs.length - 1;
36
+ const before = runs.slice(0, stop ? at : at + 1).findLastIndex(run => run.kind === 'stop' && run.state === 'done');
37
+ const session = runs.slice(before + 1, at + 1);
38
+ const from = session[0]?.at ?? '';
39
+ const to = new Date().toISOString();
40
+ const inRange = (entry) => (entry.at ?? '') >= from && (entry.at ?? '') <= to;
41
+ const dir = path.join(temp, 'sessions', stop?.id ?? 'current');
42
+ // What is going on is gathered afresh each time: nothing of an earlier look stays behind.
43
+ if (!stop)
44
+ fs.rmSync(dir, { recursive: true, force: true });
45
+ fs.mkdirSync(path.join(dir, 'logs'), { recursive: true });
46
+ const write = (name, body) => {
47
+ fs.writeFileSync(path.join(dir, name), body, { mode: 0o600 });
48
+ };
49
+ const lines = (entries) => entries.map(entry => JSON.stringify(entry)).join('\n');
50
+ write('runs.json', JSON.stringify(session.map(({ layout, ...run }) => run), null, 2));
51
+ const clicks = jsonl(read(path.join(temp, 'debug.jsonl'))).filter(inRange);
52
+ write('debug.jsonl', lines(clicks));
53
+ const messages = jsonl(read(path.join(temp, 'messages.jsonl'))).filter(entry => !entry.at || inRange(entry));
54
+ write('messages.jsonl', lines(messages));
55
+ const ups = session
56
+ .map(run => [run.id, redact(read(path.join(temp, `up-${run.id}.log`)))])
57
+ .filter(([, text]) => text);
58
+ for (const [id, text] of ups)
59
+ write(`up-${id}.log`, text);
60
+ const apps = [...new Set(session.flatMap(run => Object.keys(run.nodes ?? {})))];
61
+ const appLogs = apps
62
+ .map(id => {
63
+ const log = path.join(logsDir, `${id}.log`);
64
+ return [id, redact(lastLines(read(`${log}.history`) + read(log), 3000))];
65
+ })
66
+ .filter(([, text]) => text.trim());
67
+ for (const [id, text] of appLogs)
68
+ write(path.join('logs', `${id}.log`), text);
69
+ write('canvas.log', redact(lastLines(read(path.join(logsDir, 'canvas.log')), 400)));
70
+ // What went wrong, at a glance; the details are in the files next to it.
71
+ const failed = session.filter(run => run.state === 'failed');
72
+ const left = ups.filter(([, text]) => text.includes('Left for the agent:'));
73
+ const pageErrors = clicks.filter(entry => entry.kind === 'error');
74
+ const warnings = clicks.filter(entry => entry.kind === 'message');
75
+ const questions = messages.filter(message => message.kind === 'question');
76
+ const clicked = {};
77
+ for (const { kind, what = '' } of clicks)
78
+ if (kind === 'click')
79
+ clicked[what] = (clicked[what] ?? 0) + 1;
80
+ const repeats = Object.entries(clicked)
81
+ .filter(([, count]) => count >= 4)
82
+ .sort((a, b) => b[1] - a[1])
83
+ .slice(0, 8);
84
+ const logProblems = appLogs
85
+ .map(([id, text]) => [id, text.split('\n').filter(line => PROBLEM.test(line)).length])
86
+ .filter(([, count]) => count);
87
+ const states = STATES.map(state => [state, session.filter(run => run.state === state).length])
88
+ .filter(([, count]) => count)
89
+ .map(([state, count]) => `${String(count)} ${state}`);
90
+ const quoted = (texts) => texts.map(text => `"${String(text)}"`).join('; ');
91
+ const summary = [
92
+ `# Session ${from.slice(0, 16)} to ${to.slice(0, 16)}`,
93
+ '',
94
+ `${String(session.length)} runs: ${states.join(', ')}.`,
95
+ ...failed.map(run => `- Failed ${String(run.kind)} ${run.id}: ${run.reason ?? 'no reason given'}`),
96
+ ...left.map(([id, text]) => `- Koppler left work for the agent in ${id}:\n${indented(text.split('Left for the agent:')[1] ?? '')}`),
97
+ `${String(questions.length)} questions from the agent${questions.length ? `: ${quoted(questions.map(question => question.title))}` : ''}.`,
98
+ `${String(pageErrors.length)} page errors${pageErrors.length
99
+ ? `: ${pageErrors
100
+ .slice(0, 5)
101
+ .map(error => String(error.what))
102
+ .join('; ')}`
103
+ : ''}.`,
104
+ `Messages the user saw: ${quoted(warnings.slice(-10).map(warning => warning.what)) || 'none'}.`,
105
+ repeats.length
106
+ ? `Clicked many times (a sign of something not working): ${repeats.map(([what, count]) => `${what} x${String(count)}`).join('; ')}.`
107
+ : 'Nothing clicked over and over.',
108
+ logProblems.length
109
+ ? `Error-like lines in app logs: ${logProblems.map(([id, count]) => `${id} ${String(count)}`).join(', ')} (logs/).`
110
+ : 'No error-like lines in app logs.',
111
+ ];
112
+ write('summary.md', summary.join('\n') + '\n');
113
+ const quiet = ![failed, left, pageErrors, questions, repeats, logProblems].some(list => list.length);
114
+ return { dir, quiet };
115
+ }
116
+ /** The previous log of an app is kept before a new run starts, so a session keeps every run's output (rolling, 5 MB). */
117
+ export function keepPreviousLog(log) {
118
+ try {
119
+ const old = fs.readFileSync(log, 'utf8');
120
+ if (!old.trim())
121
+ return;
122
+ const history = `${log}.history`;
123
+ const joined = read(history) + old + '\n';
124
+ fs.writeFileSync(history, joined.length > 5_000_000 ? joined.slice(-5_000_000) : joined, { mode: 0o600 });
125
+ }
126
+ catch {
127
+ // No log yet: nothing to keep.
128
+ }
129
+ }
130
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/engine/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,124 @@
1
+ // Sharing a board with the team: it joins the stack's team boards through a pull request on the stack's repo on
2
+ // GitHub, made from the shared file as it is there, so nothing else of this Mac's copy goes with it and the clone here
3
+ // is never touched. A stack that is not on GitHub keeps the board in its file here, for the user to commit.
4
+ import { execFileSync } from 'node:child_process';
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { migrate } from "../core/presets.js";
8
+ import { validateStack } from "../core/stack.js";
9
+ import { fileUrl, proposeFile, readFile, gh } from "../github.js";
10
+ import { readJson } from "../json-files.js";
11
+ import { sourceOf } from "./linked.js";
12
+ import { saveStack } from "./stack-file.js";
13
+ const git = (cwd, ...args) => {
14
+ try {
15
+ return execFileSync('git', ['-C', cwd, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
16
+ }
17
+ catch {
18
+ return '';
19
+ }
20
+ };
21
+ /** A GitHub remote as owner and repo: git@github.com:o/r.git, https://github.com/o/r(.git), ssh://git@github.com/o/r. */
22
+ export function githubRepo(remote) {
23
+ const found = /^(?:git@github\.com:|(?:https|ssh):\/\/(?:[\w.-]+@)?github\.com\/)([\w.-]+)\/([\w.-]+?)(?:\.git)?\/?$/.exec(remote.trim());
24
+ return found?.[1] && found[2] ? { owner: found[1], repo: found[2] } : null;
25
+ }
26
+ /** The stack file on GitHub a pull request goes to: a linked stack's source, else the GitHub repo its folder is in. */
27
+ export async function shareTarget(dir) {
28
+ const linked = sourceOf(dir);
29
+ if (linked?.file)
30
+ return { file: linked.file, repo: `${linked.file.owner}/${linked.file.repo}` };
31
+ const top = git(dir, 'rev-parse', '--show-toplevel');
32
+ const remote = top ? githubRepo(git(dir, 'remote', 'get-url', 'origin')) : null;
33
+ if (!top || !remote)
34
+ return { error: "The stack's folder is not a clone of a repo on GitHub." };
35
+ const branch = await gh([`repos/${remote.owner}/${remote.repo}`, '--jq', '.default_branch']);
36
+ if (!branch.ok)
37
+ return { error: `Could not reach ${remote.owner}/${remote.repo}. Check that gh is signed in.` };
38
+ // Git names the clone by its real path: a folder reached through a link (/var is /private/var on macOS) is too.
39
+ const stackPath = path
40
+ .relative(top, path.join(fs.realpathSync(dir), 'stack.json'))
41
+ .split(path.sep)
42
+ .join('/');
43
+ if (stackPath.startsWith('..'))
44
+ return { error: "The stack's file is not inside its repo." };
45
+ return { file: { ...remote, ref: branch.out.trim(), path: stackPath }, repo: `${remote.owner}/${remote.repo}` };
46
+ }
47
+ /** The team's stack with this board among its team boards: replacing one of the same name, else added at the end. */
48
+ export function withBoard(stack, board) {
49
+ const presets = (Array.isArray(stack['presets']) ? stack['presets'] : []);
50
+ const at = presets.findIndex(one => one.name === board.name);
51
+ return { ...stack, presets: at < 0 ? [...presets, board] : presets.map((one, n) => (n === at ? board : one)) };
52
+ }
53
+ /** What of a board the team gets: its apps, cables and places, never this Mac's own notes on it. */
54
+ export function teamBoard(preset) {
55
+ const checked = migrate(preset);
56
+ if (checked.error !== undefined)
57
+ return { error: checked.error };
58
+ const { name, version, nodes, edges } = checked.preset;
59
+ const { layout, outside } = preset;
60
+ return {
61
+ board: {
62
+ name,
63
+ ...(version !== undefined && { version }),
64
+ nodes,
65
+ edges,
66
+ ...(layout ? { layout } : {}),
67
+ ...(outside ? { outside } : {}),
68
+ },
69
+ };
70
+ }
71
+ /** Opens the pull request that adds the board to the stack's team boards on GitHub. */
72
+ export async function shareBoard(dir, preset) {
73
+ const team = teamBoard(preset);
74
+ if (team.error !== undefined)
75
+ return { error: `${preset.name}: ${team.error}` };
76
+ const { board } = team;
77
+ const target = await shareTarget(dir);
78
+ if (target.error !== undefined) {
79
+ if (!sourceOf(dir) && !git(dir, 'rev-parse', '--show-toplevel').length)
80
+ return keepHere(dir, board);
81
+ const remote = githubRepo(git(dir, 'remote', 'get-url', 'origin'));
82
+ return remote ? target : keepHere(dir, board);
83
+ }
84
+ const text = await readFile(target.file);
85
+ if (text === null)
86
+ return { error: `Could not read ${fileUrl(target.file)}. Check that gh is signed in.` };
87
+ let shared;
88
+ try {
89
+ shared = JSON.parse(text);
90
+ }
91
+ catch {
92
+ return { error: `${fileUrl(target.file)} is not JSON.` };
93
+ }
94
+ const next = withBoard(shared, board);
95
+ const problems = validateStack(next);
96
+ if (problems.length)
97
+ return { error: `The team's stack would not pass its check: ${problems.slice(0, 3).join('; ')}` };
98
+ const replaces = (shared['presets'] ?? []).some(one => one.name === board.name);
99
+ const slug = board.name
100
+ .toLowerCase()
101
+ .replace(/[^a-z0-9]+/g, '-')
102
+ .replace(/^-|-$/g, '')
103
+ .slice(0, 40) || 'board';
104
+ const title = `${replaces ? 'Update' : 'Add'} the "${board.name}" board`;
105
+ const opened = await proposeFile(target.file, `${JSON.stringify(next, null, 2)}\n`, {
106
+ branch: `koppler/board-${slug}-${Date.now().toString(36)}`,
107
+ title,
108
+ body: `${title} among the team boards of this stack, as shared from the Koppler canvas.\n\nOnce merged, it shows under Team boards for everyone who uses the stack.`,
109
+ });
110
+ return opened.error !== undefined ? opened : { url: opened.url, repo: target.repo };
111
+ }
112
+ /** A stack that is not on GitHub: the board joins its file here, for the user to commit. */
113
+ function keepHere(dir, board) {
114
+ const source = path.join(dir, 'stack.json');
115
+ const before = readJson(source);
116
+ if (!before)
117
+ return { error: `cannot read ${source}` };
118
+ const next = withBoard(before, board);
119
+ const problems = validateStack(next);
120
+ if (problems.length)
121
+ return { error: `The stack would not pass its check: ${problems.slice(0, 3).join('; ')}` };
122
+ return saveStack(source, before, next) ?? { local: true };
123
+ }
124
+ //# sourceMappingURL=share.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"share.js","sourceRoot":"","sources":["../../src/engine/share.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,206 @@
1
+ // Changes to the stack file in use: adding and removing apps and outside systems, and setting any field.
2
+ import path from 'node:path';
3
+ import { readJson, writeJson } from "../json-files.js";
4
+ import { kindsFor } from "../core/kinds.js";
5
+ import { validateStack } from "../core/stack.js";
6
+ import { getAt, parsePath, setAt } from "../core/stack-path.js";
7
+ import { brokenLock, LOCKED } from "./locks.js";
8
+ import { readData, writeData } from "../data.js";
9
+ import { loadConfig, readIndex } from "./config.js";
10
+ import { candidateRepos } from "./repos.js";
11
+ const NO_STACK = 'No stack is set up yet: koppler init';
12
+ /** The stack file in use, with where it is. */
13
+ function stackInUse(config) {
14
+ if (!config.stack)
15
+ return { error: NO_STACK };
16
+ const source = path.join(config.stack, 'stack.json');
17
+ const stack = readJson(source);
18
+ return stack ? { source, stack } : { error: `cannot read ${source}` };
19
+ }
20
+ const NOT_SET_UP = 'Run facts not collected yet: read the repo (launchSettings/README/infra) and fill run, url, prereqs before using.';
21
+ /** Adds picked clones to stack.json as apps that are not set up yet. Only clones from the index can be added. */
22
+ export function addApps(picks, { config = loadConfig(), index = readIndex() } = {}) {
23
+ if (!config.stack || !index)
24
+ return { error: NO_STACK };
25
+ const used = stackInUse(config);
26
+ if (used.error !== undefined)
27
+ return used;
28
+ const { source, stack } = used;
29
+ const known = new Map(candidateRepos(index, stack, config.stack).map(candidate => [candidate.slug, candidate]));
30
+ const added = [];
31
+ for (const picked of (Array.isArray(picks) ? picks : []).slice(0, 200)) {
32
+ const pick = (picked ?? {});
33
+ const candidate = known.get(pick.slug ?? '');
34
+ if (!candidate || candidate.inStack)
35
+ continue;
36
+ const kind = Object.hasOwn(kindsFor(stack), pick.kind ?? '') ? pick.kind : candidate.kind;
37
+ if (!kind)
38
+ return { error: `Say what kind of app ${candidate.slug} is.` };
39
+ const repo = candidate.slug.split('/').pop() ?? '';
40
+ const base = repo
41
+ .toLowerCase()
42
+ .replace(/[^a-z0-9-]+/g, '-')
43
+ .replace(/^-+|-+$/g, '') || 'app';
44
+ let id = base;
45
+ for (let n = 2; stack.apps[id]; n++)
46
+ id = `${base}-${String(n)}`;
47
+ const name = typeof pick.name === 'string' && pick.name.trim() ? pick.name.trim() : candidate.name;
48
+ stack.apps[id] = { name: name.slice(0, 80), kind, slug: candidate.slug, pending: NOT_SET_UP };
49
+ candidate.inStack = id;
50
+ added.push(id);
51
+ }
52
+ // ponytail: picks are added to `stack` in place above; the copy taken here is what was there before.
53
+ if (added.length) {
54
+ const refused = saveStack(source, readJson(source), stack);
55
+ if (refused)
56
+ return refused;
57
+ }
58
+ return { added };
59
+ }
60
+ /** The stack file in use, as edits write it and locks are kept by; null with no stack. */
61
+ export const stackSource = (config = loadConfig()) => config.stack ? path.join(config.stack, 'stack.json') : null;
62
+ /** Writes the stack file, unless the change alters a field the user locked: then it says which, and writes nothing. */
63
+ export function saveStack(source, before, next) {
64
+ const locked = brokenLock(source, before, next);
65
+ if (locked)
66
+ return { error: LOCKED(locked) };
67
+ writeJson(source, next);
68
+ return null;
69
+ }
70
+ /** Copies the stack file in use to the canvas's registry again, keeping what the last sync found: a quick reload after one field changed. */
71
+ export function refreshRegistry() {
72
+ const source = stackSource();
73
+ const registry = readData('registry');
74
+ const stack = source ? readJson(source) : null;
75
+ if (source && registry && stack)
76
+ writeData('registry', { ...stack, source, found: registry['found'] });
77
+ }
78
+ /**
79
+ * Replaces the whole stack with an edited copy, as the settings panels save it: checked against every rule first and
80
+ * against the locks, and written only when it passes. The problems are given as `where: what`, so each finds its field.
81
+ */
82
+ export function replaceStack(next, { config = loadConfig() } = {}) {
83
+ const used = stackInUse(config);
84
+ if (used.error !== undefined)
85
+ return used;
86
+ const before = new Set(validateStack(used.stack));
87
+ const problems = validateStack(next).filter(problem => !before.has(problem));
88
+ if (problems.length)
89
+ return { error: problems.slice(0, 5).join('; '), problems };
90
+ return saveStack(used.source, used.stack, next) ?? { problems: [] };
91
+ }
92
+ /** What the stack in use holds at a path (the whole stack without one). */
93
+ export function readStackAt(pathText = '', { config = loadConfig() } = {}) {
94
+ const used = stackInUse(config);
95
+ if (used.error !== undefined)
96
+ return used;
97
+ if (!pathText)
98
+ return { value: used.stack };
99
+ const parsed = parsePath(pathText);
100
+ if ('error' in parsed)
101
+ return parsed;
102
+ return { value: getAt(used.stack, parsed) };
103
+ }
104
+ /**
105
+ * Changes one field of the stack in use, or takes it out when no value is given. The first of the values that keeps
106
+ * the stack valid is written (a value typed in a terminal may be meant as text or as JSON); a change that breaks one of
107
+ * the stack's rules is refused with what it breaks, and nothing is written.
108
+ */
109
+ export function editStack(pathText, values, { config = loadConfig() } = {}) {
110
+ const used = stackInUse(config);
111
+ if (used.error !== undefined)
112
+ return used;
113
+ const parsed = parsePath(pathText);
114
+ if ('error' in parsed)
115
+ return parsed;
116
+ const before = new Set(validateStack(used.stack));
117
+ let first = '';
118
+ for (const value of values ?? [undefined]) {
119
+ let next;
120
+ try {
121
+ next = setAt(used.stack, parsed, value);
122
+ }
123
+ catch (error) {
124
+ return { error: `${pathText}: ${error.message}` };
125
+ }
126
+ const broken = validateStack(next).filter(problem => !before.has(problem));
127
+ if (!broken.length)
128
+ return saveStack(used.source, used.stack, next) ?? { path: pathText, value };
129
+ first ||= broken.slice(0, 5).join('; ');
130
+ }
131
+ return { error: first };
132
+ }
133
+ /**
134
+ * Adds a system the apps call that never runs here (a SaaS API, a managed service), with the apps that call it, so it
135
+ * shows on their boards as an outside card.
136
+ */
137
+ export function addOutside(given, { config = loadConfig() } = {}) {
138
+ const used = stackInUse(config);
139
+ if (used.error !== undefined)
140
+ return used;
141
+ const { source, stack } = used;
142
+ const text = (value, max) => (typeof value === 'string' ? value.trim().slice(0, max) : '');
143
+ const name = text(given.name, 80);
144
+ if (!name)
145
+ return { error: 'Give the outside system a name.' };
146
+ const url = text(given.url, 500);
147
+ if (url && !/^https?:\/\//.test(url))
148
+ return { error: `${url} is not a web address (https://...).` };
149
+ if (url && !URL.canParse(url))
150
+ return { error: `${url} is not a web address.` };
151
+ const apps = stack.apps;
152
+ if (Object.values(apps).some(app => app.name?.toLowerCase() === name.toLowerCase()))
153
+ return { error: `The stack already has ${name}.` };
154
+ const ids = (list) => [...new Set(Array.isArray(list) ? list : [])].filter((id) => typeof id === 'string');
155
+ const callers = ids(given.calledBy);
156
+ const called = ids(given.sendsTo);
157
+ const unknown = [...callers, ...called].filter(id => !apps[id] || apps[id].outside);
158
+ if (unknown.length)
159
+ return { error: `Not apps of the stack that run here: ${[...new Set(unknown)].join(', ')}.` };
160
+ if (!callers.length && !called.length)
161
+ return { error: `Say which apps call ${name}, or which it sends to: its card shows next to them.` };
162
+ const base = name
163
+ .toLowerCase()
164
+ .replace(/[^a-z0-9-]+/g, '-')
165
+ .replace(/^-+|-+$/g, '') || 'outside';
166
+ let id = base;
167
+ for (let n = 2; apps[id]; n++)
168
+ id = `${base}-${String(n)}`;
169
+ apps[id] = {
170
+ name,
171
+ kind: 'other',
172
+ outside: text(given.why, 200) || 'An outside system: it never runs here.',
173
+ ...(url && { deployedUrl: url }),
174
+ ...(called.length && { consumes: Object.fromEntries(called.map(to => [to, {}])) }),
175
+ };
176
+ for (const caller of callers) {
177
+ const app = apps[caller];
178
+ if (app)
179
+ app.consumes = { ...app.consumes, [id]: {} };
180
+ }
181
+ return saveStack(source, readJson(source), stack) ?? { added: id };
182
+ }
183
+ /**
184
+ * Takes an app out of the stack. Other apps' calls to it go too, since a call to a missing app makes the stack invalid.
185
+ * The clone stays where it is; boards that list the app skip it when they open.
186
+ */
187
+ export function removeApp(id, { config = loadConfig() } = {}) {
188
+ const used = stackInUse(config);
189
+ if (used.error !== undefined)
190
+ return used;
191
+ const { source, stack } = used;
192
+ const app = stack.apps?.[id];
193
+ if (!app)
194
+ return { error: `${id} is not in the stack` };
195
+ const name = app.name ?? id;
196
+ const { [id]: removed, ...apps } = stack.apps;
197
+ for (const other of Object.values(apps))
198
+ if (other.consumes)
199
+ other.consumes = dropKey(other.consumes, id);
200
+ return saveStack(source, readJson(source), { ...stack, apps }) ?? { removed: id, name };
201
+ }
202
+ function dropKey(from, key) {
203
+ const { [key]: dropped, ...rest } = from;
204
+ return rest;
205
+ }
206
+ //# sourceMappingURL=stack-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stack-file.js","sourceRoot":"","sources":["../../src/engine/stack-file.ts"],"names":[],"mappings":"AAAA,yGAAyG;AACzG,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAA2B,UAAU,EAAE,SAAS,EAAkB,MAAM,aAAa,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,QAAQ,GAAG,sCAAsC,CAAC;AAExD,+CAA+C;AAC/C,SAAS,UAAU,CAAC,MAAc;IAChC,IAAI,CAAC,MAAM,CAAC,KAAK;QAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAqB,CAAC;IACnD,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,MAAM,EAAE,EAAE,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,GACd,mHAAmH,CAAC;AAEtH,iHAAiH;AACjH,MAAM,UAAU,OAAO,CACrB,KAAc,EACd,EAAE,MAAM,GAAG,UAAU,EAAE,EAAE,KAAK,GAAG,SAAS,EAAE,KAAgD,EAAE;IAE9F,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IACxD,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IAC/B,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IAChH,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,KAAmB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;QACtF,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,CAAqD,CAAC;QAChF,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,OAAO;YAAE,SAAS;QAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC;QAC1F,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,KAAK,EAAE,wBAAwB,SAAS,CAAC,IAAI,MAAM,EAAE,CAAC;QAC1E,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;QACnD,MAAM,IAAI,GACR,IAAI;aACD,WAAW,EAAE;aACb,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC;aAC5B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC;QACtC,IAAI,EAAE,GAAG,IAAI,CAAC;QACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;YAAE,EAAE,GAAG,GAAG,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC;QACnG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;QAC9F,SAAS,CAAC,OAAO,GAAG,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IACD,qGAAqG;IACrG,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;QAC3D,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC;IAC9B,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,CAAC;AACnB,CAAC;AAED,0FAA0F;AAC1F,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,SAAiB,UAAU,EAAE,EAAiB,EAAE,CAC1E,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAE9D,uHAAuH;AACvH,MAAM,UAAU,SAAS,CAAC,MAAc,EAAE,MAAe,EAAE,IAAa;IACtE,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAChD,IAAI,MAAM;QAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;IAC7C,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACxB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,6IAA6I;AAC7I,MAAM,UAAU,eAAe;IAC7B,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAE,QAAQ,CAAC,MAAM,CAAsB,CAAC,CAAC,CAAC,IAAI,CAAC;IACrE,IAAI,MAAM,IAAI,QAAQ,IAAI,KAAK;QAAE,SAAS,CAAC,UAAU,EAAE,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AACzG,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,IAAa,EACb,EAAE,MAAM,GAAG,UAAU,EAAE,KAA0B,EAAE;IAEnD,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7E,IAAI,QAAQ,CAAC,MAAM;QAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC;IACjF,OAAO,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;AACtE,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,WAAW,CACzB,QAAQ,GAAG,EAAE,EACb,EAAE,MAAM,GAAG,UAAU,EAAE,KAA0B,EAAE;IAEnD,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC1C,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IAC5C,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,OAAO,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IACrC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;AAC9C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CACvB,QAAgB,EAChB,MAAkB,EAClB,EAAE,MAAM,GAAG,UAAU,EAAE,KAA0B,EAAE;IAEnD,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,OAAO,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAClD,IAAI,KAAK,GAAG,EAAE,CAAC;IACf,KAAK,MAAM,KAAK,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1C,IAAI,IAAa,CAAC;QAClB,IAAI,CAAC;YACH,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,EAAE,KAAK,EAAE,GAAG,QAAQ,KAAM,KAAe,CAAC,OAAO,EAAE,EAAE,CAAC;QAC/D,CAAC;QACD,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QAC3E,IAAI,CAAC,MAAM,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QACjG,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC1B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,KAA8F,EAC9F,EAAE,MAAM,GAAG,UAAU,EAAE,KAA0B,EAAE;IAEnD,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IAC/B,MAAM,IAAI,GAAG,CAAC,KAAc,EAAE,GAAW,EAAE,EAAE,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC5G,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAClC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,KAAK,EAAE,iCAAiC,EAAE,CAAC;IAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACjC,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,sCAAsC,EAAE,CAAC;IACrG,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,wBAAwB,EAAE,CAAC;IAChF,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACxB,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;QACjF,OAAO,EAAE,KAAK,EAAE,yBAAyB,IAAI,GAAG,EAAE,CAAC;IACrD,MAAM,GAAG,GAAG,CAAC,IAAa,EAAE,EAAE,CAC5B,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,IAAkB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,CAAC,CAAC;IACpH,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAClC,MAAM,OAAO,GAAG,CAAC,GAAG,OAAO,EAAE,GAAG,MAAM,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;IACpF,IAAI,OAAO,CAAC,MAAM;QAAE,OAAO,EAAE,KAAK,EAAE,wCAAwC,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IAClH,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM;QACnC,OAAO,EAAE,KAAK,EAAE,uBAAuB,IAAI,sDAAsD,EAAE,CAAC;IACtG,MAAM,IAAI,GACR,IAAI;SACD,WAAW,EAAE;SACb,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC;SAC5B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC;IAC1C,IAAI,EAAE,GAAG,IAAI,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;QAAE,EAAE,GAAG,GAAG,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,IAAI,CAAC,EAAE,CAAC,GAAG;QACT,IAAI;QACJ,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,wCAAwC;QACzE,GAAG,CAAC,GAAG,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC;QAChC,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnF,CAAC;IACF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,IAAI,GAAG;YAAE,GAAG,CAAC,QAAQ,GAAG,EAAE,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IACxD,CAAC;IACD,OAAO,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AACrE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CACvB,EAAU,EACV,EAAE,MAAM,GAAG,UAAU,EAAE,KAA0B,EAAE;IAEnD,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IAC/B,MAAM,GAAG,GAAI,KAAK,CAAC,IAAsC,EAAE,CAAC,EAAE,CAAC,CAAC;IAChE,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,sBAAsB,EAAE,CAAC;IACxD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;IAC5B,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC;IAC9C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;QAAE,IAAI,KAAK,CAAC,QAAQ;YAAE,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC1G,OAAO,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AAC1F,CAAC;AAED,SAAS,OAAO,CAAI,IAAuB,EAAE,GAAW;IACtD,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;IACzC,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,86 @@
1
+ // The stack file shared in the team's repo: its schema, the check CI runs on it, and a pull request with what this
2
+ // Mac's copy learned since its last commit.
3
+ import { execFileSync } from 'node:child_process';
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import * as z from 'zod';
7
+ import { StackSchema, validateStack } from "../core/stack.js";
8
+ import { fail } from "../failure.js";
9
+ import { proposeFile, readFile } from "../github.js";
10
+ import { changes, proposeChanges, sourceOf } from "./linked.js";
11
+ import { shareTarget } from "./share.js";
12
+ import { currentStack } from "./stacks.js";
13
+ const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
14
+ /** The JSON Schema of stack.json, for editors (`$schema`) and the settings panels. */
15
+ export const stackSchema = () => z.toJSONSchema(StackSchema, { unrepresentable: 'any', io: 'input' });
16
+ /** The stack file's problems, every check `koppler stack set` makes; none when it is fine. */
17
+ export function checkStackFile(file) {
18
+ let data;
19
+ try {
20
+ data = JSON.parse(fs.readFileSync(file, 'utf8'));
21
+ }
22
+ catch (error) {
23
+ return [`${file} cannot be read as JSON: ${error.message}`];
24
+ }
25
+ return validateStack(data);
26
+ }
27
+ /**
28
+ * What changed from `base` to `mine`, made on `theirs`: the changes this copy made, on the file as others left it. A
29
+ * list is taken whole.
30
+ */
31
+ export function patch(base, mine, theirs) {
32
+ if (JSON.stringify(base) === JSON.stringify(mine))
33
+ return theirs;
34
+ if (!isObject(base) || !isObject(mine) || !isObject(theirs))
35
+ return mine;
36
+ const changed = [...new Set([...Object.keys(base), ...Object.keys(mine)])].filter(key => JSON.stringify(base[key]) !== JSON.stringify(mine[key]));
37
+ const kept = Object.entries(theirs).filter(([key]) => !changed.includes(key) || key in mine);
38
+ return Object.fromEntries([
39
+ ...kept.map(([key, value]) => [key, changed.includes(key) ? patch(base[key], mine[key], value) : value]),
40
+ ...changed.filter(key => key in mine && !(key in theirs)).map(key => [key, mine[key]]),
41
+ ]);
42
+ }
43
+ const git = (cwd, ...args) => execFileSync('git', ['-C', cwd, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
44
+ /** `koppler stack propose`: a pull request on the stack's repo with what the copy here changed since its last commit. */
45
+ export async function proposeStack() {
46
+ const dir = currentStack() ?? fail('no stack in use (koppler use)');
47
+ if (sourceOf(dir)) {
48
+ const done = await proposeChanges(dir);
49
+ if (done.error !== undefined)
50
+ fail(done.error);
51
+ console.log(`PROPOSED: ${done.url}`);
52
+ return;
53
+ }
54
+ const target = await shareTarget(dir);
55
+ if (target.error !== undefined)
56
+ fail(target.error);
57
+ const file = path.join(dir, 'stack.json');
58
+ const mine = JSON.parse(fs.readFileSync(file, 'utf8'));
59
+ let base;
60
+ try {
61
+ base = JSON.parse(git(dir, 'show', `HEAD:./stack.json`));
62
+ }
63
+ catch {
64
+ fail(`stack.json is not committed in ${dir} yet: commit it first`);
65
+ }
66
+ const list = changes(base, mine);
67
+ if (!list.length)
68
+ fail('Nothing to propose: stack.json matches its last commit.');
69
+ const text = await readFile(target.file);
70
+ if (text === null)
71
+ fail(`Could not read the stack on ${target.repo}. Check that gh is signed in.`);
72
+ const next = patch(base, mine, JSON.parse(text));
73
+ const problems = validateStack(next);
74
+ if (problems.length)
75
+ fail(`The stack would not pass its check: ${problems.slice(0, 3).join('; ')}`);
76
+ const name = typeof mine.name === 'string' ? mine.name : 'stack';
77
+ const opened = await proposeFile(target.file, `${JSON.stringify(next, null, 2)}\n`, {
78
+ branch: `koppler/stack-${Date.now().toString(36)}`,
79
+ title: `${name}: ${String(list.length)} ${list.length === 1 ? 'change' : 'changes'} from Koppler`,
80
+ body: `What Koppler learned while running these apps locally:\n\n${list.map(line => `- ${line}`).join('\n')}\n\nNames and addresses only; no secret values.`,
81
+ });
82
+ if (opened.error !== undefined)
83
+ fail(opened.error);
84
+ console.log(`PROPOSED ${String(list.length)} ${list.length === 1 ? 'change' : 'changes'} on ${target.repo}: ${opened.url}`);
85
+ }
86
+ //# sourceMappingURL=stack-share.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stack-share.js","sourceRoot":"","sources":["../../src/engine/stack-share.ts"],"names":[],"mappings":"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"}