aicodeman 1.19.7 → 1.20.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.
Files changed (142) hide show
  1. package/dist/git-clone.d.ts +7 -0
  2. package/dist/git-clone.d.ts.map +1 -1
  3. package/dist/git-clone.js +10 -2
  4. package/dist/git-clone.js.map +1 -1
  5. package/dist/ralph-loop.d.ts.map +1 -1
  6. package/dist/ralph-loop.js +8 -1
  7. package/dist/ralph-loop.js.map +1 -1
  8. package/dist/session.d.ts +34 -0
  9. package/dist/session.d.ts.map +1 -1
  10. package/dist/session.js +61 -1
  11. package/dist/session.js.map +1 -1
  12. package/dist/tmux-manager.d.ts.map +1 -1
  13. package/dist/tmux-manager.js +11 -8
  14. package/dist/tmux-manager.js.map +1 -1
  15. package/dist/types/session.d.ts +8 -0
  16. package/dist/types/session.d.ts.map +1 -1
  17. package/dist/types/session.js.map +1 -1
  18. package/dist/types/update.d.ts +45 -0
  19. package/dist/types/update.d.ts.map +1 -1
  20. package/dist/utils/antigravity-cli-resolver.d.ts +4 -0
  21. package/dist/utils/antigravity-cli-resolver.d.ts.map +1 -1
  22. package/dist/utils/antigravity-cli-resolver.js +17 -29
  23. package/dist/utils/antigravity-cli-resolver.js.map +1 -1
  24. package/dist/utils/claude-cli-resolver.d.ts +1 -0
  25. package/dist/utils/claude-cli-resolver.d.ts.map +1 -1
  26. package/dist/utils/claude-cli-resolver.js +15 -28
  27. package/dist/utils/claude-cli-resolver.js.map +1 -1
  28. package/dist/utils/cli-executable-resolver.d.ts +98 -0
  29. package/dist/utils/cli-executable-resolver.d.ts.map +1 -0
  30. package/dist/utils/cli-executable-resolver.js +229 -0
  31. package/dist/utils/cli-executable-resolver.js.map +1 -0
  32. package/dist/utils/codex-cli-resolver.d.ts +1 -0
  33. package/dist/utils/codex-cli-resolver.d.ts.map +1 -1
  34. package/dist/utils/codex-cli-resolver.js +8 -30
  35. package/dist/utils/codex-cli-resolver.js.map +1 -1
  36. package/dist/utils/gemini-cli-resolver.d.ts +1 -0
  37. package/dist/utils/gemini-cli-resolver.d.ts.map +1 -1
  38. package/dist/utils/gemini-cli-resolver.js +8 -29
  39. package/dist/utils/gemini-cli-resolver.js.map +1 -1
  40. package/dist/utils/index.d.ts +6 -6
  41. package/dist/utils/index.d.ts.map +1 -1
  42. package/dist/utils/index.js +6 -6
  43. package/dist/utils/index.js.map +1 -1
  44. package/dist/utils/opencode-cli-resolver.d.ts +1 -0
  45. package/dist/utils/opencode-cli-resolver.d.ts.map +1 -1
  46. package/dist/utils/opencode-cli-resolver.js +8 -31
  47. package/dist/utils/opencode-cli-resolver.js.map +1 -1
  48. package/dist/utils/pi-cli-resolver.d.ts +12 -3
  49. package/dist/utils/pi-cli-resolver.d.ts.map +1 -1
  50. package/dist/utils/pi-cli-resolver.js +40 -55
  51. package/dist/utils/pi-cli-resolver.js.map +1 -1
  52. package/dist/web/approval-inbox.d.ts +45 -8
  53. package/dist/web/approval-inbox.d.ts.map +1 -1
  54. package/dist/web/approval-inbox.js +116 -28
  55. package/dist/web/approval-inbox.js.map +1 -1
  56. package/dist/web/public/admin-ui.js.gz +0 -0
  57. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  58. package/dist/web/public/app.56c520ff.js.gz +0 -0
  59. package/dist/web/public/approvals-ui.js.gz +0 -0
  60. package/dist/web/public/{constants.dc5d04bb.js → constants.5abbd823.js} +53 -0
  61. package/dist/web/public/constants.5abbd823.js.br +0 -0
  62. package/dist/web/public/constants.5abbd823.js.gz +0 -0
  63. package/dist/web/public/cron-ui.js.gz +0 -0
  64. package/dist/web/public/entrance-animations.js.gz +0 -0
  65. package/dist/web/public/home-sessions.js.gz +0 -0
  66. package/dist/web/public/i18n.196a992e.js +1 -0
  67. package/dist/web/public/i18n.196a992e.js.br +0 -0
  68. package/dist/web/public/i18n.196a992e.js.gz +0 -0
  69. package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
  70. package/dist/web/public/index.html +18 -5
  71. package/dist/web/public/index.html.br +0 -0
  72. package/dist/web/public/index.html.gz +0 -0
  73. package/dist/web/public/input-cjk.63794d0b.js.gz +0 -0
  74. package/dist/web/public/keyboard-accessory.0454a3e9.js.gz +0 -0
  75. package/dist/web/public/mobile-handlers.0f83a608.js.gz +0 -0
  76. package/dist/web/public/mobile-overview.js.gz +0 -0
  77. package/dist/web/public/mobile.49aeb17e.css.gz +0 -0
  78. package/dist/web/public/notification-manager.5d229063.js.gz +0 -0
  79. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  80. package/dist/web/public/panels-ui.5e97a36e.js.gz +0 -0
  81. package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
  82. package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
  83. package/dist/web/public/readmymind-ui.js.gz +0 -0
  84. package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
  85. package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
  86. package/dist/web/public/session-lineage.js.gz +0 -0
  87. package/dist/web/public/session-ui.71a0ce9a.js.gz +0 -0
  88. package/dist/web/public/settings-ui.1a62e935.js +67 -0
  89. package/dist/web/public/settings-ui.1a62e935.js.br +0 -0
  90. package/dist/web/public/settings-ui.1a62e935.js.gz +0 -0
  91. package/dist/web/public/styles.68f9e118.css.gz +0 -0
  92. package/dist/web/public/subagent-windows.e6ca799f.js.gz +0 -0
  93. package/dist/web/public/sw.js.gz +0 -0
  94. package/dist/web/public/terminal-ui.5f4158b7.js +2 -0
  95. package/dist/web/public/terminal-ui.5f4158b7.js.br +0 -0
  96. package/dist/web/public/terminal-ui.5f4158b7.js.gz +0 -0
  97. package/dist/web/public/ultracode-panel.js.gz +0 -0
  98. package/dist/web/public/ultracode-windows.js.gz +0 -0
  99. package/dist/web/public/upload.html.gz +0 -0
  100. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  101. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  102. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  103. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  104. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  105. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  106. package/dist/web/public/vendor/xterm-predictive-echo.bd6882b8.js.gz +0 -0
  107. package/dist/web/public/vendor/xterm-zerolag-input.6fee72f2.js.gz +0 -0
  108. package/dist/web/public/vendor/xterm.css.gz +0 -0
  109. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  110. package/dist/web/public/voice-input.4ec670b0.js.gz +0 -0
  111. package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
  112. package/dist/web/public/webview-tabs.js.gz +0 -0
  113. package/dist/web/repo-status.d.ts +124 -0
  114. package/dist/web/repo-status.d.ts.map +1 -0
  115. package/dist/web/repo-status.js +353 -0
  116. package/dist/web/repo-status.js.map +1 -0
  117. package/dist/web/response-viewer-transcript.d.ts +12 -0
  118. package/dist/web/response-viewer-transcript.d.ts.map +1 -0
  119. package/dist/web/response-viewer-transcript.js +345 -0
  120. package/dist/web/response-viewer-transcript.js.map +1 -0
  121. package/dist/web/routes/session-routes.d.ts.map +1 -1
  122. package/dist/web/routes/session-routes.js +41 -22
  123. package/dist/web/routes/session-routes.js.map +1 -1
  124. package/dist/web/routes/system-routes.d.ts.map +1 -1
  125. package/dist/web/routes/system-routes.js +5 -0
  126. package/dist/web/routes/system-routes.js.map +1 -1
  127. package/dist/web/session-listener-wiring.d.ts +1 -0
  128. package/dist/web/session-listener-wiring.d.ts.map +1 -1
  129. package/dist/web/session-listener-wiring.js +26 -3
  130. package/dist/web/session-listener-wiring.js.map +1 -1
  131. package/package.json +1 -1
  132. package/dist/web/public/constants.dc5d04bb.js.br +0 -0
  133. package/dist/web/public/constants.dc5d04bb.js.gz +0 -0
  134. package/dist/web/public/i18n.6a46f014.js +0 -1
  135. package/dist/web/public/i18n.6a46f014.js.br +0 -0
  136. package/dist/web/public/i18n.6a46f014.js.gz +0 -0
  137. package/dist/web/public/settings-ui.b62c70a3.js +0 -67
  138. package/dist/web/public/settings-ui.b62c70a3.js.br +0 -0
  139. package/dist/web/public/settings-ui.b62c70a3.js.gz +0 -0
  140. package/dist/web/public/terminal-ui.4723f5d4.js +0 -4
  141. package/dist/web/public/terminal-ui.4723f5d4.js.br +0 -0
  142. package/dist/web/public/terminal-ui.4723f5d4.js.gz +0 -0
