@usepowerplant/cli 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +155 -25
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -78652,7 +78652,7 @@ import os6 from "node:os";
78652
78652
  // ../../apps/collector/src/http.ts
78653
78653
  var import_undici = __toESM(require_undici(), 1);
78654
78654
  var CLI_VERSION_HEADER = "x-powerplant-cli-version";
78655
- var cliVersion = true ? "0.3.0" : "0.0.0-checkout";
78655
+ var cliVersion = true ? "0.3.1" : "0.0.0-checkout";
78656
78656
  function cliVersionHeaders() {
78657
78657
  return { [CLI_VERSION_HEADER]: cliVersion, "user-agent": `powerplant-cli/${cliVersion}` };
78658
78658
  }
@@ -79492,18 +79492,53 @@ import { setImmediate as yieldToEventLoop } from "node:timers/promises";
79492
79492
  import path4 from "node:path";
79493
79493
 
79494
79494
  // ../../apps/collector/src/scan.ts
79495
- import { readdirSync, readFileSync as readFileSync5, statSync as statSync2 } from "node:fs";
79495
+ import { closeSync, openSync, readdirSync, readFileSync as readFileSync5, readSync, statSync as statSync2 } from "node:fs";
79496
+ import { StringDecoder } from "node:string_decoder";
79496
79497
  var ACTIVE_GRACE_MS = SESSION_ACTIVE_WINDOW_MS;
79497
79498
  var jsonObjectSchema = external_exports.record(external_exports.string(), external_exports.unknown());
79498
79499
  function* parseJsonl(text) {
79499
79500
  for (const line of text.split("\n")) {
79500
- if (!line.trim()) continue;
79501
- try {
79502
- const value = JSON.parse(line);
79503
- const parsed = jsonObjectSchema.safeParse(value);
79504
- if (parsed.success) yield parsed.data;
79505
- } catch {
79506
- }
79501
+ const record2 = parseJsonlLine(line);
79502
+ if (record2 !== null) yield record2;
79503
+ }
79504
+ }
79505
+ function parseJsonlLine(line) {
79506
+ if (!line.trim()) return null;
79507
+ try {
79508
+ const value = JSON.parse(line);
79509
+ const parsed = jsonObjectSchema.safeParse(value);
79510
+ return parsed.success ? parsed.data : null;
79511
+ } catch {
79512
+ return null;
79513
+ }
79514
+ }
79515
+ var READ_CHUNK_BYTES = 64 * 1024;
79516
+ function readFirstRecord(file2, matches) {
79517
+ let fd;
79518
+ try {
79519
+ fd = openSync(file2, "r");
79520
+ } catch (error52) {
79521
+ if (error52.code === "ENOENT") return null;
79522
+ throw error52;
79523
+ }
79524
+ const buffer = Buffer.alloc(READ_CHUNK_BYTES);
79525
+ const decoder = new StringDecoder("utf8");
79526
+ let carry = "";
79527
+ try {
79528
+ for (; ; ) {
79529
+ const bytes = readSync(fd, buffer, 0, buffer.length, null);
79530
+ if (bytes === 0) break;
79531
+ const lines = (carry + decoder.write(buffer.subarray(0, bytes))).split("\n");
79532
+ carry = lines.pop() ?? "";
79533
+ for (const line of lines) {
79534
+ const record2 = parseJsonlLine(line);
79535
+ if (record2 !== null && matches(record2)) return record2;
79536
+ }
79537
+ }
79538
+ const last = parseJsonlLine(carry + decoder.end());
79539
+ return last !== null && matches(last) ? last : null;
79540
+ } finally {
79541
+ closeSync(fd);
79507
79542
  }
79508
79543
  }
