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,223 @@
1
+ // Waits for Send/Stop prompts from the standalone canvas, prints them as JSON lines and consumes them.
2
+ // While it waits, temp/listener.json says an agent session is connected, so the canvas can show it.
3
+ // Usage: koppler listen take connect this session: takes over from any other, and prints this session's id first
4
+ // koppler listen as <id> listen again as that session; refused once another session has taken over, or after a disconnect
5
+ // koppler listen stop stop whichever session is listening (disconnect); only take connects again
6
+ // Run it in the background; it exits once prompts arrive.
7
+ import { execFileSync } from 'node:child_process';
8
+ import crypto from 'node:crypto';
9
+ import fs from 'node:fs';
10
+ import path from 'node:path';
11
+ import { tempDir } from "../paths.js";
12
+ import { Failure } from "../failure.js";
13
+ import { writeJson } from "../json-files.js";
14
+ const startOf = (pid) => {
15
+ try {
16
+ const started = execFileSync('ps', ['-p', String(pid), '-o', 'lstart='], {
17
+ encoding: 'utf8',
18
+ stdio: ['ignore', 'pipe', 'ignore'],
19
+ });
20
+ return started.trim() || null;
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ };
26
+ /** The listener a record names, if it still runs. */
27
+ export const listenerPid = (record) => record?.pid && record.started && startOf(record.pid) === record.started ? record.pid : null;
28
+ const stop = (pid) => {
29
+ try {
30
+ process.kill(pid, 'SIGTERM');
31
+ }
32
+ catch {
33
+ // It ended by itself.
34
+ }
35
+ };
36
+ const stamp = () => new Date().toISOString();
37
+ const newSession = () => crypto.randomBytes(4).toString('hex');
38
+ const REPLACED = 'Another session is connected to the canvas now, or this one was disconnected. This one no longer gets Sends.';
39
+ /** Ends the agent's work on what it took, when that was no run (a fill request, an answer): true when it was working. */
40
+ export function finishTaken() {
41
+ const file = path.join(tempDir(), 'listener.json');
42
+ try {
43
+ const { busy, ...rest } = JSON.parse(fs.readFileSync(file, 'utf8'));
44
+ if (!busy)
45
+ return false;
46
+ writeJson(file, rest);
47
+ return true;
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ }
53
+ export async function listen(argv) {
54
+ const temp = tempDir();
55
+ const inbox = path.join(temp, 'prompts.jsonl');
56
+ const taking = inbox + '.taking';
57
+ const listener = path.join(temp, 'listener.json');
58
+ const lock = listener + '.lock';
59
+ const read = () => {
60
+ try {
61
+ return JSON.parse(fs.readFileSync(listener, 'utf8'));
62
+ }
63
+ catch {
64
+ return null;
65
+ }
66
+ };
67
+ const write = (record) => {
68
+ writeJson(listener, record);
69
+ };
70
+ // Who is connected changes in one step at a time: a session being replaced cannot undo what the new one just wrote.
71
+ function locked(work) {
72
+ fs.mkdirSync(temp, { recursive: true });
73
+ for (let tries = 0;; tries++) {
74
+ try {
75
+ fs.mkdirSync(lock);
76
+ break;
77
+ }
78
+ catch {
79
+ // A lock left by a process that died is taken over.
80
+ const age = Date.now() - (fs.statSync(lock, { throwIfNoEntry: false })?.mtimeMs ?? 0);
81
+ if (tries > 200 || age > 5000)
82
+ fs.rmSync(lock, { recursive: true, force: true });
83
+ else
84
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
85
+ }
86
+ }
87
+ try {
88
+ return work();
89
+ }
90
+ finally {
91
+ fs.rmSync(lock, { recursive: true, force: true });
92
+ }
93
+ }
94
+ if (argv[0] === 'stop') {
95
+ const was = locked(() => {
96
+ const current = read();
97
+ const other = listenerPid(current);
98
+ // The marker stays, so a session listening again as itself is told it was disconnected.
99
+ write({ pid: null, disconnected: stamp() });
100
+ if (other && other !== process.pid)
101
+ stop(other);
102
+ return current && !current.disconnected;
103
+ });
104
+ console.log(was
105
+ ? 'Disconnected: Sends from the canvas now wait until an agent session connects.'
106
+ : 'No agent session was connected.');
107
+ return;
108
+ }
109
+ // One session listens at a time, so a Send never lands in a session the user is not looking at. A session listening
110
+ // again names itself, so one the user has moved away from cannot take the connection back.
111
+ const as = argv[0] === 'as' ? argv[1] : undefined;
112
+ const takeOver = argv[0] === 'take';
113
+ const joined = locked(() => {
114
+ const current = read();
115
+ const running = listenerPid(current);
116
+ const other = running && running !== process.pid ? running : null;
117
+ if (!takeOver && current?.disconnected)
118
+ return {
119
+ refused: 'This session was disconnected from the canvas. It no longer gets Sends; run /koppler-run connect here to connect again.',
120
+ };
121
+ if (!takeOver && (as ? current?.session && current.session !== as : other)) {
122
+ const from = current?.since ? ` (since ${current.since})` : '';
123
+ return {
124
+ refused: `Another session is connected to the canvas now${from}. This one no longer gets Sends; run /koppler-run connect here to take it back.`,
125
+ };
126
+ }
127
+ const since = other || !current?.since ? stamp() : current.since;
128
+ const session = takeOver ? newSession() : (as ?? current?.session ?? newSession());
129
+ // Registered before the other is stopped: on its way out it sees it is no longer the one connected.
130
+ write({ pid: process.pid, started: startOf(process.pid), since, cwd: process.cwd(), session });
131
+ if (other)
132
+ stop(other);
133
+ return { since, session };
134
+ });
135
+ if (joined.refused !== undefined)
136
+ throw new Failure(joined.refused, 3);
137
+ const { since, session } = joined;
138
+ if (takeOver)
139
+ console.log(`connected as session ${session}; listen again with: koppler listen as ${session}`);
140
+ const release = () => {
141
+ locked(() => {
142
+ if (read()?.pid === process.pid)
143
+ fs.rmSync(listener, { force: true });
144
+ });
145
+ };
146
+ for (const signal of ['SIGTERM', 'SIGINT', 'SIGHUP'])
147
+ process.on(signal, () => {
148
+ try {
149
+ release();
150
+ }
151
+ catch {
152
+ // Ending anyway.
153
+ }
154
+ process.exit(0);
155
+ });
156
+ // True once prompts were handed over; a session that was replaced or disconnected meanwhile takes nothing and ends.
157
+ const take = () => locked(() => {
158
+ if (read()?.session !== session)
159
+ throw new Failure(REPLACED, 3);
160
+ // What a listener that died half-way left behind is handed over first, never written over.
161
+ let text = '';
162
+ try {
163
+ text = fs.readFileSync(taking, 'utf8');
164
+ }
165
+ catch {
166
+ // Nothing was left behind.
167
+ }
168
+ if (!text.trim()) {
169
+ try {
170
+ fs.renameSync(inbox, taking);
171
+ }
172
+ catch {
173
+ return false;
174
+ }
175
+ text = fs.readFileSync(taking, 'utf8');
176
+ }
177
+ const picked = pickPrompts(text.split('\n'));
178
+ // Each prompt handed to the agent is marked in the run history, so the canvas stops showing it as waiting.
179
+ for (const prompt of picked) {
180
+ const id = prompt.data?.['id'];
181
+ if (typeof id !== 'string' || !/^[a-z0-9]{6,40}$/.test(id))
182
+ continue;
183
+ const event = JSON.stringify({ id, event: 'picked', at: stamp() });
184
+ fs.appendFileSync(path.join(temp, 'history.jsonl'), event + '\n', { mode: 0o600 });
185
+ }
186
+ process.stdout.write(picked.map(prompt => JSON.stringify(prompt) + '\n').join(''));
187
+ fs.unlinkSync(taking);
188
+ // Still connected while the session works on it: the next listen keeps the same "since".
189
+ try {
190
+ write({ pid: null, since, busy: stamp(), cwd: process.cwd(), session });
191
+ }
192
+ catch {
193
+ // The prompts are out; the marker is a courtesy.
194
+ }
195
+ return true;
196
+ });
197
+ while (!take())
198
+ await new Promise(done => setTimeout(done, 1000));
199
+ }
200
+ /**
201
+ * What a listener hands the agent from the queue: of the Sends and Stops only the newest (it replaces the rest, which
202
+ * then show as replaced), every other prompt as it is; each with the minutes it waited, so a stale one is asked about first.
203
+ */
204
+ export function pickPrompts(lines, now = Date.now()) {
205
+ const all = lines.flatMap(line => {
206
+ try {
207
+ const prompt = JSON.parse(line);
208
+ return prompt ? [prompt] : [];
209
+ }
210
+ catch {
211
+ return [];
212
+ }
213
+ });
214
+ const boards = all.filter(prompt => prompt.tag === 'koppler' || prompt.tag === 'koppler-stop');
215
+ const newest = boards.at(-1);
216
+ return all
217
+ .filter(prompt => !boards.includes(prompt) || prompt === newest)
218
+ .map(prompt => ({
219
+ ...prompt,
220
+ waited: Math.max(0, Math.round((now - Date.parse(prompt.at ?? '')) / 60000)) || 0,
221
+ }));
222
+ }
223
+ //# sourceMappingURL=listen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listen.js","sourceRoot":"","sources":["../../src/engine/listen.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ // Fields of a stack the user locked on this Mac: no command, no canvas edit and no agent changes them until the user
2
+ // unlocks them, on the canvas or in their own terminal. Kept beside the approvals, out of the shared stack file.
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { writeJson } from "../json-files.js";
6
+ import { getAt, parsePath } from "../core/stack-path.js";
7
+ import { configDir } from "../paths.js";
8
+ const locksFile = () => path.join(configDir(), 'locked-fields.json');
9
+ const all = () => {
10
+ try {
11
+ return JSON.parse(fs.readFileSync(locksFile(), 'utf8'));
12
+ }
13
+ catch {
14
+ return {};
15
+ }
16
+ };
17
+ /** The locked fields of the stack kept at this file, as paths. */
18
+ export const lockedPaths = (source) => all()[source] ?? [];
19
+ export function setLock(source, at, on) {
20
+ if ('error' in parsePath(at))
21
+ return { error: `${at} is not a path such as apps.orders.url` };
22
+ const rest = lockedPaths(source).filter(one => one !== at);
23
+ writeJson(locksFile(), { ...all(), [source]: on ? [...rest, at] : rest });
24
+ return {};
25
+ }
26
+ /** The first locked field the change would alter, or null when it leaves every one as it was. */
27
+ export function brokenLock(source, before, after) {
28
+ for (const at of lockedPaths(source)) {
29
+ const parts = parsePath(at);
30
+ if ('error' in parts)
31
+ continue;
32
+ if (JSON.stringify(getAt(before, parts)) !== JSON.stringify(getAt(after, parts)))
33
+ return at;
34
+ }
35
+ return null;
36
+ }
37
+ export const LOCKED = (at) => `${at} is locked; unlock it on the canvas, or with koppler stack unlock ${at} in your own terminal`;
38
+ //# sourceMappingURL=locks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locks.js","sourceRoot":"","sources":["../../src/engine/locks.ts"],"names":[],"mappings":"AAAA,qHAAqH;AACrH,iHAAiH;AACjH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,oBAAoB,CAAC,CAAC;AACrE,MAAM,GAAG,GAAG,GAAyC,EAAE;IACrD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,EAAE,MAAM,CAAC,CAAyC,CAAC;IAClG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC,CAAC;AAEF,kEAAkE;AAClE,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAc,EAAY,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;AAE7E,MAAM,UAAU,OAAO,CAAC,MAAc,EAAE,EAAU,EAAE,EAAW;IAC7D,IAAI,OAAO,IAAI,SAAS,CAAC,EAAE,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,wCAAwC,EAAE,CAAC;IAC9F,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC;IAC3D,SAAS,CAAC,SAAS,EAAE,EAAE,EAAE,GAAG,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1E,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,MAAe,EAAE,KAAc;IACxE,KAAK,MAAM,EAAE,IAAI,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;QAC5B,IAAI,OAAO,IAAI,KAAK;YAAE,SAAS;QAC/B,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAAE,OAAO,EAAE,CAAC;IAC9F,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,EAAU,EAAE,EAAE,CACnC,GAAG,EAAE,qEAAqE,EAAE,uBAAuB,CAAC"}
@@ -0,0 +1,177 @@
1
+ // Messages from the agent to the canvas, and the user's answers back. Plain text only: the canvas never renders them as code.
2
+ // Usage: koppler say [--file <json>] a message, as JSON on stdin or in a file (see sanitizeMessage)
3
+ // koppler say --answered <id> [--text <how>] the user answered in the chat instead; the canvas shows it as answered
4
+ import crypto from 'node:crypto';
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { flagValue } from "../cli/output.js";
8
+ import { fail } from "../failure.js";
9
+ import { tempDir } from "../paths.js";
10
+ import { runs } from "./history.js";
11
+ import { APP_ID, RUN_ID } from "../ids.js";
12
+ const file = () => path.join(tempDir(), 'messages.jsonl');
13
+ const text = (value, max) => (typeof value === 'string' ? value.trim().slice(0, max) : '');
14
+ const listOf = (value) => (Array.isArray(value) ? value : []);
15
+ const texts = (value, most, max) => listOf(value)
16
+ .slice(0, most)
17
+ .map(item => text(item, max))
18
+ .filter(Boolean);
19
+ const matching = (pattern, value) => typeof value === 'string' && pattern.test(value);
20
+ const KINDS = ['question', 'result', 'failure', 'info'];
21
+ const append = (line) => {
22
+ fs.appendFileSync(file(), JSON.stringify(line) + '\n', { mode: 0o600 });
23
+ };
24
+ /**
25
+ * A message as it may be shown: only known fields, each cut to size.
26
+ * { kind, title, summary?, run?, apps?: [ids], sections?: [{ title, items: [text], commands?: [text] }], choices?: [{ id, label, detail? }] }
27
+ */
28
+ export function sanitizeMessage(given) {
29
+ if (!given || typeof given !== 'object')
30
+ return { error: 'a message is a JSON object' };
31
+ const input = given;
32
+ const title = text(input['title'], 120);
33
+ if (!title)
34
+ return { error: 'a message needs a title' };
35
+ const asked = listOf(input['choices']);
36
+ const kind = KINDS.includes(input['kind']) ? input['kind'] : asked.length ? 'question' : 'info';
37
+ const sections = listOf(input['sections'])
38
+ .slice(0, 8)
39
+ .map((section) => {
40
+ const { title: heading, items, commands } = (section ?? {});
41
+ const run = texts(commands, 6, 1000);
42
+ return { title: text(heading, 80), items: texts(items, 12, 400), ...(run.length && { commands: run }) };
43
+ })
44
+ .filter(section => section.title || section.items.length || section.commands);
45
+ const choices = asked
46
+ .slice(0, 6)
47
+ .map((choice, at) => {
48
+ const { id, label, detail } = (choice ?? {});
49
+ const more = text(detail, 300);
50
+ return {
51
+ id: matching(/^[a-z0-9-]{1,32}$/, id) ? id : String(at + 1),
52
+ label: text(label, 80),
53
+ ...(more && { detail: more }),
54
+ };
55
+ })
56
+ .filter(choice => choice.label);
57
+ const summary = text(input['summary'], 800);
58
+ const { run, apps } = input;
59
+ return {
60
+ message: {
61
+ kind,
62
+ title,
63
+ ...(summary && { summary }),
64
+ ...(matching(RUN_ID, run) && { run }),
65
+ ...(Array.isArray(apps) && { apps: apps.filter(app => matching(APP_ID, app)).slice(0, 30) }),
66
+ ...(sections.length && { sections }),
67
+ ...(choices.length && { choices }),
68
+ },
69
+ };
70
+ }
71
+ /**
72
+ * What Koppler says on the canvas when it could not carry out a run or a Stop itself: its own last words, with each
73
+ * command the user has to run (the indented lines) set apart, so the canvas gives it a Copy button.
74
+ */
75
+ export function refusal(run, kind, said) {
76
+ const lines = said.split('\n');
77
+ const last = lines.slice(Math.max(0, lines.findLastIndex(line => line.startsWith('koppler: '))));
78
+ const isCommand = (line) => /^ {2}\S/.test(line);
79
+ const commands = last.filter(isCommand).map(line => line.trim());
80
+ // What comes before the commands says why; what comes after says what to do next.
81
+ const first = last.findIndex(isCommand);
82
+ const why = (first < 0 ? last : last.slice(0, first)).join(' ').replace(/^koppler: /, '');
83
+ const after = first < 0 ? [] : last.slice(first).filter(line => line.trim() && !isCommand(line));
84
+ return {
85
+ kind: 'failure',
86
+ from: 'koppler',
87
+ run,
88
+ title: `Koppler could not finish this ${kind === 'stop' ? 'Stop' : 'run'}`,
89
+ ...(text(why, 800) && { summary: text(why, 800) }),
90
+ ...(commands.length && {
91
+ sections: [{ title: '', items: after.slice(0, 12).map(line => text(line, 400)), commands: commands.slice(0, 6) }],
92
+ }),
93
+ };
94
+ }
95
+ export const appendMessage = (message) => {
96
+ const entry = { id: crypto.randomBytes(6).toString('hex'), at: new Date().toISOString(), ...message };
97
+ fs.mkdirSync(path.dirname(file()), { recursive: true });
98
+ append(entry);
99
+ return entry;
100
+ };
101
+ export const appendAnswer = (id, answer) => {
102
+ append({ answer: id, at: new Date().toISOString(), ...answer });
103
+ };
104
+ /** The latest messages, each with its answer when there is one. A question about a run a newer Send replaced is closed, and so is what Koppler said about it. */
105
+ export function readMessages(limit = 20, history = runs()) {
106
+ let raw;
107
+ try {
108
+ raw = fs.readFileSync(file(), 'utf8');
109
+ }
110
+ catch {
111
+ return [];
112
+ }
113
+ const list = [];
114
+ const byId = new Map();
115
+ for (const line of raw.split('\n')) {
116
+ let entry;
117
+ try {
118
+ entry = JSON.parse(line);
119
+ }
120
+ catch {
121
+ continue;
122
+ }
123
+ if (!entry)
124
+ continue;
125
+ const { answer, at, choice, text: said, via } = entry;
126
+ if (answer) {
127
+ const message = byId.get(answer);
128
+ if (message && !message.answered)
129
+ message.answered = { at, ...(choice && { choice }), ...(said && { text: said }), via: via ?? 'canvas' };
130
+ }
131
+ else if (entry['id'] && entry['title']) {
132
+ const message = entry;
133
+ byId.set(message.id, message);
134
+ list.push(message);
135
+ }
136
+ }
137
+ const order = history.map(run => run.id);
138
+ for (const message of list) {
139
+ const at = message.run ? order.indexOf(message.run) : -1;
140
+ const next = at >= 0 ? history[at + 1] : undefined;
141
+ // What Koppler could not do in a run is history once another run was sent.
142
+ const closes = message.kind === 'question' || message.from === 'koppler';
143
+ if (closes && !message.answered && next)
144
+ message.answered = { at: next.at, via: 'replaced' };
145
+ }
146
+ return list.slice(-limit);
147
+ }
148
+ /** Clears the panel's history. A question still waiting for an answer is kept, so the agent's open question is never lost. */
149
+ export function clearMessages() {
150
+ const keep = readMessages(1000)
151
+ .filter(message => message.kind === 'question' && !message.answered)
152
+ .map(({ answered, ...message }) => message);
153
+ fs.writeFileSync(file(), keep.map(message => JSON.stringify(message) + '\n').join(''), { mode: 0o600 });
154
+ return { cleared: true, kept: keep.length };
155
+ }
156
+ export function sayCommand(args) {
157
+ if (args.includes('--answered')) {
158
+ const id = flagValue(args, '--answered');
159
+ if (!id || !readMessages(200).some(message => message.id === id))
160
+ fail(`no message ${String(id)}`);
161
+ appendAnswer(id, { text: text(flagValue(args, '--text') ?? 'answered in the chat', 400), via: 'chat' });
162
+ console.log(`Marked ${id} as answered.`);
163
+ return;
164
+ }
165
+ let input;
166
+ try {
167
+ input = JSON.parse(fs.readFileSync(flagValue(args, '--file') ?? 0, 'utf8'));
168
+ }
169
+ catch {
170
+ fail('give the message as JSON on stdin or with --file');
171
+ }
172
+ const cleaned = sanitizeMessage(input);
173
+ if (cleaned.error !== undefined)
174
+ fail(cleaned.error);
175
+ console.log(`Shown on the canvas as ${appendMessage(cleaned.message).id}`);
176
+ }
177
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/engine/messages.ts"],"names":[],"mappings":"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"}