@orion-agents/orion-code 0.3.15 → 0.3.18
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/CHANGELOG.md +254 -0
- package/dist/runtime/release-receipts.d.ts +15 -3
- package/dist/runtime/release-receipts.js +27 -1
- package/dist/services/session-storage.d.ts +9 -0
- package/dist/services/session-storage.js +27 -1
- package/dist/web/git-compare-service.d.ts +45 -0
- package/dist/web/git-compare-service.js +160 -0
- package/dist/web/git-diff-document.d.ts +83 -0
- package/dist/web/git-diff-document.js +214 -0
- package/dist/web/git-file-source.d.ts +25 -0
- package/dist/web/git-file-source.js +21 -0
- package/dist/web/git-history-service.d.ts +151 -0
- package/dist/web/git-history-service.js +610 -0
- package/dist/web/git-index-transaction.d.ts +45 -0
- package/dist/web/git-index-transaction.js +174 -0
- package/dist/web/git-patch-builder.d.ts +30 -0
- package/dist/web/git-patch-builder.js +138 -0
- package/dist/web/git-read-model-service.d.ts +338 -18
- package/dist/web/git-read-model-service.js +1039 -110
- package/dist/web/git-version-service.d.ts +71 -0
- package/dist/web/git-version-service.js +255 -0
- package/dist/web/native-directory-picker.d.ts +63 -0
- package/dist/web/native-directory-picker.js +169 -0
- package/dist/web/protocol.d.ts +51 -2
- package/dist/web/server.js +384 -8
- package/dist/web/workbench-controller.d.ts +126 -10
- package/dist/web/workbench-controller.js +383 -15
- package/dist/web/workspace-open-telemetry.d.ts +55 -0
- package/dist/web/workspace-open-telemetry.js +73 -0
- package/dist/web-client/assets/FilesPanel-BARQCmne.js +1 -0
- package/dist/web-client/assets/GitPanel-DjB9eSiJ.js +4 -0
- package/dist/web-client/assets/ReviewPanel-YhEGlp-G.js +5 -0
- package/dist/web-client/assets/{TerminalPanel-BpsLphLv.js → TerminalPanel-DnWh3Vp2.js} +1 -1
- package/dist/web-client/assets/{index-D3sxrAh_.css → index--hnGXXjj.css} +1 -1
- package/dist/web-client/assets/index-Drja0_BT.js +20 -0
- package/dist/web-client/assets/{useAutoNotice-BeSf9Dfq.js → useAutoNotice-DfQi4G1X.js} +1 -1
- package/dist/web-client/index.html +2 -2
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/dist/web-client/assets/DiffViewer-C0A42Lma.js +0 -5
- package/dist/web-client/assets/FilesPanel-DiXKnFMj.js +0 -1
- package/dist/web-client/assets/GitPanel--_Yrbldp.js +0 -1
- package/dist/web-client/assets/ReviewPanel-BilzL9Ma.js +0 -1
- package/dist/web-client/assets/index-C2M48tcK.js +0 -20
package/CHANGELOG.md
CHANGED
|
@@ -22,6 +22,260 @@ which is **not** a pass.
|
|
|
22
22
|
|
|
23
23
|
## [Unreleased]
|
|
24
24
|
|
|
25
|
+
## [0.3.18]
|
|
26
|
+
|
|
27
|
+
> **Status: released.** Closes the assertion and evidence backlog of the Git
|
|
28
|
+
> right-hand work panel shipped in 0.3.17, and fixes the CRLF part-staging
|
|
29
|
+
> defect found right after that release.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- **CRLF part-staging was impossible:** the rebuilt context reused the
|
|
34
|
+
normalised display text, dropping the carriage return, so the patch never
|
|
35
|
+
matched the stored bytes. The raw line is reused so the context keeps its
|
|
36
|
+
line endings, and a selection that still cannot apply is refused explicitly
|
|
37
|
+
with `git_patch_not_applicable` instead of surfacing as a Git failure.
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- **Assertion coverage for the G317 backlog:** deleting and renaming through
|
|
42
|
+
the panel (git records R with the content preserved), line-level staging on a
|
|
43
|
+
CRLF file, a signing failure (HEAD and the staged work untouched), bare and
|
|
44
|
+
non-repository workspaces, and a clean repository with readable history.
|
|
45
|
+
- **Conflict-assertion split:** the plan requirement (a conflict is never shown
|
|
46
|
+
as an ordinary stageable diff) is asserted separately from whether the ideal
|
|
47
|
+
three-way view rendered — the earlier single assertion was flaky because the
|
|
48
|
+
three-way view loads asynchronously.
|
|
49
|
+
- **Browser evidence:** conflict, comparison, blame, narrow-viewport and
|
|
50
|
+
open-in-Files screenshots under docs/plan/evidence/v0.3.17-git/screenshots/.
|
|
51
|
+
|
|
52
|
+
### Known gaps
|
|
53
|
+
|
|
54
|
+
- The bare-repository `status()` contract is measured but not yet pinned (its
|
|
55
|
+
current behaviour is neither a refusal nor an empty status).
|
|
56
|
+
- Performance samples for the S5/S6 reads, dark-theme screenshot assertions,
|
|
57
|
+
and the agent-led file-change scenario are still open.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
## [0.3.17]
|
|
61
|
+
|
|
62
|
+
> **Status: candidate.** Opening a local project is made fast and honest
|
|
63
|
+
> (`docs/plan/v0.3.16-plan-v1.md`): the directory preview no longer waits on the
|
|
64
|
+
> session catalog, the Finder browse action no longer occupies the global
|
|
65
|
+
> Context mutation admission, and every entry point shares one
|
|
66
|
+
> inspect → confirm → activate contract. Covers S2, S3 and S5; S1 (per-stage
|
|
67
|
+
> telemetry) and S4 (staged activate baseline) are not implemented yet. Not
|
|
68
|
+
> merged, tagged or published.
|
|
69
|
+
>
|
|
70
|
+
> **Update:** S1 (per-stage telemetry) and the S4 headline (the foreground
|
|
71
|
+
> snapshot no longer gates the first render) also landed on this branch, plus a
|
|
72
|
+
> bounded inspect probe. The measured Host-side receipt is in
|
|
73
|
+
> `docs/plan/evidence/v0.3.17-open-baseline.md`.
|
|
74
|
+
>
|
|
75
|
+
> **Update 2:** the Git right-hand work panel (`docs/plan/v0.3.17-plan.md`,
|
|
76
|
+
> S0–S6) is implemented on this branch as well. Evidence per stage is under
|
|
77
|
+
> `docs/plan/evidence/v0.3.17-git/`, and the scenario matrix is
|
|
78
|
+
> `g317-status.md`. All functional gaps are closed; the remaining items are
|
|
79
|
+
> assertion and evidence backlog, listed under **Known gaps** below.
|
|
80
|
+
|
|
81
|
+
### Added
|
|
82
|
+
|
|
83
|
+
- **Per-stage open-path telemetry:** `WorkspaceOpenTelemetryV1` keeps a bounded
|
|
84
|
+
ring of `{ requestId, operation, outcome, stage ms, errorCode }` for the
|
|
85
|
+
picker, the inspect probe and the Context transition, and exposes it
|
|
86
|
+
read-only through Diagnostics as `workspaceOpen`. Traces never carry a path,
|
|
87
|
+
a label or file content.
|
|
88
|
+
|
|
89
|
+
### Fixed
|
|
90
|
+
|
|
91
|
+
- **The preview no longer blocks on the session catalog:** `inspect` used to
|
|
92
|
+
call `countSessionsByProject()`, which can rebuild the catalog and hold its
|
|
93
|
+
file lock for up to 10s before the confirmation card appeared. It now reads
|
|
94
|
+
the warm cache only (`peekCachedSessionCounts()`) and reports
|
|
95
|
+
`sessionCountStatus: 'deferred'` when the catalog is cold; the card stays
|
|
96
|
+
usable and shows 会话数读取中 instead of stalling.
|
|
97
|
+
|
|
98
|
+
### Changed
|
|
99
|
+
|
|
100
|
+
- **The native picker is browse admission, not a mutation:**
|
|
101
|
+
`POST /workspaces/pick-directory` no longer goes through `executeMutation()`,
|
|
102
|
+
so an open Finder dialog does not raise `activeMutationCount` or enter the
|
|
103
|
+
idempotency ledger and can no longer make a project switch look like a busy
|
|
104
|
+
Runtime. Single-flight (`picker_busy`) and the nonce/origin/content-type
|
|
105
|
+
guards are unchanged.
|
|
106
|
+
- **One path contract:** the advanced manual entry and the pinned/recent rows
|
|
107
|
+
now go through the same inspect → confirm → activate state machine as the
|
|
108
|
+
Finder entry. Nothing activates without an explicit confirmation, and
|
|
109
|
+
availability plus the Git/Folder kind are always re-checked.
|
|
110
|
+
- **The picker timeout is wired:** `timeoutMs` reaches the default `execFile`
|
|
111
|
+
runner, and a timeout reports its own `picker_timeout` reason code instead of
|
|
112
|
+
being folded into `picker_unavailable`.
|
|
113
|
+
- **The preview probe is bounded:** `inspectDirectory` now uses asynchronous fs
|
|
114
|
+
raced against a 250ms budget, so a hung network mount or a broken symlink
|
|
115
|
+
cannot freeze the Node event loop; on timeout the directory is reported
|
|
116
|
+
`unreadable` with an `inspect_budget_exceeded` trace code.
|
|
117
|
+
- **The foreground snapshot no longer gates the first render:** `loadBaseline`
|
|
118
|
+
dispatches the identity baseline (bootstrap, workspaces, sessions, settings)
|
|
119
|
+
before restoring the session, so the project shell and session list appear
|
|
120
|
+
while the transcript is still loading. The reducer already marks the active
|
|
121
|
+
session `loading`, so the Composer stays disabled until the snapshot settles.
|
|
122
|
+
- **Activation no longer re-requests the workspace page:** the page returned by
|
|
123
|
+
`POST /workspaces/activate` is reused by the baseline when its `activeId`
|
|
124
|
+
matches the freshly bootstrapped Context, so opening a project issues one
|
|
125
|
+
fewer round-trip. A mismatch falls back to the normal request.
|
|
126
|
+
- **The confirmation card reports discrete stages:** while opening, the card
|
|
127
|
+
shows `准备项目 → 加载本地 Runtime → 恢复会话(如有)` with only the stage the
|
|
128
|
+
Host actually reports marked `aria-current="step"`, instead of one vague
|
|
129
|
+
"正在打开…" label covering the whole wait.
|
|
130
|
+
|
|
131
|
+
### Tests
|
|
132
|
+
|
|
133
|
+
- `tests/web-workbench-workspace-picker.test.ts` — the preview never calls the
|
|
134
|
+
catalog rebuild entry point; the count is `ready` when the cache is warm.
|
|
135
|
+
- `tests/session-catalog-peek.test.ts` — the peek answers `null` on a cold
|
|
136
|
+
module instead of loading or locking.
|
|
137
|
+
- `tests/native-directory-picker.test.ts` — a timeout keeps its own reason code.
|
|
138
|
+
- `tests/web-ui-workspace-picker.test.tsx` — the card stays actionable while the
|
|
139
|
+
count is deferred, and the stage list only appears after confirming.
|
|
140
|
+
- `tests/workspace-open-telemetry.test.ts` — stage offsets, duplicate marks,
|
|
141
|
+
bounded retention, and that a serialized trace contains no path.
|
|
142
|
+
- `tests/e2e/web-v0317-workspace-open.spec.ts` — W316P-01/02/04/08/09 driven by a
|
|
143
|
+
scripted picker: no browse side effects, inspect-then-confirm, cancellation,
|
|
144
|
+
the shared advanced-path contract, and the no-path-leak regression.
|
|
145
|
+
- `tests/e2e/*` — the four manual-path specs confirm on the card before
|
|
146
|
+
activating.
|
|
147
|
+
|
|
148
|
+
### Test seams
|
|
149
|
+
|
|
150
|
+
- **`ORION_CODE_WEB_PICKER_FIXTURE`** points the Host at a JSON script file so
|
|
151
|
+
end-to-end runs never open a real Finder dialog (`{"path": …}` /
|
|
152
|
+
`{"paths": […]}` / `{"outcome": "cancelled" | "unavailable"}`). The script is
|
|
153
|
+
re-read on every invocation, and an absent or malformed file behaves like a
|
|
154
|
+
cancelled dialog. It is browse-only: the returned path still has to be
|
|
155
|
+
confirmed and passes through the same guarded activation flow.
|
|
156
|
+
|
|
157
|
+
### Added — Git right-hand work panel (S0–S6)
|
|
158
|
+
|
|
159
|
+
- **Change navigation:** staged / unstaged / untracked / conflict groups with a
|
|
160
|
+
host-side filter and search, counts that cover the whole matching set, and a
|
|
161
|
+
pagination cursor bound to the group and query it was issued for.
|
|
162
|
+
- **Diff reading:** unified and side-by-side, old/new line numbers, continuous
|
|
163
|
+
hunks, fold state, line anchors, word-level diff (`--word-diff=plain`) and
|
|
164
|
+
ignore-all-space — the last two are bound into the pagination cursor, so a
|
|
165
|
+
page can never be continued under a different view.
|
|
166
|
+
- **Writes through an index transaction:** every index change holds
|
|
167
|
+
`index.lock` (and only ever removes a lock it created), writes to a private
|
|
168
|
+
sibling index, verifies it, and publishes with a single atomic rename.
|
|
169
|
+
Hunk-level and line-level staging rebuild the patch on the Host from the
|
|
170
|
+
reviewed document — the browser only ever sends ids.
|
|
171
|
+
- **Commit form and result recovery:** summary plus multi-line body, the
|
|
172
|
+
identity / signing / hook configuration that will actually run, a preview of
|
|
173
|
+
the real index (independent of list filters), the draft preserved on failure,
|
|
174
|
+
and idempotent replay by `requestId`.
|
|
175
|
+
- **History:** search across the whole reachable history, commit details,
|
|
176
|
+
root commits compared with the empty tree, merge parents selectable and
|
|
177
|
+
labelled, file history with `--follow`, blame, historical blobs for files
|
|
178
|
+
that no longer exist locally, and a commit graph drawn by git itself.
|
|
179
|
+
- **Branch and version comparison:** two-endpoint snapshot and diverged-from
|
|
180
|
+
(merge-base) modes; the resolved commit ids are part of the result so an open
|
|
181
|
+
comparison can be frozen, and a multi-merge-base pair is refused rather than
|
|
182
|
+
silently picking one.
|
|
183
|
+
- **Conflict views:** base / ours / theirs read from index stages 1/2/3 plus
|
|
184
|
+
the working tree, with a stage that legitimately does not exist reported as
|
|
185
|
+
absent instead of being elided.
|
|
186
|
+
- **Submodule and LFS:** recorded gitlink SHAs are shown without traversing
|
|
187
|
+
submodules, and LFS pointers are reported as state without fetching objects.
|
|
188
|
+
- **Image versions:** a bounded, magic-byte-identified image becomes a data URL
|
|
189
|
+
so both versions of a picture can be shown without a download.
|
|
190
|
+
- **Cross-panel links:** "open in Files" translates a Git token into a Files
|
|
191
|
+
token on the Host, and the composer reference carries the file, comparison
|
|
192
|
+
source, revision and line range into the draft without sending it.
|
|
193
|
+
- **Responsive and keyboard:** three tiers keyed off the panel container (not
|
|
194
|
+
the window), with a narrow layout that never writes the downgrade back into
|
|
195
|
+
the stored preference; arrow keys, Enter, F7 / Shift+F7 change navigation,
|
|
196
|
+
Escape unwinding one layer at a time, and Space toggling selection only —
|
|
197
|
+
never a Git write.
|
|
198
|
+
|
|
199
|
+
### Fixed — Git panel
|
|
200
|
+
|
|
201
|
+
- **Staging could silently do nothing:** the private index copy received a
|
|
202
|
+
fresh mtime, which defeated Git’s racy-clean rule — a file edited within the
|
|
203
|
+
same timestamp granularity and with an unchanged size was not re-hashed, so
|
|
204
|
+
`git add` succeeded while changing nothing. The copy now preserves the
|
|
205
|
+
original timestamps, and staging verifies each path with `hash-object
|
|
206
|
+
--path` against the index entry.
|
|
207
|
+
- **`localhost` was rejected site-wide:** the Host and Origin checks compared
|
|
208
|
+
header strings exactly, so `http://localhost:<port>` returned 421 (and, with
|
|
209
|
+
only the Host check fixed, every mutation would have returned 403). Both now
|
|
210
|
+
accept any loopback spelling of the same endpoint while still refusing
|
|
211
|
+
attacker-controlled names and a mismatched port.
|
|
212
|
+
- **`git apply -` and `git commit -F -` blocked forever:** the async
|
|
213
|
+
`execFile` `input` option is silently ignored, so stdin was never closed.
|
|
214
|
+
Writes now use `spawn` and close stdin explicitly, and a timeout kills the
|
|
215
|
+
whole process group so a slow hook cannot be orphaned.
|
|
216
|
+
- **Refs came back empty:** `for-each-ref` does not support the `%x1f` escapes
|
|
217
|
+
that `git log --format` does; it printed them literally. Refs are now parsed
|
|
218
|
+
from a space-separated format.
|
|
219
|
+
- **A detached HEAD could be reported as a branch:** the check was "no ref
|
|
220
|
+
points at HEAD", which is wrong when detaching onto a branch tip. It now
|
|
221
|
+
asks git whether HEAD is a symbolic ref.
|
|
222
|
+
|
|
223
|
+
### Known gaps
|
|
224
|
+
|
|
225
|
+
- Assertion and evidence backlog for the Git panel: an end-to-end spec
|
|
226
|
+
(`tests/e2e/web-v0317-git-workspace.spec.ts`), automated assertions for the
|
|
227
|
+
Files cross-panel link, and scenario variants not yet covered (staging a
|
|
228
|
+
deletion or rename, CRLF line selection, signing failure, bare repository
|
|
229
|
+
and linked worktree cases, agent-led file changes).
|
|
230
|
+
- Narrow-viewport and dark-theme screenshots, plus performance samples for the
|
|
231
|
+
reads added in S5 and S6.
|
|
232
|
+
- `npm run typecheck:e2e` fails on this branch with 12 errors in the v0.3.12
|
|
233
|
+
and v0.3.13 specs; the same 12 errors reproduce on the v0.3.16 baseline, so
|
|
234
|
+
they are pre-existing and unrelated to the Git panel.
|
|
235
|
+
|
|
236
|
+
## [0.3.16] — CANDIDATE
|
|
237
|
+
|
|
238
|
+
> **Status: candidate.** LOCAL WORKSPACE opens a new project without typing a
|
|
239
|
+
> path (`docs/plan/v0.3.16-plan.md`): a Host-side native folder picker, and a
|
|
240
|
+
> read-only inspect step separated from activation. Covers slices S1 and S2;
|
|
241
|
+
> controlled workspace-roots discovery (S3) is not implemented yet. No protocol
|
|
242
|
+
> removals or dependency changes. Not merged, tagged or published.
|
|
243
|
+
|
|
244
|
+
### Added
|
|
245
|
+
|
|
246
|
+
- **Native folder picker (Host):** `POST /workspaces/pick-directory` runs the OS
|
|
247
|
+
directory chooser on the Host. The AppleScript is a fixed constant and the
|
|
248
|
+
prompt / default location travel as `argv` through `execFile`, so a chosen
|
|
249
|
+
path is never interpolated into a shell or script string. Cancelling is a
|
|
250
|
+
normal outcome, platforms without an adapter report `picker_unavailable`
|
|
251
|
+
rather than faking a selection, and only one picker may be open at a time.
|
|
252
|
+
- **Read-only workspace inspect:** `POST /workspaces/inspect` resolves the
|
|
253
|
+
canonical path, availability (`available` / `missing` / `not_directory` /
|
|
254
|
+
`unreadable`) and Git-vs-folder kind, plus existing workspace id, session
|
|
255
|
+
count and pin order. It never registers a workspace, activates a Context or
|
|
256
|
+
reads file contents.
|
|
257
|
+
- **Project picker dialog:** the LOCAL WORKSPACE dialog now leads with
|
|
258
|
+
`从 Finder 选择文件夹…`, groups pinned and recent projects, and shows a
|
|
259
|
+
confirmation card (canonical path, kind, availability, session count). The
|
|
260
|
+
manual absolute-path entry moved into a collapsed 高级 disclosure.
|
|
261
|
+
|
|
262
|
+
### Changed
|
|
263
|
+
|
|
264
|
+
- Browsing, searching, inspecting and cancelling never call `activateContext`,
|
|
265
|
+
pause the event stream or stop a running Session; only confirming 打开项目
|
|
266
|
+
enters the existing activation flow.
|
|
267
|
+
|
|
268
|
+
### Tests
|
|
269
|
+
|
|
270
|
+
- `tests/native-directory-picker.test.ts` — outcome mapping, argv isolation,
|
|
271
|
+
trailing-separator handling and non-macOS unavailability.
|
|
272
|
+
- `tests/web-workbench-workspace-picker.test.ts` — inspect is strictly
|
|
273
|
+
read-only and the picker is single-in-flight.
|
|
274
|
+
- `tests/web-ui-workspace-picker.test.tsx` — state-machine invariants (no
|
|
275
|
+
activation without a confirmed candidate) and the confirmation card.
|
|
276
|
+
- `tests/e2e/*` — the four specs that filled the manual path now open the
|
|
277
|
+
advanced disclosure first.
|
|
278
|
+
|
|
25
279
|
## [0.3.15] — CANDIDATE
|
|
26
280
|
|
|
27
281
|
> **Status: candidate.** Web Workbench visual & interaction convergence
|
|
@@ -9,12 +9,24 @@ export declare const WEB_E2E_WEB32_SCENARIOS_V1: readonly ["WEB32-P0-01", "WEB32
|
|
|
9
9
|
export declare const WEB_E2E_WEB33_THEME_SCENARIOS_V1: readonly ["WEB33-P0-01", "WEB33-P0-02", "WEB33-P0-03", "WEB33-P0-04", "WEB33-P0-05", "WEB33-P0-06", "WEB33-P0-07", "WEB33-P0-08", "WEB33-P0-09", "WEB33-P0-10", "WEB33-P0-11", "WEB33-P0-12"];
|
|
10
10
|
export declare const WEB_E2E_WEB33_SESSION_SCENARIOS_V1: readonly ["WEB33-P0-16", "WEB33-P0-17", "WEB33-P0-18", "WEB33-P0-19", "WEB33-P0-20", "WEB33-P0-21", "WEB33-P0-22", "WEB33-P0-23", "WEB33-P0-24"];
|
|
11
11
|
export declare const WEB_E2E_WEB33_SCENARIOS_V1: readonly ("WEB33-P0-01" | "WEB33-P0-02" | "WEB33-P0-03" | "WEB33-P0-04" | "WEB33-P0-05" | "WEB33-P0-06" | "WEB33-P0-07" | "WEB33-P0-08" | "WEB33-P0-09" | "WEB33-P0-10" | "WEB33-P0-11" | "WEB33-P0-12" | "WEB33-P0-16" | "WEB33-P0-17" | "WEB33-P0-18" | "WEB33-P0-19" | "WEB33-P0-20" | "WEB33-P0-21" | "WEB33-P0-22" | "WEB33-P0-23" | "WEB33-P0-24")[];
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
/**
|
|
13
|
+
* v0.3.17 — LOCAL WORKSPACE open path. The picker is driven by a script file
|
|
14
|
+
* (`ORION_CODE_WEB_PICKER_FIXTURE`) so CI never opens a real Finder dialog.
|
|
15
|
+
*/
|
|
16
|
+
export declare const WEB_E2E_WEB38_SCENARIOS_V1: readonly ["WEB38-P0-01", "WEB38-P0-02", "WEB38-P0-03", "WEB38-P0-04", "WEB38-P0-05"];
|
|
17
|
+
/**
|
|
18
|
+
* v0.3.17 — Git right-hand work panel (`docs/plan/v0.3.17-plan.md`).
|
|
19
|
+
*
|
|
20
|
+
* One id per user-visible capability the plan calls P0/P1 and that a browser can
|
|
21
|
+
* actually verify; the per-stage unit suites are registered separately.
|
|
22
|
+
*/
|
|
23
|
+
export declare const WEB_E2E_WEB317_SCENARIOS_V1: readonly string[];
|
|
24
|
+
export declare const WEB_E2E_FULL_SCENARIOS_V1: readonly string[];
|
|
25
|
+
export declare const WEB_E2E_SETTINGS_SCENARIOS_V1: readonly string[];
|
|
14
26
|
export declare const WEB_E2E_WEB31_CRITICAL_SCENARIOS_V1: readonly ["WEB31-P0-01", "WEB31-P0-02", "WEB31-P0-08", "WEB31-P0-09", "WEB31-P0-10", "WEB31-P0-12"];
|
|
15
27
|
export declare const WEB_E2E_WEB32_CRITICAL_SCENARIOS_V1: readonly ["WEB32-P0-01", "WEB32-P0-04", "WEB32-P0-05", "WEB32-P0-06", "WEB32-P0-09", "WEB32-P0-11", "WEB32-P0-12"];
|
|
16
28
|
export declare const WEB_E2E_WEB33_CRITICAL_SCENARIOS_V1: readonly ["WEB33-P0-01", "WEB33-P0-03", "WEB33-P0-08", "WEB33-P0-10", "WEB33-P0-12", "WEB33-P0-16", "WEB33-P0-17", "WEB33-P0-18", "WEB33-P0-20", "WEB33-P0-21", "WEB33-P0-23", "WEB33-P0-24"];
|
|
17
|
-
export declare const WEB_E2E_CRITICAL_SCENARIOS_V1: readonly
|
|
29
|
+
export declare const WEB_E2E_CRITICAL_SCENARIOS_V1: readonly string[];
|
|
18
30
|
export type ReleaseGateDecisionV1 = 'GO' | 'NO_GO';
|
|
19
31
|
export interface TarballArtifactReceiptV1 {
|
|
20
32
|
readonly version: typeof RELEASE_RECEIPT_VERSION_V1;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.WEB_E2E_CRITICAL_SCENARIOS_V1 = exports.WEB_E2E_WEB33_CRITICAL_SCENARIOS_V1 = exports.WEB_E2E_WEB32_CRITICAL_SCENARIOS_V1 = exports.WEB_E2E_WEB31_CRITICAL_SCENARIOS_V1 = exports.WEB_E2E_SETTINGS_SCENARIOS_V1 = exports.WEB_E2E_FULL_SCENARIOS_V1 = exports.WEB_E2E_WEB33_SCENARIOS_V1 = exports.WEB_E2E_WEB33_SESSION_SCENARIOS_V1 = exports.WEB_E2E_WEB33_THEME_SCENARIOS_V1 = exports.WEB_E2E_WEB32_SCENARIOS_V1 = exports.WEB_E2E_WEB31_SCENARIOS_V1 = exports.WEB_E2E_LEGACY_SCENARIOS_V1 = exports.SUPPORTED_RELEASE_VERSION_LINE_V1 = exports.SUPPORTED_RELEASE_NODE_FLOORS_V1 = exports.SUPPORTED_RELEASE_NODE_MAJORS_V1 = exports.RELEASE_RECEIPT_VERSION_V1 = void 0;
|
|
3
|
+
exports.WEB_E2E_CRITICAL_SCENARIOS_V1 = exports.WEB_E2E_WEB33_CRITICAL_SCENARIOS_V1 = exports.WEB_E2E_WEB32_CRITICAL_SCENARIOS_V1 = exports.WEB_E2E_WEB31_CRITICAL_SCENARIOS_V1 = exports.WEB_E2E_SETTINGS_SCENARIOS_V1 = exports.WEB_E2E_FULL_SCENARIOS_V1 = exports.WEB_E2E_WEB317_SCENARIOS_V1 = exports.WEB_E2E_WEB38_SCENARIOS_V1 = exports.WEB_E2E_WEB33_SCENARIOS_V1 = exports.WEB_E2E_WEB33_SESSION_SCENARIOS_V1 = exports.WEB_E2E_WEB33_THEME_SCENARIOS_V1 = exports.WEB_E2E_WEB32_SCENARIOS_V1 = exports.WEB_E2E_WEB31_SCENARIOS_V1 = exports.WEB_E2E_LEGACY_SCENARIOS_V1 = exports.SUPPORTED_RELEASE_VERSION_LINE_V1 = exports.SUPPORTED_RELEASE_NODE_FLOORS_V1 = exports.SUPPORTED_RELEASE_NODE_MAJORS_V1 = exports.RELEASE_RECEIPT_VERSION_V1 = void 0;
|
|
4
4
|
exports.createTarballArtifactReceiptV1 = createTarballArtifactReceiptV1;
|
|
5
5
|
exports.verifyTarballArtifactReceiptV1 = verifyTarballArtifactReceiptV1;
|
|
6
6
|
exports.createRuntimeMatrixReceiptV1 = createRuntimeMatrixReceiptV1;
|
|
@@ -101,11 +101,37 @@ exports.WEB_E2E_WEB33_SCENARIOS_V1 = Object.freeze([
|
|
|
101
101
|
...exports.WEB_E2E_WEB33_THEME_SCENARIOS_V1,
|
|
102
102
|
...exports.WEB_E2E_WEB33_SESSION_SCENARIOS_V1,
|
|
103
103
|
]);
|
|
104
|
+
/**
|
|
105
|
+
* v0.3.17 — LOCAL WORKSPACE open path. The picker is driven by a script file
|
|
106
|
+
* (`ORION_CODE_WEB_PICKER_FIXTURE`) so CI never opens a real Finder dialog.
|
|
107
|
+
*/
|
|
108
|
+
exports.WEB_E2E_WEB38_SCENARIOS_V1 = Object.freeze([
|
|
109
|
+
'WEB38-P0-01',
|
|
110
|
+
'WEB38-P0-02',
|
|
111
|
+
'WEB38-P0-03',
|
|
112
|
+
'WEB38-P0-04',
|
|
113
|
+
'WEB38-P0-05',
|
|
114
|
+
]);
|
|
115
|
+
/**
|
|
116
|
+
* v0.3.17 — Git right-hand work panel (`docs/plan/v0.3.17-plan.md`).
|
|
117
|
+
*
|
|
118
|
+
* One id per user-visible capability the plan calls P0/P1 and that a browser can
|
|
119
|
+
* actually verify; the per-stage unit suites are registered separately.
|
|
120
|
+
*/
|
|
121
|
+
exports.WEB_E2E_WEB317_SCENARIOS_V1 = Object.freeze([
|
|
122
|
+
'WEB39-P0-01',
|
|
123
|
+
'WEB39-P0-02',
|
|
124
|
+
'WEB39-P0-03',
|
|
125
|
+
'WEB39-P0-04',
|
|
126
|
+
'WEB39-P0-05',
|
|
127
|
+
]);
|
|
104
128
|
exports.WEB_E2E_FULL_SCENARIOS_V1 = Object.freeze([
|
|
105
129
|
...exports.WEB_E2E_LEGACY_SCENARIOS_V1,
|
|
106
130
|
...exports.WEB_E2E_WEB31_SCENARIOS_V1,
|
|
107
131
|
...exports.WEB_E2E_WEB32_SCENARIOS_V1,
|
|
108
132
|
...exports.WEB_E2E_WEB33_SCENARIOS_V1,
|
|
133
|
+
...exports.WEB_E2E_WEB38_SCENARIOS_V1,
|
|
134
|
+
...exports.WEB_E2E_WEB317_SCENARIOS_V1,
|
|
109
135
|
]);
|
|
110
136
|
exports.WEB_E2E_SETTINGS_SCENARIOS_V1 = Object.freeze(exports.WEB_E2E_FULL_SCENARIOS_V1.filter(id => id.startsWith('SET-P0-')));
|
|
111
137
|
exports.WEB_E2E_WEB31_CRITICAL_SCENARIOS_V1 = Object.freeze([
|
|
@@ -620,6 +620,15 @@ export declare function listSessions(limit?: number): SessionMeta[];
|
|
|
620
620
|
* Sessions are excluded so the picker count matches the default listing.
|
|
621
621
|
*/
|
|
622
622
|
export declare function countSessionsByProject(): ReadonlyMap<string, number>;
|
|
623
|
+
/**
|
|
624
|
+
* v0.3.17 — Counts **only when the catalog is already warm**.
|
|
625
|
+
*
|
|
626
|
+
* Previewing a directory must never make the user wait for a catalog rebuild or
|
|
627
|
+
* a file-lock acquisition (the lock ceiling is 10s). This validates the
|
|
628
|
+
* in-memory cache with a cheap `stat` — no scan, no lock, no write — and
|
|
629
|
+
* returns `null` otherwise so callers can defer the count instead of blocking.
|
|
630
|
+
*/
|
|
631
|
+
export declare function peekCachedSessionCounts(): ReadonlyMap<string, number> | null;
|
|
623
632
|
/**
|
|
624
633
|
* List sessions for a single canonical project (default listings exclude
|
|
625
634
|
* archived sessions).
|
|
@@ -53,6 +53,7 @@ exports.loadSessionHistoryWithDiagnostics = loadSessionHistoryWithDiagnostics;
|
|
|
53
53
|
exports.loadSessionRestoreBundle = loadSessionRestoreBundle;
|
|
54
54
|
exports.listSessions = listSessions;
|
|
55
55
|
exports.countSessionsByProject = countSessionsByProject;
|
|
56
|
+
exports.peekCachedSessionCounts = peekCachedSessionCounts;
|
|
56
57
|
exports.listProjectSessions = listProjectSessions;
|
|
57
58
|
exports.listArchivedProjectSessions = listArchivedProjectSessions;
|
|
58
59
|
exports.findSession = findSession;
|
|
@@ -2233,8 +2234,33 @@ function listSessions(limit) {
|
|
|
2233
2234
|
* Sessions are excluded so the picker count matches the default listing.
|
|
2234
2235
|
*/
|
|
2235
2236
|
function countSessionsByProject() {
|
|
2237
|
+
return countSessionsByProjectFromCatalog(loadOrRebuildSessionCatalog());
|
|
2238
|
+
}
|
|
2239
|
+
/**
|
|
2240
|
+
* v0.3.17 — Counts **only when the catalog is already warm**.
|
|
2241
|
+
*
|
|
2242
|
+
* Previewing a directory must never make the user wait for a catalog rebuild or
|
|
2243
|
+
* a file-lock acquisition (the lock ceiling is 10s). This validates the
|
|
2244
|
+
* in-memory cache with a cheap `stat` — no scan, no lock, no write — and
|
|
2245
|
+
* returns `null` otherwise so callers can defer the count instead of blocking.
|
|
2246
|
+
*/
|
|
2247
|
+
function peekCachedSessionCounts() {
|
|
2248
|
+
const cache = sessionCatalogCache;
|
|
2249
|
+
if (!cache)
|
|
2250
|
+
return null;
|
|
2251
|
+
try {
|
|
2252
|
+
const stat = (0, fs_1.statSync)(cache.path);
|
|
2253
|
+
if (stat.mtimeMs !== cache.mtimeMs || stat.size !== cache.size)
|
|
2254
|
+
return null;
|
|
2255
|
+
}
|
|
2256
|
+
catch {
|
|
2257
|
+
return null;
|
|
2258
|
+
}
|
|
2259
|
+
return countSessionsByProjectFromCatalog(cache.catalog);
|
|
2260
|
+
}
|
|
2261
|
+
function countSessionsByProjectFromCatalog(catalog) {
|
|
2236
2262
|
const counts = new Map();
|
|
2237
|
-
for (const session of Object.values(
|
|
2263
|
+
for (const session of Object.values(catalog.sessions)) {
|
|
2238
2264
|
if (session.archivedAt)
|
|
2239
2265
|
continue;
|
|
2240
2266
|
counts.set(session.projectPath, (counts.get(session.projectPath) ?? 0) + 1);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export type GitCompareModeV1 = 'snapshot' | 'merge-base';
|
|
2
|
+
export interface GitCompareFileV1 {
|
|
3
|
+
readonly path: string;
|
|
4
|
+
readonly renamedFrom: string | null;
|
|
5
|
+
readonly additions: number;
|
|
6
|
+
readonly deletions: number;
|
|
7
|
+
readonly binary: boolean;
|
|
8
|
+
readonly sensitive: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface GitCompareResultV1 {
|
|
11
|
+
readonly mode: GitCompareModeV1;
|
|
12
|
+
readonly requestedBaseRef: string;
|
|
13
|
+
readonly requestedHeadRef: string;
|
|
14
|
+
/** The commits the comparison actually used; the UI freezes the result against these. */
|
|
15
|
+
readonly baseOid: string;
|
|
16
|
+
readonly headOid: string;
|
|
17
|
+
readonly baseLabel: string;
|
|
18
|
+
readonly headLabel: string;
|
|
19
|
+
/** Set only in merge-base mode: the divergence point that was used. */
|
|
20
|
+
readonly mergeBaseOid: string | null;
|
|
21
|
+
readonly files: readonly GitCompareFileV1[];
|
|
22
|
+
readonly additions: number;
|
|
23
|
+
readonly deletions: number;
|
|
24
|
+
readonly truncated: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface GitCompareReaderOptions {
|
|
27
|
+
readonly runGit: (args: readonly string[]) => Promise<string>;
|
|
28
|
+
}
|
|
29
|
+
export declare function createGitCompareReader(options: GitCompareReaderOptions): {
|
|
30
|
+
/** Validates a pair of refs and returns their commits, without computing anything yet. */
|
|
31
|
+
resolvePair(baseRef: string, headRef: string): Promise<Readonly<{
|
|
32
|
+
baseRef: string;
|
|
33
|
+
headRef: string;
|
|
34
|
+
baseOid: string;
|
|
35
|
+
headOid: string;
|
|
36
|
+
baseLabel: string;
|
|
37
|
+
headLabel: string;
|
|
38
|
+
}>>;
|
|
39
|
+
compare(input: {
|
|
40
|
+
readonly baseRef: string;
|
|
41
|
+
readonly headRef: string;
|
|
42
|
+
readonly mode: GitCompareModeV1;
|
|
43
|
+
}): Promise<GitCompareResultV1>;
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=git-compare-service.d.ts.map
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createGitCompareReader = createGitCompareReader;
|
|
4
|
+
/**
|
|
5
|
+
* v0.3.17 S5 — branch and version comparison (plan G5).
|
|
6
|
+
*
|
|
7
|
+
* Two modes, and the difference between them is the whole point:
|
|
8
|
+
* - `snapshot`: the tree difference between A and B;
|
|
9
|
+
* - `merge-base`: what B introduced since the branches diverged.
|
|
10
|
+
*
|
|
11
|
+
* Semantics this module is responsible for:
|
|
12
|
+
* - refs are resolved to **commits** before anything else, and the resolved OIDs are part of
|
|
13
|
+
* the result, so the UI can detect that a ref moved and freeze the opened result instead
|
|
14
|
+
* of silently comparing against a different tree (plan G5);
|
|
15
|
+
* - `merge-base --all` with more than one answer is refused — picking one candidate would
|
|
16
|
+
* present an arbitrary choice as if it were the unique divergence point, so the caller is
|
|
17
|
+
* told to use the two-endpoint mode instead (plan G5);
|
|
18
|
+
* - no common ancestor and a shallow clone with missing objects produce distinct, explicit
|
|
19
|
+
* errors rather than an empty file list that looks like a legitimate answer;
|
|
20
|
+
* - a ref name is validated before it ever reaches argv: revision expressions, ranges and
|
|
21
|
+
* pathspec magic are refused.
|
|
22
|
+
*/
|
|
23
|
+
const redaction_1 = require("../services/redaction");
|
|
24
|
+
const errors_1 = require("./errors");
|
|
25
|
+
const REF_NAME_PATTERN = /^[0-9A-Za-z](?:[0-9A-Za-z._/-]{0,250}[0-9A-Za-z])?$/u;
|
|
26
|
+
const COMPARE_FILE_LIMIT = 2000;
|
|
27
|
+
/** Resolves a ref-ish name to a commit, refusing anything that is not a plain ref name. */
|
|
28
|
+
function assertRefName(value, name) {
|
|
29
|
+
if (typeof value !== 'string' || !REF_NAME_PATTERN.test(value) || value.includes('..')) {
|
|
30
|
+
throw new errors_1.WebWorkbenchError(400, `${name} must be a branch, tag or commit hash.`, 'git_ref_invalid');
|
|
31
|
+
}
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
async function resolveCommit(runGit, ref) {
|
|
35
|
+
const resolved = (await runGit(['rev-parse', '--verify', '--quiet', `${ref}^{commit}`]).catch(() => ''))
|
|
36
|
+
.toString()
|
|
37
|
+
.trim();
|
|
38
|
+
if (!/^[0-9a-f]{7,64}$/iu.test(resolved)) {
|
|
39
|
+
throw new errors_1.WebWorkbenchError(404, `The ref ${ref} could not be resolved to a commit.`, 'git_ref_not_found');
|
|
40
|
+
}
|
|
41
|
+
return resolved;
|
|
42
|
+
}
|
|
43
|
+
function createGitCompareReader(options) {
|
|
44
|
+
const { runGit } = options;
|
|
45
|
+
const numstat = async (base, head) => {
|
|
46
|
+
const raw = await runGit([
|
|
47
|
+
'diff',
|
|
48
|
+
'--numstat',
|
|
49
|
+
'-z',
|
|
50
|
+
'--no-ext-diff',
|
|
51
|
+
'--no-textconv',
|
|
52
|
+
base,
|
|
53
|
+
head,
|
|
54
|
+
]);
|
|
55
|
+
const files = [];
|
|
56
|
+
const fields = raw.split('\0');
|
|
57
|
+
let index = 0;
|
|
58
|
+
while (index < fields.length) {
|
|
59
|
+
const record = fields[index];
|
|
60
|
+
index += 1;
|
|
61
|
+
if (!record)
|
|
62
|
+
continue;
|
|
63
|
+
const [added, deleted, inlinePath] = record.split('\t');
|
|
64
|
+
let path = inlinePath ?? '';
|
|
65
|
+
let renamedFrom = null;
|
|
66
|
+
if (path === '' && index + 1 < fields.length) {
|
|
67
|
+
renamedFrom = fields[index];
|
|
68
|
+
path = fields[index + 1];
|
|
69
|
+
index += 2;
|
|
70
|
+
}
|
|
71
|
+
if (!path)
|
|
72
|
+
continue;
|
|
73
|
+
const binary = added === '-' || deleted === '-';
|
|
74
|
+
files.push(Object.freeze({
|
|
75
|
+
path,
|
|
76
|
+
renamedFrom: renamedFrom || null,
|
|
77
|
+
additions: binary ? 0 : Number(added) || 0,
|
|
78
|
+
deletions: binary ? 0 : Number(deleted) || 0,
|
|
79
|
+
binary,
|
|
80
|
+
sensitive: (0, redaction_1.isSensitiveFilePath)(path),
|
|
81
|
+
}));
|
|
82
|
+
if (files.length >= COMPARE_FILE_LIMIT)
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
return Object.freeze({
|
|
86
|
+
files,
|
|
87
|
+
additions: files.reduce((sum, file) => sum + file.additions, 0),
|
|
88
|
+
deletions: files.reduce((sum, file) => sum + file.deletions, 0),
|
|
89
|
+
truncated: files.length >= COMPARE_FILE_LIMIT,
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
return {
|
|
93
|
+
/** Validates a pair of refs and returns their commits, without computing anything yet. */
|
|
94
|
+
async resolvePair(baseRef, headRef) {
|
|
95
|
+
const base = assertRefName(baseRef, 'base');
|
|
96
|
+
const head = assertRefName(headRef, 'head');
|
|
97
|
+
const [baseOid, headOid] = await Promise.all([
|
|
98
|
+
resolveCommit(runGit, base),
|
|
99
|
+
resolveCommit(runGit, head),
|
|
100
|
+
]);
|
|
101
|
+
return Object.freeze({
|
|
102
|
+
baseRef: base,
|
|
103
|
+
headRef: head,
|
|
104
|
+
baseOid,
|
|
105
|
+
headOid,
|
|
106
|
+
baseLabel: base,
|
|
107
|
+
headLabel: head,
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
async compare(input) {
|
|
111
|
+
const pair = await this.resolvePair(input.baseRef, input.headRef);
|
|
112
|
+
const mode = input.mode === 'merge-base' ? 'merge-base' : 'snapshot';
|
|
113
|
+
let baseOid = pair.baseOid;
|
|
114
|
+
let mergeBaseOid = null;
|
|
115
|
+
if (mode === 'merge-base') {
|
|
116
|
+
// `--all` is the honest way to ask: a single answer means one divergence point, and
|
|
117
|
+
// more than one means the question has no unique answer.
|
|
118
|
+
const raw = await runGit(['merge-base', '--all', pair.baseOid, pair.headOid]).catch(error => {
|
|
119
|
+
const message = error instanceof Error ? error.message : '';
|
|
120
|
+
if (/missing|bad object/iu.test(message)) {
|
|
121
|
+
throw new errors_1.WebWorkbenchError(409, 'The repository does not contain the objects needed for this comparison (a shallow clone?).', 'git_shallow_history');
|
|
122
|
+
}
|
|
123
|
+
// git exits non-zero with no candidates when the histories are unrelated, and with
|
|
124
|
+
// a diagnostic when objects are missing — those are different failures.
|
|
125
|
+
throw new errors_1.WebWorkbenchError(409, 'The two refs have no common ancestor; use the two-endpoint comparison instead.', 'git_merge_base_missing');
|
|
126
|
+
});
|
|
127
|
+
const candidates = raw
|
|
128
|
+
.split('\n')
|
|
129
|
+
.map(line => line.trim())
|
|
130
|
+
.filter(line => /^[0-9a-f]{7,64}$/iu.test(line));
|
|
131
|
+
if (candidates.length === 0) {
|
|
132
|
+
throw new errors_1.WebWorkbenchError(409, 'The two refs have no common ancestor; use the two-endpoint comparison instead.', 'git_merge_base_missing');
|
|
133
|
+
}
|
|
134
|
+
if (candidates.length > 1) {
|
|
135
|
+
throw new errors_1.WebWorkbenchError(409, `These refs have ${candidates.length} merge bases, so there is no unique divergence point. Use the two-endpoint comparison instead.`, 'git_merge_base_multiple');
|
|
136
|
+
}
|
|
137
|
+
mergeBaseOid = candidates[0];
|
|
138
|
+
baseOid = mergeBaseOid;
|
|
139
|
+
}
|
|
140
|
+
const stats = await numstat(baseOid, pair.headOid);
|
|
141
|
+
return Object.freeze({
|
|
142
|
+
mode,
|
|
143
|
+
requestedBaseRef: pair.baseRef,
|
|
144
|
+
requestedHeadRef: pair.headRef,
|
|
145
|
+
baseOid,
|
|
146
|
+
headOid: pair.headOid,
|
|
147
|
+
baseLabel: mode === 'merge-base' && mergeBaseOid !== null
|
|
148
|
+
? `共同祖先 ${mergeBaseOid.slice(0, 10)}`
|
|
149
|
+
: pair.baseLabel,
|
|
150
|
+
headLabel: pair.headLabel,
|
|
151
|
+
mergeBaseOid,
|
|
152
|
+
files: stats.files,
|
|
153
|
+
additions: stats.additions,
|
|
154
|
+
deletions: stats.deletions,
|
|
155
|
+
truncated: stats.truncated,
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=git-compare-service.js.map
|