79509
79544
  function listDirs(dir) {
@@ -79635,6 +79670,24 @@ async function collectClaudeCodeSessions(options = {}) {
79635
79670
  const stat = statIfExists(transcriptPath);
79636
79671
  if (!stat) continue;
79637
79672
  if (options.sinceMs !== void 0 && stat.mtimeMs < options.sinceMs) continue;
79673
+ const sessionId = path5.basename(entry, ".jsonl");
79674
+ const endObservedAt = observedSessionEnd(lifecycle.get(sessionId), stat.mtimeMs);
79675
+ const status = endObservedAt === null && now - stat.mtimeMs < ACTIVE_GRACE_MS ? "active" : "inactive";
79676
+ if (options.locationsOnly) {
79677
+ const cwd = transcriptCwd(transcriptPath);
79678
+ const decision2 = captureDecision(cwd, capture);
79679
+ if (!decision2.capture) {
79680
+ skippedOutOfScope += 1;
79681
+ continue;
79682
+ }
79683
+ items.push({
79684
+ mtimeMs: stat.mtimeMs,
79685
+ transcriptPath,
79686
+ ...cwd && { cwd },
79687
+ item: { source: "claude-code", externalId: sessionId, status, capturedVia: decision2.via }
79688
+ });
79689
+ continue;
79690
+ }
79638
79691
  const text = readIfExists2(transcriptPath);
79639
79692
  if (text === null) continue;
79640
79693
  const summary = parseClaudeCodeTranscript(text);
@@ -79644,9 +79697,7 @@ async function collectClaudeCodeSessions(options = {}) {
79644
79697
  skippedOutOfScope += 1;
79645
79698
  continue;
79646
79699
  }
79647
- const sessionId = path5.basename(entry, ".jsonl");
79648
79700
  const usage = addSubagentUsage(summary.usage, path5.join(projectDir, sessionId, "subagents"));
79649
- const endObservedAt = observedSessionEnd(lifecycle.get(sessionId), stat.mtimeMs);
79650
79701
  const endedAt = endObservedAt ?? summary.endedAt;
79651
79702
  items.push({
79652
79703
  mtimeMs: stat.mtimeMs,
@@ -79655,7 +79706,7 @@ async function collectClaudeCodeSessions(options = {}) {
79655
79706
  item: {
79656
79707
  source: "claude-code",
79657
79708
  externalId: sessionId,
79658
- status: endObservedAt === null && now - stat.mtimeMs < ACTIVE_GRACE_MS ? "active" : "inactive",
79709
+ status,
79659
79710
  ...summary.startedAt && { startedAt: summary.startedAt },
79660
79711
  ...endedAt && { endedAt },
79661
79712
  ...summary.model && { model: summary.model },
@@ -79683,6 +79734,13 @@ async function collectClaudeCodeSessions(options = {}) {
79683
79734
  skippedOutOfScope
79684
79735
  };
79685
79736
  }
79737
+ function transcriptCwd(transcriptPath) {
79738
+ const record2 = readFirstRecord(
79739
+ transcriptPath,
79740
+ (candidate) => typeof candidate.cwd === "string" && candidate.cwd !== ""
79741
+ );
79742
+ return typeof record2?.cwd === "string" ? record2.cwd : void 0;
79743
+ }
79686
79744
  function toUsage(u) {
79687
79745
  const n = (value) => typeof value === "number" ? value : 0;
79688
79746
  return {
@@ -79765,6 +79823,21 @@ function parseCodexRollout(text) {
79765
79823
  ...typeof git?.repository_url === "string" && repoFullNameFrom(git.repository_url)
79766
79824
  };
79767
79825
  }
79826
+ function parseRolloutFile(rolloutPath) {
79827
+ const text = readIfExists2(rolloutPath);
79828
+ return text === null ? null : parseCodexRollout(text);
79829
+ }
79830
+ function rolloutLocation(rolloutPath) {
79831
+ const record2 = readFirstRecord(rolloutPath, (candidate) => candidate.type === "session_meta");
79832
+ const meta3 = record2?.payload;
79833
+ if (!meta3 || typeof meta3 !== "object" || Array.isArray(meta3)) return { kind: "session", totals: null };
79834
+ const { thread_source: threadSource, cwd } = meta3;
79835
+ return {
79836
+ kind: threadSource === "subagent" ? "subagent" : "session",
79837
+ totals: null,
79838
+ ...typeof cwd === "string" && cwd !== "" && { cwd }
79839
+ };
79840
+ }
79768
79841
  function repoFullNameFrom(url2) {
79769
79842
  const fullName = parseGithubRepo(url2);
79770
79843
  return fullName ? { repoFullName: fullName } : {};
@@ -79802,7 +79875,7 @@ async function collectCodexSessions(options = {}) {
79802
79875
  const sessionsDir = options.codexDir ?? path6.join(os8.homedir(), ".codex", "sessions");
79803
79876
  const now = options.now ?? Date.now();
79804
79877
  const capture = options.capture ?? captureEverythingUnresolved;
79805
- const threadNames = loadThreadNames(sessionsDir);
79878
+ const threadNames = options.locationsOnly ? /* @__PURE__ */ new Map() : loadThreadNames(sessionsDir);
79806
79879
  const items = [];
79807
79880
  let skippedOutOfScope = 0;
79808
79881
  let skippedSubagentThreads = 0;
@@ -79813,9 +79886,7 @@ async function collectCodexSessions(options = {}) {
79813
79886
  if (!stat) continue;
79814
79887
  if (options.sinceMs !== void 0 && Math.max(stat.mtimeMs, stat.ctimeMs) < options.sinceMs)
79815
79888
  continue;
79816
- const text = readIfExists2(rolloutPath);
79817
- if (text === null) continue;
79818
- const summary = parseCodexRollout(text);
79889
+ const summary = options.locationsOnly ? rolloutLocation(rolloutPath) : parseRolloutFile(rolloutPath);
79819
79890
  if (!summary) continue;
79820
79891
  if (summary.kind === "subagent") {
79821
79892
  skippedSubagentThreads += 1;
@@ -79826,6 +79897,21 @@ async function collectCodexSessions(options = {}) {
79826
79897
  skippedOutOfScope += 1;
79827
79898
  continue;
79828
79899
  }
79900
+ if (options.locationsOnly) {
79901
+ items.push({
79902
+ mtimeMs: stat.mtimeMs,
79903
+ transcriptPath: rolloutPath,
79904
+ ...summary.cwd && { cwd: summary.cwd },
79905
+ item: {
79906
+ source: "codex",
79907
+ externalId: path6.basename(rolloutPath, ".jsonl"),
79908
+ status: now - stat.mtimeMs < ACTIVE_GRACE_MS ? "active" : "inactive",
79909
+ archived,
79910
+ capturedVia: decision.via
79911
+ }
79912
+ });
79913
+ continue;
79914
+ }
79829
79915
  const repoDir = summary.repoName ?? (summary.cwd ? path6.basename(summary.cwd) : void 0);
79830
79916
  const repoFullName = decision.repoFullName ?? summary.repoFullName;
79831
79917
  const title = summary.sessionId ? threadNames.get(summary.sessionId) : void 0;
@@ -79894,8 +79980,29 @@ var emptyResult = () => ({
79894
79980
  skippedUnreadable: 0,
79895
79981
  unavailable: null
79896
79982
  });
79983
+ var SQLITE_EXPERIMENTAL_WARNING = "SQLite is an experimental feature and might change at any time";
79984
+ async function withSqliteExperimentalWarningSuppressed(load) {
79985
+ const forward = process.emitWarning.bind(process);
79986
+ process.emitWarning = (warning2, ...rest) => {
79987
+ const message = typeof warning2 === "string" ? warning2 : warning2.message;
79988
+ if (message === SQLITE_EXPERIMENTAL_WARNING) return;
79989
+ Reflect.apply(forward, process, [warning2, ...rest]);
79990
+ };
79991
+ try {
79992
+ return await load();
79993
+ } finally {
79994
+ process.emitWarning = forward;
79995
+ }
79996
+ }
79997
+ var sqliteModule;
79998
+ function loadSqlite() {
79999
+ sqliteModule ??= withSqliteExperimentalWarningSuppressed(
80000
+ () => import("node:sqlite")
80001
+ );
80002
+ return sqliteModule;
80003
+ }
79897
80004
  async function openCursorDatabase(dbPath) {
79898
- const sqlite = await import("node:sqlite");
80005
+ const sqlite = await loadSqlite();
79899
80006
  return new sqlite.DatabaseSync(dbPath, { readOnly: true });
79900
80007
  }
79901
80008
  function describeError(error52) {
@@ -80240,8 +80347,8 @@ async function findRecentProtectedSessions(options = {}, deps = defaultDiscovery
80240
80347
  const sinceMs = now - DEEP_FIRST_SYNC_DAYS * 24 * 60 * 60 * 1e3;
80241
80348
  const capture = captureEverythingUnresolved;
80242
80349
  const results = [
80243
- await deps.collectClaude({ sinceMs, capture }),
80244
- await deps.collectCodex({ sinceMs, capture }),
80350
+ await deps.collectClaude({ sinceMs, capture, locationsOnly: true }),
80351
+ await deps.collectCodex({ sinceMs, capture, locationsOnly: true }),
80245
80352
  await deps.collectCursor({ sinceMs, capture, locationsOnly: true })
80246
80353
  ];
80247
80354
  return groupProtectedSessions(
@@ -80478,17 +80585,40 @@ function installerPathValue() {
80478
80585
  }
80479
80586
  function installService(config2, command) {
80480
80587
  requireMacos();
80588
+ bootout(config2.appEnv);
80589
+ if (!waitForUnload(() => isLoaded(config2.appEnv))) {
80590
+ const service = `gui/${uid()}/${label(config2.appEnv)}`;
80591
+ throw new Error(
80592
+ `launchd is still unloading ${label(config2.appEnv)} after ${UNLOAD_WAIT_MS / 1e3}s \u2014 re-run powerplant init, or unload it by hand first: launchctl bootout ${service}`
80593
+ );
80594
+ }
80481
80595
  const launcher = launcherPath(config2.appEnv);
80482
80596
  mkdirSync4(path10.dirname(launcher), { recursive: true });
80483
80597
  writeFileSync4(launcher, buildLauncherScript(command));
80484
80598
  chmodSync2(launcher, 493);
80485
80599
  const file2 = plistPath(config2.appEnv);
80486
80600
  mkdirSync4(path10.dirname(file2), { recursive: true });
80487
- bootout(config2.appEnv);
80488
80601
  writeFileSync4(file2, buildLaunchdPlist(config2.appEnv, installerPathValue()));
80489
80602
  runLaunchctl(["bootstrap", `gui/${uid()}`, file2]);
80490
80603
  return `${file2} (watching for session changes; log: ${logPath(config2.appEnv)})`;
80491
80604
  }
80605
+ var UNLOAD_WAIT_MS = 1e4;
80606
+ var UNLOAD_POLL_MS = 100;
80607
+ function waitForUnload(loaded, options = {}) {
80608
+ const deadlineMs = options.deadlineMs ?? UNLOAD_WAIT_MS;
80609
+ const pollMs = options.pollMs ?? UNLOAD_POLL_MS;
80610
+ const sleep = options.sleep ?? sleepSync;
80611
+ const now = options.now ?? Date.now;
80612
+ const deadline = now() + deadlineMs;
80613
+ while (loaded()) {
80614
+ if (now() >= deadline) return false;
80615
+ sleep(pollMs);
80616
+ }
80617
+ return true;
80618
+ }
80619
+ function sleepSync(ms) {
80620
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
80621
+ }
80492
80622
  function serviceUpToDate(config2, command) {
80493
80623
  if (process.platform !== "darwin") return false;
80494
80624
  const launcher = launcherPath(config2.appEnv);
@@ -82999,7 +83129,7 @@ function whichAllWith(pathValue) {
82999
83129
  }
83000
83130
 
83001
83131
  // src/doctor.ts
83002
- import { closeSync, fstatSync, openSync, readSync } from "node:fs";
83132
+ import { closeSync as closeSync2, fstatSync, openSync as openSync2, readSync as readSync2 } from "node:fs";
83003
83133
 
83004
83134
  // src/statusReport.ts
83005
83135
  function renderStatus(i) {
@@ -83158,7 +83288,7 @@ function redactedLogTail(raw) {
83158
83288
  function readDoctorLogTail(path19) {
83159
83289
  let fd;
83160
83290
  try {
83161
- fd = openSync(path19, "r");
83291
+ fd = openSync2(path19, "r");
83162
83292
  } catch (error52) {
83163
83293
  if (error52.code === "ENOENT") return { path: path19, lines: null };
83164
83294
  return { path: path19, lines: null, readError: error52 instanceof Error ? error52.message : String(error52) };
@@ -83167,14 +83297,14 @@ function readDoctorLogTail(path19) {
83167
83297
  const size = fstatSync(fd).size;
83168
83298
  const offset = Math.max(0, size - LOG_TAIL_READ_BYTES);
83169
83299
  const buffer = Buffer.alloc(Math.min(size, LOG_TAIL_READ_BYTES));
83170
- readSync(fd, buffer, 0, buffer.length, offset);
83300
+ readSync2(fd, buffer, 0, buffer.length, offset);
83171
83301
  let text = buffer.toString("utf8");
83172
83302
  if (offset > 0) text = text.slice(text.indexOf("\n") + 1);
83173
83303
  return { path: path19, lines: redactedLogTail(text) };
83174
83304
  } catch (error52) {
83175
83305
  return { path: path19, lines: null, readError: error52 instanceof Error ? error52.message : String(error52) };
83176
83306
  } finally {
83177
- closeSync(fd);
83307
+ closeSync2(fd);
83178
83308
  }
83179
83309
  }
83180
83310
  function renderDoctorReport(i) {
@@ -84010,7 +84140,7 @@ function createTerminalOutput(options) {
84010
84140
  }
84011
84141
 
84012
84142
  // src/version.ts
84013
- var CLI_VERSION = true ? "0.3.0" : "0.0.0-checkout";
84143
+ var CLI_VERSION = true ? "0.3.1" : "0.0.0-checkout";
84014
84144
  var CLI_PACKAGE_NAME = "@usepowerplant/cli";
84015
84145
 
84016
84146
  // src/workspace.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usepowerplant/cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },