@usepowerplant/cli 0.3.0 → 0.3.2
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/dist/cli.js +401 -129
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -36252,7 +36252,7 @@ ${captureLines}` : capture.stack;
|
|
|
36252
36252
|
});
|
|
36253
36253
|
|
|
36254
36254
|
// src/cli.ts
|
|
36255
|
-
import { spawnSync as
|
|
36255
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
36256
36256
|
import { existsSync as existsSync7, realpathSync as realpathSync2 } from "node:fs";
|
|
36257
36257
|
import { parseArgs } from "node:util";
|
|
36258
36258
|
|
|
@@ -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.
|
|
78655
|
+
var cliVersion = true ? "0.3.2" : "0.0.0-checkout";
|
|
78656
78656
|
function cliVersionHeaders() {
|
|
78657
78657
|
return { [CLI_VERSION_HEADER]: cliVersion, "user-agent": `powerplant-cli/${cliVersion}` };
|
|
78658
78658
|
}
|
|
@@ -78729,30 +78729,78 @@ import os5 from "node:os";
|
|
|
78729
78729
|
import path3 from "node:path";
|
|
78730
78730
|
|
|
78731
78731
|
// ../../apps/collector/src/gitOwner.ts
|
|
78732
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
78732
78733
|
import { readFileSync as readFileSync3, statSync } from "node:fs";
|
|
78733
78734
|
import os4 from "node:os";
|
|
78734
78735
|
import path2 from "node:path";
|
|
78735
|
-
|
|
78736
|
+
var GITHUB_HOSTS = /* @__PURE__ */ new Set(["github.com", "ssh.github.com"]);
|
|
78737
|
+
function parseRemoteUrl(remoteUrl) {
|
|
78736
78738
|
const trimmed = remoteUrl.trim();
|
|
78737
|
-
const
|
|
78738
|
-
if (
|
|
78739
|
-
const
|
|
78740
|
-
|
|
78741
|
-
if (authority === "github.com" && owner && rawRepo && extra === void 0) {
|
|
78742
|
-
const repo = rawRepo.toLowerCase().endsWith(".git") ? rawRepo.slice(0, -4) : rawRepo;
|
|
78743
|
-
return `${owner.toLowerCase()}/${repo}`;
|
|
78744
|
-
}
|
|
78739
|
+
const colon = trimmed.indexOf(":");
|
|
78740
|
+
if (!trimmed.includes("://") && colon > 0 && !trimmed.slice(0, colon).includes("/")) {
|
|
78741
|
+
const host = trimmed.slice(0, colon).split("@").at(-1) ?? "";
|
|
78742
|
+
return { host: host.toLowerCase(), path: trimmed.slice(colon + 1), overSsh: true };
|
|
78745
78743
|
}
|
|
78746
78744
|
try {
|
|
78747
78745
|
const url2 = new URL(trimmed);
|
|
78748
|
-
if (url2.hostname
|
|
78749
|
-
const
|
|
78750
|
-
|
|
78751
|
-
|
|
78746
|
+
if (!url2.hostname) return null;
|
|
78747
|
+
const scheme = url2.protocol.slice(0, -1).toLowerCase();
|
|
78748
|
+
return {
|
|
78749
|
+
host: url2.hostname.toLowerCase(),
|
|
78750
|
+
path: url2.pathname,
|
|
78751
|
+
overSsh: scheme === "ssh" || scheme === "git+ssh"
|
|
78752
|
+
};
|
|
78752
78753
|
} catch {
|
|
78753
78754
|
return null;
|
|
78754
78755
|
}
|
|
78755
78756
|
}
|
|
78757
|
+
function parseGithubRepo(remoteUrl, sshHostnameOf = sshEffectiveHostname) {
|
|
78758
|
+
const remote = parseRemoteUrl(remoteUrl);
|
|
78759
|
+
if (!remote) return null;
|
|
78760
|
+
const host = remote.overSsh && !GITHUB_HOSTS.has(remote.host) ? sshHostnameOf(remote.host)?.toLowerCase() : remote.host;
|
|
78761
|
+
if (!host || !GITHUB_HOSTS.has(host)) return null;
|
|
78762
|
+
const [owner, rawRepo, extra] = remote.path.split("/").filter(Boolean);
|
|
78763
|
+
if (!owner || !rawRepo || extra !== void 0) return null;
|
|
78764
|
+
return `${owner.toLowerCase()}/${rawRepo.replace(/\.git$/i, "")}`;
|
|
78765
|
+
}
|
|
78766
|
+
var SSH_PROBE_TIMEOUT_MS = 2e3;
|
|
78767
|
+
var SSH_PROBE_CACHE_TTL_MS = 5 * 6e4;
|
|
78768
|
+
var SSH_HOST_PATTERN = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
78769
|
+
var sshProbeCache = /* @__PURE__ */ new Map();
|
|
78770
|
+
function sshEffectiveHostname(host, options = {}) {
|
|
78771
|
+
if (!SSH_HOST_PATTERN.test(host)) return null;
|
|
78772
|
+
const key = `${options.configFile ?? ""}\0${host}`;
|
|
78773
|
+
const cached3 = sshProbeCache.get(key);
|
|
78774
|
+
const now = Date.now();
|
|
78775
|
+
if (cached3 && cached3.expiresAt > now) return cached3.hostname;
|
|
78776
|
+
const hostname4 = probeSshHostname(host, options);
|
|
78777
|
+
sshProbeCache.set(key, { hostname: hostname4, expiresAt: now + SSH_PROBE_CACHE_TTL_MS });
|
|
78778
|
+
return hostname4;
|
|
78779
|
+
}
|
|
78780
|
+
function probeSshHostname(host, options) {
|
|
78781
|
+
try {
|
|
78782
|
+
const dump = execFileSync2(
|
|
78783
|
+
"ssh",
|
|
78784
|
+
["-G", ...options.configFile ? ["-F", options.configFile] : [], host],
|
|
78785
|
+
{
|
|
78786
|
+
encoding: "utf8",
|
|
78787
|
+
timeout: options.timeoutMs ?? SSH_PROBE_TIMEOUT_MS,
|
|
78788
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
78789
|
+
windowsHide: true
|
|
78790
|
+
}
|
|
78791
|
+
);
|
|
78792
|
+
return hostnameFromSshConfigDump(dump);
|
|
78793
|
+
} catch {
|
|
78794
|
+
return null;
|
|
78795
|
+
}
|
|
78796
|
+
}
|
|
78797
|
+
function hostnameFromSshConfigDump(dump) {
|
|
78798
|
+
for (const line of dump.split("\n")) {
|
|
78799
|
+
const match = /^hostname\s+(\S+)$/i.exec(line.trim());
|
|
78800
|
+
if (match?.[1]) return match[1];
|
|
78801
|
+
}
|
|
78802
|
+
return null;
|
|
78803
|
+
}
|
|
78756
78804
|
function parseOriginUrl(configText) {
|
|
78757
78805
|
let inOrigin = false;
|
|
78758
78806
|
for (const raw of configText.split("\n")) {
|
|
@@ -78818,17 +78866,17 @@ function findGitConfigPath(cwd, environment = {}) {
|
|
|
78818
78866
|
const dirs = resolveGitDirs(cwd, environment);
|
|
78819
78867
|
return dirs ? path2.join(dirs.commonDir, "config") : null;
|
|
78820
78868
|
}
|
|
78821
|
-
function resolveGithubRepo(cwd, environment = {}) {
|
|
78869
|
+
function resolveGithubRepo(cwd, environment = {}, sshHostnameOf = sshEffectiveHostname) {
|
|
78822
78870
|
const configPath = findGitConfigPath(cwd, environment);
|
|
78823
78871
|
const configText = configPath ? readIfExists(configPath) : null;
|
|
78824
78872
|
const remoteUrl = configText ? parseOriginUrl(configText) : null;
|
|
78825
|
-
return remoteUrl ? parseGithubRepo(remoteUrl) : null;
|
|
78873
|
+
return remoteUrl ? parseGithubRepo(remoteUrl, sshHostnameOf) : null;
|
|
78826
78874
|
}
|
|
78827
|
-
function repoResolver(environment = {}) {
|
|
78875
|
+
function repoResolver(environment = {}, sshHostnameOf = sshEffectiveHostname) {
|
|
78828
78876
|
const cache = /* @__PURE__ */ new Map();
|
|
78829
78877
|
return (cwd) => {
|
|
78830
78878
|
const key = path2.resolve(cwd);
|
|
78831
|
-
if (!cache.has(key)) cache.set(key, resolveGithubRepo(key, environment));
|
|
78879
|
+
if (!cache.has(key)) cache.set(key, resolveGithubRepo(key, environment, sshHostnameOf));
|
|
78832
78880
|
return cache.get(key);
|
|
78833
78881
|
};
|
|
78834
78882
|
}
|
|
@@ -78860,17 +78908,18 @@ var captureEverythingUnresolved = {
|
|
|
78860
78908
|
function captureDecision(cwd, policy) {
|
|
78861
78909
|
switch (policy.mode) {
|
|
78862
78910
|
case "scope":
|
|
78863
|
-
|
|
78911
|
+
if (cwd === void 0) return { capture: false, reason: "no-cwd" };
|
|
78912
|
+
return isWithinScope(cwd, policy.scopePaths, policy.environment) ? memberChoice(cwd, policy) : { capture: false, reason: "outside-policy" };
|
|
78864
78913
|
case "org-repos": {
|
|
78865
|
-
if (cwd === void 0) return { capture: false };
|
|
78914
|
+
if (cwd === void 0) return { capture: false, reason: "no-cwd" };
|
|
78866
78915
|
if (policy.memberFolders.length > 0 && isWithinScope(cwd, policy.memberFolders, policy.environment)) {
|
|
78867
78916
|
return memberChoice(cwd, policy);
|
|
78868
78917
|
}
|
|
78869
78918
|
const repoFullName = policy.repoOf(cwd);
|
|
78870
|
-
if (repoFullName === null) return { capture: false };
|
|
78919
|
+
if (repoFullName === null) return { capture: false, reason: "no-github-repo" };
|
|
78871
78920
|
const repo = repoFullName.toLowerCase();
|
|
78872
78921
|
const admitted = policy.allowedRepos !== null ? policy.allowedRepos.includes(repo) : policy.allowedOwners.length > 0 && policy.allowedOwners.includes(ownerOfRepo(repo));
|
|
78873
|
-
return admitted ? { capture: true, via: "org-policy", repoFullName } : { capture: false };
|
|
78922
|
+
return admitted ? { capture: true, via: "org-policy", repoFullName } : { capture: false, reason: "outside-policy" };
|
|
78874
78923
|
}
|
|
78875
78924
|
}
|
|
78876
78925
|
}
|
|
@@ -78893,7 +78942,7 @@ function describeCapturePolicy(policy) {
|
|
|
78893
78942
|
case "scope":
|
|
78894
78943
|
return `sessions under ${policy.scopePaths.join(", ")}`;
|
|
78895
78944
|
case "org-repos": {
|
|
78896
|
-
const repoPart = policy.allowedRepos !== null ? policy.allowedRepos.length > 0 ? `sessions in the org's
|
|
78945
|
+
const repoPart = policy.allowedRepos !== null ? policy.allowedRepos.length > 0 ? `sessions in the org's allowlisted repos: ${listSome(policy.allowedRepos)}` : "the org's repo allowlist is empty \u2014 no repo-based uploads" : policy.allowedOwners.length > 0 ? `sessions in GitHub repos owned by ${policy.allowedOwners.join(", ")}` : "org-owned GitHub repos \u2014 but the org has no GitHub integration, so nothing uploads";
|
|
78897
78946
|
return policy.memberFolders.length > 0 ? `${repoPart}, plus folders you added: ${policy.memberFolders.join(", ")}` : repoPart;
|
|
78898
78947
|
}
|
|
78899
78948
|
}
|
|
@@ -78914,10 +78963,17 @@ function describeStoredCapture(credentials) {
|
|
|
78914
78963
|
repoOf: () => null
|
|
78915
78964
|
});
|
|
78916
78965
|
}
|
|
78917
|
-
function
|
|
78966
|
+
function countCaptureSkip(skips, reason) {
|
|
78967
|
+
if (reason === "outside-policy") skips.skippedOutOfScope += 1;
|
|
78968
|
+
else skips.skippedNoRepo += 1;
|
|
78969
|
+
}
|
|
78970
|
+
function describeSkipReason(policy) {
|
|
78918
78971
|
if (policy.mode !== "org-repos") return "outside the sync boundary, stayed local";
|
|
78919
|
-
|
|
78920
|
-
|
|
78972
|
+
return policy.allowedRepos !== null ? "outside the org's allowlisted repos, stayed local" : "not in org-owned repos, stayed local";
|
|
78973
|
+
}
|
|
78974
|
+
function describeNoRepoSkipReason(policy, platform2 = process.platform) {
|
|
78975
|
+
if (policy.mode !== "org-repos") return "with no working directory recorded, stayed local";
|
|
78976
|
+
return platform2 === "darwin" ? "with no readable GitHub repo or in macOS-protected folders, stayed local (run `powerplant init` to review Git repository scanning)" : "with no readable GitHub repo, stayed local";
|
|
78921
78977
|
}
|
|
78922
78978
|
function normalizeScopePath(raw) {
|
|
78923
78979
|
const expanded = raw === "~" || raw.startsWith("~/") ? path3.join(os5.homedir(), raw.slice(1)) : raw;
|
|
@@ -78939,6 +78995,37 @@ function isPathWithinScopes(resolved, scopePaths) {
|
|
|
78939
78995
|
);
|
|
78940
78996
|
}
|
|
78941
78997
|
|
|
78998
|
+
// ../../apps/collector/src/syncWindow.ts
|
|
78999
|
+
var STEADY_SYNC_WINDOW_DAYS = 3;
|
|
79000
|
+
var DEEP_FIRST_SYNC_DAYS = 90;
|
|
79001
|
+
var DEEP_FIRST_SYNC_MAX_SESSIONS = 100;
|
|
79002
|
+
function resolveSyncWindow(explicitSince, deepSyncedAt) {
|
|
79003
|
+
if (explicitSince !== void 0) {
|
|
79004
|
+
const days = Number(explicitSince);
|
|
79005
|
+
if (!Number.isFinite(days) || days <= 0) {
|
|
79006
|
+
throw new ExpectedCliError(`--since must be a positive number of days, got "${explicitSince}"`);
|
|
79007
|
+
}
|
|
79008
|
+
return { days, deepSweep: false };
|
|
79009
|
+
}
|
|
79010
|
+
if (deepSyncedAt === void 0) return { days: DEEP_FIRST_SYNC_DAYS, deepSweep: true };
|
|
79011
|
+
return { days: STEADY_SYNC_WINDOW_DAYS, deepSweep: false };
|
|
79012
|
+
}
|
|
79013
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
79014
|
+
function resolveScanFloor(window2, nowMs, narrowSinceMs) {
|
|
79015
|
+
if (window2.deepSweep || narrowSinceMs === void 0) return nowMs - window2.days * DAY_MS;
|
|
79016
|
+
return narrowSinceMs;
|
|
79017
|
+
}
|
|
79018
|
+
function rawShipFloorMs(appEnv, nowMs, explicitSince) {
|
|
79019
|
+
if (appEnv !== "development" || explicitSince !== void 0) return null;
|
|
79020
|
+
return nowMs - STEADY_SYNC_WINDOW_DAYS * DAY_MS;
|
|
79021
|
+
}
|
|
79022
|
+
function describeScanReach(window2, narrowSinceMs) {
|
|
79023
|
+
if (window2.deepSweep || narrowSinceMs === void 0) {
|
|
79024
|
+
return `from the last ${window2.days} day(s)`;
|
|
79025
|
+
}
|
|
79026
|
+
return `changed since ${new Date(narrowSinceMs).toISOString()}`;
|
|
79027
|
+
}
|
|
79028
|
+
|
|
78942
79029
|
// ../../apps/collector/src/upload.ts
|
|
78943
79030
|
import { readFileSync as readFileSync4 } from "node:fs";
|
|
78944
79031
|
import { gzipSync } from "node:zlib";
|
|
@@ -79257,6 +79344,9 @@ async function runEnrollment(config2, options) {
|
|
|
79257
79344
|
log3(`Enrolled "${result.machine.name}" (${result.machine.id}) into org "${result.org.name}".`);
|
|
79258
79345
|
log3(`Machine token stored in ${location}.`);
|
|
79259
79346
|
log3(`Syncing: ${describeCapturePolicy(policy)}.`);
|
|
79347
|
+
log3(
|
|
79348
|
+
`First sync backfills up to the ${DEEP_FIRST_SYNC_MAX_SESSIONS} most recently active of those sessions from the last ${DEEP_FIRST_SYNC_DAYS} days; later syncs cover the last ${STEADY_SYNC_WINDOW_DAYS} days.`
|
|
79349
|
+
);
|
|
79260
79350
|
return credentials;
|
|
79261
79351
|
}
|
|
79262
79352
|
async function enroll(config2, options, createAuthClient = gotrueAuthClient) {
|
|
@@ -79453,7 +79543,7 @@ function addFolder(config2, rawPath, enrollCommand, log3, deps = defaultDeps) {
|
|
|
79453
79543
|
});
|
|
79454
79544
|
const org = credentials.orgName ?? "your org";
|
|
79455
79545
|
log3(
|
|
79456
|
-
credentials.captureMode === "org-repos" ? `Sessions under ${folder} will now upload to ${org} \u2014 even outside the org's
|
|
79546
|
+
credentials.captureMode === "org-repos" ? `Sessions under ${folder} will now upload to ${org} \u2014 even outside the org's allowlisted repos.` : `Added ${folder} to this machine's sync boundary.`
|
|
79457
79547
|
);
|
|
79458
79548
|
log3("Takes effect on the next sync pass; run the sync command to apply now.");
|
|
79459
79549
|
}
|
|
@@ -79492,18 +79582,53 @@ import { setImmediate as yieldToEventLoop } from "node:timers/promises";
|
|
|
79492
79582
|
import path4 from "node:path";
|
|
79493
79583
|
|
|
79494
79584
|
// ../../apps/collector/src/scan.ts
|
|
79495
|
-
import { readdirSync, readFileSync as readFileSync5, statSync as statSync2 } from "node:fs";
|
|
79585
|
+
import { closeSync, openSync, readdirSync, readFileSync as readFileSync5, readSync, statSync as statSync2 } from "node:fs";
|
|
79586
|
+
import { StringDecoder } from "node:string_decoder";
|
|
79496
79587
|
var ACTIVE_GRACE_MS = SESSION_ACTIVE_WINDOW_MS;
|
|
79497
79588
|
var jsonObjectSchema = external_exports.record(external_exports.string(), external_exports.unknown());
|
|
79498
79589
|
function* parseJsonl(text) {
|
|
79499
79590
|
for (const line of text.split("\n")) {
|
|
79500
|
-
|
|
79501
|
-
|
|
79502
|
-
|
|
79503
|
-
|
|
79504
|
-
|
|
79505
|
-
|
|
79506
|
-
|
|
79591
|
+
const record2 = parseJsonlLine(line);
|
|
79592
|
+
if (record2 !== null) yield record2;
|
|
79593
|
+
}
|
|
79594
|
+
}
|
|
79595
|
+
function parseJsonlLine(line) {
|
|
79596
|
+
if (!line.trim()) return null;
|
|
79597
|
+
try {
|
|
79598
|
+
const value = JSON.parse(line);
|
|
79599
|
+
const parsed = jsonObjectSchema.safeParse(value);
|
|
79600
|
+
return parsed.success ? parsed.data : null;
|
|
79601
|
+
} catch {
|
|
79602
|
+
return null;
|
|
79603
|
+
}
|
|
79604
|
+
}
|
|
79605
|
+
var READ_CHUNK_BYTES = 64 * 1024;
|
|
79606
|
+
function readFirstRecord(file2, matches) {
|
|
79607
|
+
let fd;
|
|
79608
|
+
try {
|
|
79609
|
+
fd = openSync(file2, "r");
|
|
79610
|
+
} catch (error52) {
|
|
79611
|
+
if (error52.code === "ENOENT") return null;
|
|
79612
|
+
throw error52;
|
|
79613
|
+
}
|
|
79614
|
+
const buffer = Buffer.alloc(READ_CHUNK_BYTES);
|
|
79615
|
+
const decoder = new StringDecoder("utf8");
|
|
79616
|
+
let carry = "";
|
|
79617
|
+
try {
|
|
79618
|
+
for (; ; ) {
|
|
79619
|
+
const bytes = readSync(fd, buffer, 0, buffer.length, null);
|
|
79620
|
+
if (bytes === 0) break;
|
|
79621
|
+
const lines = (carry + decoder.write(buffer.subarray(0, bytes))).split("\n");
|
|
79622
|
+
carry = lines.pop() ?? "";
|
|
79623
|
+
for (const line of lines) {
|
|
79624
|
+
const record2 = parseJsonlLine(line);
|
|
79625
|
+
if (record2 !== null && matches(record2)) return record2;
|
|
79626
|
+
}
|
|
79627
|
+
}
|
|
79628
|
+
const last = parseJsonlLine(carry + decoder.end());
|
|
79629
|
+
return last !== null && matches(last) ? last : null;
|
|
79630
|
+
} finally {
|
|
79631
|
+
closeSync(fd);
|
|
79507
79632
|
}
|
|
79508
79633
|
}
|
|
79509
79634
|
function listDirs(dir) {
|
|
@@ -79535,6 +79660,10 @@ function readIfExists2(file2) {
|
|
|
79535
79660
|
throw error52;
|
|
79536
79661
|
}
|
|
79537
79662
|
}
|
|
79663
|
+
var MAX_TRANSCRIPT_FILE_BYTES = 256 * 1024 * 1024;
|
|
79664
|
+
function describeReadError(error52) {
|
|
79665
|
+
return error52 instanceof Error ? error52.message : String(error52);
|
|
79666
|
+
}
|
|
79538
79667
|
|
|
79539
79668
|
// ../../apps/collector/src/lifecycleJournal.ts
|
|
79540
79669
|
var LIFECYCLE_JOURNAL_FILENAME = "session-lifecycle.jsonl";
|
|
@@ -79625,7 +79754,7 @@ async function collectClaudeCodeSessions(options = {}) {
|
|
|
79625
79754
|
const capture = options.capture ?? captureEverythingUnresolved;
|
|
79626
79755
|
const projectsDir = path5.join(claudeDir, "projects");
|
|
79627
79756
|
const items = [];
|
|
79628
|
-
|
|
79757
|
+
const skips = { skippedOutOfScope: 0, skippedNoRepo: 0 };
|
|
79629
79758
|
const home = path5.dirname(claudeDir);
|
|
79630
79759
|
const lifecycle = options.lifecycle ?? readLifecycleJournal(home);
|
|
79631
79760
|
for (const projectDir of listDirs(projectsDir)) {
|
|
@@ -79635,18 +79764,34 @@ async function collectClaudeCodeSessions(options = {}) {
|
|
|
79635
79764
|
const stat = statIfExists(transcriptPath);
|
|
79636
79765
|
if (!stat) continue;
|
|
79637
79766
|
if (options.sinceMs !== void 0 && stat.mtimeMs < options.sinceMs) continue;
|
|
79767
|
+
const sessionId = path5.basename(entry, ".jsonl");
|
|
79768
|
+
const endObservedAt = observedSessionEnd(lifecycle.get(sessionId), stat.mtimeMs);
|
|
79769
|
+
const status = endObservedAt === null && now - stat.mtimeMs < ACTIVE_GRACE_MS ? "active" : "inactive";
|
|
79770
|
+
if (options.locationsOnly) {
|
|
79771
|
+
const cwd = transcriptCwd(transcriptPath);
|
|
79772
|
+
const decision2 = captureDecision(cwd, capture);
|
|
79773
|
+
if (!decision2.capture) {
|
|
79774
|
+
countCaptureSkip(skips, decision2.reason);
|
|
79775
|
+
continue;
|
|
79776
|
+
}
|
|
79777
|
+
items.push({
|
|
79778
|
+
mtimeMs: stat.mtimeMs,
|
|
79779
|
+
transcriptPath,
|
|
79780
|
+
...cwd && { cwd },
|
|
79781
|
+
item: { source: "claude-code", externalId: sessionId, status, capturedVia: decision2.via }
|
|
79782
|
+
});
|
|
79783
|
+
continue;
|
|
79784
|
+
}
|
|
79638
79785
|
const text = readIfExists2(transcriptPath);
|
|
79639
79786
|
if (text === null) continue;
|
|
79640
79787
|
const summary = parseClaudeCodeTranscript(text);
|
|
79641
79788
|
if (!summary) continue;
|
|
79642
79789
|
const decision = captureDecision(summary.cwd, capture);
|
|
79643
79790
|
if (!decision.capture) {
|
|
79644
|
-
|
|
79791
|
+
countCaptureSkip(skips, decision.reason);
|
|
79645
79792
|
continue;
|
|
79646
79793
|
}
|
|
79647
|
-
const sessionId = path5.basename(entry, ".jsonl");
|
|
79648
79794
|
const usage = addSubagentUsage(summary.usage, path5.join(projectDir, sessionId, "subagents"));
|
|
79649
|
-
const endObservedAt = observedSessionEnd(lifecycle.get(sessionId), stat.mtimeMs);
|
|
79650
79795
|
const endedAt = endObservedAt ?? summary.endedAt;
|
|
79651
79796
|
items.push({
|
|
79652
79797
|
mtimeMs: stat.mtimeMs,
|
|
@@ -79655,7 +79800,7 @@ async function collectClaudeCodeSessions(options = {}) {
|
|
|
79655
79800
|
item: {
|
|
79656
79801
|
source: "claude-code",
|
|
79657
79802
|
externalId: sessionId,
|
|
79658
|
-
status
|
|
79803
|
+
status,
|
|
79659
79804
|
...summary.startedAt && { startedAt: summary.startedAt },
|
|
79660
79805
|
...endedAt && { endedAt },
|
|
79661
79806
|
...summary.model && { model: summary.model },
|
|
@@ -79680,9 +79825,16 @@ async function collectClaudeCodeSessions(options = {}) {
|
|
|
79680
79825
|
}
|
|
79681
79826
|
return {
|
|
79682
79827
|
sessions: items.sort((a, b2) => b2.mtimeMs - a.mtimeMs),
|
|
79683
|
-
|
|
79828
|
+
...skips
|
|
79684
79829
|
};
|
|
79685
79830
|
}
|
|
79831
|
+
function transcriptCwd(transcriptPath) {
|
|
79832
|
+
const record2 = readFirstRecord(
|
|
79833
|
+
transcriptPath,
|
|
79834
|
+
(candidate) => typeof candidate.cwd === "string" && candidate.cwd !== ""
|
|
79835
|
+
);
|
|
79836
|
+
return typeof record2?.cwd === "string" ? record2.cwd : void 0;
|
|
79837
|
+
}
|
|
79686
79838
|
function toUsage(u) {
|
|
79687
79839
|
const n = (value) => typeof value === "number" ? value : 0;
|
|
79688
79840
|
return {
|
|
@@ -79765,6 +79917,21 @@ function parseCodexRollout(text) {
|
|
|
79765
79917
|
...typeof git?.repository_url === "string" && repoFullNameFrom(git.repository_url)
|
|
79766
79918
|
};
|
|
79767
79919
|
}
|
|
79920
|
+
function parseRolloutFile(rolloutPath) {
|
|
79921
|
+
const text = readIfExists2(rolloutPath);
|
|
79922
|
+
return text === null ? null : parseCodexRollout(text);
|
|
79923
|
+
}
|
|
79924
|
+
function rolloutLocation(rolloutPath) {
|
|
79925
|
+
const record2 = readFirstRecord(rolloutPath, (candidate) => candidate.type === "session_meta");
|
|
79926
|
+
const meta3 = record2?.payload;
|
|
79927
|
+
if (!meta3 || typeof meta3 !== "object" || Array.isArray(meta3)) return { kind: "session", totals: null };
|
|
79928
|
+
const { thread_source: threadSource, cwd } = meta3;
|
|
79929
|
+
return {
|
|
79930
|
+
kind: threadSource === "subagent" ? "subagent" : "session",
|
|
79931
|
+
totals: null,
|
|
79932
|
+
...typeof cwd === "string" && cwd !== "" && { cwd }
|
|
79933
|
+
};
|
|
79934
|
+
}
|
|
79768
79935
|
function repoFullNameFrom(url2) {
|
|
79769
79936
|
const fullName = parseGithubRepo(url2);
|
|
79770
79937
|
return fullName ? { repoFullName: fullName } : {};
|
|
@@ -79802,41 +79969,95 @@ async function collectCodexSessions(options = {}) {
|
|
|
79802
79969
|
const sessionsDir = options.codexDir ?? path6.join(os8.homedir(), ".codex", "sessions");
|
|
79803
79970
|
const now = options.now ?? Date.now();
|
|
79804
79971
|
const capture = options.capture ?? captureEverythingUnresolved;
|
|
79805
|
-
const
|
|
79972
|
+
const maxBytes = options.maxTranscriptBytes ?? MAX_TRANSCRIPT_FILE_BYTES;
|
|
79806
79973
|
const items = [];
|
|
79807
|
-
|
|
79974
|
+
const candidates = [];
|
|
79975
|
+
const unreadable = [];
|
|
79976
|
+
const skips = { skippedOutOfScope: 0, skippedNoRepo: 0 };
|
|
79808
79977
|
let skippedSubagentThreads = 0;
|
|
79809
|
-
|
|
79978
|
+
let skippedTooLarge = 0;
|
|
79979
|
+
const externalIdOf = (rolloutPath) => path6.basename(rolloutPath, ".jsonl");
|
|
79980
|
+
const admitFrom = async (rootDir, archived) => {
|
|
79810
79981
|
for (const rolloutPath of rolloutFiles(rootDir)) {
|
|
79811
79982
|
await yieldToEventLoop2();
|
|
79812
79983
|
const stat = statIfExists(rolloutPath);
|
|
79813
79984
|
if (!stat) continue;
|
|
79814
79985
|
if (options.sinceMs !== void 0 && Math.max(stat.mtimeMs, stat.ctimeMs) < options.sinceMs)
|
|
79815
79986
|
continue;
|
|
79816
|
-
|
|
79817
|
-
|
|
79818
|
-
const summary = parseCodexRollout(text);
|
|
79819
|
-
if (!summary) continue;
|
|
79820
|
-
if (summary.kind === "subagent") {
|
|
79821
|
-
skippedSubagentThreads += 1;
|
|
79987
|
+
if (stat.size > maxBytes) {
|
|
79988
|
+
skippedTooLarge += 1;
|
|
79822
79989
|
continue;
|
|
79823
79990
|
}
|
|
79824
|
-
|
|
79825
|
-
|
|
79826
|
-
|
|
79827
|
-
|
|
79991
|
+
try {
|
|
79992
|
+
const location = rolloutLocation(rolloutPath);
|
|
79993
|
+
if (location.kind === "subagent") {
|
|
79994
|
+
skippedSubagentThreads += 1;
|
|
79995
|
+
continue;
|
|
79996
|
+
}
|
|
79997
|
+
const decision = captureDecision(location.cwd, capture);
|
|
79998
|
+
if (!decision.capture) {
|
|
79999
|
+
countCaptureSkip(skips, decision.reason);
|
|
80000
|
+
continue;
|
|
80001
|
+
}
|
|
80002
|
+
if (options.locationsOnly) {
|
|
80003
|
+
items.push({
|
|
80004
|
+
mtimeMs: stat.mtimeMs,
|
|
80005
|
+
transcriptPath: rolloutPath,
|
|
80006
|
+
...location.cwd && { cwd: location.cwd },
|
|
80007
|
+
item: {
|
|
80008
|
+
source: "codex",
|
|
80009
|
+
externalId: externalIdOf(rolloutPath),
|
|
80010
|
+
status: now - stat.mtimeMs < ACTIVE_GRACE_MS ? "active" : "inactive",
|
|
80011
|
+
archived,
|
|
80012
|
+
capturedVia: decision.via
|
|
80013
|
+
}
|
|
80014
|
+
});
|
|
80015
|
+
continue;
|
|
80016
|
+
}
|
|
80017
|
+
candidates.push({
|
|
80018
|
+
rolloutPath,
|
|
80019
|
+
archived,
|
|
80020
|
+
mtimeMs: stat.mtimeMs,
|
|
80021
|
+
...location.cwd && { cwd: location.cwd },
|
|
80022
|
+
decision
|
|
80023
|
+
});
|
|
80024
|
+
} catch (error52) {
|
|
80025
|
+
unreadable.push(`${externalIdOf(rolloutPath)}: ${describeReadError(error52)}`);
|
|
79828
80026
|
}
|
|
79829
|
-
|
|
80027
|
+
}
|
|
80028
|
+
};
|
|
80029
|
+
await admitFrom(sessionsDir, false);
|
|
80030
|
+
await admitFrom(codexArchivedSessionsDir(sessionsDir), true);
|
|
80031
|
+
if (options.locationsOnly) {
|
|
80032
|
+
return {
|
|
80033
|
+
sessions: items.sort((a, b2) => b2.mtimeMs - a.mtimeMs),
|
|
80034
|
+
...skips,
|
|
80035
|
+
skippedSubagentThreads,
|
|
80036
|
+
skippedTooLarge,
|
|
80037
|
+
unreadable,
|
|
80038
|
+
heldBack: 0
|
|
80039
|
+
};
|
|
80040
|
+
}
|
|
80041
|
+
candidates.sort((a, b2) => b2.mtimeMs - a.mtimeMs);
|
|
80042
|
+
const chosen = options.maxSessions === void 0 ? candidates : candidates.slice(0, options.maxSessions);
|
|
80043
|
+
const threadNames = chosen.length > 0 ? loadThreadNames(sessionsDir) : /* @__PURE__ */ new Map();
|
|
80044
|
+
for (const candidate of chosen) {
|
|
80045
|
+
await yieldToEventLoop2();
|
|
80046
|
+
const { rolloutPath, archived, mtimeMs, cwd, decision } = candidate;
|
|
80047
|
+
try {
|
|
80048
|
+
const summary = parseRolloutFile(rolloutPath);
|
|
80049
|
+
if (!summary) continue;
|
|
80050
|
+
const repoDir = summary.repoName ?? (cwd ? path6.basename(cwd) : void 0);
|
|
79830
80051
|
const repoFullName = decision.repoFullName ?? summary.repoFullName;
|
|
79831
80052
|
const title = summary.sessionId ? threadNames.get(summary.sessionId) : void 0;
|
|
79832
80053
|
items.push({
|
|
79833
|
-
mtimeMs
|
|
80054
|
+
mtimeMs,
|
|
79834
80055
|
transcriptPath: rolloutPath,
|
|
79835
|
-
...
|
|
80056
|
+
...cwd && { cwd },
|
|
79836
80057
|
item: {
|
|
79837
80058
|
source: "codex",
|
|
79838
|
-
externalId:
|
|
79839
|
-
status: now -
|
|
80059
|
+
externalId: externalIdOf(rolloutPath),
|
|
80060
|
+
status: now - mtimeMs < ACTIVE_GRACE_MS ? "active" : "inactive",
|
|
79840
80061
|
archived,
|
|
79841
80062
|
...summary.startedAt && { startedAt: summary.startedAt },
|
|
79842
80063
|
...summary.endedAt && { endedAt: summary.endedAt },
|
|
@@ -79854,14 +80075,17 @@ async function collectCodexSessions(options = {}) {
|
|
|
79854
80075
|
capturedVia: decision.via
|
|
79855
80076
|
}
|
|
79856
80077
|
});
|
|
80078
|
+
} catch (error52) {
|
|
80079
|
+
unreadable.push(`${externalIdOf(rolloutPath)}: ${describeReadError(error52)}`);
|
|
79857
80080
|
}
|
|
79858
|
-
}
|
|
79859
|
-
await collectFrom(sessionsDir, false);
|
|
79860
|
-
await collectFrom(codexArchivedSessionsDir(sessionsDir), true);
|
|
80081
|
+
}
|
|
79861
80082
|
return {
|
|
79862
80083
|
sessions: items.sort((a, b2) => b2.mtimeMs - a.mtimeMs),
|
|
79863
|
-
|
|
79864
|
-
skippedSubagentThreads
|
|
80084
|
+
...skips,
|
|
80085
|
+
skippedSubagentThreads,
|
|
80086
|
+
skippedTooLarge,
|
|
80087
|
+
unreadable,
|
|
80088
|
+
heldBack: candidates.length - chosen.length
|
|
79865
80089
|
};
|
|
79866
80090
|
}
|
|
79867
80091
|
|
|
@@ -79889,13 +80113,35 @@ function cursorSpoolDir(home) {
|
|
|
79889
80113
|
var emptyResult = () => ({
|
|
79890
80114
|
sessions: [],
|
|
79891
80115
|
skippedOutOfScope: 0,
|
|
80116
|
+
skippedNoRepo: 0,
|
|
79892
80117
|
skippedSubagentComposers: 0,
|
|
79893
80118
|
skippedNonLocalComposers: 0,
|
|
79894
80119
|
skippedUnreadable: 0,
|
|
79895
80120
|
unavailable: null
|
|
79896
80121
|
});
|
|
80122
|
+
var SQLITE_EXPERIMENTAL_WARNING = "SQLite is an experimental feature and might change at any time";
|
|
80123
|
+
async function withSqliteExperimentalWarningSuppressed(load) {
|
|
80124
|
+
const forward = process.emitWarning.bind(process);
|
|
80125
|
+
process.emitWarning = (warning2, ...rest) => {
|
|
80126
|
+
const message = typeof warning2 === "string" ? warning2 : warning2.message;
|
|
80127
|
+
if (message === SQLITE_EXPERIMENTAL_WARNING) return;
|
|
80128
|
+
Reflect.apply(forward, process, [warning2, ...rest]);
|
|
80129
|
+
};
|
|
80130
|
+
try {
|
|
80131
|
+
return await load();
|
|
80132
|
+
} finally {
|
|
80133
|
+
process.emitWarning = forward;
|
|
80134
|
+
}
|
|
80135
|
+
}
|
|
80136
|
+
var sqliteModule;
|
|
80137
|
+
function loadSqlite() {
|
|
80138
|
+
sqliteModule ??= withSqliteExperimentalWarningSuppressed(
|
|
80139
|
+
() => import("node:sqlite")
|
|
80140
|
+
);
|
|
80141
|
+
return sqliteModule;
|
|
80142
|
+
}
|
|
79897
80143
|
async function openCursorDatabase(dbPath) {
|
|
79898
|
-
const sqlite = await
|
|
80144
|
+
const sqlite = await loadSqlite();
|
|
79899
80145
|
return new sqlite.DatabaseSync(dbPath, { readOnly: true });
|
|
79900
80146
|
}
|
|
79901
80147
|
function describeError(error52) {
|
|
@@ -80118,7 +80364,7 @@ function collectFromStore(db, userDir, options, result) {
|
|
|
80118
80364
|
const cwd = facts.workspacePath ?? workspaceFolderOf(userDir, workspaceId);
|
|
80119
80365
|
const decision = captureDecision(cwd, capture);
|
|
80120
80366
|
if (!decision.capture) {
|
|
80121
|
-
result.
|
|
80367
|
+
countCaptureSkip(result, decision.reason);
|
|
80122
80368
|
continue;
|
|
80123
80369
|
}
|
|
80124
80370
|
if (options.locationsOnly) {
|
|
@@ -80199,36 +80445,6 @@ function collectFromStore(db, userDir, options, result) {
|
|
|
80199
80445
|
}
|
|
80200
80446
|
}
|
|
80201
80447
|
|
|
80202
|
-
// ../../apps/collector/src/syncWindow.ts
|
|
80203
|
-
var STEADY_SYNC_WINDOW_DAYS = 3;
|
|
80204
|
-
var DEEP_FIRST_SYNC_DAYS = 90;
|
|
80205
|
-
function resolveSyncWindow(explicitSince, deepSyncedAt) {
|
|
80206
|
-
if (explicitSince !== void 0) {
|
|
80207
|
-
const days = Number(explicitSince);
|
|
80208
|
-
if (!Number.isFinite(days) || days <= 0) {
|
|
80209
|
-
throw new ExpectedCliError(`--since must be a positive number of days, got "${explicitSince}"`);
|
|
80210
|
-
}
|
|
80211
|
-
return { days, deepSweep: false };
|
|
80212
|
-
}
|
|
80213
|
-
if (deepSyncedAt === void 0) return { days: DEEP_FIRST_SYNC_DAYS, deepSweep: true };
|
|
80214
|
-
return { days: STEADY_SYNC_WINDOW_DAYS, deepSweep: false };
|
|
80215
|
-
}
|
|
80216
|
-
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
80217
|
-
function resolveScanFloor(window2, nowMs, narrowSinceMs) {
|
|
80218
|
-
if (window2.deepSweep || narrowSinceMs === void 0) return nowMs - window2.days * DAY_MS;
|
|
80219
|
-
return narrowSinceMs;
|
|
80220
|
-
}
|
|
80221
|
-
function rawShipFloorMs(appEnv, nowMs, explicitSince) {
|
|
80222
|
-
if (appEnv !== "development" || explicitSince !== void 0) return null;
|
|
80223
|
-
return nowMs - STEADY_SYNC_WINDOW_DAYS * DAY_MS;
|
|
80224
|
-
}
|
|
80225
|
-
function describeScanReach(window2, narrowSinceMs) {
|
|
80226
|
-
if (window2.deepSweep || narrowSinceMs === void 0) {
|
|
80227
|
-
return `from the last ${window2.days} day(s)`;
|
|
80228
|
-
}
|
|
80229
|
-
return `changed since ${new Date(narrowSinceMs).toISOString()}`;
|
|
80230
|
-
}
|
|
80231
|
-
|
|
80232
80448
|
// ../../apps/collector/src/protectedRepoDiscovery.ts
|
|
80233
80449
|
var defaultDiscoveryDeps = {
|
|
80234
80450
|
collectClaude: collectClaudeCodeSessions,
|
|
@@ -80240,8 +80456,8 @@ async function findRecentProtectedSessions(options = {}, deps = defaultDiscovery
|
|
|
80240
80456
|
const sinceMs = now - DEEP_FIRST_SYNC_DAYS * 24 * 60 * 60 * 1e3;
|
|
80241
80457
|
const capture = captureEverythingUnresolved;
|
|
80242
80458
|
const results = [
|
|
80243
|
-
await deps.collectClaude({ sinceMs, capture }),
|
|
80244
|
-
await deps.collectCodex({ sinceMs, capture }),
|
|
80459
|
+
await deps.collectClaude({ sinceMs, capture, locationsOnly: true }),
|
|
80460
|
+
await deps.collectCodex({ sinceMs, capture, locationsOnly: true }),
|
|
80245
80461
|
await deps.collectCursor({ sinceMs, capture, locationsOnly: true })
|
|
80246
80462
|
];
|
|
80247
80463
|
return groupProtectedSessions(
|
|
@@ -80405,7 +80621,7 @@ function installCollectorHooks(homeDir = os11.homedir()) {
|
|
|
80405
80621
|
}
|
|
80406
80622
|
|
|
80407
80623
|
// ../../apps/collector/src/service.ts
|
|
80408
|
-
import { execFileSync as
|
|
80624
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
80409
80625
|
import { chmodSync as chmodSync2, existsSync as existsSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync7, rmSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
80410
80626
|
import os12 from "node:os";
|
|
80411
80627
|
import path10 from "node:path";
|
|
@@ -80478,17 +80694,40 @@ function installerPathValue() {
|
|
|
80478
80694
|
}
|
|
80479
80695
|
function installService(config2, command) {
|
|
80480
80696
|
requireMacos();
|
|
80697
|
+
bootout(config2.appEnv);
|
|
80698
|
+
if (!waitForUnload(() => isLoaded(config2.appEnv))) {
|
|
80699
|
+
const service = `gui/${uid()}/${label(config2.appEnv)}`;
|
|
80700
|
+
throw new Error(
|
|
80701
|
+
`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}`
|
|
80702
|
+
);
|
|
80703
|
+
}
|
|
80481
80704
|
const launcher = launcherPath(config2.appEnv);
|
|
80482
80705
|
mkdirSync4(path10.dirname(launcher), { recursive: true });
|
|
80483
80706
|
writeFileSync4(launcher, buildLauncherScript(command));
|
|
80484
80707
|
chmodSync2(launcher, 493);
|
|
80485
80708
|
const file2 = plistPath(config2.appEnv);
|
|
80486
80709
|
mkdirSync4(path10.dirname(file2), { recursive: true });
|
|
80487
|
-
bootout(config2.appEnv);
|
|
80488
80710
|
writeFileSync4(file2, buildLaunchdPlist(config2.appEnv, installerPathValue()));
|
|
80489
80711
|
runLaunchctl(["bootstrap", `gui/${uid()}`, file2]);
|
|
80490
80712
|
return `${file2} (watching for session changes; log: ${logPath(config2.appEnv)})`;
|
|
80491
80713
|
}
|
|
80714
|
+
var UNLOAD_WAIT_MS = 1e4;
|
|
80715
|
+
var UNLOAD_POLL_MS = 100;
|
|
80716
|
+
function waitForUnload(loaded, options = {}) {
|
|
80717
|
+
const deadlineMs = options.deadlineMs ?? UNLOAD_WAIT_MS;
|
|
80718
|
+
const pollMs = options.pollMs ?? UNLOAD_POLL_MS;
|
|
80719
|
+
const sleep = options.sleep ?? sleepSync;
|
|
80720
|
+
const now = options.now ?? Date.now;
|
|
80721
|
+
const deadline = now() + deadlineMs;
|
|
80722
|
+
while (loaded()) {
|
|
80723
|
+
if (now() >= deadline) return false;
|
|
80724
|
+
sleep(pollMs);
|
|
80725
|
+
}
|
|
80726
|
+
return true;
|
|
80727
|
+
}
|
|
80728
|
+
function sleepSync(ms) {
|
|
80729
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
80730
|
+
}
|
|
80492
80731
|
function serviceUpToDate(config2, command) {
|
|
80493
80732
|
if (process.platform !== "darwin") return false;
|
|
80494
80733
|
const launcher = launcherPath(config2.appEnv);
|
|
@@ -80530,7 +80769,7 @@ function restartService(config2) {
|
|
|
80530
80769
|
}
|
|
80531
80770
|
function runLaunchctl(args) {
|
|
80532
80771
|
try {
|
|
80533
|
-
|
|
80772
|
+
execFileSync3("launchctl", args, { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
|
|
80534
80773
|
} catch (cause) {
|
|
80535
80774
|
throw new Error(launchctlFailureMessage(args[0] ?? "", cause), { cause });
|
|
80536
80775
|
}
|
|
@@ -80545,7 +80784,7 @@ function launchctlFailureMessage(subcommand, cause) {
|
|
|
80545
80784
|
}
|
|
80546
80785
|
function isLoaded(appEnv) {
|
|
80547
80786
|
try {
|
|
80548
|
-
|
|
80787
|
+
execFileSync3("launchctl", ["print", `gui/${uid()}/${label(appEnv)}`], { stdio: "ignore" });
|
|
80549
80788
|
return true;
|
|
80550
80789
|
} catch {
|
|
80551
80790
|
return false;
|
|
@@ -80553,7 +80792,7 @@ function isLoaded(appEnv) {
|
|
|
80553
80792
|
}
|
|
80554
80793
|
function bootout(appEnv) {
|
|
80555
80794
|
try {
|
|
80556
|
-
|
|
80795
|
+
execFileSync3("launchctl", ["bootout", `gui/${uid()}/${label(appEnv)}`], { stdio: "ignore" });
|
|
80557
80796
|
return true;
|
|
80558
80797
|
} catch {
|
|
80559
80798
|
return false;
|
|
@@ -80884,7 +81123,7 @@ async function resolveCapturePolicy(config2, credentials, log3, deps = defaultPo
|
|
|
80884
81123
|
}
|
|
80885
81124
|
if (repos !== null && repos.length === 0) {
|
|
80886
81125
|
log3(
|
|
80887
|
-
"\u26A0 The org's
|
|
81126
|
+
"\u26A0 The org's repo allowlist is empty \u2014 no repo-based uploads" + (memberFolders.length > 0 ? "; only folders you added upload." : ".")
|
|
80888
81127
|
);
|
|
80889
81128
|
} else if (repos === null && owners.length === 0 && !ownersUnresolved) {
|
|
80890
81129
|
log3(
|
|
@@ -80911,26 +81150,31 @@ async function runSyncPass(config2, options = {}) {
|
|
|
80911
81150
|
const window2 = resolveSyncWindow(options.explicitSince, credentials.deepSyncedAt);
|
|
80912
81151
|
if (window2.deepSweep) {
|
|
80913
81152
|
log3(
|
|
80914
|
-
`First sync for this enrollment \u2014 sweeping the last ${window2.days} days to
|
|
81153
|
+
`First sync for this enrollment \u2014 sweeping the last ${window2.days} days and backfilling up to the ${DEEP_FIRST_SYNC_MAX_SESSIONS} most recently active sessions already on this machine (later syncs cover ${STEADY_SYNC_WINDOW_DAYS} days).`
|
|
80915
81154
|
);
|
|
80916
81155
|
}
|
|
80917
81156
|
const capture = await resolveCapturePolicy(config2, credentials, log3);
|
|
80918
81157
|
const sinceMs = resolveScanFloor(window2, Date.now(), options.narrowSinceMs);
|
|
81158
|
+
const maxSessions = window2.deepSweep ? DEEP_FIRST_SYNC_MAX_SESSIONS : void 0;
|
|
80919
81159
|
const claude = await collectClaudeCodeSessions({ sinceMs, capture });
|
|
80920
|
-
const codex = await collectCodexSessions({ sinceMs, capture });
|
|
81160
|
+
const codex = await collectCodexSessions({ sinceMs, capture, maxSessions });
|
|
80921
81161
|
const cursor = await collectCursorSessions({ sinceMs, capture });
|
|
80922
81162
|
if (cursor.unavailable !== null) {
|
|
80923
81163
|
log3(`\u26A0 Cursor sessions skipped this pass \u2014 ${cursor.unavailable}. Other syncing is unaffected.`);
|
|
80924
81164
|
}
|
|
80925
|
-
const
|
|
81165
|
+
const inWindow = [...claude.sessions, ...codex.sessions, ...cursor.sessions].sort(
|
|
80926
81166
|
(a, b2) => b2.mtimeMs - a.mtimeMs
|
|
80927
81167
|
);
|
|
81168
|
+
const collected = maxSessions === void 0 ? inWindow : inWindow.slice(0, maxSessions);
|
|
81169
|
+
const heldBack = inWindow.length - collected.length + codex.heldBack;
|
|
80928
81170
|
const skippedOutOfScope = claude.skippedOutOfScope + codex.skippedOutOfScope + cursor.skippedOutOfScope;
|
|
81171
|
+
const skippedNoRepo = claude.skippedNoRepo + codex.skippedNoRepo + cursor.skippedNoRepo;
|
|
80929
81172
|
const items = collected.map((c) => c.item);
|
|
80930
81173
|
const active = items.filter((s) => s.status === "active").length;
|
|
80931
81174
|
log3(
|
|
80932
|
-
`Found ${
|
|
81175
|
+
`Found ${inWindow.length} local session(s) (${claude.sessions.length} claude, ${codex.sessions.length} codex, ${cursor.sessions.length} cursor) ` + describeScanReach(window2, options.narrowSinceMs) + (active > 0 ? ` (${active} still active)` : "") + (skippedOutOfScope > 0 ? ` \xB7 ${skippedOutOfScope} ${describeSkipReason(capture)}` : "") + (skippedNoRepo > 0 ? ` \xB7 ${skippedNoRepo} ${describeNoRepoSkipReason(capture)}` : "") + (codex.skippedSubagentThreads > 0 ? ` \xB7 ${codex.skippedSubagentThreads} codex subagent thread(s) skipped (spend unmeasured)` : "") + (cursor.skippedSubagentComposers > 0 ? ` \xB7 ${cursor.skippedSubagentComposers} cursor subagent thread(s) skipped` : "") + (cursor.skippedNonLocalComposers > 0 ? ` \xB7 ${cursor.skippedNonLocalComposers} cursor session(s) skipped (not local to this machine)` : "") + (cursor.skippedUnreadable > 0 ? ` \xB7 ${cursor.skippedUnreadable} cursor session(s) unreadable, skipped` : "") + (codex.skippedTooLarge > 0 ? ` \xB7 ${codex.skippedTooLarge} codex rollout(s) skipped (over ${MAX_TRANSCRIPT_FILE_BYTES / (1024 * 1024)}MB \u2014 too large to read)` : "") + (codex.unreadable.length > 0 ? ` \xB7 ${codex.unreadable.length} codex rollout(s) unreadable, skipped` : "") + (heldBack > 0 ? ` \xB7 ${heldBack} older session(s) held back \u2014 the first sync backfills the ${DEEP_FIRST_SYNC_MAX_SESSIONS} most recently active` : "") + "."
|
|
80933
81176
|
);
|
|
81177
|
+
for (const failure of codex.unreadable) log3(` codex rollout skipped \u2014 ${failure}`);
|
|
80934
81178
|
if (options.dryRun) {
|
|
80935
81179
|
for (const s of items.slice(0, 10)) {
|
|
80936
81180
|
log3(` ${s.externalId} ${s.model ?? "model unmeasured"} ${s.title ?? "(untitled)"}`);
|
|
@@ -81068,7 +81312,7 @@ import os14 from "node:os";
|
|
|
81068
81312
|
import path14 from "node:path";
|
|
81069
81313
|
|
|
81070
81314
|
// ../../apps/collector/src/wake.ts
|
|
81071
|
-
import { execFile as execFile2, execFileSync as
|
|
81315
|
+
import { execFile as execFile2, execFileSync as execFileSync4 } from "node:child_process";
|
|
81072
81316
|
import { existsSync as existsSync3 } from "node:fs";
|
|
81073
81317
|
import path13 from "node:path";
|
|
81074
81318
|
var WAKE_POLL_MS = 5e3;
|
|
@@ -81170,7 +81414,7 @@ function resolveClaudeBin(homeDir, env = process.env) {
|
|
|
81170
81414
|
const found = candidates.find((candidate) => existsSync3(candidate));
|
|
81171
81415
|
if (found) return found;
|
|
81172
81416
|
try {
|
|
81173
|
-
const fromPath =
|
|
81417
|
+
const fromPath = execFileSync4("which", ["claude"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
81174
81418
|
return fromPath === "" ? null : fromPath;
|
|
81175
81419
|
} catch {
|
|
81176
81420
|
return null;
|
|
@@ -81192,7 +81436,7 @@ function versionAtLeast(version2, floor) {
|
|
|
81192
81436
|
return true;
|
|
81193
81437
|
}
|
|
81194
81438
|
function execClaudeVersion(claudeBin) {
|
|
81195
|
-
return
|
|
81439
|
+
return execFileSync4(claudeBin, ["--version"], {
|
|
81196
81440
|
stdio: ["ignore", "pipe", "ignore"],
|
|
81197
81441
|
timeout: VERSION_TIMEOUT_MS
|
|
81198
81442
|
}).toString();
|
|
@@ -81766,7 +82010,7 @@ function describeError3(error52) {
|
|
|
81766
82010
|
}
|
|
81767
82011
|
|
|
81768
82012
|
// src/connect.ts
|
|
81769
|
-
import { execFileSync as
|
|
82013
|
+
import { execFileSync as execFileSync5, spawnSync } from "node:child_process";
|
|
81770
82014
|
import {
|
|
81771
82015
|
existsSync as existsSync5,
|
|
81772
82016
|
mkdirSync as mkdirSync6,
|
|
@@ -81992,7 +82236,7 @@ function desktopClaudeBinaries(homeDir) {
|
|
|
81992
82236
|
}
|
|
81993
82237
|
function defaultWhichAll(name) {
|
|
81994
82238
|
try {
|
|
81995
|
-
return
|
|
82239
|
+
return execFileSync5("which", ["-a", name], { encoding: "utf8" }).split("\n").map((line) => line.trim()).filter(Boolean);
|
|
81996
82240
|
} catch {
|
|
81997
82241
|
return [];
|
|
81998
82242
|
}
|
|
@@ -82785,7 +83029,7 @@ function realRunInteractive(bin, args, env) {
|
|
|
82785
83029
|
}
|
|
82786
83030
|
|
|
82787
83031
|
// src/connections.ts
|
|
82788
|
-
import { execFileSync as
|
|
83032
|
+
import { execFileSync as execFileSync6, spawnSync as spawnSync2 } from "node:child_process";
|
|
82789
83033
|
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync7 } from "node:fs";
|
|
82790
83034
|
import path16 from "node:path";
|
|
82791
83035
|
function probeConnections(clients, deps) {
|
|
@@ -82988,7 +83232,7 @@ function runWithPath(pathValue, budget = UNLIMITED_TIME_BUDGET) {
|
|
|
82988
83232
|
function whichAllWith(pathValue) {
|
|
82989
83233
|
return (name) => {
|
|
82990
83234
|
try {
|
|
82991
|
-
return
|
|
83235
|
+
return execFileSync6("which", ["-a", name], {
|
|
82992
83236
|
encoding: "utf8",
|
|
82993
83237
|
env: { ...process.env, PATH: pathValue }
|
|
82994
83238
|
}).split("\n").map((line) => line.trim()).filter(Boolean);
|
|
@@ -82999,14 +83243,14 @@ function whichAllWith(pathValue) {
|
|
|
82999
83243
|
}
|
|
83000
83244
|
|
|
83001
83245
|
// src/doctor.ts
|
|
83002
|
-
import { closeSync, fstatSync, openSync, readSync } from "node:fs";
|
|
83246
|
+
import { closeSync as closeSync2, fstatSync, openSync as openSync2, readSync as readSync2 } from "node:fs";
|
|
83003
83247
|
|
|
83004
83248
|
// src/statusReport.ts
|
|
83005
83249
|
function renderStatus(i) {
|
|
83006
83250
|
const lines = [];
|
|
83007
83251
|
lines.push(`Environment: ${i.config.appEnv} (API ${i.config.apiUrl})`);
|
|
83008
83252
|
lines.push(
|
|
83009
|
-
`CLI: powerplant ${i.cliVersion}` + (i.latestVersion !== null && i.latestVersion !== i.cliVersion ? ` \u2014 ${i.latestVersion} is available:
|
|
83253
|
+
`CLI: powerplant ${i.cliVersion}` + (i.latestVersion !== null && i.latestVersion !== i.cliVersion ? ` \u2014 ${i.latestVersion} is available: powerplant update` : "")
|
|
83010
83254
|
);
|
|
83011
83255
|
const unsupported = unsupportedVersionNotice(i.cliVersion, i.oldestSupportedVersion);
|
|
83012
83256
|
if (unsupported !== null) lines.push(unsupported);
|
|
@@ -83158,7 +83402,7 @@ function redactedLogTail(raw) {
|
|
|
83158
83402
|
function readDoctorLogTail(path19) {
|
|
83159
83403
|
let fd;
|
|
83160
83404
|
try {
|
|
83161
|
-
fd =
|
|
83405
|
+
fd = openSync2(path19, "r");
|
|
83162
83406
|
} catch (error52) {
|
|
83163
83407
|
if (error52.code === "ENOENT") return { path: path19, lines: null };
|
|
83164
83408
|
return { path: path19, lines: null, readError: error52 instanceof Error ? error52.message : String(error52) };
|
|
@@ -83167,14 +83411,14 @@ function readDoctorLogTail(path19) {
|
|
|
83167
83411
|
const size = fstatSync(fd).size;
|
|
83168
83412
|
const offset = Math.max(0, size - LOG_TAIL_READ_BYTES);
|
|
83169
83413
|
const buffer = Buffer.alloc(Math.min(size, LOG_TAIL_READ_BYTES));
|
|
83170
|
-
|
|
83414
|
+
readSync2(fd, buffer, 0, buffer.length, offset);
|
|
83171
83415
|
let text = buffer.toString("utf8");
|
|
83172
83416
|
if (offset > 0) text = text.slice(text.indexOf("\n") + 1);
|
|
83173
83417
|
return { path: path19, lines: redactedLogTail(text) };
|
|
83174
83418
|
} catch (error52) {
|
|
83175
83419
|
return { path: path19, lines: null, readError: error52 instanceof Error ? error52.message : String(error52) };
|
|
83176
83420
|
} finally {
|
|
83177
|
-
|
|
83421
|
+
closeSync2(fd);
|
|
83178
83422
|
}
|
|
83179
83423
|
}
|
|
83180
83424
|
function renderDoctorReport(i) {
|
|
@@ -83477,7 +83721,7 @@ async function realPromptLine(question) {
|
|
|
83477
83721
|
}
|
|
83478
83722
|
|
|
83479
83723
|
// src/memberSessions.ts
|
|
83480
|
-
import { execFileSync as
|
|
83724
|
+
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
83481
83725
|
import { existsSync as existsSync6 } from "node:fs";
|
|
83482
83726
|
import os16 from "node:os";
|
|
83483
83727
|
import path17 from "node:path";
|
|
@@ -83552,7 +83796,7 @@ function detectCodexDesktop(probe = { exists: existsSync6, readUrlSchemes: realR
|
|
|
83552
83796
|
function realReadUrlSchemes(bundlePath) {
|
|
83553
83797
|
if (process.platform !== "darwin") return [];
|
|
83554
83798
|
try {
|
|
83555
|
-
const json2 =
|
|
83799
|
+
const json2 = execFileSync7(
|
|
83556
83800
|
"plutil",
|
|
83557
83801
|
["-extract", "CFBundleURLTypes", "json", "-o", "-", path17.join(bundlePath, "Contents", "Info.plist")],
|
|
83558
83802
|
{ stdio: ["ignore", "pipe", "ignore"] }
|
|
@@ -84009,8 +84253,38 @@ function createTerminalOutput(options) {
|
|
|
84009
84253
|
return { log: log3, activity };
|
|
84010
84254
|
}
|
|
84011
84255
|
|
|
84256
|
+
// src/update.ts
|
|
84257
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
84258
|
+
var npmLsSchema = external_exports.object({
|
|
84259
|
+
dependencies: external_exports.record(external_exports.string(), external_exports.object({ version: external_exports.string().min(1) })).optional()
|
|
84260
|
+
});
|
|
84261
|
+
function runCapturing(command, args) {
|
|
84262
|
+
const result = spawnSync4(command, args, {
|
|
84263
|
+
encoding: "utf8",
|
|
84264
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
84265
|
+
});
|
|
84266
|
+
return { stdout: result.stdout ?? "" };
|
|
84267
|
+
}
|
|
84268
|
+
function installedGlobalVersion(packageName, run = runCapturing) {
|
|
84269
|
+
const { stdout } = run("npm", ["ls", "-g", packageName, "--json"]);
|
|
84270
|
+
try {
|
|
84271
|
+
const parsed = npmLsSchema.safeParse(JSON.parse(stdout));
|
|
84272
|
+
return parsed.success ? parsed.data.dependencies?.[packageName]?.version ?? null : null;
|
|
84273
|
+
} catch {
|
|
84274
|
+
return null;
|
|
84275
|
+
}
|
|
84276
|
+
}
|
|
84277
|
+
var RESTART_NOTE = "Sync service will restart within minutes. (You can do this now with powerplant restart.)";
|
|
84278
|
+
function updatedMessage(version2) {
|
|
84279
|
+
if (version2 === null) {
|
|
84280
|
+
return `Updated, but could not read the installed version from npm. Check with: powerplant status
|
|
84281
|
+
${RESTART_NOTE}`;
|
|
84282
|
+
}
|
|
84283
|
+
return `Updated to v${version2}. ${RESTART_NOTE}`;
|
|
84284
|
+
}
|
|
84285
|
+
|
|
84012
84286
|
// src/version.ts
|
|
84013
|
-
var CLI_VERSION = true ? "0.3.
|
|
84287
|
+
var CLI_VERSION = true ? "0.3.2" : "0.0.0-checkout";
|
|
84014
84288
|
var CLI_PACKAGE_NAME = "@usepowerplant/cli";
|
|
84015
84289
|
|
|
84016
84290
|
// src/workspace.ts
|
|
@@ -84131,7 +84405,7 @@ Commands:
|
|
|
84131
84405
|
sync Run one sync pass in the foreground [--since <days>] [--dry-run].
|
|
84132
84406
|
folders The folders you explicitly sync on this machine:
|
|
84133
84407
|
powerplant folders [add <path> | remove <path>]. Additions on
|
|
84134
|
-
top of your org's
|
|
84408
|
+
top of your org's allowlisted repos \u2014 no re-enroll needed.
|
|
84135
84409
|
workspace Your workspaces, and which one Powerplant and your connected
|
|
84136
84410
|
agents use: powerplant workspace [<name>] switches to <name>.
|
|
84137
84411
|
resume-session Reopen a synced session in its own CLI, right here in
|
|
@@ -84443,7 +84717,7 @@ The access you granted these agents stays on your Powerplant account until you r
|
|
|
84443
84717
|
}
|
|
84444
84718
|
case "update": {
|
|
84445
84719
|
console.log(`Updating ${CLI_PACKAGE_NAME} via npm\u2026`);
|
|
84446
|
-
const npm =
|
|
84720
|
+
const npm = spawnSync5("npm", ["install", "-g", CLI_PACKAGE_NAME], { stdio: "inherit" });
|
|
84447
84721
|
if (npm.error) {
|
|
84448
84722
|
throw new ExpectedCliError(
|
|
84449
84723
|
`Could not run npm (${npm.error.message}) \u2014 update by hand: npm install -g ${CLI_PACKAGE_NAME}`
|
|
@@ -84452,9 +84726,7 @@ The access you granted these agents stays on your Powerplant account until you r
|
|
|
84452
84726
|
if (npm.status !== 0) {
|
|
84453
84727
|
throw new ExpectedCliError("npm install failed \u2014 see the output above.");
|
|
84454
84728
|
}
|
|
84455
|
-
console.log(
|
|
84456
|
-
"Updated. The background service restarts itself onto the new version within minutes (or now: powerplant restart)."
|
|
84457
|
-
);
|
|
84729
|
+
console.log(updatedMessage(installedGlobalVersion(CLI_PACKAGE_NAME)));
|
|
84458
84730
|
return;
|
|
84459
84731
|
}
|
|
84460
84732
|
case "restart": {
|