aicodeman 1.44.0 → 1.45.0
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/README.md +6 -2
- package/dist/case-rename.d.ts +65 -0
- package/dist/case-rename.d.ts.map +1 -0
- package/dist/case-rename.js +198 -0
- package/dist/case-rename.js.map +1 -0
- package/dist/cli-agent.d.ts +60 -1
- package/dist/cli-agent.d.ts.map +1 -1
- package/dist/cli-agent.js +225 -13
- package/dist/cli-agent.js.map +1 -1
- package/dist/config/auth-config.d.ts +16 -1
- package/dist/config/auth-config.d.ts.map +1 -1
- package/dist/config/auth-config.js +16 -1
- package/dist/config/auth-config.js.map +1 -1
- package/dist/host-sensors.d.ts +115 -0
- package/dist/host-sensors.d.ts.map +1 -0
- package/dist/host-sensors.js +457 -0
- package/dist/host-sensors.js.map +1 -0
- package/dist/remote-hosts.d.ts +7 -0
- package/dist/remote-hosts.d.ts.map +1 -1
- package/dist/remote-hosts.js +17 -3
- package/dist/remote-hosts.js.map +1 -1
- package/dist/remote-pane-history.d.ts.map +1 -1
- package/dist/remote-pane-history.js +2 -2
- package/dist/remote-pane-history.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +13 -0
- package/dist/session.js.map +1 -1
- package/dist/tmux-manager.d.ts +7 -3
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +47 -15
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tmux-prefix-table.d.ts +37 -5
- package/dist/tmux-prefix-table.d.ts.map +1 -1
- package/dist/tmux-prefix-table.js +40 -5
- package/dist/tmux-prefix-table.js.map +1 -1
- package/dist/tui/tui-app.d.ts +8 -1
- package/dist/tui/tui-app.d.ts.map +1 -1
- package/dist/tui/tui-app.js +49 -12
- package/dist/tui/tui-app.js.map +1 -1
- package/dist/tui/tui-client.d.ts +90 -3
- package/dist/tui/tui-client.d.ts.map +1 -1
- package/dist/tui/tui-client.js +195 -26
- package/dist/tui/tui-client.js.map +1 -1
- package/dist/web/agent-inbox-persistence.d.ts +52 -0
- package/dist/web/agent-inbox-persistence.d.ts.map +1 -0
- package/dist/web/agent-inbox-persistence.js +139 -0
- package/dist/web/agent-inbox-persistence.js.map +1 -0
- package/dist/web/agent-inbox.d.ts +140 -0
- package/dist/web/agent-inbox.d.ts.map +1 -0
- package/dist/web/agent-inbox.js +258 -0
- package/dist/web/agent-inbox.js.map +1 -0
- package/dist/web/middleware/auth.d.ts +2 -0
- package/dist/web/middleware/auth.d.ts.map +1 -1
- package/dist/web/middleware/auth.js +145 -52
- package/dist/web/middleware/auth.js.map +1 -1
- package/dist/web/public/admin-ui.js.gz +0 -0
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/app.42d0abae.js +43 -0
- package/dist/web/public/app.42d0abae.js.br +0 -0
- package/dist/web/public/app.42d0abae.js.gz +0 -0
- package/dist/web/public/approvals-ui.js.gz +0 -0
- package/dist/web/public/{constants.4866ecc3.js → constants.0b3cdde9.js} +11 -0
- package/dist/web/public/constants.0b3cdde9.js.br +0 -0
- package/dist/web/public/constants.0b3cdde9.js.gz +0 -0
- package/dist/web/public/cron-ui.js.gz +0 -0
- package/dist/web/public/entrance-animations.js.gz +0 -0
- package/dist/web/public/git-status-ui.js.gz +0 -0
- package/dist/web/public/header-divider.eb2d2463.js +1 -0
- package/dist/web/public/header-divider.eb2d2463.js.br +0 -0
- package/dist/web/public/header-divider.eb2d2463.js.gz +0 -0
- package/dist/web/public/home-sessions.js.gz +0 -0
- package/dist/web/public/host-wake-ui.js.gz +0 -0
- package/dist/web/public/i18n.1e68220d.js +2 -0
- package/dist/web/public/i18n.1e68220d.js.br +0 -0
- package/dist/web/public/i18n.1e68220d.js.gz +0 -0
- package/dist/web/public/image-input.941386e7.js.gz +0 -0
- package/dist/web/public/index.html +74 -11
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.8bc46081.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.4e8b20ac.js.gz +0 -0
- package/dist/web/public/{mobile-handlers.44adaa07.js → mobile-handlers.1c0cbcd8.js} +4 -2
- package/dist/web/public/mobile-handlers.1c0cbcd8.js.br +0 -0
- package/dist/web/public/mobile-handlers.1c0cbcd8.js.gz +0 -0
- package/dist/web/public/mobile-ime-preview.2dbaf262.js.gz +0 -0
- package/dist/web/public/mobile-overview.js.gz +0 -0
- package/dist/web/public/mobile.12fb18de.css.gz +0 -0
- package/dist/web/public/notification-manager.feb82c04.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.cbb24ba1.js → panels-ui.78c3defb.js} +76 -70
- package/dist/web/public/panels-ui.78c3defb.js.br +0 -0
- package/dist/web/public/panels-ui.78c3defb.js.gz +0 -0
- package/dist/web/public/ralph-panel.b350e2ef.js.gz +0 -0
- package/dist/web/public/ralph-wizard.b6c0dd19.js.gz +0 -0
- package/dist/web/public/readmymind-ui.js.gz +0 -0
- package/dist/web/public/reboot-restore-ui.js.gz +0 -0
- package/dist/web/public/respawn-ui.d7adcf6a.js.gz +0 -0
- package/dist/web/public/sanitize-html.e7147e76.js.gz +0 -0
- package/dist/web/public/session-lineage.js.gz +0 -0
- package/dist/web/public/{session-ui.9c1f009b.js → session-ui.ea6b1994.js} +28 -26
- package/dist/web/public/session-ui.ea6b1994.js.br +0 -0
- package/dist/web/public/session-ui.ea6b1994.js.gz +0 -0
- package/dist/web/public/{settings-ui.fea9ddae.js → settings-ui.085dd202.js} +3 -3
- package/dist/web/public/settings-ui.085dd202.js.br +0 -0
- package/dist/web/public/settings-ui.085dd202.js.gz +0 -0
- package/dist/web/public/spreadsheet-preview-worker.js.gz +0 -0
- package/dist/web/public/spreadsheet-preview.75c751de.js.gz +0 -0
- package/dist/web/public/spreadsheet-xlsx-core.js.gz +0 -0
- package/dist/web/public/styles.3b59b41d.css +1 -0
- package/dist/web/public/styles.3b59b41d.css.br +0 -0
- package/dist/web/public/styles.3b59b41d.css.gz +0 -0
- package/dist/web/public/subagent-windows.07277a58.js.gz +0 -0
- package/dist/web/public/sw.js +2 -2
- package/dist/web/public/sw.js.br +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/tab-layout-browser.2d6189f7.js.gz +0 -0
- package/dist/web/public/tab-rail-resize.7157e0bd.js.gz +0 -0
- package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.gz +0 -0
- package/dist/web/public/terminal-split.js.gz +0 -0
- package/dist/web/public/terminal-tile.js +8 -1
- package/dist/web/public/terminal-tile.js.br +0 -0
- package/dist/web/public/terminal-tile.js.gz +0 -0
- package/dist/web/public/terminal-ui.fb2a27f5.js +2 -0
- package/dist/web/public/terminal-ui.fb2a27f5.js.br +0 -0
- package/dist/web/public/terminal-ui.fb2a27f5.js.gz +0 -0
- package/dist/web/public/tile-grid.js.gz +0 -0
- package/dist/web/public/ultracode-panel.js.gz +0 -0
- package/dist/web/public/ultracode-windows.js.gz +0 -0
- package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
- package/dist/web/public/vendor/exceljs.min.js.gz +0 -0
- package/dist/web/public/vendor/fflate.min.js.gz +0 -0
- package/dist/web/public/vendor/marked.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-predictive-echo.ae5e3d5d.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/voice-input.2342c37b.js.gz +0 -0
- package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
- package/dist/web/public/webview-tabs.js.gz +0 -0
- package/dist/web/public/whats-new.json +9 -9
- package/dist/web/route-helpers.d.ts +57 -6
- package/dist/web/route-helpers.d.ts.map +1 -1
- package/dist/web/route-helpers.js +72 -4
- package/dist/web/route-helpers.js.map +1 -1
- package/dist/web/routes/case-routes.d.ts.map +1 -1
- package/dist/web/routes/case-routes.js +183 -2
- package/dist/web/routes/case-routes.js.map +1 -1
- package/dist/web/routes/inbox-routes.d.ts +43 -0
- package/dist/web/routes/inbox-routes.d.ts.map +1 -0
- package/dist/web/routes/inbox-routes.js +148 -0
- package/dist/web/routes/inbox-routes.js.map +1 -0
- package/dist/web/routes/index.d.ts +1 -0
- package/dist/web/routes/index.d.ts.map +1 -1
- package/dist/web/routes/index.js +1 -0
- package/dist/web/routes/index.js.map +1 -1
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +25 -47
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/routes/system-routes.d.ts.map +1 -1
- package/dist/web/routes/system-routes.js +23 -2
- package/dist/web/routes/system-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +26 -0
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +48 -0
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/sensitive-path.d.ts.map +1 -1
- package/dist/web/sensitive-path.js +2 -0
- package/dist/web/sensitive-path.js.map +1 -1
- package/dist/web/server.d.ts +2 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +61 -3
- package/dist/web/server.js.map +1 -1
- package/dist/web/session-wait-registry.d.ts +48 -4
- package/dist/web/session-wait-registry.d.ts.map +1 -1
- package/dist/web/session-wait-registry.js +144 -6
- package/dist/web/session-wait-registry.js.map +1 -1
- package/dist/web/sse-events.d.ts +4 -1
- package/dist/web/sse-events.d.ts.map +1 -1
- package/dist/web/sse-events.js +4 -1
- package/dist/web/sse-events.js.map +1 -1
- package/package.json +1 -1
- package/skills/codeman/SKILL.md +7 -4
- package/skills/codeman/preamble.sh +3 -3
- package/skills/codeman/reference/endpoints.md +24 -3
- package/skills/codeman/reference/verbs.md +47 -0
- package/dist/web/public/app.9314ee60.js +0 -43
- package/dist/web/public/app.9314ee60.js.br +0 -0
- package/dist/web/public/app.9314ee60.js.gz +0 -0
- package/dist/web/public/constants.4866ecc3.js.br +0 -0
- package/dist/web/public/constants.4866ecc3.js.gz +0 -0
- package/dist/web/public/i18n.22ca902b.js +0 -2
- package/dist/web/public/i18n.22ca902b.js.br +0 -0
- package/dist/web/public/i18n.22ca902b.js.gz +0 -0
- package/dist/web/public/mobile-handlers.44adaa07.js.br +0 -0
- package/dist/web/public/mobile-handlers.44adaa07.js.gz +0 -0
- package/dist/web/public/panels-ui.cbb24ba1.js.br +0 -0
- package/dist/web/public/panels-ui.cbb24ba1.js.gz +0 -0
- package/dist/web/public/session-ui.9c1f009b.js.br +0 -0
- package/dist/web/public/session-ui.9c1f009b.js.gz +0 -0
- package/dist/web/public/settings-ui.fea9ddae.js.br +0 -0
- package/dist/web/public/settings-ui.fea9ddae.js.gz +0 -0
- package/dist/web/public/styles.e096fcd6.css +0 -1
- package/dist/web/public/styles.e096fcd6.css.br +0 -0
- package/dist/web/public/styles.e096fcd6.css.gz +0 -0
- package/dist/web/public/terminal-ui.68dbd352.js +0 -2
- package/dist/web/public/terminal-ui.68dbd352.js.br +0 -0
- package/dist/web/public/terminal-ui.68dbd352.js.gz +0 -0
package/README.md
CHANGED
|
@@ -580,7 +580,7 @@ loginctl enable-linger $USER
|
|
|
580
580
|
1. First request → browser shows Basic Auth prompt (username: `admin` or `CODEMAN_USERNAME`)
|
|
581
581
|
2. On success → server issues a `codeman_session` cookie (24h TTL, auto-extends on activity)
|
|
582
582
|
3. Subsequent requests authenticate silently via cookie
|
|
583
|
-
4. 10 failed attempts per IP → 429 rate limit (15-minute decay)
|
|
583
|
+
4. 10 failed attempts per IP → 429 rate limit (15-minute decay). In multi-user mode the per-IP cap is 50, and each account allows 10 failed guesses per IP; past either, even a correct password from that IP waits for the decay, while the account still logs in from another IP
|
|
584
584
|
|
|
585
585
|
**Always set `CODEMAN_PASSWORD`** before exposing via tunnel — without it, anyone with the URL has full access to your sessions.
|
|
586
586
|
|
|
@@ -670,7 +670,7 @@ By default Codeman launches sessions with `--dangerously-skip-permissions`, so t
|
|
|
670
670
|
|
|
671
671
|
- **Loopback by default** — the server binary binds `127.0.0.1`, reachable only from the same machine, so the no-password default is safe out of the box (the guided installer asks about network access and configures the binding + password for you). Binding a non-loopback host without `CODEMAN_PASSWORD` _starts but prints a loud warning_ with three concrete fixes (set a password, loopback + an authenticated tunnel, or explicitly acknowledge with `--allow-unauthenticated-network`)
|
|
672
672
|
- **Optional auth, real sessions** — HTTP Basic via `CODEMAN_USERNAME` (default `admin`) / `CODEMAN_PASSWORD`. Success issues an opaque 256-bit `codeman_session` cookie (`randomBytes(32)`) — validated server-side, not client-signed, so it can't be forged offline (24h TTL, auto-extend, device-context audit log)
|
|
673
|
-
- **Per-IP rate limiting** — 10 failed attempts → `429` with `Retry-After` (15-min decay). A valid cookie or correct password recovers _immediately_ even while an attacker hammers the same IP — important because all tunnel traffic shares one loopback IP. QR auth has its own separate limiter
|
|
673
|
+
- **Per-IP rate limiting** — 10 failed attempts → `429` with `Retry-After` (15-min decay). A valid cookie or correct password recovers _immediately_ even while an attacker hammers the same IP — important because all tunnel traffic shares one loopback IP. Multi-user mode allows 50 failures per IP instead, plus 10 failed guesses per account from one IP; both are checked before the password, so past either a correct password from that IP also waits for the decay, while the account still logs in from another IP. QR auth has its own separate limiter
|
|
674
674
|
- **Configurable permission mode** - `--dangerously-skip-permissions` is only the default. **App Settings → Agents & CLIs → Claude → Startup Mode** can switch new sessions to Anthropic's classifier-guarded `auto` mode (low-prompt, needs Claude Code 2.1.207+), `normal` prompting, or an explicit allowed-tools list. In multi-user mode, non-granted users are forced to `auto`, and shell sessions / skip-permissions require an explicit per-user grant
|
|
675
675
|
|
|
676
676
|
### Always-on browser hardening (v0.9.5)
|
|
@@ -988,6 +988,9 @@ codeman agent read "$SID" --tail 3000 # terminal tail, ANSI
|
|
|
988
988
|
codeman agent send "$SID" 'run the tests, then print WORKDONE followed by _4711' # hook-less modes (opencode, pi, …): ask for the marker in halves …
|
|
989
989
|
codeman agent wait "$SID" --match WORKDONE_4711 # … and wait on the joined form, which the prompt's echo never contains
|
|
990
990
|
codeman agent interrupt "$SID" # a bare ESC, conversation intact
|
|
991
|
+
codeman agent post "$SID" 'context for later, not a prompt' # stored in its inbox, not typed into the pane
|
|
992
|
+
codeman agent inbox --wait --timeout 300000 # your own inbox: blocks while nothing is new
|
|
993
|
+
codeman agent ack <msg-id> # remove what you handled (read never removes)
|
|
991
994
|
codeman agent rm "$SID" # any session except this one
|
|
992
995
|
```
|
|
993
996
|
|
|
@@ -1067,6 +1070,7 @@ REST over Fastify — **~230 handlers across 25 route modules**, plus an SSE str
|
|
|
1067
1070
|
| `GET` | `/api/sessions/:id/last-response` | The last answer as clean text, read from the transcript (claude, codex, deepseek) |
|
|
1068
1071
|
| `GET` | `/api/sessions/:id/wait` | Block until a signal fires (`?until=stop,idle,exit&timeout=&fresh=`); a timeout is a `200` |
|
|
1069
1072
|
| `GET` | `/api/sessions/:id/wait-output` | Block until a literal string appears (`?match=&nocase=&from=now\|buffer&timeout=`) |
|
|
1073
|
+
| `POST` / `GET` | `/api/sessions/:id/inbox` | Agent inbox: post `{text}` / read every pending message (`?wait=` blocks while nothing is new); `POST …/inbox/ack {ids}` removes them, receiver only |
|
|
1070
1074
|
| `GET` | `/api/sessions/unified` | Unified live + history list (Session Manager) — `?q=&limit=` |
|
|
1071
1075
|
| `POST` | `/api/sessions/:id/pin` | Pin/unpin in the Session Manager (`{pinned}`) |
|
|
1072
1076
|
| `PUT` | `/api/session-order` | Sync tab order across devices (`{order: [ids]}`) |
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Renaming a case (its name and its folder) without losing what points at the folder.
|
|
3
|
+
*
|
|
4
|
+
* A case folder is held in more places than an open file. A running agent has it as its working
|
|
5
|
+
* directory and its tmux pane was started `cd`-ed into it; Claude Code keys its conversation history
|
|
6
|
+
* by the folder's path (`<config>/projects/<path with every non-alphanumeric as "-">`), so a moved
|
|
7
|
+
* folder silently orphans every conversation; on Windows, SMB and some FUSE mounts a directory with an
|
|
8
|
+
* open handle simply cannot be renamed (EBUSY / EPERM / EACCES). This module owns the three parts of
|
|
9
|
+
* that which are not route logic:
|
|
10
|
+
*
|
|
11
|
+
* - the in-process lock (`withCaseLock` / `isCasePathLocked`) that keeps a session from starting in a
|
|
12
|
+
* folder while it is being moved, and two renames from overlapping;
|
|
13
|
+
* - the record of sessions constructed but not yet registered (`isCaseSessionStarting`), which a
|
|
14
|
+
* rename refuses to move a folder under, since it can neither list nor stop them;
|
|
15
|
+
* - the move itself, retried a few times for a transient lock and turned into a message that says
|
|
16
|
+
* what to do when it is a real one;
|
|
17
|
+
* - carrying Claude Code's history for the folder over to the new path.
|
|
18
|
+
*
|
|
19
|
+
* Pure of the server: every function takes the paths it works on.
|
|
20
|
+
*
|
|
21
|
+
* @module case-rename
|
|
22
|
+
*/
|
|
23
|
+
export type CaseRenameErrorCode = 'INVALID' | 'EXISTS' | 'BUSY' | 'LOCKED' | 'CROSS_DEVICE' | 'UNSUPPORTED';
|
|
24
|
+
export declare class CaseRenameError extends Error {
|
|
25
|
+
readonly code: CaseRenameErrorCode;
|
|
26
|
+
constructor(code: CaseRenameErrorCode, message: string);
|
|
27
|
+
}
|
|
28
|
+
/** True when `path` is, or is inside, a folder that is being renamed right now. */
|
|
29
|
+
export declare function isCasePathLocked(path: string): boolean;
|
|
30
|
+
/** `session` (any object standing for it) is being created with `workingDir`; `Session`'s constructor calls this. */
|
|
31
|
+
export declare function noteCaseSessionStarting(session: object, workingDir: string, now?: number): void;
|
|
32
|
+
/** `session` is in the server's session map (or was stopped): the rename route sees it from here on. */
|
|
33
|
+
export declare function caseSessionSettled(session: object): void;
|
|
34
|
+
/** True when a session is being created at or inside `path`, so a rename of it must wait. */
|
|
35
|
+
export declare function isCaseSessionStarting(path: string, now?: number): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Hold `from` and `to` for the length of `fn`. A second caller touching either (or anything inside
|
|
38
|
+
* them) gets `BUSY` instead of racing the move; `Session`'s constructor refuses a working directory
|
|
39
|
+
* inside a held folder, so nothing starts in a case that is half-moved.
|
|
40
|
+
*/
|
|
41
|
+
export declare function withCaseLock<T>(from: string, to: string, fn: () => Promise<T>): Promise<T>;
|
|
42
|
+
/**
|
|
43
|
+
* `rename(from, to)` for a folder that must not already exist at `to`. A lock error is retried (a
|
|
44
|
+
* handle another process is closing, an antivirus scan) before it is reported as locked.
|
|
45
|
+
* `fs.rename` onto an EMPTY existing directory replaces it on POSIX, so the target is checked first.
|
|
46
|
+
*/
|
|
47
|
+
export declare function moveCaseFolder(from: string, to: string, attempts?: number, delayMs?: number): Promise<void>;
|
|
48
|
+
/** The directory name Claude Code uses for a project path: every non-alphanumeric becomes `-`. */
|
|
49
|
+
export declare function claudeProjectDirName(path: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Move Claude Code's history for the old folder to the new one, so `--resume` and the past-sessions list
|
|
52
|
+
* still find the conversations. Returns whether anything moved.
|
|
53
|
+
*
|
|
54
|
+
* The new path can already have history: Claude Code never removes a project directory, and deleting a
|
|
55
|
+
* case does not either, so a case deleted earlier under the new name leaves one behind. The old history is
|
|
56
|
+
* then merged into it entry by entry (conversations are UUID-named, so they do not collide), never
|
|
57
|
+
* overwriting: an entry both directories have (a `memory/` folder, say) stays at the old path. A merge
|
|
58
|
+
* only goes between two real directories, never through a symlink.
|
|
59
|
+
*/
|
|
60
|
+
export declare function moveClaudeHistory(oldPath: string, newPath: string, configDir?: string): Promise<boolean>;
|
|
61
|
+
/** Replace `from` by `to` in a list, keeping its position (the case order). */
|
|
62
|
+
export declare function renameInOrder(order: unknown, from: string, to: string): string[] | null;
|
|
63
|
+
/** The folder a linked case moves to: its own parent, the new name. */
|
|
64
|
+
export declare const siblingPath: (path: string, newName: string) => string;
|
|
65
|
+
//# sourceMappingURL=case-rename.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"case-rename.d.ts","sourceRoot":"","sources":["../src/case-rename.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAMH,MAAM,MAAM,mBAAmB,GAC3B,SAAS,GACT,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,cAAc,GACd,aAAa,CAAC;AAElB,qBAAa,eAAgB,SAAQ,KAAK;IAEtC,QAAQ,CAAC,IAAI,EAAE,mBAAmB;gBAAzB,IAAI,EAAE,mBAAmB,EAClC,OAAO,EAAE,MAAM;CAKlB;AAMD,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMtD;AAkBD,qHAAqH;AACrH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,IAAI,CAGnG;AAED,wGAAwG;AACxG,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAExD;AAED,6FAA6F;AAC7F,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,OAAO,CAO7E;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAehG;AAKD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,SAAI,EAAE,OAAO,SAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCzG;AAED,kGAAkG;AAClG,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAoC9G;AAED,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAGvF;AAED,uEAAuE;AACvE,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,EAAE,SAAS,MAAM,KAAG,MAAsC,CAAC"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Renaming a case (its name and its folder) without losing what points at the folder.
|
|
3
|
+
*
|
|
4
|
+
* A case folder is held in more places than an open file. A running agent has it as its working
|
|
5
|
+
* directory and its tmux pane was started `cd`-ed into it; Claude Code keys its conversation history
|
|
6
|
+
* by the folder's path (`<config>/projects/<path with every non-alphanumeric as "-">`), so a moved
|
|
7
|
+
* folder silently orphans every conversation; on Windows, SMB and some FUSE mounts a directory with an
|
|
8
|
+
* open handle simply cannot be renamed (EBUSY / EPERM / EACCES). This module owns the three parts of
|
|
9
|
+
* that which are not route logic:
|
|
10
|
+
*
|
|
11
|
+
* - the in-process lock (`withCaseLock` / `isCasePathLocked`) that keeps a session from starting in a
|
|
12
|
+
* folder while it is being moved, and two renames from overlapping;
|
|
13
|
+
* - the record of sessions constructed but not yet registered (`isCaseSessionStarting`), which a
|
|
14
|
+
* rename refuses to move a folder under, since it can neither list nor stop them;
|
|
15
|
+
* - the move itself, retried a few times for a transient lock and turned into a message that says
|
|
16
|
+
* what to do when it is a real one;
|
|
17
|
+
* - carrying Claude Code's history for the folder over to the new path.
|
|
18
|
+
*
|
|
19
|
+
* Pure of the server: every function takes the paths it works on.
|
|
20
|
+
*
|
|
21
|
+
* @module case-rename
|
|
22
|
+
*/
|
|
23
|
+
import fs from 'node:fs/promises';
|
|
24
|
+
import { dirname, join, resolve, sep } from 'node:path';
|
|
25
|
+
import { claudeProjectsDir } from './utils/claude-transcript.js';
|
|
26
|
+
export class CaseRenameError extends Error {
|
|
27
|
+
code;
|
|
28
|
+
constructor(code, message) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.code = code;
|
|
31
|
+
this.name = 'CaseRenameError';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const locked = new Set();
|
|
35
|
+
const within = (child, parent) => child === parent || child.startsWith(parent + sep);
|
|
36
|
+
/** True when `path` is, or is inside, a folder that is being renamed right now. */
|
|
37
|
+
export function isCasePathLocked(path) {
|
|
38
|
+
const target = resolve(path);
|
|
39
|
+
for (const held of locked) {
|
|
40
|
+
if (within(target, held))
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Sessions constructed but not yet in the server's session map, where the rename route looks for the
|
|
47
|
+
* sessions it must list or stop. A create awaits a few things between `new Session` and that map (the
|
|
48
|
+
* owner's layout lock, a settings read), and a rename that ran in that gap neither saw nor stopped the
|
|
49
|
+
* session, moved the folder, and left it to start in a path that no longer existed.
|
|
50
|
+
*/
|
|
51
|
+
const starting = new Map();
|
|
52
|
+
/** A session that never reaches the map (a create that failed, a one-shot prompt) stops counting after this. */
|
|
53
|
+
const STARTING_TTL_MS = 30000;
|
|
54
|
+
const dropExpiredStarts = (now) => {
|
|
55
|
+
for (const [session, entry] of starting) {
|
|
56
|
+
if (entry.until <= now)
|
|
57
|
+
starting.delete(session);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
/** `session` (any object standing for it) is being created with `workingDir`; `Session`'s constructor calls this. */
|
|
61
|
+
export function noteCaseSessionStarting(session, workingDir, now = Date.now()) {
|
|
62
|
+
dropExpiredStarts(now);
|
|
63
|
+
starting.set(session, { path: resolve(workingDir), until: now + STARTING_TTL_MS });
|
|
64
|
+
}
|
|
65
|
+
/** `session` is in the server's session map (or was stopped): the rename route sees it from here on. */
|
|
66
|
+
export function caseSessionSettled(session) {
|
|
67
|
+
starting.delete(session);
|
|
68
|
+
}
|
|
69
|
+
/** True when a session is being created at or inside `path`, so a rename of it must wait. */
|
|
70
|
+
export function isCaseSessionStarting(path, now = Date.now()) {
|
|
71
|
+
dropExpiredStarts(now);
|
|
72
|
+
const root = resolve(path);
|
|
73
|
+
for (const entry of starting.values()) {
|
|
74
|
+
if (within(entry.path, root))
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Hold `from` and `to` for the length of `fn`. A second caller touching either (or anything inside
|
|
81
|
+
* them) gets `BUSY` instead of racing the move; `Session`'s constructor refuses a working directory
|
|
82
|
+
* inside a held folder, so nothing starts in a case that is half-moved.
|
|
83
|
+
*/
|
|
84
|
+
export async function withCaseLock(from, to, fn) {
|
|
85
|
+
const held = [resolve(from), resolve(to)];
|
|
86
|
+
for (const p of held) {
|
|
87
|
+
for (const other of locked) {
|
|
88
|
+
if (within(p, other) || within(other, p)) {
|
|
89
|
+
throw new CaseRenameError('BUSY', 'Another rename is already working on this case. Try again in a moment.');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
for (const p of held)
|
|
94
|
+
locked.add(p);
|
|
95
|
+
try {
|
|
96
|
+
return await fn();
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
for (const p of held)
|
|
100
|
+
locked.delete(p);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
104
|
+
const LOCK_CODES = new Set(['EBUSY', 'EPERM', 'EACCES']);
|
|
105
|
+
/**
|
|
106
|
+
* `rename(from, to)` for a folder that must not already exist at `to`. A lock error is retried (a
|
|
107
|
+
* handle another process is closing, an antivirus scan) before it is reported as locked.
|
|
108
|
+
* `fs.rename` onto an EMPTY existing directory replaces it on POSIX, so the target is checked first.
|
|
109
|
+
*/
|
|
110
|
+
export async function moveCaseFolder(from, to, attempts = 4, delayMs = 150) {
|
|
111
|
+
if (await fs.lstat(to).then(() => true, () => false)) {
|
|
112
|
+
throw new CaseRenameError('EXISTS', `A folder named ${to} already exists.`);
|
|
113
|
+
}
|
|
114
|
+
for (let i = 0;; i++) {
|
|
115
|
+
try {
|
|
116
|
+
await fs.rename(from, to);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
const code = err.code ?? '';
|
|
121
|
+
if (code === 'EXDEV') {
|
|
122
|
+
throw new CaseRenameError('CROSS_DEVICE', 'The new folder would be on a different drive or mount; it is not moved.');
|
|
123
|
+
}
|
|
124
|
+
if (LOCK_CODES.has(code)) {
|
|
125
|
+
if (i < attempts - 1) {
|
|
126
|
+
await sleep(delayMs * (i + 1));
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
throw new CaseRenameError('LOCKED', `The folder is in use by another program (${code}), so it could not be renamed. Close anything that has it open ` +
|
|
130
|
+
'(an editor, a file browser or a sync client) and try again.');
|
|
131
|
+
}
|
|
132
|
+
if (code === 'ENOTEMPTY' || code === 'EEXIST') {
|
|
133
|
+
throw new CaseRenameError('EXISTS', `A folder named ${to} already exists.`);
|
|
134
|
+
}
|
|
135
|
+
throw err;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/** The directory name Claude Code uses for a project path: every non-alphanumeric becomes `-`. */
|
|
140
|
+
export function claudeProjectDirName(path) {
|
|
141
|
+
return resolve(path).replace(/[^a-zA-Z0-9]/g, '-');
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Move Claude Code's history for the old folder to the new one, so `--resume` and the past-sessions list
|
|
145
|
+
* still find the conversations. Returns whether anything moved.
|
|
146
|
+
*
|
|
147
|
+
* The new path can already have history: Claude Code never removes a project directory, and deleting a
|
|
148
|
+
* case does not either, so a case deleted earlier under the new name leaves one behind. The old history is
|
|
149
|
+
* then merged into it entry by entry (conversations are UUID-named, so they do not collide), never
|
|
150
|
+
* overwriting: an entry both directories have (a `memory/` folder, say) stays at the old path. A merge
|
|
151
|
+
* only goes between two real directories, never through a symlink.
|
|
152
|
+
*/
|
|
153
|
+
export async function moveClaudeHistory(oldPath, newPath, configDir) {
|
|
154
|
+
const projects = claudeProjectsDir(configDir);
|
|
155
|
+
const from = join(projects, claudeProjectDirName(oldPath));
|
|
156
|
+
const to = join(projects, claudeProjectDirName(newPath));
|
|
157
|
+
if (from === to)
|
|
158
|
+
return false;
|
|
159
|
+
const fromStat = await fs.lstat(from).catch(() => null);
|
|
160
|
+
if (!fromStat)
|
|
161
|
+
return false;
|
|
162
|
+
const toStat = await fs.lstat(to).catch(() => null);
|
|
163
|
+
if (!toStat) {
|
|
164
|
+
try {
|
|
165
|
+
await fs.rename(from, to);
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return false; // history is a convenience; the folder move already succeeded
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (!fromStat.isDirectory() || !toStat.isDirectory())
|
|
173
|
+
return false;
|
|
174
|
+
let moved = false;
|
|
175
|
+
for (const entry of await fs.readdir(from).catch(() => [])) {
|
|
176
|
+
const target = join(to, entry);
|
|
177
|
+
if (await fs.lstat(target).then(() => true, () => false))
|
|
178
|
+
continue;
|
|
179
|
+
try {
|
|
180
|
+
await fs.rename(join(from, entry), target);
|
|
181
|
+
moved = true;
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
/* left at the old path, like an entry both have */
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
await fs.rmdir(from).catch(() => { }); // succeeds only once nothing was left behind
|
|
188
|
+
return moved;
|
|
189
|
+
}
|
|
190
|
+
/** Replace `from` by `to` in a list, keeping its position (the case order). */
|
|
191
|
+
export function renameInOrder(order, from, to) {
|
|
192
|
+
if (!Array.isArray(order) || !order.includes(from))
|
|
193
|
+
return null;
|
|
194
|
+
return order.map((n) => (n === from ? to : n));
|
|
195
|
+
}
|
|
196
|
+
/** The folder a linked case moves to: its own parent, the new name. */
|
|
197
|
+
export const siblingPath = (path, newName) => join(dirname(path), newName);
|
|
198
|
+
//# sourceMappingURL=case-rename.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"case-rename.js","sourceRoot":"","sources":["../src/case-rename.ts"],"names":[],"mappings":"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"}
|
package/dist/cli-agent.d.ts
CHANGED
|
@@ -72,6 +72,15 @@ export declare function defaultClientId(selfId: string, suffix?: string): string
|
|
|
72
72
|
* titles, keypad modes) plus the charset designators (`ESC ( B`) it leaves in.
|
|
73
73
|
*/
|
|
74
74
|
export declare function stripAnsi(text: string): string;
|
|
75
|
+
/**
|
|
76
|
+
* Inbox message text for a terminal. Another session wrote it, so an escape sequence
|
|
77
|
+
* in it would otherwise reach the reader's terminal (retitle it, or move the cursor
|
|
78
|
+
* over the `from` line above it). ANSI sequences go as in `read --tail`, and every
|
|
79
|
+
* control character left (C0 other than newline and tab, DEL, C1) is shown as `\xHH`,
|
|
80
|
+
* so a bare `\r` or backspace cannot overwrite the header either. A CRLF line end
|
|
81
|
+
* stays a line end. `--json` prints the text as stored.
|
|
82
|
+
*/
|
|
83
|
+
export declare function terminalSafeText(text: string): string;
|
|
75
84
|
/** Parse a positive-integer option (`--timeout` ms, `--tail` bytes); the server rejects anything else. */
|
|
76
85
|
export declare function parsePositiveInt(raw: string | undefined, fallback: number, flag?: string): number;
|
|
77
86
|
/**
|
|
@@ -156,7 +165,16 @@ export declare function resolveSessionId(deps: AgentDeps, id: string): Promise<{
|
|
|
156
165
|
error: string;
|
|
157
166
|
code: number;
|
|
158
167
|
}>;
|
|
159
|
-
/**
|
|
168
|
+
/** One session's row in `GET /api/agent-inbox/summary`. */
|
|
169
|
+
export interface InboxSummaryRow {
|
|
170
|
+
pending: number;
|
|
171
|
+
unseen: number;
|
|
172
|
+
oldestUnseenAt: number | null;
|
|
173
|
+
parked: boolean;
|
|
174
|
+
}
|
|
175
|
+
/** The INBOX cell of `ls`: `3 (1 new)`, `parked`, both, or empty. */
|
|
176
|
+
export declare function inboxCell(row: InboxSummaryRow | undefined): string;
|
|
177
|
+
/** `agent ls` — every session the caller can see, self marked, with its inbox counts. */
|
|
160
178
|
export declare function agentLs(deps: AgentDeps): Promise<number>;
|
|
161
179
|
export interface SpawnOptions {
|
|
162
180
|
caseName: string;
|
|
@@ -222,6 +240,47 @@ export declare function agentInterrupt(deps: AgentDeps, options: {
|
|
|
222
240
|
export declare function agentRm(deps: AgentDeps, options: {
|
|
223
241
|
id: string;
|
|
224
242
|
}): Promise<number>;
|
|
243
|
+
/** A message as `GET …/inbox` hands it out. */
|
|
244
|
+
export interface InboxMessageRow {
|
|
245
|
+
id: string;
|
|
246
|
+
from: string | null;
|
|
247
|
+
text: string;
|
|
248
|
+
createdAt: number;
|
|
249
|
+
seen: boolean;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* The message `post` was given, which must be ONE argument, as with `send`: several
|
|
253
|
+
* would be an unquoted `$(cat notes.md)` split by the shell, and joining the pieces
|
|
254
|
+
* with spaces loses its newlines. Unlike `send`, newlines inside the one argument are
|
|
255
|
+
* fine: the inbox stores text as data and never types it.
|
|
256
|
+
*/
|
|
257
|
+
export declare function postTextFromArgs(words: readonly string[]): {
|
|
258
|
+
text: string;
|
|
259
|
+
} | {
|
|
260
|
+
error: string;
|
|
261
|
+
};
|
|
262
|
+
/** `agent post` — store a message in another session's inbox. Never types into its pane. */
|
|
263
|
+
export declare function agentPost(deps: AgentDeps, options: {
|
|
264
|
+
id: string;
|
|
265
|
+
text: string;
|
|
266
|
+
}): Promise<number>;
|
|
267
|
+
export interface InboxReadOptions {
|
|
268
|
+
/** Another session's inbox (a peek); omitted = this session's own. */
|
|
269
|
+
id?: string;
|
|
270
|
+
/** Long-poll budget in ms; the read parks while nothing is unseen. */
|
|
271
|
+
waitMs?: number;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* `agent inbox` — read this session's inbox (or peek at another's). Removes nothing:
|
|
275
|
+
* a message stays until `agent ack <id>`. Only reading your OWN inbox marks messages
|
|
276
|
+
* seen. Exit 0 with mail, 2 when a `--wait` ran out with nothing unseen or the server
|
|
277
|
+
* cut it short (restart, detach), 3 when the session is gone from the list.
|
|
278
|
+
*/
|
|
279
|
+
export declare function agentInboxRead(deps: AgentDeps, options: InboxReadOptions): Promise<number>;
|
|
280
|
+
/** `agent ack` — remove messages from THIS session's inbox by id. Idempotent. */
|
|
281
|
+
export declare function agentAck(deps: AgentDeps, options: {
|
|
282
|
+
ids: string[];
|
|
283
|
+
}): Promise<number>;
|
|
225
284
|
/** Register `codeman agent …` on the program. */
|
|
226
285
|
export declare function registerAgentCommands(program: Command): Command;
|
|
227
286
|
//# sourceMappingURL=cli-agent.d.ts.map
|
package/dist/cli-agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-agent.d.ts","sourceRoot":"","sources":["../src/cli-agent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli-agent.d.ts","sourceRoot":"","sources":["../src/cli-agent.ts"],"names":[],"mappings":"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"}
|