@@ -0,0 +1,124 @@
1
+ /**
2
+ * @fileoverview Repository-status check (App Settings → Updates → "Repository
3
+ * status").
4
+ *
5
+ * INFORMATIONAL companion to the release-tag self-updater (`self-update.ts`).
6
+ * Where the updater answers "is there a newer published release tag, and do you
7
+ * want to `git checkout` it", this module answers "where does my local checkout
8
+ * sit relative to the upstream project AND my own fork" — by commit ahead/behind
9
+ * count plus a short list of the incoming commits.
10
+ *
11
+ * This needs the actual commits locally, so it does a READ-ONLY `git fetch` of
12
+ * the compared ref per remote (updates only remote-tracking refs under `.git`,
13
+ * never the working tree), then `git rev-list`/`git log`. Works uniformly for
14
+ * GitHub and non-GitHub remotes (e.g. Bitbucket) — no release tags required.
15
+ *
16
+ * Remote selection (generalizable): by default the union of `origin` and the
17
+ * current branch's `@{upstream}` tracking remote, deduped. Override with the
18
+ * `CODEMAN_UPDATE_REMOTES` env var (comma-separated remote names) for any other
19
+ * layout (e.g. the common `origin`=fork / `upstream`=canonical convention).
20
+ *
21
+ * Split PURE helpers (parsing + remote-set/role/compare-ref decisions, unit
22
+ * tested) from the IO wrapper `getRepositoryStatus()` (touches git).
23
+ *
24
+ * PERFORMANCE: every git invocation is ASYNC (`execFile`), never `execFileSync`
25
+ * — the fetch path can spend `2 × FETCH_TIMEOUT_MS` per remote on the network,
26
+ * and a synchronous version froze the whole event loop (SSE, PTY streaming) for
27
+ * up to a minute per request. The computation is additionally single-flight
28
+ * with a short TTL cache: concurrent requests share one in-flight promise, and
29
+ * a fresh-enough result is served without spawning git at all.
30
+ *
31
+ * SECURITY: remote URLs (and git stderr echoing them) can embed
32
+ * `scheme://user:token@host` credentials, so every `url`/`error` field is
33
+ * passed through `redactGitCredentials()` (shared with `git-clone.ts`) before
34
+ * it reaches a client.
35
+ *
36
+ * @module web/repo-status
37
+ */
38
+ import type { RepoIncomingCommit, RepoRemoteRole, RepoRemoteStatus, RepositoryStatusResult } from '../types/update.js';
39
+ /**
40
+ * Parse `git rev-list --left-right --count HEAD...<ref>` output.
41
+ * Git prints two tab-separated counts: LEFT (commits in HEAD not in ref → ahead)
42
+ * and RIGHT (commits in ref not in HEAD → behind). Returns null on malformed input.
43
+ */
44
+ export declare function parseAheadBehind(out: string): {
45
+ ahead: number;
46
+ behind: number;
47
+ } | null;
48
+ /**
49
+ * Parse `git log --oneline` output into commits. Each non-empty line is
50
+ * `<sha> <subject>`; the first whitespace-delimited token is the SHA.
51
+ */
52
+ export declare function parseLogLines(out: string): RepoIncomingCommit[];
53
+ /**
54
+ * Extract the default branch name from `git ls-remote --symref <remote> HEAD`
55
+ * output (a line like `ref: refs/heads/master\tHEAD`). Returns null if absent.
56
+ *
57
+ * SECURITY: this parses UNTRUSTED remote output, and the result flows into a
58
+ * later `git fetch <remote> <branch>` positional. The capture is constrained to
59
+ * start with an alphanumeric (no leading `-`) so a hostile remote can't return
60
+ * `ref: refs/heads/--upload-pack=<cmd>\tHEAD` and smuggle an argv flag (RCE) —
61
+ * see `isSafeGitPositional` for the defense-in-depth re-check at the call site.
62
+ */
63
+ export declare function parseSymrefDefaultBranch(out: string): string | null;
64
+ /**
65
+ * Reject a value that would be unsafe as a git positional argument (remote name
66
+ * or branch). A leading `-` lets untrusted ls-remote/symref output or a stray
67
+ * `CODEMAN_UPDATE_REMOTES` entry inject an option (e.g. `--upload-pack=<cmd>`)
68
+ * into a subsequent `git fetch`. Empty values are rejected too.
69
+ */
70
+ export declare function isSafeGitPositional(value: string | null | undefined): value is string;
71
+ /** Split a comma-separated env value into trimmed, non-empty names. */
72
+ export declare function parseRemotesEnv(value: string | null | undefined): string[];
73
+ /**
74
+ * Decide which remote NAMES the status view covers, in display order.
75
+ *
76
+ * - If `envRemotes` is non-empty, use exactly those that actually exist (order
77
+ * preserved). This is the full-control escape hatch.
78
+ * - Otherwise the union of `origin` (if it exists) and the tracking remote (if
79
+ * any), deduped. The tracking remote is listed first when it isn't `origin`,
80
+ * so "your fork" leads and "upstream" follows.
81
+ */
82
+ export declare function resolveRemoteSet(opts: {
83
+ existingRemotes: string[];
84
+ trackingRemote: string | null;
85
+ envRemotes: string[];
86
+ }): string[];
87
+ /** Classify a remote's role relative to the tracking remote. */
88
+ export declare function roleForRemote(name: string, trackingRemote: string | null): RepoRemoteRole;
89
+ /**
90
+ * Extract the remote name from an `@{upstream}` short ref like `origin/master`.
91
+ *
92
+ * A ref with NO slash is a LOCAL-branch upstream (`git branch -u otherbranch`):
93
+ * `rev-parse --abbrev-ref @{upstream}` prints just the branch name, and there
94
+ * is no remote in it — the old `slice(0, indexOf('/'))` became `slice(0, -1)`
95
+ * there and yielded garbage like `maste`. Returns null for that case (treated
96
+ * as "no tracking remote"), and for empty/degenerate refs.
97
+ */
98
+ export declare function parseTrackingRemote(trackingRef: string | null | undefined): string | null;
99
+ /**
100
+ * Redact embedded `scheme://user:secret@host` credentials from the fields of
101
+ * one remote's status that can carry them: the remote URL itself, and the
102
+ * error string (which may include git stderr echoing that URL back).
103
+ */
104
+ export declare function redactRemoteStatus(status: RepoRemoteStatus): RepoRemoteStatus;
105
+ /**
106
+ * Single-flight TTL cache around an async compute: concurrent callers await
107
+ * the SAME in-flight promise, and a result younger than `ttlMs` is returned
108
+ * without recomputing. A rejected compute is never cached, so the next call
109
+ * retries. `now` is injectable for deterministic TTL tests.
110
+ */
111
+ export declare function createSingleFlightCache<T>(ttlMs: number, compute: () => Promise<T>): {
112
+ get(now?: number): Promise<T>;
113
+ };
114
+ /**
115
+ * Inspect how the local checkout sits relative to the configured remotes.
116
+ * Each remote is fetched + compared independently; a single unreachable remote
117
+ * surfaces as that card's `error` and never fails the whole call.
118
+ *
119
+ * Single-flight + TTL-cached: concurrent requests share one in-flight
120
+ * computation, and a result younger than `STATUS_CACHE_TTL_MS` is served
121
+ * without spawning git.
122
+ */
123
+ export declare function getRepositoryStatus(): Promise<RepositoryStatusResult>;
124
+ //# sourceMappingURL=repo-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-status.d.ts","sourceRoot":"","sources":["../../src/web/repo-status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAQH,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAiBvH;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAItF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAa/D;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGnE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,IAAI,MAAM,CAErF;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,CAM1E;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,GAAG,MAAM,EAAE,CAYX;AAED,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,cAAc,CAIzF;AAMD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAKzF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,CAI7E;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EACvC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACxB;IAAE,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;CAAE,CAyBnC;AAiKD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAErE"}
@@ -0,0 +1,353 @@
1
+ /**
2
+ * @fileoverview Repository-status check (App Settings → Updates → "Repository
3
+ * status").
4
+ *
5
+ * INFORMATIONAL companion to the release-tag self-updater (`self-update.ts`).
6
+ * Where the updater answers "is there a newer published release tag, and do you
7
+ * want to `git checkout` it", this module answers "where does my local checkout
8
+ * sit relative to the upstream project AND my own fork" — by commit ahead/behind
9
+ * count plus a short list of the incoming commits.
10
+ *
11
+ * This needs the actual commits locally, so it does a READ-ONLY `git fetch` of
12
+ * the compared ref per remote (updates only remote-tracking refs under `.git`,
13
+ * never the working tree), then `git rev-list`/`git log`. Works uniformly for
14
+ * GitHub and non-GitHub remotes (e.g. Bitbucket) — no release tags required.
15
+ *
16
+ * Remote selection (generalizable): by default the union of `origin` and the
17
+ * current branch's `@{upstream}` tracking remote, deduped. Override with the
18
+ * `CODEMAN_UPDATE_REMOTES` env var (comma-separated remote names) for any other
19
+ * layout (e.g. the common `origin`=fork / `upstream`=canonical convention).
20
+ *
21
+ * Split PURE helpers (parsing + remote-set/role/compare-ref decisions, unit
22
+ * tested) from the IO wrapper `getRepositoryStatus()` (touches git).
23
+ *
24
+ * PERFORMANCE: every git invocation is ASYNC (`execFile`), never `execFileSync`
25
+ * — the fetch path can spend `2 × FETCH_TIMEOUT_MS` per remote on the network,
26
+ * and a synchronous version froze the whole event loop (SSE, PTY streaming) for
27
+ * up to a minute per request. The computation is additionally single-flight
28
+ * with a short TTL cache: concurrent requests share one in-flight promise, and
29
+ * a fresh-enough result is served without spawning git at all.
30
+ *
31
+ * SECURITY: remote URLs (and git stderr echoing them) can embed
32
+ * `scheme://user:token@host` credentials, so every `url`/`error` field is
33
+ * passed through `redactGitCredentials()` (shared with `git-clone.ts`) before
34
+ * it reaches a client.
35
+ *
36
+ * @module web/repo-status
37
+ */
38
+ import { execFile } from 'node:child_process';
39
+ import { createRequire } from 'node:module';
40
+ import { promisify } from 'node:util';
41
+ import { EXEC_TIMEOUT_MS } from '../config/exec-timeout.js';
42
+ import { gitNonInteractiveEnv, redactGitCredentials } from '../git-clone.js';
43
+ import { getInstallInfo } from './self-update.js';
44
+ const require = createRequire(import.meta.url);
45
+ const { version: APP_VERSION } = require('../../package.json');
46
+ const execFileAsync = promisify(execFile);
47
+ /** Network/git timeout for the fetch path (longer than EXEC_TIMEOUT_MS — hits network). */
48
+ const FETCH_TIMEOUT_MS = 15_000;
49
+ /** Max incoming commit subjects to list per remote. */
50
+ const MAX_INCOMING = 10;
51
+ /** Fresh-enough window for a cached status — repeated UI polls reuse it instead of re-running git. */
52
+ const STATUS_CACHE_TTL_MS = 45_000;
53
+ // ─────────────────────────────────────────────────────────────────────────────
54
+ // PURE helpers (unit tested, no IO)
55
+ // ─────────────────────────────────────────────────────────────────────────────
56
+ /**
57
+ * Parse `git rev-list --left-right --count HEAD...<ref>` output.
58
+ * Git prints two tab-separated counts: LEFT (commits in HEAD not in ref → ahead)
59
+ * and RIGHT (commits in ref not in HEAD → behind). Returns null on malformed input.
60
+ */
61
+ export function parseAheadBehind(out) {
62
+ const m = out.trim().match(/^(\d+)\s+(\d+)$/);
63
+ if (!m)
64
+ return null;
65
+ return { ahead: parseInt(m[1], 10), behind: parseInt(m[2], 10) };
66
+ }
67
+ /**
68
+ * Parse `git log --oneline` output into commits. Each non-empty line is
69
+ * `<sha> <subject>`; the first whitespace-delimited token is the SHA.
70
+ */
71
+ export function parseLogLines(out) {
72
+ const commits = [];
73
+ for (const line of out.split('\n')) {
74
+ const trimmed = line.trim();
75
+ if (!trimmed)
76
+ continue;
77
+ const idx = trimmed.indexOf(' ');
78
+ if (idx === -1) {
79
+ commits.push({ sha: trimmed, subject: '' });
80
+ }
81
+ else {
82
+ commits.push({ sha: trimmed.slice(0, idx), subject: trimmed.slice(idx + 1).trim() });
83
+ }
84
+ }
85
+ return commits;
86
+ }
87
+ /**
88
+ * Extract the default branch name from `git ls-remote --symref <remote> HEAD`
89
+ * output (a line like `ref: refs/heads/master\tHEAD`). Returns null if absent.
90
+ *
91
+ * SECURITY: this parses UNTRUSTED remote output, and the result flows into a
92
+ * later `git fetch <remote> <branch>` positional. The capture is constrained to
93
+ * start with an alphanumeric (no leading `-`) so a hostile remote can't return
94
+ * `ref: refs/heads/--upload-pack=<cmd>\tHEAD` and smuggle an argv flag (RCE) —
95
+ * see `isSafeGitPositional` for the defense-in-depth re-check at the call site.
96
+ */
97
+ export function parseSymrefDefaultBranch(out) {
98
+ const m = out.match(/^ref:\s+refs\/heads\/([A-Za-z0-9_./][A-Za-z0-9_./+-]*)\s+HEAD$/m);
99
+ return m ? m[1] : null;
100
+ }
101
+ /**
102
+ * Reject a value that would be unsafe as a git positional argument (remote name
103
+ * or branch). A leading `-` lets untrusted ls-remote/symref output or a stray
104
+ * `CODEMAN_UPDATE_REMOTES` entry inject an option (e.g. `--upload-pack=<cmd>`)
105
+ * into a subsequent `git fetch`. Empty values are rejected too.
106
+ */
107
+ export function isSafeGitPositional(value) {
108
+ return typeof value === 'string' && value.length > 0 && !value.startsWith('-');
109
+ }
110
+ /** Split a comma-separated env value into trimmed, non-empty names. */
111
+ export function parseRemotesEnv(value) {
112
+ if (!value)
113
+ return [];
114
+ return value
115
+ .split(',')
116
+ .map((s) => s.trim())
117
+ .filter(Boolean);
118
+ }
119
+ /**
120
+ * Decide which remote NAMES the status view covers, in display order.
121
+ *
122
+ * - If `envRemotes` is non-empty, use exactly those that actually exist (order
123
+ * preserved). This is the full-control escape hatch.
124
+ * - Otherwise the union of `origin` (if it exists) and the tracking remote (if
125
+ * any), deduped. The tracking remote is listed first when it isn't `origin`,
126
+ * so "your fork" leads and "upstream" follows.
127
+ */
128
+ export function resolveRemoteSet(opts) {
129
+ const exists = new Set(opts.existingRemotes);
130
+ if (opts.envRemotes.length > 0) {
131
+ return dedupe(opts.envRemotes.filter((n) => exists.has(n)));
132
+ }
133
+ const out = [];
134
+ if (opts.trackingRemote && exists.has(opts.trackingRemote) && opts.trackingRemote !== 'origin') {
135
+ out.push(opts.trackingRemote);
136
+ }
137
+ if (exists.has('origin'))
138
+ out.push('origin');
139
+ if (opts.trackingRemote && exists.has(opts.trackingRemote))
140
+ out.push(opts.trackingRemote);
141
+ return dedupe(out);
142
+ }
143
+ /** Classify a remote's role relative to the tracking remote. */
144
+ export function roleForRemote(name, trackingRemote) {
145
+ if (trackingRemote && name === trackingRemote)
146
+ return 'tracking';
147
+ if (name === 'origin' || name === 'upstream')
148
+ return 'upstream';
149
+ return 'other';
150
+ }
151
+ function dedupe(names) {
152
+ return [...new Set(names)];
153
+ }
154
+ /**
155
+ * Extract the remote name from an `@{upstream}` short ref like `origin/master`.
156
+ *
157
+ * A ref with NO slash is a LOCAL-branch upstream (`git branch -u otherbranch`):
158
+ * `rev-parse --abbrev-ref @{upstream}` prints just the branch name, and there
159
+ * is no remote in it — the old `slice(0, indexOf('/'))` became `slice(0, -1)`
160
+ * there and yielded garbage like `maste`. Returns null for that case (treated
161
+ * as "no tracking remote"), and for empty/degenerate refs.
162
+ */
163
+ export function parseTrackingRemote(trackingRef) {
164
+ if (!trackingRef)
165
+ return null;
166
+ const idx = trackingRef.indexOf('/');
167
+ if (idx <= 0)
168
+ return null;
169
+ return trackingRef.slice(0, idx);
170
+ }
171
+ /**
172
+ * Redact embedded `scheme://user:secret@host` credentials from the fields of
173
+ * one remote's status that can carry them: the remote URL itself, and the
174
+ * error string (which may include git stderr echoing that URL back).
175
+ */
176
+ export function redactRemoteStatus(status) {
177
+ const out = { ...status, url: redactGitCredentials(status.url) };
178
+ if (out.error !== undefined)
179
+ out.error = redactGitCredentials(out.error);
180
+ return out;
181
+ }
182
+ /**
183
+ * Single-flight TTL cache around an async compute: concurrent callers await
184
+ * the SAME in-flight promise, and a result younger than `ttlMs` is returned
185
+ * without recomputing. A rejected compute is never cached, so the next call
186
+ * retries. `now` is injectable for deterministic TTL tests.
187
+ */
188
+ export function createSingleFlightCache(ttlMs, compute) {
189
+ let cachedAt = 0;
190
+ let cachedValue;
191
+ let hasValue = false;
192
+ let inFlight = null;
193
+ return {
194
+ get(now = Date.now()) {
195
+ if (hasValue && now - cachedAt < ttlMs)
196
+ return Promise.resolve(cachedValue);
197
+ if (inFlight)
198
+ return inFlight;
199
+ inFlight = compute().then((value) => {
200
+ cachedValue = value;
201
+ hasValue = true;
202
+ cachedAt = Date.now();
203
+ inFlight = null;
204
+ return value;
205
+ }, (err) => {
206
+ inFlight = null;
207
+ throw err;
208
+ });
209
+ return inFlight;
210
+ },
211
+ };
212
+ }
213
+ /**
214
+ * Run git non-interactively (no credential or SSH prompts — a missing key/cred
215
+ * fails fast instead of hanging; env via the shared `gitNonInteractiveEnv()`,
216
+ * which also closes the askpass/GCM/DISPLAY prompt paths). ASYNC on purpose:
217
+ * the fetch path hits the network for up to `FETCH_TIMEOUT_MS`, and a sync
218
+ * spawn would block the event loop for the whole wait.
219
+ */
220
+ async function runGit(args, cwd, timeout = EXEC_TIMEOUT_MS) {
221
+ try {
222
+ const { stdout } = await execFileAsync('git', args, {
223
+ cwd,
224
+ encoding: 'utf-8',
225
+ timeout,
226
+ env: gitNonInteractiveEnv(),
227
+ });
228
+ return { ok: true, stdout: stdout.trim(), stderr: '' };
229
+ }
230
+ catch (err) {
231
+ const e = err;
232
+ return {
233
+ ok: false,
234
+ stdout: e.stdout ? String(e.stdout).trim() : '',
235
+ stderr: e.stderr ? String(e.stderr).trim() : '',
236
+ };
237
+ }
238
+ }
239
+ /** First line of stderr, trimmed — a compact human-readable failure reason. */
240
+ function firstLine(s) {
241
+ return (s
242
+ .split('\n')
243
+ .find((l) => l.trim())
244
+ ?.trim() ?? 'git command failed');
245
+ }
246
+ /** Compute ahead/behind + incoming commits for one already-selected remote. */
247
+ async function statusForRemote(dir, name, trackingRemote, trackingRef) {
248
+ const role = roleForRemote(name, trackingRemote);
249
+ const url = (await runGit(['remote', 'get-url', name], dir)).stdout || '';
250
+ const base = { name, url, role, compareRef: '', ahead: 0, behind: 0, incoming: [] };
251
+ // SECURITY: the remote name reaches git as a positional; reject a `-` prefix
252
+ // (e.g. a stray CODEMAN_UPDATE_REMOTES entry) before it can act as a flag.
253
+ if (!isSafeGitPositional(name)) {
254
+ return { ...base, error: `Refusing unsafe remote name "${name}".` };
255
+ }
256
+ // Resolve the compare ref + the remote branch to fetch.
257
+ let branch;
258
+ if (role === 'tracking' && trackingRef) {
259
+ // e.g. trackingRef = "bitbucket/local" → branch = "local"
260
+ branch = trackingRef.slice(name.length + 1) || null;
261
+ }
262
+ else {
263
+ const symref = await runGit(['ls-remote', '--symref', name, 'HEAD'], dir, FETCH_TIMEOUT_MS);
264
+ branch = symref.ok ? parseSymrefDefaultBranch(symref.stdout) : null;
265
+ if (!branch && !symref.ok) {
266
+ return { ...base, error: `Could not reach ${name}: ${firstLine(symref.stderr)}` };
267
+ }
268
+ branch = branch ?? 'master';
269
+ }
270
+ if (!branch)
271
+ return { ...base, error: `Could not resolve a branch on ${name}.` };
272
+ // SECURITY: defense-in-depth — `branch` may come from untrusted symref output
273
+ // or a tracking-ref slice; never let a `-`-prefixed value reach `git fetch`.
274
+ if (!isSafeGitPositional(branch)) {
275
+ return { ...base, error: `Refusing unsafe branch name "${branch}" from ${name}.` };
276
+ }
277
+ const compareRef = `${name}/${branch}`;
278
+ // Read-only fetch of just that ref so the local rev-list/log can see it.
279
+ // `--` ends option parsing so neither `name` nor `branch` can be read as a flag.
280
+ const fetched = await runGit(['fetch', '--no-tags', name, '--', branch], dir, FETCH_TIMEOUT_MS);
281
+ if (!fetched.ok) {
282
+ return { ...base, compareRef, error: `Could not fetch ${compareRef}: ${firstLine(fetched.stderr)}` };
283
+ }
284
+ const counts = await runGit(['rev-list', '--left-right', '--count', `HEAD...${compareRef}`], dir);
285
+ if (!counts.ok) {
286
+ return { ...base, compareRef, error: `Could not compare against ${compareRef}: ${firstLine(counts.stderr)}` };
287
+ }
288
+ const ab = parseAheadBehind(counts.stdout);
289
+ if (!ab)
290
+ return { ...base, compareRef, error: `Unexpected git output comparing ${compareRef}.` };
291
+ const log = await runGit(['log', '--oneline', '-n', String(MAX_INCOMING), `HEAD..${compareRef}`], dir);
292
+ const incoming = log.ok ? parseLogLines(log.stdout) : [];
293
+ return { ...base, compareRef, ahead: ab.ahead, behind: ab.behind, incoming };
294
+ }
295
+ /** The uncached computation behind `getRepositoryStatus()`. */
296
+ async function computeRepositoryStatus() {
297
+ const checkedAt = Date.now();
298
+ const info = getInstallInfo();
299
+ const base = {
300
+ checkedAt,
301
+ isGit: info.installKind === 'git',
302
+ currentVersion: info.currentVersion || APP_VERSION,
303
+ remotes: [],
304
+ };
305
+ if (info.installKind !== 'git') {
306
+ return { ...base, error: 'Not a git install — repository status is unavailable.' };
307
+ }
308
+ const dir = info.installDir;
309
+ // Tracking ref of the current branch, e.g. "bitbucket/local" (empty if none).
310
+ const trackingRef = (await runGit(['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}'], dir)).stdout || null;
311
+ const trackingRemote = parseTrackingRemote(trackingRef);
312
+ // A local-branch upstream (no slash) carries no remote — drop the ref too, so
313
+ // nothing downstream can mistake a bare branch name for a remote-tracking ref.
314
+ const remoteTrackingRef = trackingRemote ? trackingRef : null;
315
+ const remotesOut = await runGit(['remote'], dir);
316
+ const existingRemotes = remotesOut.ok
317
+ ? remotesOut.stdout
318
+ .split('\n')
319
+ .map((s) => s.trim())
320
+ .filter(Boolean)
321
+ : [];
322
+ const selected = resolveRemoteSet({
323
+ existingRemotes,
324
+ trackingRemote,
325
+ envRemotes: parseRemotesEnv(process.env.CODEMAN_UPDATE_REMOTES),
326
+ });
327
+ if (selected.length === 0) {
328
+ return { ...base, error: 'No comparable remotes found (set CODEMAN_UPDATE_REMOTES to choose).' };
329
+ }
330
+ // Sequential on purpose: concurrent `git fetch` in one repo can contend on
331
+ // ref locks, and the event loop no longer cares how long this takes.
332
+ const remotes = [];
333
+ for (const name of selected) {
334
+ // SECURITY: redact `scheme://user:token@host` credentials from the URL and
335
+ // any error string (git stderr echoes the URL back) before they leave the server.
336
+ remotes.push(redactRemoteStatus(await statusForRemote(dir, name, trackingRemote ?? '', remoteTrackingRef)));
337
+ }
338
+ return { ...base, remotes };
339
+ }
340
+ const statusCache = createSingleFlightCache(STATUS_CACHE_TTL_MS, computeRepositoryStatus);
341
+ /**
342
+ * Inspect how the local checkout sits relative to the configured remotes.
343
+ * Each remote is fetched + compared independently; a single unreachable remote
344
+ * surfaces as that card's `error` and never fails the whole call.
345
+ *
346
+ * Single-flight + TTL-cached: concurrent requests share one in-flight
347
+ * computation, and a result younger than `STATUS_CACHE_TTL_MS` is served
348
+ * without spawning git.
349
+ */
350
+ export function getRepositoryStatus() {
351
+ return statusCache.get();
352
+ }
353
+ //# sourceMappingURL=repo-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-status.js","sourceRoot":"","sources":["../../src/web/repo-status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export type ResponseViewerTranscriptKind = 'prompt' | 'response' | 'status' | 'tool';
2
+ export interface ResponseViewerTranscriptBlock {
3
+ kind: ResponseViewerTranscriptKind;
4
+ label: 'Prompt' | 'Response' | 'Status' | 'Tool';
5
+ /** What the frontend renders by: 'user' gets the "You" badge, everything else the agent badge. */
6
+ role: 'user' | 'assistant';
7
+ text: string;
8
+ }
9
+ export declare function isExternalCliTranscriptMode(mode: string | null | undefined): boolean;
10
+ export declare function parseExternalCliTranscript(buffer: string, mode: string | null | undefined): ResponseViewerTranscriptBlock[];
11
+ export declare function getLastTranscriptResponse(blocks: ResponseViewerTranscriptBlock[]): string;
12
+ //# sourceMappingURL=response-viewer-transcript.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-viewer-transcript.d.ts","sourceRoot":"","sources":["../../src/web/response-viewer-transcript.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,4BAA4B,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;AAErF,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,4BAA4B,CAAC;IACnC,KAAK,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;IACjD,kGAAkG;IAClG,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;CACd;AA+PD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAEpF;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9B,6BAA6B,EAAE,CAmFjC;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,6BAA6B,EAAE,GAAG,MAAM,CAKzF"}