koppler 0.0.5 → 0.0.6
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.
- package/dist/.built +1 -1
- package/dist/cli/doctor.js +1 -1
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/running.js +62 -1
- package/dist/cli/running.js.map +1 -1
- package/dist/core/comments.js +7 -0
- package/dist/core/comments.js.map +1 -0
- package/dist/core/logfmt.js +30 -20
- package/dist/core/logfmt.js.map +1 -1
- package/dist/core/shared-log.js +29 -0
- package/dist/core/shared-log.js.map +1 -0
- package/dist/engine/processes.js +17 -4
- package/dist/engine/processes.js.map +1 -1
- package/dist/engine/run.js +6 -3
- package/dist/engine/run.js.map +1 -1
- package/dist/engine/traces.js +51 -0
- package/dist/engine/traces.js.map +1 -1
- package/dist/engine/up.js +3 -2
- package/dist/engine/up.js.map +1 -1
- package/dist/engine/watch.js +2 -3
- package/dist/engine/watch.js.map +1 -1
- package/dist/koppler.mjs +12 -4
- package/dist/koppler.mjs.map +1 -1
- package/dist/page/assets/{index-C3Zy4K9N.js → index-B0cSsUor.js} +35 -34
- package/dist/page/assets/{index-DntXDvS8.css → index-DxC9KFhp.css} +1 -1
- package/dist/page/index.html +2 -2
- package/dist/server/cable-test.js +30 -0
- package/dist/server/cable-test.js.map +1 -1
- package/dist/server/files.js +16 -0
- package/dist/server/files.js.map +1 -1
- package/dist/server/mobile.js +1 -1
- package/dist/server/mobile.js.map +1 -1
- package/dist/server/routes.js +30 -2
- package/dist/server/routes.js.map +1 -1
- package/dist/server/sanitize.js +2 -0
- package/dist/server/sanitize.js.map +1 -1
- package/dist/server/shared-log.js +104 -0
- package/dist/server/shared-log.js.map +1 -0
- package/dist/stamp.mjs +37 -0
- package/dist/stamp.mjs.map +1 -0
- package/package.json +1 -1
- package/skills/koppler-run/SKILL.md +5 -4
- package/skills/koppler-run/VERSION +1 -1
- package/skills/koppler-run/references/permissions.md +13 -0
- package/skills/koppler-run/references/status.md +1 -1
package/dist/stamp.mjs
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Runs an app's command and keeps when each line it writes arrives, for the log of every app in time order: its output
|
|
2
|
+
// goes to its log unchanged, and each line's time (ms since the epoch) to the times file beside it, one per line.
|
|
3
|
+
// Usage (through koppler run): stamp.mjs <log> -- <command...>
|
|
4
|
+
import { spawn } from 'node:child_process';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
const [log = '', dash, ...command] = process.argv.slice(2);
|
|
7
|
+
if (!log || dash !== '--' || command.length === 0) {
|
|
8
|
+
console.error('usage: stamp.mjs <log> -- <command...>');
|
|
9
|
+
process.exit(2);
|
|
10
|
+
}
|
|
11
|
+
const out = fs.openSync(log, 'a', 0o600);
|
|
12
|
+
const times = fs.openSync(`${log}.times`, 'a', 0o600);
|
|
13
|
+
// Its errors and its output in the order it wrote them: one stream, as when both went straight to the log.
|
|
14
|
+
const child = spawn('sh', ['-c', 'exec "$@" 2>&1', 'sh', ...command], { stdio: ['ignore', 'pipe', 'ignore'] });
|
|
15
|
+
child.stdout.on('data', (chunk) => {
|
|
16
|
+
fs.writeSync(out, chunk);
|
|
17
|
+
let lines = 0;
|
|
18
|
+
for (const byte of chunk)
|
|
19
|
+
if (byte === 10)
|
|
20
|
+
lines++;
|
|
21
|
+
if (lines)
|
|
22
|
+
fs.writeSync(times, `${String(Date.now())}\n`.repeat(lines));
|
|
23
|
+
});
|
|
24
|
+
for (const signal of ['SIGTERM', 'SIGINT', 'SIGHUP'])
|
|
25
|
+
process.on(signal, () => {
|
|
26
|
+
child.kill(signal);
|
|
27
|
+
});
|
|
28
|
+
child.on('error', error => {
|
|
29
|
+
fs.writeSync(out, `${error.message}\n`);
|
|
30
|
+
fs.writeSync(times, `${String(Date.now())}\n`);
|
|
31
|
+
process.exit(127);
|
|
32
|
+
});
|
|
33
|
+
// Ends as the app ends, with its exit code: a step that runs to completion is judged by it.
|
|
34
|
+
child.on('close', (code, signal) => {
|
|
35
|
+
process.exit(code ?? (signal ? 128 : 1));
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=stamp.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stamp.mjs","sourceRoot":"","sources":["../src/stamp.mts"],"names":[],"mappings":"AAAA,uHAAuH;AACvH,kHAAkH;AAClH,+DAA+D;AAC/D,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,MAAM,CAAC,GAAG,GAAG,EAAE,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC3D,IAAI,CAAC,GAAG,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;IAClD,OAAO,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;IACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AACD,MAAM,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AACzC,MAAM,KAAK,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,GAAG,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AACtD,2GAA2G;AAC3G,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC/G,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;IACxC,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACzB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,IAAI,IAAI,KAAK,EAAE;YAAE,KAAK,EAAE,CAAC;IACnD,IAAI,KAAK;QAAE,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAC1E,CAAC,CAAC,CAAC;AACH,KAAK,MAAM,MAAM,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAU;IAC3D,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;QACtB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrB,CAAC,CAAC,CAAC;AACL,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE;IACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;IACxC,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAC/C,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC;AACH,4FAA4F;AAC5F,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;IACjC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3C,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@ While this session is connected to the canvas (see Connect), anything that needs
|
|
|
11
11
|
When it is about a run, give its id; otherwise leave `run` out:
|
|
12
12
|
`koppler say` with JSON on stdin: `{ "kind": "question" | "result" | "failure", "run"?: "<data.id of the run>", "title", "summary", "sections": [{ "title", "items": [...], "commands": [...] }], "choices": [{ "id", "label", "detail" }] }`.
|
|
13
13
|
Put anything the user has to run in a section's `commands`, never in `items`: the canvas shows each command in a box with a Copy button.
|
|
14
|
+
When something blocks you or a run (Claude Code's auto mode refusing a command, a permission prompt denied, an expired sign-in), the message also says how to keep it from happening again, in a section titled "So it does not happen again", when there is a way: for auto mode, the allow rule from `references/permissions.md` as a command to copy, and where it goes. Say it in the chat reply too.
|
|
14
15
|
Short plain sentences; no Markdown, it is shown as text. The same question also goes in your chat reply.
|
|
15
16
|
An answer from the canvas arrives as a `koppler-reply` prompt (`data.message`, `data.choice` and its `label`, or `data.text`): act on it like the user's own reply. If the user answers in the chat instead, run `koppler say --answered <message id>` so the canvas stops asking.
|
|
16
17
|
|
|
@@ -88,7 +89,7 @@ On that prompt:
|
|
|
88
89
|
| 3 | Per app: prerequisites (AWS SSO, tunnels, hosts, dev certs, files) | `references/preparing-nodes.md` |
|
|
89
90
|
| 3b | What the user has to do (AWS sign-in, hosts entries, the HTTPS certificate, secrets, `sudo` steps): `koppler doctor <every local app> --env <env>` checks each one live and words it for someone who never ran the backend. Hand the user exactly its numbered steps (secrets in their own terminal, never with `!`); with `--json`, put them on the cards as `user` steps with `copy` | `references/secrets.md` |
|
|
90
91
|
| 4 | Run (auto) or checklist (manual) | inline |
|
|
91
|
-
| 4a | Something does not answer: `koppler logs <name>` (secrets masked, `-f` follows), and `koppler doctor --flow` tests every connection of what runs, like the canvas's Flow check. To see where one request failed across the apps, `koppler traces start`, restart the apps involved, repeat the request, then `koppler traces open <app>` | inline |
|
|
92
|
+
| 4a | Something does not answer: `koppler logs <name>` (secrets masked, `-f` follows; `koppler logs --all` shows every app on the board in one stream, to follow a request across apps), and `koppler doctor --flow` tests every connection of what runs, like the canvas's Flow check. To see where one request failed across the apps, `koppler traces start`, restart the apps involved, repeat the request, then `koppler traces open <app>` | inline |
|
|
92
93
|
| 4b | Canvas open (`koppler canvas`): keep each card's checklist current with `koppler status step <app> "<label>" <state>`, so it shows what is done, what waits for the user and what failed | `references/status.md` |
|
|
93
94
|
| 5 | Stop and revert: Koppler does it (`koppler down`); you only finish what it left (canvas button, tag `koppler-stop`, or asked in chat) | `references/action-log.md` |
|
|
94
95
|
| 6 | What a run will do, and marking it done: `koppler plan`, `koppler history done`, `koppler history` | inline, section 1 |
|
|
@@ -127,7 +128,7 @@ You get the run only when Koppler could not do all of it (the prompt says what i
|
|
|
127
128
|
A cable left for you ("point it at ...") means its caller is not started yet: make the change, log it (`references/action-log.md`), then run the same `koppler up --run <id>` again, which starts the caller and marks the run done.
|
|
128
129
|
To check data (after a fix, for a report), propose `koppler query <app> --env <env> "<one SQL statement>"`: read-only, the stored connection, rows only. On PROD the user runs it in their own terminal and tells you what matters; never run it yourself there, the rows are production data.
|
|
129
130
|
Allowing the safe `koppler` commands without prompts: `references/permissions.md`.
|
|
130
|
-
When the user asks in chat, start with `koppler up <apps> --cable <caller>:<callee>... --env <env>` too: one `--cable` per call that should reach a local app. It is recorded like Run on the canvas and added to what already runs on that environment, so the canvas, the history and Stop all see it. A single `koppler run <app>` is not part of any run; the canvas shows it as running outside a run, so use it only for one-off commands, never to start a board. If you are blocked from starting something (auto mode, PROD), never hand over a list of commands: point the user to press Run on the canvas, or give the single `koppler up <apps> --env <env>` line.
|
|
131
|
+
When the user asks in chat, start with `koppler up <apps> --cable <caller>:<callee>... --env <env>` too: one `--cable` per call that should reach a local app. It is recorded like Run on the canvas and added to what already runs on that environment, so the canvas, the history and Stop all see it. A single `koppler run <app>` is not part of any run; the canvas shows it as running outside a run, so use it only for one-off commands, never to start a board. If you are blocked from starting something (auto mode, PROD), never hand over a list of commands: point the user to press Run on the canvas, or give the single `koppler up <apps> --env <env>` line, and say that an answer on the canvas cannot lift the block, only the allow rule in `references/permissions.md` can.
|
|
131
132
|
|
|
132
133
|
### A board asked for in chat
|
|
133
134
|
|
|
@@ -145,9 +146,9 @@ Plan tunnels with `koppler plan <every local app> --env <env>` and follow it: on
|
|
|
145
146
|
|
|
146
147
|
- `auto`: start tunnels and apps with `koppler`. Its processes are detached: they outlive agent background-job limits and Claude Code restarts, log where the canvas reads, and stop by name.
|
|
147
148
|
- Stack apps of any kind: `koppler run <app> [--env <env>] [--device ios|android|phone|<id>]` (each kind has its own rules): .NET runs its `-LOCAL` profile; Flutter builds and launches on the device, keeps its log on the card and closes the app on stop; Node and web front ends run `npm run` of their `script` (else `dev` or `start`), with the framework's default port and, with `--lan`, its own host flag. It loads the app's stored secrets (the only way the agent may start an app with them; never `with-secrets`, which stays denied), first tests each one against its own address (so open tunnels before apps; on a production env add `--test` only when the user agreed to the extra Keychain prompt), and refuses a port that is already held. `--dry-run` prints the command without starting it, e.g. for a manual-mode checklist.
|
|
148
|
-
- Anything else: `koppler run <app or tunnel name> [--cwd <repo>] [--port <p>] [--bind <address>] [--
|
|
149
|
+
- Anything else: `koppler run <app or tunnel name> [--cwd <repo>] [--port <p>] [--bind <address>] [--keep-alive] -- <command>`; an arbitrary command never gets secrets. Use `--keep-alive` for tunnels (it starts the command again whenever it exits; to restart something that runs, `koppler stop` it first), and `--bind` for one that keeps its port on its own address (`koppler plan` prints it).
|
|
149
150
|
Wait for each app's `url` to answer (or a startup error in its log) before the next. An `ide` node still runs from the CLI unless the user said they start it in the IDE.
|
|
150
|
-
A Flutter app the user starts from the IDE still gets its log on the card: `koppler run <app> --cwd <repo> --
|
|
151
|
+
A Flutter app the user starts from the IDE still gets its log on the card: `koppler run <app> --cwd <repo> --keep-alive -- fvm flutter logs -d <device id>` (`references/status.md`).
|
|
151
152
|
When `koppler run` reports a held port: if it is the same app (same repo binary, expected `LAUNCH_PROFILE`/env, e.g. started from Rider), reuse it; if it is anything else, ask before stopping it.
|
|
152
153
|
- `manual`: the checklist below, one block per app.
|
|
153
154
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.0.
|
|
1
|
+
0.0.6
|
|
@@ -23,3 +23,16 @@ Leave these asking every time, because they start or stop things, change code, b
|
|
|
23
23
|
`koppler stack unlock` asks the person at the terminal, like approval.
|
|
24
24
|
`koppler doctor --approve` cannot be allowed at all: it asks the person at the terminal, and refuses anywhere else.
|
|
25
25
|
On PROD the user's Run on PROD on the canvas is the confirmation; `koppler query` stays in the user's own terminal.
|
|
26
|
+
|
|
27
|
+
## Auto mode in Claude Code
|
|
28
|
+
|
|
29
|
+
In auto mode, Claude Code refuses a command that may change production, such as `koppler up` on PROD, and offers no prompt to approve it.
|
|
30
|
+
An answer on the canvas does not change that: only your Claude Code settings do.
|
|
31
|
+
To let the agent start PROD runs, allow the command in `permissions.allow`, in `~/.claude/settings.json` for every project or in the project's `.claude/settings.json`:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{ "permissions": { "allow": ["Bash(koppler up:*)"] } }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
An allowed command skips the auto mode check, on every environment, so only add it if the Run on PROD you confirm on the canvas is the check you want.
|
|
38
|
+
Without the rule, start the run yourself: press Run on the canvas again, or run the `koppler up --run <id>` the agent gives you in your own terminal.
|
|
@@ -57,7 +57,7 @@ Started through `koppler`, it keeps running across agent sessions.
|
|
|
57
57
|
|---|---|
|
|
58
58
|
| The agent or the user, through `koppler run <app> ...` | written by `koppler` |
|
|
59
59
|
| The user, in the foreground of their own terminal | `bin/run-logged <app> -- <command>` |
|
|
60
|
-
| The user, from an IDE: a Flutter app | `koppler run <app> --cwd <repo> --
|
|
60
|
+
| The user, from an IDE: a Flutter app | `koppler run <app> --cwd <repo> --keep-alive -- fvm flutter logs -d <device id>` copies the app's output from the simulator, emulator or phone (`fvm flutter devices` lists ids). It follows the app across restarts and shows lines only when the app prints |
|
|
61
61
|
| The user, from an IDE: anything else | none; the output stays in the IDE. The card still shows the port state and checklist |
|
|
62
62
|
| A web front end's browser console | not captured yet; its dev server's output is, when started through `koppler run` |
|
|
63
63
|
|