@usepowerplant/cli 0.4.12 → 0.4.13
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 +16 -125
- package/dist/cli.js +218 -154
- package/dist/menubar/Powerplant.app.zip +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
This file is the public npm page of @usepowerplant/cli. Write only what a
|
|
3
|
+
customer needs. Team procedures go in RELEASING.md, which npm does not publish.
|
|
4
|
+
Rule: docs/client-engineering-rules.md, "Releases".
|
|
5
|
+
-->
|
|
6
|
+
|
|
1
7
|
# @usepowerplant/cli
|
|
2
8
|
|
|
3
9
|
Set up a machine for [Powerplant](https://powerplant.sh): enroll it into
|
|
@@ -19,7 +25,7 @@ re-run at any time — finished steps are skipped with a note:
|
|
|
19
25
|
1. **Enroll this machine.** Your browser opens so you can approve the
|
|
20
26
|
machine into your org. On macOS, the credential is encrypted in a local
|
|
21
27
|
file with its key in Keychain; other platforms use a file only your user
|
|
22
|
-
can read.
|
|
28
|
+
can read.
|
|
23
29
|
2. **Install the background service.** The sync service watches your local
|
|
24
30
|
coding-agent sessions (Claude Code, Codex, and Cursor) and uploads the
|
|
25
31
|
ones in scope. If recent sessions name repositories under a macOS-protected
|
|
@@ -119,8 +125,10 @@ powerplant update
|
|
|
119
125
|
That reinstalls `@usepowerplant/cli` with whichever package manager put it
|
|
120
126
|
on this machine (npm, pnpm, bun, or yarn), so the copy that is running is the
|
|
121
127
|
copy that gets replaced, then restarts the background sync onto the new
|
|
122
|
-
version.
|
|
123
|
-
|
|
128
|
+
version. When this machine already has the newest version available to it,
|
|
129
|
+
`update` says so and changes nothing. Running the manager's own global
|
|
130
|
+
install yourself works too: the service notices the new version on disk and
|
|
131
|
+
restarts itself within moments.
|
|
124
132
|
When the install can't be replaced this way (a folder only an administrator
|
|
125
133
|
can write, or a layout the CLI doesn't recognise), `update` says why and
|
|
126
134
|
prints the command to run instead. `powerplant status` tells you when a
|
|
@@ -144,134 +152,17 @@ The one exception is a registry that does not serve the release yet (npm's
|
|
|
144
152
|
cache can lag a publish by a few minutes): the sync tries that release
|
|
145
153
|
again on its next hourly checks, up to three times, and the menu says so.
|
|
146
154
|
|
|
155
|
+
Powerplant offers your machine a release only after the team has tested
|
|
156
|
+
it, so the version it offers can be older than the newest version listed on
|
|
157
|
+
npm. Powerplant never offers or automatically installs a version older
|
|
158
|
+
than the one you have.
|
|
159
|
+
|
|
147
160
|
Powerplant also publishes the oldest version it still supports. When yours
|
|
148
161
|
is older, `powerplant status`, `powerplant doctor`, the menu bar app, and
|
|
149
162
|
the sync service log all say so and point you at `powerplant update`. Nothing
|
|
150
163
|
stops working on that notice — an unsupported version keeps syncing, but
|
|
151
164
|
it is no longer tested or fixed, so update when you see it.
|
|
152
165
|
|
|
153
|
-
### How a release reaches production
|
|
154
|
-
|
|
155
|
-
The same CLI package serves every environment. Staging, development, and
|
|
156
|
-
agent installs follow npm's `next` tag; enrolled production installs follow
|
|
157
|
-
the exact version promoted through the production API. Fresh installs
|
|
158
|
-
without a tag resolve npm's `latest`. Use a separate staging install below
|
|
159
|
-
to test `next` while keeping your production install on its promoted version.
|
|
160
|
-
|
|
161
|
-
1. **Release.** The "Release CLI to next" workflow (GitHub Actions,
|
|
162
|
-
manual) builds the CLI, signs and notarizes the menu bar app into the
|
|
163
|
-
package, and publishes to the `next` tag. Staging, development, and
|
|
164
|
-
agent installs with automatic updates enabled check at boot and hourly
|
|
165
|
-
while the service is running. Sleeping machines, disabled updates, and
|
|
166
|
-
failed installs can leave a machine behind. Verify the installed staging
|
|
167
|
-
version and exercise sync and the menu actions you changed before
|
|
168
|
-
promoting; elapsed time alone is not evidence of testing. Promote only
|
|
169
|
-
after the workflow's "Wait until npm serves the release" step is green:
|
|
170
|
-
npm's CDN caches the package document for about five minutes after a
|
|
171
|
-
publish, and a production machine that checks inside that window gets
|
|
172
|
-
"no matching version" and waits an hour to try again.
|
|
173
|
-
2. **Promote.** From a repo checkout with npm package access and `gh`
|
|
174
|
-
signed in, name the version you tested:
|
|
175
|
-
|
|
176
|
-
```bash
|
|
177
|
-
pnpm cli:promote:release 0.5.0
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
Or, if `next` still points at that tested version:
|
|
181
|
-
|
|
182
|
-
```bash
|
|
183
|
-
pnpm cli:promote:release
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
Omitting the version resolves `next` when the command runs.
|
|
187
|
-
The script moves npm's `latest` from your terminal, with your security
|
|
188
|
-
key in the browser (npm requires a live second factor for every
|
|
189
|
-
dist-tag move, so no CI credential can do this half), then dispatches
|
|
190
|
-
the Promote CLI workflow, which verifies the tag and points production
|
|
191
|
-
machines at the same artifact. The script watches the workflow; confirm
|
|
192
|
-
the pointer write succeeded before calling the promotion complete.
|
|
193
|
-
If the tag moved but that write failed, fresh installs are ahead of
|
|
194
|
-
enrolled machines. Re-dispatch **the same version**, then check that run:
|
|
195
|
-
|
|
196
|
-
```bash
|
|
197
|
-
gh workflow run promote-cli.yml -f version=0.5.0
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
A failed Slack announcement after a successful pointer write does not
|
|
201
|
-
undo promotion. Read the failed step before retrying.
|
|
202
|
-
3. **Adopt.** Running production services with automatic updates enabled
|
|
203
|
-
check for the promoted version at boot and hourly. Other users can
|
|
204
|
-
update from the menu bar or `powerplant update`; `powerplant status`
|
|
205
|
-
reports the available version. Until something is promoted, production
|
|
206
|
-
sees no update target, only the supported-version floor.
|
|
207
|
-
|
|
208
|
-
Promoting an older version is the brake: production holds there, `latest`
|
|
209
|
-
moves back for new installs, and no enrolled machine ever downgrades. A
|
|
210
|
-
bad release is undone by a new release and a new promotion.
|
|
211
|
-
|
|
212
|
-
### Staging beside production on one Mac
|
|
213
|
-
|
|
214
|
-
Use production for day-to-day team work. Staging is for testing enrollment,
|
|
215
|
-
onboarding, and releases. See the [internal machine setup guide](../../README.md#set-up-your-machine)
|
|
216
|
-
for access and data boundaries: running sync in both environments uploads
|
|
217
|
-
and processes eligible sessions twice.
|
|
218
|
-
|
|
219
|
-
A machine has one global `powerplant` by default, and the background sync
|
|
220
|
-
for each environment runs whatever copy `init` was run from. To try
|
|
221
|
-
releases on staging while production keeps the promoted version, give
|
|
222
|
-
staging its own copy under its own npm prefix and set it up from there:
|
|
223
|
-
|
|
224
|
-
```bash
|
|
225
|
-
npm install -g --prefix ~/.powerplant-staging @usepowerplant/cli@next
|
|
226
|
-
~/.powerplant-staging/bin/powerplant init --env staging
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
`init --env staging` registers its own MCP entry, `powerplant-staging`, in each
|
|
230
|
-
selected agent, beside the production `powerplant` entry. Agents see its tools
|
|
231
|
-
as `mcp__powerplant-staging__*`. A CLI older than this change registered
|
|
232
|
-
staging as `powerplant`: if that entry still points at staging, run a bare
|
|
233
|
-
`powerplant connect` from your production install once to restore it. Include
|
|
234
|
-
`--env staging` on every staging command.
|
|
235
|
-
|
|
236
|
-
The `@next` pin matters: a bare install resolves `latest`, the production
|
|
237
|
-
channel, so the new staging copy would start on the promoted release and
|
|
238
|
-
test nothing until its first update.
|
|
239
|
-
|
|
240
|
-
The staging daemon runs that copy and updates it in place (the updater
|
|
241
|
-
pins npm to the prefix the running copy lives under), so
|
|
242
|
-
`~/.powerplant-staging` follows the `next` channel while the production
|
|
243
|
-
install follows the promoted one. Run staging commands through that path,
|
|
244
|
-
`~/.powerplant-staging/bin/powerplant status --env staging`.
|
|
245
|
-
Use `~/.powerplant-staging/bin/powerplant update --env staging` to update
|
|
246
|
-
now, then `~/.powerplant-staging/bin/powerplant doctor --env staging` to
|
|
247
|
-
check the installed version, service, and install location.
|
|
248
|
-
|
|
249
|
-
### Menu bar apps during release testing
|
|
250
|
-
|
|
251
|
-
The npm package, including `@next`, contains **Powerplant.app**, the
|
|
252
|
-
production app. Only production `init` installs it and the production
|
|
253
|
-
service refreshes it after CLI updates. Staging `init` does neither.
|
|
254
|
-
|
|
255
|
-
Run **Powerplant Staging.app** separately, alongside production. Its icon
|
|
256
|
-
has an "S", and it uses the CLI at `~/.powerplant-staging/bin/powerplant`
|
|
257
|
-
when present. Follow the menu bar app's [local build instructions](../../apps/menubar/README.md#development)
|
|
258
|
-
or [signed release instructions](../../apps/menubar/README.md#release).
|
|
259
|
-
Rebuild or replace that staging app when testing native app changes;
|
|
260
|
-
updating the staging CLI does not update it. This tests the staging variant,
|
|
261
|
-
not the exact signed production bundle embedded in the npm package.
|
|
262
|
-
|
|
263
|
-
### Web and API promotion is separate
|
|
264
|
-
|
|
265
|
-
Merges to `main` deploy to staging automatically. The
|
|
266
|
-
[Promote to Production workflow](../../.github/workflows/promote.yml) can
|
|
267
|
-
promote the live staging API commit after its E2E check passes, with the
|
|
268
|
-
production schema and web build from that commit. Automatic runs happen
|
|
269
|
-
on CI completion and an hourly catch-up during weekdays, 09:00 to before
|
|
270
|
-
16:00 America/Vancouver, unless `AUTO_PROMOTE_PAUSED=true`. A manual
|
|
271
|
-
workflow dispatch can promote outside that window. Staging does not wait
|
|
272
|
-
indefinitely for manual QA. This workflow does not promote the CLI;
|
|
273
|
-
`pnpm cli:promote:release` does not deploy the web or API.
|
|
274
|
-
|
|
275
166
|
### Moving from @powerplant-sh/cli
|
|
276
167
|
|
|
277
168
|
Releases up to 0.2.1 were published as `@powerplant-sh/cli`. That package
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
3
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="66f773d5-e0b2-5538-a184-3ee15c3a469a")}catch(e){}}();
|
|
4
4
|
const __cliNodeMajor = Number(process.versions.node.split('.')[0]);
|
|
5
5
|
if (__cliNodeMajor < 20) {
|
|
6
6
|
console.error(`powerplant needs Node 20 or newer — this is Node ${process.versions.node}.`);
|
|
@@ -85883,11 +85883,11 @@ import path12 from "node:path";
|
|
|
85883
85883
|
import { existsSync as existsSync6, lstatSync as lstatSync4 } from "node:fs";
|
|
85884
85884
|
import path11 from "node:path";
|
|
85885
85885
|
|
|
85886
|
-
// ../../apps/collector/src/
|
|
85886
|
+
// ../../apps/collector/src/codexTranscriptDiscovery.ts
|
|
85887
85887
|
import { createHash } from "node:crypto";
|
|
85888
85888
|
import { closeSync, lstatSync, openSync, readSync, readdirSync, realpathSync } from "node:fs";
|
|
85889
85889
|
import path2 from "node:path";
|
|
85890
|
-
var
|
|
85890
|
+
var transcriptDiscoveryRecordSchema = external_exports.object({
|
|
85891
85891
|
sessionId: external_exports.string().regex(/^[a-zA-Z0-9_-]{1,200}$/),
|
|
85892
85892
|
transcriptPath: external_exports.string().max(4096).nullable()
|
|
85893
85893
|
});
|
|
@@ -85900,7 +85900,7 @@ function codexTranscriptRecordPath(home, sessionId) {
|
|
|
85900
85900
|
`${createHash("sha256").update(sessionId).digest("hex")}.json`
|
|
85901
85901
|
);
|
|
85902
85902
|
}
|
|
85903
|
-
function
|
|
85903
|
+
function readCodexTranscriptDiscoveryRecords(home, roots) {
|
|
85904
85904
|
const dir = codexTranscriptRecordsDir(home);
|
|
85905
85905
|
let files;
|
|
85906
85906
|
try {
|
|
@@ -85912,9 +85912,9 @@ function readCodexTranscriptHints(home, roots) {
|
|
|
85912
85912
|
for (const file2 of files) {
|
|
85913
85913
|
if (!/^[a-f0-9]{64}\.json$/.test(file2)) continue;
|
|
85914
85914
|
try {
|
|
85915
|
-
const
|
|
85916
|
-
if (!lstatSync(
|
|
85917
|
-
const fd = openSync(
|
|
85915
|
+
const recordPath = path2.join(dir, file2);
|
|
85916
|
+
if (!lstatSync(recordPath).isFile()) continue;
|
|
85917
|
+
const fd = openSync(recordPath, "r");
|
|
85918
85918
|
let text;
|
|
85919
85919
|
try {
|
|
85920
85920
|
const buffer = Buffer.alloc(32 * 1024 + 1);
|
|
@@ -85924,10 +85924,10 @@ function readCodexTranscriptHints(home, roots) {
|
|
|
85924
85924
|
} finally {
|
|
85925
85925
|
closeSync(fd);
|
|
85926
85926
|
}
|
|
85927
|
-
const
|
|
85928
|
-
if (file2 !== `${createHash("sha256").update(
|
|
85929
|
-
if (
|
|
85930
|
-
const rolloutPath = path2.normalize(
|
|
85927
|
+
const record2 = transcriptDiscoveryRecordSchema.parse(JSON.parse(text));
|
|
85928
|
+
if (file2 !== `${createHash("sha256").update(record2.sessionId).digest("hex")}.json`) continue;
|
|
85929
|
+
if (record2.transcriptPath === null || !path2.isAbsolute(record2.transcriptPath)) continue;
|
|
85930
|
+
const rolloutPath = path2.normalize(record2.transcriptPath);
|
|
85931
85931
|
if (path2.extname(rolloutPath) !== ".jsonl") continue;
|
|
85932
85932
|
const rootIndex = roots.findIndex((root2) => {
|
|
85933
85933
|
const relative2 = path2.relative(root2, rolloutPath);
|
|
@@ -85937,7 +85937,7 @@ function readCodexTranscriptHints(home, roots) {
|
|
|
85937
85937
|
const root = roots[rootIndex];
|
|
85938
85938
|
if (realpathSync(rolloutPath) !== path2.join(realpathSync(root), path2.relative(root, rolloutPath)))
|
|
85939
85939
|
continue;
|
|
85940
|
-
result.push({ rolloutPath, sessionId:
|
|
85940
|
+
result.push({ rolloutPath, sessionId: record2.sessionId, archived: rootIndex === 1 });
|
|
85941
85941
|
} catch {
|
|
85942
85942
|
}
|
|
85943
85943
|
}
|
|
@@ -85950,7 +85950,7 @@ import { createHmac } from "node:crypto";
|
|
|
85950
85950
|
// ../../apps/collector/src/http.ts
|
|
85951
85951
|
var import_undici = __toESM(require_undici(), 1);
|
|
85952
85952
|
var CLI_VERSION_HEADER = "x-powerplant-cli-version";
|
|
85953
|
-
var cliVersion = true ? "0.4.
|
|
85953
|
+
var cliVersion = true ? "0.4.13" : "0.0.0-checkout";
|
|
85954
85954
|
function cliVersionHeaders() {
|
|
85955
85955
|
return { [CLI_VERSION_HEADER]: cliVersion, "user-agent": `powerplant-cli/${cliVersion}` };
|
|
85956
85956
|
}
|
|
@@ -86257,7 +86257,7 @@ function bindDiagnosticEnrollment(diagnostic, token) {
|
|
|
86257
86257
|
passEnrollments.set(diagnostic, token);
|
|
86258
86258
|
}
|
|
86259
86259
|
|
|
86260
|
-
// ../../apps/collector/src/
|
|
86260
|
+
// ../../apps/collector/src/codexSessions.ts
|
|
86261
86261
|
import { closeSync as closeSync3, mkdirSync as mkdirSync2, openSync as openSync3, renameSync, rmSync as rmSync2, utimesSync, writeSync } from "node:fs";
|
|
86262
86262
|
import os6 from "node:os";
|
|
86263
86263
|
import path5 from "node:path";
|
|
@@ -86697,13 +86697,17 @@ function isPathWithinScopes(resolved, scopePaths) {
|
|
|
86697
86697
|
);
|
|
86698
86698
|
}
|
|
86699
86699
|
|
|
86700
|
-
// ../../apps/collector/src/
|
|
86700
|
+
// ../../apps/collector/src/codexSessions.ts
|
|
86701
86701
|
function parseCodexRollout(text, sharedUsage) {
|
|
86702
86702
|
let meta3 = null;
|
|
86703
|
+
let initialEvent;
|
|
86704
|
+
let records = 0;
|
|
86703
86705
|
let model;
|
|
86704
86706
|
const timestamps = [];
|
|
86705
86707
|
const usage = sharedUsage ?? createCodexUsageReader();
|
|
86706
86708
|
for (const line of parseJsonl(text)) {
|
|
86709
|
+
records += 1;
|
|
86710
|
+
if (records === 2 && meta3 !== null) initialEvent = line;
|
|
86707
86711
|
if (typeof line.timestamp === "string") timestamps.push(line.timestamp);
|
|
86708
86712
|
usage.pushRecord(line);
|
|
86709
86713
|
const payload = line.payload;
|
|
@@ -86719,8 +86723,10 @@ function parseCodexRollout(text, sharedUsage) {
|
|
|
86719
86723
|
if (meta3 === null && timestamps.length === 0) return null;
|
|
86720
86724
|
const git = meta3?.git ?? null;
|
|
86721
86725
|
const sessionId = meta3?.id ?? meta3?.session_id;
|
|
86726
|
+
const facts = meta3 === null ? {} : harnessSessionFacts("codex", { ...meta3, initialEvent });
|
|
86722
86727
|
return {
|
|
86723
|
-
kind:
|
|
86728
|
+
kind: facts.subagent ? "subagent" : "session",
|
|
86729
|
+
...facts.standaloneReview && { standaloneReview: true },
|
|
86724
86730
|
...typeof sessionId === "string" && sessionId !== "" && { sessionId },
|
|
86725
86731
|
startedAt: timestamps[0],
|
|
86726
86732
|
endedAt: timestamps[timestamps.length - 1],
|
|
@@ -86756,6 +86762,7 @@ function foldRolloutPages(pages, usage) {
|
|
|
86756
86762
|
const repoFullName = first("repoFullName");
|
|
86757
86763
|
return {
|
|
86758
86764
|
kind: pages[0].kind,
|
|
86765
|
+
...pages[0].standaloneReview && { standaloneReview: true },
|
|
86759
86766
|
...sessionId !== void 0 && { sessionId },
|
|
86760
86767
|
...startedAt !== void 0 && { startedAt },
|
|
86761
86768
|
...endedAt !== void 0 && { endedAt },
|
|
@@ -86777,21 +86784,21 @@ function rolloutIdentity(rolloutPath) {
|
|
|
86777
86784
|
if (!conventional) return { threadId: name, page: false };
|
|
86778
86785
|
return { threadId: threadId2.toLowerCase(), page: pageId !== void 0 };
|
|
86779
86786
|
}
|
|
86780
|
-
function
|
|
86787
|
+
function codexMergedTranscriptsDir(home) {
|
|
86781
86788
|
return path5.join(home, ".config", "powerplant", "codex");
|
|
86782
86789
|
}
|
|
86783
|
-
function
|
|
86784
|
-
const existing = statIfExists(
|
|
86790
|
+
function mergedTranscriptIsFresh(mergedTranscriptPath, mtimeMs) {
|
|
86791
|
+
const existing = statIfExists(mergedTranscriptPath);
|
|
86785
86792
|
return existing !== null && Math.abs(existing.mtimeMs - mtimeMs) < 1;
|
|
86786
86793
|
}
|
|
86787
|
-
var
|
|
86788
|
-
constructor(
|
|
86789
|
-
this.
|
|
86790
|
-
mkdirSync2(path5.dirname(
|
|
86791
|
-
this.tmpPath = `${
|
|
86794
|
+
var MergedTranscriptWriter = class {
|
|
86795
|
+
constructor(mergedTranscriptPath) {
|
|
86796
|
+
this.mergedTranscriptPath = mergedTranscriptPath;
|
|
86797
|
+
mkdirSync2(path5.dirname(mergedTranscriptPath), { recursive: true, mode: 448 });
|
|
86798
|
+
this.tmpPath = `${mergedTranscriptPath}.${process.pid}.tmp`;
|
|
86792
86799
|
this.fd = openSync3(this.tmpPath, "w", 384);
|
|
86793
86800
|
}
|
|
86794
|
-
|
|
86801
|
+
mergedTranscriptPath;
|
|
86795
86802
|
tmpPath;
|
|
86796
86803
|
fd;
|
|
86797
86804
|
bytes = 0;
|
|
@@ -86805,7 +86812,7 @@ var MergedSpoolWriter = class {
|
|
|
86805
86812
|
finish(mtimeMs) {
|
|
86806
86813
|
this.close();
|
|
86807
86814
|
utimesSync(this.tmpPath, mtimeMs / 1e3, mtimeMs / 1e3);
|
|
86808
|
-
renameSync(this.tmpPath, this.
|
|
86815
|
+
renameSync(this.tmpPath, this.mergedTranscriptPath);
|
|
86809
86816
|
return this.bytes;
|
|
86810
86817
|
}
|
|
86811
86818
|
abandon() {
|
|
@@ -86875,7 +86882,7 @@ async function collectCodexSessions(options = {}) {
|
|
|
86875
86882
|
const now = options.now ?? Date.now();
|
|
86876
86883
|
const capture = options.capture ?? captureEverythingUnresolved;
|
|
86877
86884
|
const maxBytes = options.maxTranscriptBytes ?? MAX_TRANSCRIPT_FILE_BYTES;
|
|
86878
|
-
const
|
|
86885
|
+
const mergedTranscriptsDir = options.mergedTranscriptsDir ?? codexMergedTranscriptsDir(options.homeDir ?? os6.homedir());
|
|
86879
86886
|
const items = [];
|
|
86880
86887
|
const candidates = [];
|
|
86881
86888
|
const unreadable = [];
|
|
@@ -86886,12 +86893,12 @@ async function collectCodexSessions(options = {}) {
|
|
|
86886
86893
|
let largestMergedBytes = 0;
|
|
86887
86894
|
const externalIdOf = (rolloutPath) => path5.basename(rolloutPath, ".jsonl");
|
|
86888
86895
|
const files = /* @__PURE__ */ new Map();
|
|
86889
|
-
const observe = async (paths, archived,
|
|
86896
|
+
const observe = async (paths, archived, discoverySessionId) => {
|
|
86890
86897
|
for (const rolloutPath of paths) {
|
|
86891
86898
|
const known = files.get(rolloutPath);
|
|
86892
86899
|
if (known !== void 0) {
|
|
86893
|
-
if (
|
|
86894
|
-
else known.
|
|
86900
|
+
if (discoverySessionId === void 0) delete known.discoverySessionIds;
|
|
86901
|
+
else known.discoverySessionIds?.push(discoverySessionId);
|
|
86895
86902
|
continue;
|
|
86896
86903
|
}
|
|
86897
86904
|
await yieldToEventLoop();
|
|
@@ -86904,15 +86911,15 @@ async function collectCodexSessions(options = {}) {
|
|
|
86904
86911
|
mtimeMs: stat.mtimeMs,
|
|
86905
86912
|
changedMs: Math.max(stat.mtimeMs, stat.ctimeMs),
|
|
86906
86913
|
sizeBytes: stat.size,
|
|
86907
|
-
...
|
|
86914
|
+
...discoverySessionId !== void 0 && { discoverySessionIds: [discoverySessionId] }
|
|
86908
86915
|
});
|
|
86909
86916
|
}
|
|
86910
86917
|
};
|
|
86911
86918
|
const archiveDir = codexArchivedSessionsDir(sessionsDir);
|
|
86912
|
-
const
|
|
86913
|
-
if (
|
|
86914
|
-
for (const
|
|
86915
|
-
await observe([
|
|
86919
|
+
const discoveryHome = options.homeDir ?? (options.codexDir === void 0 ? os6.homedir() : void 0);
|
|
86920
|
+
if (discoveryHome !== void 0) {
|
|
86921
|
+
for (const record2 of readCodexTranscriptDiscoveryRecords(discoveryHome, [sessionsDir, archiveDir])) {
|
|
86922
|
+
await observe([record2.rolloutPath], record2.archived, record2.sessionId);
|
|
86916
86923
|
}
|
|
86917
86924
|
}
|
|
86918
86925
|
await observe(rolloutFiles(sessionsDir), false);
|
|
@@ -86927,17 +86934,17 @@ async function collectCodexSessions(options = {}) {
|
|
|
86927
86934
|
const stored = dedupeStoreCopies(observed);
|
|
86928
86935
|
let externalId = externalIdOf((stored.find((file2) => !file2.identity.page) ?? stored[0]).rolloutPath);
|
|
86929
86936
|
try {
|
|
86930
|
-
const
|
|
86937
|
+
const mergedTranscriptPath = stored.length > 1 ? path5.join(mergedTranscriptsDir, `${threadId2}.jsonl`) : void 0;
|
|
86931
86938
|
const changedMs = Math.max(...stored.map((file2) => file2.changedMs));
|
|
86932
86939
|
if (options.sinceMs !== void 0 && changedMs < options.sinceMs) {
|
|
86933
|
-
const neverMerged =
|
|
86940
|
+
const neverMerged = mergedTranscriptPath !== void 0 && !options.locationsOnly && statIfExists(mergedTranscriptPath) === null;
|
|
86934
86941
|
if (!neverMerged) continue;
|
|
86935
86942
|
}
|
|
86936
86943
|
await yieldToEventLoop();
|
|
86937
86944
|
const named = stored.filter((file2) => {
|
|
86938
|
-
if (file2.
|
|
86945
|
+
if (file2.discoverySessionIds === void 0) return true;
|
|
86939
86946
|
const { sessionId } = rolloutLocation(file2.rolloutPath);
|
|
86940
|
-
return sessionId !== void 0 && file2.
|
|
86947
|
+
return sessionId !== void 0 && file2.discoverySessionIds.includes(sessionId);
|
|
86941
86948
|
});
|
|
86942
86949
|
if (named.length === 0) continue;
|
|
86943
86950
|
const threadFiles = named.length > 1 ? orderPages(named) : named;
|
|
@@ -86967,7 +86974,7 @@ async function collectCodexSessions(options = {}) {
|
|
|
86967
86974
|
const thread = {
|
|
86968
86975
|
externalId,
|
|
86969
86976
|
files: threadFiles,
|
|
86970
|
-
...threadFiles.length > 1 &&
|
|
86977
|
+
...threadFiles.length > 1 && mergedTranscriptPath !== void 0 && { mergedTranscriptPath },
|
|
86971
86978
|
mtimeMs,
|
|
86972
86979
|
archived: threadFiles.every((file2) => file2.archived)
|
|
86973
86980
|
};
|
|
@@ -87017,8 +87024,8 @@ async function collectCodexSessions(options = {}) {
|
|
|
87017
87024
|
const { thread, cwd, decision } = candidate;
|
|
87018
87025
|
let writer;
|
|
87019
87026
|
try {
|
|
87020
|
-
const rewrite = thread.
|
|
87021
|
-
if (thread.
|
|
87027
|
+
const rewrite = thread.mergedTranscriptPath !== void 0 && !mergedTranscriptIsFresh(thread.mergedTranscriptPath, thread.mtimeMs);
|
|
87028
|
+
if (thread.mergedTranscriptPath !== void 0 && rewrite) writer = new MergedTranscriptWriter(thread.mergedTranscriptPath);
|
|
87022
87029
|
const pages = [];
|
|
87023
87030
|
const usage = createCodexUsageReader();
|
|
87024
87031
|
let vanished = false;
|
|
@@ -87039,11 +87046,11 @@ async function collectCodexSessions(options = {}) {
|
|
|
87039
87046
|
continue;
|
|
87040
87047
|
}
|
|
87041
87048
|
const summary = foldRolloutPages(pages, usage.finish());
|
|
87042
|
-
let
|
|
87043
|
-
if (thread.
|
|
87044
|
-
const mergedBytes = writer ? writer.finish(thread.mtimeMs) : statIfExists(thread.
|
|
87049
|
+
let transcriptUploadSourcePath = thread.files[0].rolloutPath;
|
|
87050
|
+
if (thread.mergedTranscriptPath !== void 0) {
|
|
87051
|
+
const mergedBytes = writer ? writer.finish(thread.mtimeMs) : statIfExists(thread.mergedTranscriptPath)?.size ?? 0;
|
|
87045
87052
|
writer = void 0;
|
|
87046
|
-
|
|
87053
|
+
transcriptUploadSourcePath = thread.mergedTranscriptPath;
|
|
87047
87054
|
mergedThreads += 1;
|
|
87048
87055
|
largestMergedBytes = Math.max(largestMergedBytes, mergedBytes);
|
|
87049
87056
|
}
|
|
@@ -87052,13 +87059,14 @@ async function collectCodexSessions(options = {}) {
|
|
|
87052
87059
|
const title = summary.sessionId ? threadNames.get(summary.sessionId) : void 0;
|
|
87053
87060
|
items.push({
|
|
87054
87061
|
mtimeMs: thread.mtimeMs,
|
|
87055
|
-
transcriptPath,
|
|
87062
|
+
transcriptPath: transcriptUploadSourcePath,
|
|
87056
87063
|
...cwd && { cwd },
|
|
87057
87064
|
item: {
|
|
87058
87065
|
source: "codex",
|
|
87059
87066
|
externalId: thread.externalId,
|
|
87060
87067
|
status: now - thread.mtimeMs < ACTIVE_GRACE_MS ? "active" : "inactive",
|
|
87061
87068
|
archived: thread.archived,
|
|
87069
|
+
...summary.standaloneReview && { automation: true },
|
|
87062
87070
|
...summary.startedAt && { startedAt: summary.startedAt },
|
|
87063
87071
|
...summary.endedAt && { endedAt: summary.endedAt },
|
|
87064
87072
|
...summary.model && { model: summary.model },
|
|
@@ -87219,7 +87227,7 @@ function defaultCursorUserDir(home) {
|
|
|
87219
87227
|
function cursorGlobalStorageDir(home) {
|
|
87220
87228
|
return path6.join(defaultCursorUserDir(home), "globalStorage");
|
|
87221
87229
|
}
|
|
87222
|
-
function
|
|
87230
|
+
function cursorExtractedTranscriptsDir(home) {
|
|
87223
87231
|
return path6.join(home, ".config", "powerplant", "cursor");
|
|
87224
87232
|
}
|
|
87225
87233
|
var emptyResult = () => ({
|
|
@@ -87275,7 +87283,7 @@ var cachedMetaSchema = external_exports.object({
|
|
|
87275
87283
|
model: external_exports.string().optional(),
|
|
87276
87284
|
...cursorTranscriptMetaDisclosureSchema.shape
|
|
87277
87285
|
});
|
|
87278
|
-
function
|
|
87286
|
+
function extractedTranscriptFileName(composerId) {
|
|
87279
87287
|
const sanitized = composerId.replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 80);
|
|
87280
87288
|
const hash2 = createHash2("sha256").update(composerId).digest("hex").slice(0, 8);
|
|
87281
87289
|
return `${sanitized}-${hash2}.jsonl`;
|
|
@@ -87338,7 +87346,7 @@ function toolFilePathOf(args) {
|
|
|
87338
87346
|
}
|
|
87339
87347
|
return void 0;
|
|
87340
87348
|
}
|
|
87341
|
-
var
|
|
87349
|
+
var EXTRACTED_TRANSCRIPT_META_RESERVE_BYTES = 64 * 1024;
|
|
87342
87350
|
var BUBBLE_FIELDS_SHARED = ["_v", "type", "bubbleId", "serverBubbleId", "text", "createdAt"];
|
|
87343
87351
|
var USER_BUBBLE_FIELDS = /* @__PURE__ */ new Set([...BUBBLE_FIELDS_SHARED, "context"]);
|
|
87344
87352
|
var ASSISTANT_BUBBLE_FIELDS = /* @__PURE__ */ new Set([
|
|
@@ -87582,7 +87590,7 @@ function extractTranscript(db, composerId, maxBytes) {
|
|
|
87582
87590
|
const spine = Array.isArray(data?.fullConversationHeadersOnly) ? data.fullConversationHeadersOnly : [];
|
|
87583
87591
|
if (spine.length === 0) return null;
|
|
87584
87592
|
const recorder = createOmissionRecorder();
|
|
87585
|
-
const budget = maxBytes -
|
|
87593
|
+
const budget = maxBytes - EXTRACTED_TRANSCRIPT_META_RESERVE_BYTES;
|
|
87586
87594
|
const serialized = [];
|
|
87587
87595
|
const modelCounts = /* @__PURE__ */ new Map();
|
|
87588
87596
|
let bytes = 0;
|
|
@@ -87663,7 +87671,7 @@ async function collectCursorSessions(options = {}) {
|
|
|
87663
87671
|
collectCursorTranscriptFiles(options, (id) => {
|
|
87664
87672
|
if (knownIds.has(id)) return true;
|
|
87665
87673
|
return db?.prepare("select composerId from composerHeaders where composerId = ?").get(id) != null;
|
|
87666
|
-
}, result, options.
|
|
87674
|
+
}, result, options.extractedTranscriptsDir ?? cursorExtractedTranscriptsDir(options.homeDir ?? os7.homedir()), extractedTranscriptFileName);
|
|
87667
87675
|
}
|
|
87668
87676
|
} finally {
|
|
87669
87677
|
try {
|
|
@@ -87677,9 +87685,9 @@ async function collectCursorSessions(options = {}) {
|
|
|
87677
87685
|
function collectFromStore(db, userDir, options, result, knownIds) {
|
|
87678
87686
|
const now = options.now ?? Date.now();
|
|
87679
87687
|
const capture = options.capture ?? captureEverythingUnresolved;
|
|
87680
|
-
const
|
|
87688
|
+
const extractedTranscriptsDir = options.extractedTranscriptsDir ?? cursorExtractedTranscriptsDir(options.homeDir ?? os7.homedir());
|
|
87681
87689
|
const maxBytes = options.maxTranscriptBytes ?? MAX_TRANSCRIPT_FILE_BYTES;
|
|
87682
|
-
let
|
|
87690
|
+
let extractedTranscriptsDirReady = false;
|
|
87683
87691
|
const columns = new Set(
|
|
87684
87692
|
db.prepare("pragma table_info(composerHeaders)").all().map((row) => isRecord2(row) ? row.name : void 0)
|
|
87685
87693
|
);
|
|
@@ -87729,15 +87737,15 @@ function collectFromStore(db, userDir, options, result, knownIds) {
|
|
|
87729
87737
|
});
|
|
87730
87738
|
continue;
|
|
87731
87739
|
}
|
|
87732
|
-
const
|
|
87733
|
-
const
|
|
87740
|
+
const transcriptUploadSourcePath = path6.join(extractedTranscriptsDir, extractedTranscriptFileName(raw.composerId));
|
|
87741
|
+
const extractedTranscriptStat = statIfExists(transcriptUploadSourcePath);
|
|
87734
87742
|
let model;
|
|
87735
|
-
const sourceUnchanged =
|
|
87736
|
-
if (sourceUnchanged &&
|
|
87743
|
+
const sourceUnchanged = extractedTranscriptStat !== null && Math.abs(extractedTranscriptStat.mtimeMs - lastUpdatedAt) < 1;
|
|
87744
|
+
if (sourceUnchanged && extractedTranscriptStat.size > maxBytes) {
|
|
87737
87745
|
result.skippedTooLarge += 1;
|
|
87738
87746
|
continue;
|
|
87739
87747
|
}
|
|
87740
|
-
const cached3 = sourceUnchanged ? readFirstRecord(
|
|
87748
|
+
const cached3 = sourceUnchanged ? readFirstRecord(transcriptUploadSourcePath, () => true) : null;
|
|
87741
87749
|
const cachedMeta = cachedMetaSchema.safeParse(cached3);
|
|
87742
87750
|
const preparationCurrent = cachedMeta.success && cachedMeta.data.composerId === raw.composerId;
|
|
87743
87751
|
let omissions;
|
|
@@ -87770,15 +87778,15 @@ function collectFromStore(db, userDir, options, result, knownIds) {
|
|
|
87770
87778
|
...counted && { omissions: transcript.omissions },
|
|
87771
87779
|
...unhandled.length > 0 && { unhandled }
|
|
87772
87780
|
};
|
|
87773
|
-
if (!
|
|
87774
|
-
mkdirSync3(
|
|
87775
|
-
chmodSync2(
|
|
87776
|
-
|
|
87781
|
+
if (!extractedTranscriptsDirReady) {
|
|
87782
|
+
mkdirSync3(extractedTranscriptsDir, { recursive: true, mode: 448 });
|
|
87783
|
+
chmodSync2(extractedTranscriptsDir, 448);
|
|
87784
|
+
extractedTranscriptsDirReady = true;
|
|
87777
87785
|
}
|
|
87778
87786
|
const body = [JSON.stringify(meta3), ...transcript.serialized, ""].join("\n");
|
|
87779
|
-
if (
|
|
87780
|
-
writeFileSync2(
|
|
87781
|
-
utimesSync2(
|
|
87787
|
+
if (extractedTranscriptStat !== null) chmodSync2(transcriptUploadSourcePath, 384);
|
|
87788
|
+
writeFileSync2(transcriptUploadSourcePath, body, { encoding: "utf8", mode: 384 });
|
|
87789
|
+
utimesSync2(transcriptUploadSourcePath, new Date(lastUpdatedAt), new Date(lastUpdatedAt));
|
|
87782
87790
|
}
|
|
87783
87791
|
if (hasCaptureGaps(omissions)) result.partiallyInterpreted += 1;
|
|
87784
87792
|
for (const observation of unhandledObservations(unhandled, raw.composerId)) {
|
|
@@ -87797,7 +87805,7 @@ function collectFromStore(db, userDir, options, result, knownIds) {
|
|
|
87797
87805
|
const startBranch = facts.branches[0];
|
|
87798
87806
|
result.sessions.push({
|
|
87799
87807
|
mtimeMs: lastUpdatedAt,
|
|
87800
|
-
transcriptPath:
|
|
87808
|
+
transcriptPath: transcriptUploadSourcePath,
|
|
87801
87809
|
...cwd && { cwd },
|
|
87802
87810
|
item: {
|
|
87803
87811
|
source: "cursor",
|
|
@@ -87931,7 +87939,7 @@ function parseCursorTranscriptFile(text) {
|
|
|
87931
87939
|
if (!lines.some((line) => CONTENT_LINE_TYPES2.has(line.type))) return null;
|
|
87932
87940
|
return { lines, omissions: recorder.omissions(), unhandled: recorder.unhandled() };
|
|
87933
87941
|
}
|
|
87934
|
-
function
|
|
87942
|
+
function extractedTranscriptBody(metaFor, lines, maxBytes) {
|
|
87935
87943
|
for (const keepPayloads of [true, false]) {
|
|
87936
87944
|
const payloadsDropped = keepPayloads ? 0 : lines.filter((line) => line.type === "unsupported" && line.payload !== void 0).length;
|
|
87937
87945
|
const records = [
|
|
@@ -87950,7 +87958,7 @@ function spoolBody(metaFor, lines, maxBytes) {
|
|
|
87950
87958
|
}
|
|
87951
87959
|
return null;
|
|
87952
87960
|
}
|
|
87953
|
-
var
|
|
87961
|
+
var extractedTranscriptMetaSchema = external_exports.object({
|
|
87954
87962
|
type: external_exports.literal("meta"),
|
|
87955
87963
|
format: external_exports.literal("cursor-local-transcript"),
|
|
87956
87964
|
version: external_exports.literal(1),
|
|
@@ -87962,7 +87970,7 @@ var spoolMetaSchema = external_exports.object({
|
|
|
87962
87970
|
model: external_exports.string().optional(),
|
|
87963
87971
|
...cursorTranscriptMetaDisclosureSchema.shape
|
|
87964
87972
|
});
|
|
87965
|
-
var
|
|
87973
|
+
var transcriptDiscoveryRecordSchema2 = external_exports.object({
|
|
87966
87974
|
sessionId: external_exports.string().regex(/^[a-zA-Z0-9_-]{1,200}$/),
|
|
87967
87975
|
transcriptPath: external_exports.string().max(4096).nullable(),
|
|
87968
87976
|
workspaceRoots: external_exports.array(external_exports.string().max(4096)).max(8),
|
|
@@ -87994,15 +88002,15 @@ function cursorTranscriptRecordPath(home, sessionId) {
|
|
|
87994
88002
|
`${createHash3("sha256").update(sessionId).digest("hex")}.json`
|
|
87995
88003
|
);
|
|
87996
88004
|
}
|
|
87997
|
-
function collectCursorTranscriptFiles(options, isDatabaseSession, result,
|
|
88005
|
+
function collectCursorTranscriptFiles(options, isDatabaseSession, result, databaseExtractedTranscriptsDir, extractedTranscriptFileName2) {
|
|
87998
88006
|
const home = options.homeDir ?? os8.homedir();
|
|
87999
|
-
const
|
|
88000
|
-
if (!existsSync3(
|
|
88007
|
+
const recordsDir = cursorTranscriptRecordsDir(home);
|
|
88008
|
+
if (!existsSync3(recordsDir)) return;
|
|
88001
88009
|
const maxBytes = options.maxTranscriptBytes ?? MAX_TRANSCRIPT_FILE_BYTES;
|
|
88002
|
-
const
|
|
88010
|
+
const nativeExtractedTranscriptsDir = path7.join(databaseExtractedTranscriptsDir, "native");
|
|
88003
88011
|
let files;
|
|
88004
88012
|
try {
|
|
88005
|
-
files = readdirSync3(
|
|
88013
|
+
files = readdirSync3(recordsDir);
|
|
88006
88014
|
} catch {
|
|
88007
88015
|
result.skippedUnreadable++;
|
|
88008
88016
|
return;
|
|
@@ -88010,15 +88018,15 @@ function collectCursorTranscriptFiles(options, isDatabaseSession, result, databa
|
|
|
88010
88018
|
for (const file2 of files) {
|
|
88011
88019
|
if (!/^[a-f0-9]{64}\.json$/.test(file2)) continue;
|
|
88012
88020
|
try {
|
|
88013
|
-
const
|
|
88014
|
-
if (!lstatSync2(
|
|
88015
|
-
const text = readBounded(
|
|
88016
|
-
const
|
|
88017
|
-
if (file2 !== `${createHash3("sha256").update(
|
|
88018
|
-
if (isDatabaseSession(
|
|
88021
|
+
const recordPath = path7.join(recordsDir, file2);
|
|
88022
|
+
if (!lstatSync2(recordPath).isFile()) continue;
|
|
88023
|
+
const text = readBounded(recordPath, 40 * 1024);
|
|
88024
|
+
const record2 = transcriptDiscoveryRecordSchema2.parse(text === null ? null : JSON.parse(text));
|
|
88025
|
+
if (file2 !== `${createHash3("sha256").update(record2.sessionId).digest("hex")}.json`) continue;
|
|
88026
|
+
if (isDatabaseSession(record2.sessionId) || existsSync3(path7.join(databaseExtractedTranscriptsDir, extractedTranscriptFileName2(record2.sessionId))))
|
|
88019
88027
|
continue;
|
|
88020
|
-
if (
|
|
88021
|
-
const cwd =
|
|
88028
|
+
if (record2.transcriptPath === null || record2.workspaceRoots.length !== 1) continue;
|
|
88029
|
+
const cwd = record2.workspaceRoots[0];
|
|
88022
88030
|
if (!path7.isAbsolute(cwd)) continue;
|
|
88023
88031
|
const decision = captureDecision(cwd, options.capture ?? captureEverythingUnresolved);
|
|
88024
88032
|
if (!decision.capture) {
|
|
@@ -88028,14 +88036,14 @@ function collectCursorTranscriptFiles(options, isDatabaseSession, result, databa
|
|
|
88028
88036
|
const projects = path7.join(home, ".cursor", "projects");
|
|
88029
88037
|
const project = cwd.replace(/[^a-zA-Z0-9]/g, "-").replace(/^-+/, "");
|
|
88030
88038
|
const base = path7.join(projects, project, "agent-transcripts");
|
|
88031
|
-
const basename2 = `${
|
|
88032
|
-
const allowed = [path7.join(base, basename2), path7.join(base,
|
|
88033
|
-
if (!allowed.includes(
|
|
88039
|
+
const basename2 = `${record2.sessionId}.jsonl`;
|
|
88040
|
+
const allowed = [path7.join(base, basename2), path7.join(base, record2.sessionId, basename2)];
|
|
88041
|
+
if (!allowed.includes(record2.transcriptPath)) {
|
|
88034
88042
|
result.skippedUnreadable++;
|
|
88035
88043
|
continue;
|
|
88036
88044
|
}
|
|
88037
|
-
const stat = lstatSync2(
|
|
88038
|
-
if (!stat.isFile() || realpathSync2(
|
|
88045
|
+
const stat = lstatSync2(record2.transcriptPath);
|
|
88046
|
+
if (!stat.isFile() || realpathSync2(record2.transcriptPath) !== path7.join(realpathSync2(projects), path7.relative(projects, record2.transcriptPath))) {
|
|
88039
88047
|
result.skippedUnreadable++;
|
|
88040
88048
|
continue;
|
|
88041
88049
|
}
|
|
@@ -88046,7 +88054,7 @@ function collectCursorTranscriptFiles(options, isDatabaseSession, result, databa
|
|
|
88046
88054
|
}
|
|
88047
88055
|
const common = {
|
|
88048
88056
|
source: "cursor",
|
|
88049
|
-
externalId:
|
|
88057
|
+
externalId: record2.sessionId,
|
|
88050
88058
|
status: (options.now ?? Date.now()) - stat.mtimeMs < ACTIVE_GRACE_MS ? "active" : "inactive",
|
|
88051
88059
|
...decision.repoFullName && { repoFullName: decision.repoFullName },
|
|
88052
88060
|
capturedVia: decision.via
|
|
@@ -88055,15 +88063,15 @@ function collectCursorTranscriptFiles(options, isDatabaseSession, result, databa
|
|
|
88055
88063
|
result.sessions.push({
|
|
88056
88064
|
cwd,
|
|
88057
88065
|
mtimeMs: stat.mtimeMs,
|
|
88058
|
-
transcriptPath:
|
|
88066
|
+
transcriptPath: record2.transcriptPath,
|
|
88059
88067
|
item: common
|
|
88060
88068
|
});
|
|
88061
88069
|
continue;
|
|
88062
88070
|
}
|
|
88063
|
-
const
|
|
88064
|
-
const
|
|
88065
|
-
const cached3 =
|
|
88066
|
-
const fresh = cached3?.success && cached3.data.composerId ===
|
|
88071
|
+
const transcriptUploadSourcePath = path7.join(nativeExtractedTranscriptsDir, extractedTranscriptFileName2(record2.sessionId));
|
|
88072
|
+
const extractedTranscriptStat = statIfExists(transcriptUploadSourcePath);
|
|
88073
|
+
const cached3 = extractedTranscriptStat !== null && extractedTranscriptStat.size <= maxBytes && Math.abs(extractedTranscriptStat.mtimeMs - stat.mtimeMs) < 1 ? extractedTranscriptMetaSchema.safeParse(readFirstRecord(transcriptUploadSourcePath, () => true)) : null;
|
|
88074
|
+
const fresh = cached3?.success && cached3.data.composerId === record2.sessionId && cached3.data.workspacePath === cwd && cached3.data.nativeSourceSize === stat.size && cached3.data.model === record2.model;
|
|
88067
88075
|
let title;
|
|
88068
88076
|
let omissions;
|
|
88069
88077
|
let unhandled = [];
|
|
@@ -88072,7 +88080,7 @@ function collectCursorTranscriptFiles(options, isDatabaseSession, result, databa
|
|
|
88072
88080
|
omissions = cached3.data.omissions;
|
|
88073
88081
|
unhandled = cached3.data.unhandled ?? [];
|
|
88074
88082
|
} else {
|
|
88075
|
-
const raw = readBounded(
|
|
88083
|
+
const raw = readBounded(record2.transcriptPath, maxBytes);
|
|
88076
88084
|
if (raw === null) {
|
|
88077
88085
|
result.skippedTooLarge++;
|
|
88078
88086
|
continue;
|
|
@@ -88093,40 +88101,40 @@ function collectCursorTranscriptFiles(options, isDatabaseSession, result, databa
|
|
|
88093
88101
|
format: "cursor-local-transcript",
|
|
88094
88102
|
version: 1,
|
|
88095
88103
|
preparationVersion: CURSOR_TRANSCRIPT_PREPARATION_VERSION,
|
|
88096
|
-
composerId:
|
|
88104
|
+
composerId: record2.sessionId,
|
|
88097
88105
|
workspacePath: cwd,
|
|
88098
88106
|
nativeSourceSize: stat.size,
|
|
88099
88107
|
...title !== void 0 && { name: title },
|
|
88100
|
-
...
|
|
88108
|
+
...record2.model && { model: record2.model },
|
|
88101
88109
|
...Object.keys(written).length > 0 && { omissions: written },
|
|
88102
88110
|
...prepared.unhandled.length > 0 && { unhandled: prepared.unhandled }
|
|
88103
88111
|
};
|
|
88104
88112
|
};
|
|
88105
|
-
const body =
|
|
88113
|
+
const body = extractedTranscriptBody(metaFor, prepared.lines, maxBytes);
|
|
88106
88114
|
if (body === null) {
|
|
88107
88115
|
result.skippedTooLarge++;
|
|
88108
88116
|
continue;
|
|
88109
88117
|
}
|
|
88110
|
-
mkdirSync4(
|
|
88111
|
-
if (
|
|
88112
|
-
writeFileSync3(
|
|
88113
|
-
utimesSync3(
|
|
88118
|
+
mkdirSync4(nativeExtractedTranscriptsDir, { recursive: true, mode: 448 });
|
|
88119
|
+
if (extractedTranscriptStat !== null) chmodSync3(transcriptUploadSourcePath, 384);
|
|
88120
|
+
writeFileSync3(transcriptUploadSourcePath, body, { mode: 384 });
|
|
88121
|
+
utimesSync3(transcriptUploadSourcePath, stat.atime, stat.mtime);
|
|
88114
88122
|
omissions = Object.keys(written).length > 0 ? written : void 0;
|
|
88115
88123
|
unhandled = prepared.unhandled;
|
|
88116
88124
|
}
|
|
88117
88125
|
if (hasCaptureGaps(omissions)) result.partiallyInterpreted++;
|
|
88118
|
-
for (const observation of unhandledObservations(unhandled,
|
|
88126
|
+
for (const observation of unhandledObservations(unhandled, record2.sessionId)) {
|
|
88119
88127
|
observeStructure(options.onStructureDiagnostic, observation);
|
|
88120
88128
|
}
|
|
88121
88129
|
result.sessions.push({
|
|
88122
88130
|
cwd,
|
|
88123
88131
|
mtimeMs: stat.mtimeMs,
|
|
88124
|
-
transcriptPath:
|
|
88132
|
+
transcriptPath: transcriptUploadSourcePath,
|
|
88125
88133
|
item: {
|
|
88126
88134
|
...common,
|
|
88127
88135
|
endedAt: stat.mtime.toISOString(),
|
|
88128
88136
|
repoDir: path7.basename(cwd),
|
|
88129
|
-
...
|
|
88137
|
+
...record2.model && { model: record2.model },
|
|
88130
88138
|
...title !== void 0 && { title }
|
|
88131
88139
|
}
|
|
88132
88140
|
});
|
|
@@ -88290,7 +88298,7 @@ function observedSessionEnd(entry, mtimeMs) {
|
|
|
88290
88298
|
return entry.at;
|
|
88291
88299
|
}
|
|
88292
88300
|
|
|
88293
|
-
// ../../apps/collector/src/
|
|
88301
|
+
// ../../apps/collector/src/harnessHooks.ts
|
|
88294
88302
|
import {
|
|
88295
88303
|
existsSync as existsSync5,
|
|
88296
88304
|
lstatSync as lstatSync3,
|
|
@@ -88306,26 +88314,39 @@ var SESSION_CHANGE_HOOK = "powerplant-session-change.mjs";
|
|
|
88306
88314
|
var CODEX_TRANSCRIPT_HOOK = "powerplant-codex-transcript.mjs";
|
|
88307
88315
|
var CURSOR_TRANSCRIPT_HOOK = "powerplant-cursor-transcript.mjs";
|
|
88308
88316
|
var SESSION_CHANGE_FILENAME = "session-change";
|
|
88317
|
+
var SUPPORTED_HOOK_HARNESSES = ["codex-local", "cursor-local"];
|
|
88309
88318
|
var jsonObject = external_exports.record(external_exports.string(), external_exports.json());
|
|
88310
|
-
function
|
|
88311
|
-
|
|
88319
|
+
function nativeHarnessName(harness) {
|
|
88320
|
+
switch (harness) {
|
|
88321
|
+
case "codex-local":
|
|
88322
|
+
return "codex";
|
|
88323
|
+
case "cursor-local":
|
|
88324
|
+
return "cursor";
|
|
88325
|
+
default:
|
|
88326
|
+
throw new Error(`unsupported hook harness: ${String(harness)}`);
|
|
88327
|
+
}
|
|
88312
88328
|
}
|
|
88313
|
-
function
|
|
88314
|
-
return
|
|
88329
|
+
function harnessEvents(harness) {
|
|
88330
|
+
return nativeHarnessName(harness) === "cursor" ? ["sessionStart", "sessionEnd", "stop"] : ["SessionStart", "SessionEnd", "Stop"];
|
|
88331
|
+
}
|
|
88332
|
+
function harnessHookConfigPath(harness, home) {
|
|
88333
|
+
const nativeName = nativeHarnessName(harness);
|
|
88334
|
+
return path10.join(home, `.${nativeName}`, "hooks.json");
|
|
88315
88335
|
}
|
|
88316
88336
|
var UNUSABLE_CONFIG = "hooks.json contains invalid JSON or an unsupported hook structure";
|
|
88317
88337
|
var NON_REGULAR_CONFIG = "hooks.json is not a regular file; configure the hook manually";
|
|
88318
|
-
function
|
|
88338
|
+
function harnessHookCommands(harness, text) {
|
|
88319
88339
|
try {
|
|
88340
|
+
const nativeName = nativeHarnessName(harness);
|
|
88320
88341
|
const settings = jsonObject.parse(text === null ? {} : JSON.parse(text));
|
|
88321
|
-
if (
|
|
88342
|
+
if (nativeName === "cursor" && settings.version !== void 0 && settings.version !== 1) {
|
|
88322
88343
|
return { kind: "unusable", reason: "Unsupported Cursor hooks version" };
|
|
88323
88344
|
}
|
|
88324
88345
|
const hooks = jsonObject.parse(settings.hooks === void 0 ? {} : settings.hooks);
|
|
88325
88346
|
const commands = [];
|
|
88326
|
-
for (const event of
|
|
88347
|
+
for (const event of harnessEvents(harness)) {
|
|
88327
88348
|
const entries = external_exports.array(jsonObject).parse(hooks[event] === void 0 ? [] : hooks[event]);
|
|
88328
|
-
const handlers2 =
|
|
88349
|
+
const handlers2 = nativeName === "cursor" ? entries : entries.flatMap((entry) => external_exports.array(jsonObject).parse(entry.hooks));
|
|
88329
88350
|
const ours = handlers2.filter(isOurHook);
|
|
88330
88351
|
if (ours.length === 0) return { kind: "missing" };
|
|
88331
88352
|
for (const hook of ours) {
|
|
@@ -88341,20 +88362,21 @@ function isOurHook(hook) {
|
|
|
88341
88362
|
const command = hook.command;
|
|
88342
88363
|
return typeof command === "string" && [SESSION_CHANGE_HOOK, CODEX_TRANSCRIPT_HOOK, CURSOR_TRANSCRIPT_HOOK].some((script) => command.endsWith(`/${script}'`) || command.endsWith(`/${script}"`));
|
|
88343
88364
|
}
|
|
88344
|
-
function
|
|
88365
|
+
function mergeHarnessHooks(harness, text, command) {
|
|
88345
88366
|
try {
|
|
88367
|
+
const nativeName = nativeHarnessName(harness);
|
|
88346
88368
|
const settings = jsonObject.parse(text === null ? {} : JSON.parse(text));
|
|
88347
|
-
if (
|
|
88369
|
+
if (nativeName === "cursor" && settings.version !== void 0 && settings.version !== 1) {
|
|
88348
88370
|
return { action: "skip", reason: "Unsupported Cursor hooks version" };
|
|
88349
88371
|
}
|
|
88350
88372
|
const hooks = jsonObject.parse(settings.hooks === void 0 ? {} : settings.hooks);
|
|
88351
|
-
for (const event of
|
|
88373
|
+
for (const event of harnessEvents(harness)) {
|
|
88352
88374
|
const entries = external_exports.array(jsonObject).parse(hooks[event] === void 0 ? [] : hooks[event]);
|
|
88353
|
-
const handler =
|
|
88354
|
-
const ours =
|
|
88375
|
+
const handler = nativeName === "cursor" ? { command, timeout: 2 } : { type: "command", command, timeout: 2 };
|
|
88376
|
+
const ours = nativeName === "cursor" ? handler : { hooks: [handler] };
|
|
88355
88377
|
const kept = [];
|
|
88356
88378
|
for (const entry of entries) {
|
|
88357
|
-
if (
|
|
88379
|
+
if (nativeName === "cursor") {
|
|
88358
88380
|
if (!isOurHook(entry)) kept.push(entry);
|
|
88359
88381
|
continue;
|
|
88360
88382
|
}
|
|
@@ -88365,7 +88387,7 @@ function mergeProviderHooks(provider, text, command) {
|
|
|
88365
88387
|
}
|
|
88366
88388
|
hooks[event] = [...kept, ours];
|
|
88367
88389
|
}
|
|
88368
|
-
const next = { ...settings, ...
|
|
88390
|
+
const next = { ...settings, ...nativeName === "cursor" ? { version: 1 } : {}, hooks };
|
|
88369
88391
|
if (JSON.stringify(next) === JSON.stringify(settings)) return { action: "unchanged" };
|
|
88370
88392
|
return { action: "write", text: `${JSON.stringify(next, null, 2)}
|
|
88371
88393
|
` };
|
|
@@ -88376,42 +88398,47 @@ function mergeProviderHooks(provider, text, command) {
|
|
|
88376
88398
|
function shellQuote2(value) {
|
|
88377
88399
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
88378
88400
|
}
|
|
88379
|
-
function
|
|
88380
|
-
const
|
|
88381
|
-
|
|
88382
|
-
|
|
88401
|
+
function installHarnessHook(harness, home) {
|
|
88402
|
+
const nativeName = nativeHarnessName(harness);
|
|
88403
|
+
const configDir = path10.join(home, `.${nativeName}`);
|
|
88404
|
+
if (!existsSync5(configDir)) return { provider: nativeName, settings: "absent" };
|
|
88405
|
+
const configPath = harnessHookConfigPath(harness, home);
|
|
88383
88406
|
let original = null;
|
|
88384
88407
|
let mode = 384;
|
|
88385
88408
|
try {
|
|
88386
88409
|
const stat = lstatSync3(configPath);
|
|
88387
|
-
if (!stat.isFile())
|
|
88410
|
+
if (!stat.isFile()) {
|
|
88411
|
+
return { provider: nativeName, settings: "skip", detail: NON_REGULAR_CONFIG };
|
|
88412
|
+
}
|
|
88388
88413
|
mode = stat.mode & 511;
|
|
88389
88414
|
original = readFileSync6(configPath, "utf8");
|
|
88390
88415
|
} catch (error63) {
|
|
88391
88416
|
if (!(error63 instanceof Error && "code" in error63 && error63.code === "ENOENT")) throw error63;
|
|
88392
88417
|
}
|
|
88393
88418
|
const hookDir = path10.join(home, ".config", "powerplant", "hooks");
|
|
88394
|
-
const script =
|
|
88419
|
+
const script = nativeName === "cursor" ? CURSOR_TRANSCRIPT_HOOK : CODEX_TRANSCRIPT_HOOK;
|
|
88395
88420
|
const scriptPath = path10.join(hookDir, script);
|
|
88396
|
-
const merged =
|
|
88397
|
-
|
|
88421
|
+
const merged = mergeHarnessHooks(
|
|
88422
|
+
harness,
|
|
88398
88423
|
original,
|
|
88399
88424
|
`${shellQuote2(process.execPath)} ${shellQuote2(scriptPath)}`
|
|
88400
88425
|
);
|
|
88401
|
-
if (merged.action === "skip")
|
|
88426
|
+
if (merged.action === "skip") {
|
|
88427
|
+
return { provider: nativeName, settings: "skip", detail: merged.reason };
|
|
88428
|
+
}
|
|
88402
88429
|
mkdirSync6(hookDir, { recursive: true });
|
|
88403
88430
|
writeAtomic(scriptPath, readFileSync6(hookSourcePath(script), "utf8"), 448);
|
|
88404
88431
|
if (merged.action === "write") {
|
|
88405
88432
|
const current = existsSync5(configPath) ? readFileSync6(configPath, "utf8") : null;
|
|
88406
88433
|
if (current !== original)
|
|
88407
88434
|
return {
|
|
88408
|
-
provider,
|
|
88435
|
+
provider: nativeName,
|
|
88409
88436
|
settings: "skip",
|
|
88410
88437
|
detail: "hooks.json changed during installation; restart the collector to retry"
|
|
88411
88438
|
};
|
|
88412
88439
|
writeAtomic(configPath, merged.text, mode);
|
|
88413
88440
|
}
|
|
88414
|
-
return { provider, settings: merged.action };
|
|
88441
|
+
return { provider: nativeName, settings: merged.action };
|
|
88415
88442
|
}
|
|
88416
88443
|
function writeAtomic(file2, text, mode) {
|
|
88417
88444
|
const temporary = `${file2}.${randomUUID()}.tmp`;
|
|
@@ -88422,13 +88449,13 @@ function writeAtomic(file2, text, mode) {
|
|
|
88422
88449
|
rmSync4(temporary, { force: true });
|
|
88423
88450
|
}
|
|
88424
88451
|
}
|
|
88425
|
-
function
|
|
88426
|
-
return
|
|
88452
|
+
function installHarnessHooks(home) {
|
|
88453
|
+
return SUPPORTED_HOOK_HARNESSES.map((harness) => {
|
|
88427
88454
|
try {
|
|
88428
|
-
return
|
|
88455
|
+
return installHarnessHook(harness, home);
|
|
88429
88456
|
} catch (error63) {
|
|
88430
88457
|
return {
|
|
88431
|
-
provider,
|
|
88458
|
+
provider: nativeHarnessName(harness),
|
|
88432
88459
|
settings: "skip",
|
|
88433
88460
|
detail: error63 instanceof Error ? error63.message : String(error63)
|
|
88434
88461
|
};
|
|
@@ -88440,7 +88467,7 @@ function installProviderHooks(home) {
|
|
|
88440
88467
|
var HOOK_OBSERVATION_GRACE_MS = 2 * 6e4;
|
|
88441
88468
|
var hookHealthSchema = external_exports.object({
|
|
88442
88469
|
harness: hookHarnessSchema,
|
|
88443
|
-
/** What this daemon's boot-time install did (installCollectorHooks /
|
|
88470
|
+
/** What this daemon's boot-time install did (installCollectorHooks / installHarnessHooks). */
|
|
88444
88471
|
install: hookInstallOutcomeSchema,
|
|
88445
88472
|
/** unverified: no in-scope session started since the install has been
|
|
88446
88473
|
* judged yet. firing: one carried a hook record. silent: one had none. */
|
|
@@ -88469,7 +88496,10 @@ var HARNESS_NAMES = {
|
|
|
88469
88496
|
cursor: "Cursor"
|
|
88470
88497
|
};
|
|
88471
88498
|
function configPathOf(harness, home) {
|
|
88472
|
-
return harness === "claude" ? claudeSettingsPath(home) :
|
|
88499
|
+
return harness === "claude" ? claudeSettingsPath(home) : harnessHookConfigPath(localHookHarness(harness), home);
|
|
88500
|
+
}
|
|
88501
|
+
function localHookHarness(harness) {
|
|
88502
|
+
return harness === "codex" ? "codex-local" : "cursor-local";
|
|
88473
88503
|
}
|
|
88474
88504
|
function commandPaths(command) {
|
|
88475
88505
|
const paths = [];
|
|
@@ -88504,7 +88534,7 @@ function inspectHookConfig(harness, home) {
|
|
|
88504
88534
|
detail: error63 instanceof Error ? error63.message : String(error63)
|
|
88505
88535
|
};
|
|
88506
88536
|
}
|
|
88507
|
-
const read = harness === "claude" ? collectorHookCommands(text) :
|
|
88537
|
+
const read = harness === "claude" ? collectorHookCommands(text) : harnessHookCommands(localHookHarness(harness), text);
|
|
88508
88538
|
if (read.kind === "unusable") return { kind: "config-unusable", configPath, detail: read.reason };
|
|
88509
88539
|
if (read.kind === "missing") return { kind: "entries-missing", configPath };
|
|
88510
88540
|
for (const command of read.commands) {
|
|
@@ -89406,7 +89436,7 @@ async function uploadPreviewRegistration(apiUrl, machineToken, registration) {
|
|
|
89406
89436
|
function encodeRawTranscript(text) {
|
|
89407
89437
|
return gzipSync(redactJsonl(text));
|
|
89408
89438
|
}
|
|
89409
|
-
async function uploadRawSessions(apiUrl, machineToken,
|
|
89439
|
+
async function uploadRawSessions(apiUrl, machineToken, transcriptUploadSources, readTranscript = (p) => readFileSync9(p, "utf8"), shipped, progress) {
|
|
89410
89440
|
const summary = {
|
|
89411
89441
|
uploaded: 0,
|
|
89412
89442
|
skippedTooLarge: 0,
|
|
@@ -89417,18 +89447,19 @@ async function uploadRawSessions(apiUrl, machineToken, collected, readTranscript
|
|
|
89417
89447
|
};
|
|
89418
89448
|
const initiallyShipped = new Map(shipped);
|
|
89419
89449
|
let rateLimited;
|
|
89420
|
-
for (const session of
|
|
89450
|
+
for (const session of transcriptUploadSources) {
|
|
89421
89451
|
summary.uploaded += await progress?.beforeUpload?.() ?? 0;
|
|
89422
|
-
const
|
|
89423
|
-
|
|
89452
|
+
const transcriptUploadSourcePath = session.transcriptPath;
|
|
89453
|
+
const acknowledged = shipped?.get(transcriptUploadSourcePath);
|
|
89454
|
+
if (acknowledged === session.mtimeMs || acknowledged !== void 0 && acknowledged !== initiallyShipped.get(transcriptUploadSourcePath)) {
|
|
89424
89455
|
summary.skippedUnchanged += 1;
|
|
89425
89456
|
continue;
|
|
89426
89457
|
}
|
|
89427
89458
|
try {
|
|
89428
|
-
const body = encodeRawTranscript(readTranscript(
|
|
89459
|
+
const body = encodeRawTranscript(readTranscript(transcriptUploadSourcePath));
|
|
89429
89460
|
if (body.byteLength > MAX_RAW_UPLOAD_BYTES) {
|
|
89430
89461
|
summary.skippedTooLarge += 1;
|
|
89431
|
-
shipped?.set(
|
|
89462
|
+
shipped?.set(transcriptUploadSourcePath, session.mtimeMs);
|
|
89432
89463
|
continue;
|
|
89433
89464
|
}
|
|
89434
89465
|
const query = `source=${session.item.source}&externalId=${encodeURIComponent(session.item.externalId)}`;
|
|
@@ -89461,7 +89492,7 @@ async function uploadRawSessions(apiUrl, machineToken, collected, readTranscript
|
|
|
89461
89492
|
}
|
|
89462
89493
|
summary.uploaded += 1;
|
|
89463
89494
|
progress?.onUploaded?.();
|
|
89464
|
-
shipped?.set(
|
|
89495
|
+
shipped?.set(transcriptUploadSourcePath, session.mtimeMs);
|
|
89465
89496
|
} catch (error63) {
|
|
89466
89497
|
if (error63 instanceof RateLimitedError) rateLimited ??= error63;
|
|
89467
89498
|
summary.failed.push(
|
|
@@ -91332,7 +91363,7 @@ function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
|
|
|
91332
91363
|
);
|
|
91333
91364
|
reportCollectorError(error63, "watch-hook-install");
|
|
91334
91365
|
}
|
|
91335
|
-
for (const hook of
|
|
91366
|
+
for (const hook of installHarnessHooks(home)) {
|
|
91336
91367
|
hookInstalls[hook.provider] = hook.settings;
|
|
91337
91368
|
if (hook.settings === "absent") continue;
|
|
91338
91369
|
if (hook.settings === "skip") {
|
|
@@ -93378,7 +93409,7 @@ import { accessSync, constants as constants2, readFileSync as readFileSync12 } f
|
|
|
93378
93409
|
import path25 from "node:path";
|
|
93379
93410
|
|
|
93380
93411
|
// src/version.ts
|
|
93381
|
-
var CLI_VERSION = true ? "0.4.
|
|
93412
|
+
var CLI_VERSION = true ? "0.4.13" : "0.0.0-checkout";
|
|
93382
93413
|
var CLI_RELEASE_BUILD = true ? true : false;
|
|
93383
93414
|
var CLI_PACKAGE_NAME = "@usepowerplant/cli";
|
|
93384
93415
|
|
|
@@ -93498,7 +93529,13 @@ function realInspectEnvironment() {
|
|
|
93498
93529
|
|
|
93499
93530
|
// src/update.ts
|
|
93500
93531
|
var updateProgressSchema = external_exports.object({
|
|
93501
|
-
|
|
93532
|
+
/** "up-to-date": the last run installed nothing, because this build is
|
|
93533
|
+
* already at or past the release. That record is kept only so the menu
|
|
93534
|
+
* bar app that started the run can stop showing "Updating CLI…"
|
|
93535
|
+
* (menu.ts cliUpToDateAt); to every other reader it is no update at all.
|
|
93536
|
+
* A CLI from before this state rejects the record, which it reads as no
|
|
93537
|
+
* record. */
|
|
93538
|
+
state: external_exports.enum(["installing", "failed", "up-to-date"]),
|
|
93502
93539
|
/** The release aimed at; null when the registry couldn't be asked (its latest). */
|
|
93503
93540
|
target: external_exports.string().nullable(),
|
|
93504
93541
|
startedAt: external_exports.string(),
|
|
@@ -93538,7 +93575,7 @@ function readUpdateProgress(readTextFile, file2 = updateProgressPath()) {
|
|
|
93538
93575
|
}
|
|
93539
93576
|
}
|
|
93540
93577
|
function updateProgressView(progress, cliVersion2, now) {
|
|
93541
|
-
if (progress === null) return { kind: "none" };
|
|
93578
|
+
if (progress === null || progress.state === "up-to-date") return { kind: "none" };
|
|
93542
93579
|
const reached = progress.target === null ? null : compareReleaseVersions(cliVersion2, progress.target);
|
|
93543
93580
|
if (reached === 0 || reached === 1) return { kind: "none" };
|
|
93544
93581
|
if (progress.state === "failed") {
|
|
@@ -93571,13 +93608,29 @@ async function runUpdate(deps) {
|
|
|
93571
93608
|
const reserved = running?.state === "installing" ? running.target : null;
|
|
93572
93609
|
const attempts = (release2) => running?.target === release2 && running.attempts !== void 0 ? { attempts: running.attempts } : {};
|
|
93573
93610
|
deps.writeProgress({ state: "installing", target: reserved, startedAt, pid: deps.pid, ...attempts(reserved) });
|
|
93574
|
-
const
|
|
93611
|
+
const resolved = await deps.resolveTarget();
|
|
93612
|
+
if (resolved !== null) deps.recordAvailableVersion(resolved);
|
|
93613
|
+
const target = resolved ?? reserved;
|
|
93575
93614
|
if (target === null && deps.targetRequired) {
|
|
93576
93615
|
deps.clearProgress();
|
|
93577
93616
|
throw new ExpectedCliError(
|
|
93578
93617
|
"No release is promoted for production right now (or the API couldn't be reached), so there is nothing to update to. Production machines only install the promoted release."
|
|
93579
93618
|
);
|
|
93580
93619
|
}
|
|
93620
|
+
const reached = target === null ? null : compareReleaseVersions(deps.cliVersion, target);
|
|
93621
|
+
if (reached === 0 || reached === 1) {
|
|
93622
|
+
deps.writeProgress({
|
|
93623
|
+
state: "up-to-date",
|
|
93624
|
+
target,
|
|
93625
|
+
startedAt,
|
|
93626
|
+
pid: deps.pid,
|
|
93627
|
+
finishedAt: new Date(deps.now()).toISOString()
|
|
93628
|
+
});
|
|
93629
|
+
deps.log(
|
|
93630
|
+
reached === 0 ? `Already up to date (v${deps.cliVersion}).` : `Already on v${deps.cliVersion}, newer than the available release (v${target}). Nothing was installed.`
|
|
93631
|
+
);
|
|
93632
|
+
return;
|
|
93633
|
+
}
|
|
93581
93634
|
deps.writeProgress({ state: "installing", target, startedAt, pid: deps.pid, ...attempts(target) });
|
|
93582
93635
|
const { bin, args } = updateCommand(manager, target, inspection.prefix, deps.channel);
|
|
93583
93636
|
deps.log(`Updating ${CLI_PACKAGE_NAME}${target === null ? "" : ` to ${target}`} via ${manager}\u2026`);
|
|
@@ -94422,6 +94475,7 @@ function renderMenu(i) {
|
|
|
94422
94475
|
logPath: updateLogPath()
|
|
94423
94476
|
}
|
|
94424
94477
|
},
|
|
94478
|
+
...i.update.progress?.state === "up-to-date" && { cliUpToDateAt: i.update.progress.finishedAt },
|
|
94425
94479
|
...i.desktopDiagnostic && { desktopDiagnostic: i.desktopDiagnostic },
|
|
94426
94480
|
appEnv: i.config.appEnv,
|
|
94427
94481
|
cliVersion: i.cliVersion,
|
|
@@ -94725,14 +94779,17 @@ async function cachedLatestVersion(options) {
|
|
|
94725
94779
|
}
|
|
94726
94780
|
}
|
|
94727
94781
|
const version2 = await options.fetchLatest();
|
|
94782
|
+
writeVersionCache({ ...options, version: version2 });
|
|
94783
|
+
return version2;
|
|
94784
|
+
}
|
|
94785
|
+
function writeVersionCache(options) {
|
|
94728
94786
|
try {
|
|
94729
94787
|
options.writeTextFile(
|
|
94730
94788
|
options.cacheFile,
|
|
94731
|
-
JSON.stringify({ checkedAt: new Date(options.now).toISOString(), version:
|
|
94789
|
+
JSON.stringify({ checkedAt: new Date(options.now).toISOString(), version: options.version })
|
|
94732
94790
|
);
|
|
94733
94791
|
} catch {
|
|
94734
94792
|
}
|
|
94735
|
-
return version2;
|
|
94736
94793
|
}
|
|
94737
94794
|
|
|
94738
94795
|
// src/liveTerminalSessions.ts
|
|
@@ -96111,7 +96168,14 @@ The access you granted these agents stays on your Powerplant account until you r
|
|
|
96111
96168
|
}
|
|
96112
96169
|
await runUpdate({
|
|
96113
96170
|
inspection,
|
|
96171
|
+
cliVersion: CLI_VERSION,
|
|
96114
96172
|
resolveTarget: () => fetchAvailableVersion(config2, loadCredentialsSafely(config2).credentials),
|
|
96173
|
+
recordAvailableVersion: (version2) => writeVersionCache({
|
|
96174
|
+
now: Date.now(),
|
|
96175
|
+
cacheFile: versionCachePath(void 0, releaseSource(config2.appEnv)),
|
|
96176
|
+
writeTextFile: realWriteTextFile,
|
|
96177
|
+
version: version2
|
|
96178
|
+
}),
|
|
96115
96179
|
channel: releaseSource(config2.appEnv) === "npm" ? "next" : "latest",
|
|
96116
96180
|
targetRequired: releaseSource(config2.appEnv) === "promoted",
|
|
96117
96181
|
claimsReservation: process.env[UPDATE_CHILD_ENV] === "1",
|
|
@@ -96398,4 +96462,4 @@ undici/lib/web/websocket/frame.js:
|
|
|
96398
96462
|
(*! ws. MIT License. Einar Otto Stangvik <einaros@gmail.com> *)
|
|
96399
96463
|
*/
|
|
96400
96464
|
|
|
96401
|
-
//# debugId=
|
|
96465
|
+
//# debugId=66f773d5-e0b2-5538-a184-3ee15c3a469a
|
|
Binary file
|