@usepowerplant/cli 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +687 -217
- 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
|
|
|
@@ -50833,6 +50833,17 @@ function capturedSessionEvents(turns, anchor) {
|
|
|
50833
50833
|
return events;
|
|
50834
50834
|
}
|
|
50835
50835
|
|
|
50836
|
+
// ../core/src/workspaceClaims.ts
|
|
50837
|
+
function workspaceClaimsRepo(repoFullName, policy) {
|
|
50838
|
+
const repo = repoFullName.toLowerCase();
|
|
50839
|
+
if (policy.repos !== null) return policy.repos.includes(repo);
|
|
50840
|
+
const owner = repo.split("/")[0] ?? "";
|
|
50841
|
+
return policy.owners.length > 0 && policy.owners.includes(owner);
|
|
50842
|
+
}
|
|
50843
|
+
function workspaceClaimsAnyRepo(policy) {
|
|
50844
|
+
return policy.repos !== null ? policy.repos.length > 0 : policy.owners.length > 0;
|
|
50845
|
+
}
|
|
50846
|
+
|
|
50836
50847
|
// ../core/src/transcriptImages.ts
|
|
50837
50848
|
var IMAGE_DATA_URI_RE = /^data:(image\/[\w.+-]+);base64,([A-Za-z0-9+/=_-]*)$/;
|
|
50838
50849
|
function isImageDataUri(value) {
|
|
@@ -51693,6 +51704,8 @@ var SESSION_SOURCES = [
|
|
|
51693
51704
|
"cursor-cloud",
|
|
51694
51705
|
"github-pr"
|
|
51695
51706
|
];
|
|
51707
|
+
var SESSION_SPEED_MODES = ["standard", "fast"];
|
|
51708
|
+
var SESSION_SPEED_OBSERVATIONS = [...SESSION_SPEED_MODES, "unknown"];
|
|
51696
51709
|
function isSessionSource(source) {
|
|
51697
51710
|
return typeof source === "string" && SESSION_SOURCES.includes(source);
|
|
51698
51711
|
}
|
|
@@ -78558,6 +78571,23 @@ var storedCredentialsSchema = external_exports.object({
|
|
|
78558
78571
|
* alongside the owners. Absent = the org has no list — the owner check
|
|
78559
78572
|
* decides (and pre-list enrollments read the same way). */
|
|
78560
78573
|
allowedRepos: external_exports.array(external_exports.string()).optional(),
|
|
78574
|
+
/** org-repos mode: per-workspace claim policies across every workspace
|
|
78575
|
+
* the member belongs to (home first), refreshed every sync — the union
|
|
78576
|
+
* an offline pass still filters with. Absent = cached before routing;
|
|
78577
|
+
* the flat fields above are the whole boundary. */
|
|
78578
|
+
workspacePolicies: external_exports.array(
|
|
78579
|
+
external_exports.object({
|
|
78580
|
+
orgId: external_exports.string().min(1),
|
|
78581
|
+
name: external_exports.string(),
|
|
78582
|
+
owners: external_exports.array(external_exports.string()),
|
|
78583
|
+
repos: external_exports.array(external_exports.string()).nullable(),
|
|
78584
|
+
/** This entry's claims were UNRESOLVED when cached (a linked
|
|
78585
|
+
* installation whose account failed to resolve, nothing usable to
|
|
78586
|
+
* stand in) — unknown, never "claims nothing". A pass reading only
|
|
78587
|
+
* the cache must not stamp a deep sweep done over it. */
|
|
78588
|
+
unresolved: external_exports.literal(true).optional()
|
|
78589
|
+
})
|
|
78590
|
+
).optional(),
|
|
78561
78591
|
/** Folders this member explicitly chose. Under scope mode they are the
|
|
78562
78592
|
* whole boundary (never empty — enrollment requires at least one); under
|
|
78563
78593
|
* org-repos they are additions on top of the org policy
|
|
@@ -78652,7 +78682,7 @@ import os6 from "node:os";
|
|
|
78652
78682
|
// ../../apps/collector/src/http.ts
|
|
78653
78683
|
var import_undici = __toESM(require_undici(), 1);
|
|
78654
78684
|
var CLI_VERSION_HEADER = "x-powerplant-cli-version";
|
|
78655
|
-
var cliVersion = true ? "0.
|
|
78685
|
+
var cliVersion = true ? "0.4.0" : "0.0.0-checkout";
|
|
78656
78686
|
function cliVersionHeaders() {
|
|
78657
78687
|
return { [CLI_VERSION_HEADER]: cliVersion, "user-agent": `powerplant-cli/${cliVersion}` };
|
|
78658
78688
|
}
|
|
@@ -78729,30 +78759,78 @@ import os5 from "node:os";
|
|
|
78729
78759
|
import path3 from "node:path";
|
|
78730
78760
|
|
|
78731
78761
|
// ../../apps/collector/src/gitOwner.ts
|
|
78762
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
78732
78763
|
import { readFileSync as readFileSync3, statSync } from "node:fs";
|
|
78733
78764
|
import os4 from "node:os";
|
|
78734
78765
|
import path2 from "node:path";
|
|
78735
|
-
|
|
78766
|
+
var GITHUB_HOSTS = /* @__PURE__ */ new Set(["github.com", "ssh.github.com"]);
|
|
78767
|
+
function parseRemoteUrl(remoteUrl) {
|
|
78736
78768
|
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
|
-
}
|
|
78769
|
+
const colon = trimmed.indexOf(":");
|
|
78770
|
+
if (!trimmed.includes("://") && colon > 0 && !trimmed.slice(0, colon).includes("/")) {
|
|
78771
|
+
const host = trimmed.slice(0, colon).split("@").at(-1) ?? "";
|
|
78772
|
+
return { host: host.toLowerCase(), path: trimmed.slice(colon + 1), overSsh: true };
|
|
78745
78773
|
}
|
|
78746
78774
|
try {
|
|
78747
78775
|
const url2 = new URL(trimmed);
|
|
78748
|
-
if (url2.hostname
|
|
78749
|
-
const
|
|
78750
|
-
|
|
78751
|
-
|
|
78776
|
+
if (!url2.hostname) return null;
|
|
78777
|
+
const scheme = url2.protocol.slice(0, -1).toLowerCase();
|
|
78778
|
+
return {
|
|
78779
|
+
host: url2.hostname.toLowerCase(),
|
|
78780
|
+
path: url2.pathname,
|
|
78781
|
+
overSsh: scheme === "ssh" || scheme === "git+ssh"
|
|
78782
|
+
};
|
|
78783
|
+
} catch {
|
|
78784
|
+
return null;
|
|
78785
|
+
}
|
|
78786
|
+
}
|
|
78787
|
+
function parseGithubRepo(remoteUrl, sshHostnameOf = sshEffectiveHostname) {
|
|
78788
|
+
const remote = parseRemoteUrl(remoteUrl);
|
|
78789
|
+
if (!remote) return null;
|
|
78790
|
+
const host = remote.overSsh && !GITHUB_HOSTS.has(remote.host) ? sshHostnameOf(remote.host)?.toLowerCase() : remote.host;
|
|
78791
|
+
if (!host || !GITHUB_HOSTS.has(host)) return null;
|
|
78792
|
+
const [owner, rawRepo, extra] = remote.path.split("/").filter(Boolean);
|
|
78793
|
+
if (!owner || !rawRepo || extra !== void 0) return null;
|
|
78794
|
+
return `${owner.toLowerCase()}/${rawRepo.replace(/\.git$/i, "")}`;
|
|
78795
|
+
}
|
|
78796
|
+
var SSH_PROBE_TIMEOUT_MS = 2e3;
|
|
78797
|
+
var SSH_PROBE_CACHE_TTL_MS = 5 * 6e4;
|
|
78798
|
+
var SSH_HOST_PATTERN = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
78799
|
+
var sshProbeCache = /* @__PURE__ */ new Map();
|
|
78800
|
+
function sshEffectiveHostname(host, options = {}) {
|
|
78801
|
+
if (!SSH_HOST_PATTERN.test(host)) return null;
|
|
78802
|
+
const key = `${options.configFile ?? ""}\0${host}`;
|
|
78803
|
+
const cached3 = sshProbeCache.get(key);
|
|
78804
|
+
const now = Date.now();
|
|
78805
|
+
if (cached3 && cached3.expiresAt > now) return cached3.hostname;
|
|
78806
|
+
const hostname4 = probeSshHostname(host, options);
|
|
78807
|
+
sshProbeCache.set(key, { hostname: hostname4, expiresAt: now + SSH_PROBE_CACHE_TTL_MS });
|
|
78808
|
+
return hostname4;
|
|
78809
|
+
}
|
|
78810
|
+
function probeSshHostname(host, options) {
|
|
78811
|
+
try {
|
|
78812
|
+
const dump = execFileSync2(
|
|
78813
|
+
"ssh",
|
|
78814
|
+
["-G", ...options.configFile ? ["-F", options.configFile] : [], host],
|
|
78815
|
+
{
|
|
78816
|
+
encoding: "utf8",
|
|
78817
|
+
timeout: options.timeoutMs ?? SSH_PROBE_TIMEOUT_MS,
|
|
78818
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
78819
|
+
windowsHide: true
|
|
78820
|
+
}
|
|
78821
|
+
);
|
|
78822
|
+
return hostnameFromSshConfigDump(dump);
|
|
78752
78823
|
} catch {
|
|
78753
78824
|
return null;
|
|
78754
78825
|
}
|
|
78755
78826
|
}
|
|
78827
|
+
function hostnameFromSshConfigDump(dump) {
|
|
78828
|
+
for (const line of dump.split("\n")) {
|
|
78829
|
+
const match = /^hostname\s+(\S+)$/i.exec(line.trim());
|
|
78830
|
+
if (match?.[1]) return match[1];
|
|
78831
|
+
}
|
|
78832
|
+
return null;
|
|
78833
|
+
}
|
|
78756
78834
|
function parseOriginUrl(configText) {
|
|
78757
78835
|
let inOrigin = false;
|
|
78758
78836
|
for (const raw of configText.split("\n")) {
|
|
@@ -78818,17 +78896,17 @@ function findGitConfigPath(cwd, environment = {}) {
|
|
|
78818
78896
|
const dirs = resolveGitDirs(cwd, environment);
|
|
78819
78897
|
return dirs ? path2.join(dirs.commonDir, "config") : null;
|
|
78820
78898
|
}
|
|
78821
|
-
function resolveGithubRepo(cwd, environment = {}) {
|
|
78899
|
+
function resolveGithubRepo(cwd, environment = {}, sshHostnameOf = sshEffectiveHostname) {
|
|
78822
78900
|
const configPath = findGitConfigPath(cwd, environment);
|
|
78823
78901
|
const configText = configPath ? readIfExists(configPath) : null;
|
|
78824
78902
|
const remoteUrl = configText ? parseOriginUrl(configText) : null;
|
|
78825
|
-
return remoteUrl ? parseGithubRepo(remoteUrl) : null;
|
|
78903
|
+
return remoteUrl ? parseGithubRepo(remoteUrl, sshHostnameOf) : null;
|
|
78826
78904
|
}
|
|
78827
|
-
function repoResolver(environment = {}) {
|
|
78905
|
+
function repoResolver(environment = {}, sshHostnameOf = sshEffectiveHostname) {
|
|
78828
78906
|
const cache = /* @__PURE__ */ new Map();
|
|
78829
78907
|
return (cwd) => {
|
|
78830
78908
|
const key = path2.resolve(cwd);
|
|
78831
|
-
if (!cache.has(key)) cache.set(key, resolveGithubRepo(key, environment));
|
|
78909
|
+
if (!cache.has(key)) cache.set(key, resolveGithubRepo(key, environment, sshHostnameOf));
|
|
78832
78910
|
return cache.get(key);
|
|
78833
78911
|
};
|
|
78834
78912
|
}
|
|
@@ -78860,17 +78938,19 @@ var captureEverythingUnresolved = {
|
|
|
78860
78938
|
function captureDecision(cwd, policy) {
|
|
78861
78939
|
switch (policy.mode) {
|
|
78862
78940
|
case "scope":
|
|
78863
|
-
|
|
78941
|
+
if (cwd === void 0) return { capture: false, reason: "no-cwd" };
|
|
78942
|
+
return isWithinScope(cwd, policy.scopePaths, policy.environment) ? memberChoice(cwd, policy) : { capture: false, reason: "outside-policy" };
|
|
78864
78943
|
case "org-repos": {
|
|
78865
|
-
if (cwd === void 0) return { capture: false };
|
|
78944
|
+
if (cwd === void 0) return { capture: false, reason: "no-cwd" };
|
|
78866
78945
|
if (policy.memberFolders.length > 0 && isWithinScope(cwd, policy.memberFolders, policy.environment)) {
|
|
78867
78946
|
return memberChoice(cwd, policy);
|
|
78868
78947
|
}
|
|
78869
78948
|
const repoFullName = policy.repoOf(cwd);
|
|
78870
|
-
if (repoFullName === null) return { capture: false };
|
|
78871
|
-
const
|
|
78872
|
-
|
|
78873
|
-
|
|
78949
|
+
if (repoFullName === null) return { capture: false, reason: "no-github-repo" };
|
|
78950
|
+
const claims = policy.workspaceClaims ?? [
|
|
78951
|
+
{ owners: policy.allowedOwners, repos: policy.allowedRepos }
|
|
78952
|
+
];
|
|
78953
|
+
return claims.some((workspace) => workspaceClaimsRepo(repoFullName, workspace)) ? { capture: true, via: "org-policy", repoFullName } : { capture: false, reason: "outside-policy" };
|
|
78874
78954
|
}
|
|
78875
78955
|
}
|
|
78876
78956
|
}
|
|
@@ -78882,9 +78962,6 @@ function memberChoice(cwd, policy) {
|
|
|
78882
78962
|
...repoFullName !== null && { repoFullName }
|
|
78883
78963
|
};
|
|
78884
78964
|
}
|
|
78885
|
-
function ownerOfRepo(repoFullName) {
|
|
78886
|
-
return repoFullName.split("/")[0] ?? "";
|
|
78887
|
-
}
|
|
78888
78965
|
function listSome(values, max = 6) {
|
|
78889
78966
|
return values.length <= max ? values.join(", ") : `${values.slice(0, max).join(", ")} and ${values.length - max} more`;
|
|
78890
78967
|
}
|
|
@@ -78893,8 +78970,11 @@ function describeCapturePolicy(policy) {
|
|
|
78893
78970
|
case "scope":
|
|
78894
78971
|
return `sessions under ${policy.scopePaths.join(", ")}`;
|
|
78895
78972
|
case "org-repos": {
|
|
78896
|
-
const repoPart = policy.allowedRepos !== null ? policy.allowedRepos.length > 0 ? `sessions in the org's
|
|
78897
|
-
|
|
78973
|
+
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";
|
|
78974
|
+
const claiming = (policy.workspaceClaims ?? []).filter(workspaceClaimsAnyRepo);
|
|
78975
|
+
const severalWorkspaces = (policy.workspaceClaims?.length ?? 0) > 1;
|
|
78976
|
+
const withOthers = severalWorkspaces && claiming.length > 0 ? `${repoPart}, routing each repo to the workspace that syncs it (${listSome(claiming.map((workspace) => workspace.name))})` : repoPart;
|
|
78977
|
+
return policy.memberFolders.length > 0 ? `${withOthers}, plus folders you added: ${policy.memberFolders.join(", ")}` : withOthers;
|
|
78898
78978
|
}
|
|
78899
78979
|
}
|
|
78900
78980
|
}
|
|
@@ -78910,14 +78990,24 @@ function describeStoredCapture(credentials) {
|
|
|
78910
78990
|
mode: "org-repos",
|
|
78911
78991
|
allowedOwners: credentials.allowedOwners ?? [],
|
|
78912
78992
|
allowedRepos: credentials.allowedRepos ?? null,
|
|
78993
|
+
...credentials.workspacePolicies !== void 0 && {
|
|
78994
|
+
workspaceClaims: credentials.workspacePolicies
|
|
78995
|
+
},
|
|
78913
78996
|
memberFolders: credentials.scopePaths ?? [],
|
|
78914
78997
|
repoOf: () => null
|
|
78915
78998
|
});
|
|
78916
78999
|
}
|
|
78917
|
-
function
|
|
79000
|
+
function countCaptureSkip(skips, reason) {
|
|
79001
|
+
if (reason === "outside-policy") skips.skippedOutOfScope += 1;
|
|
79002
|
+
else skips.skippedNoRepo += 1;
|
|
79003
|
+
}
|
|
79004
|
+
function describeSkipReason(policy) {
|
|
78918
79005
|
if (policy.mode !== "org-repos") return "outside the sync boundary, stayed local";
|
|
78919
|
-
|
|
78920
|
-
|
|
79006
|
+
return policy.allowedRepos !== null ? "outside the org's allowlisted repos, stayed local" : "not in org-owned repos, stayed local";
|
|
79007
|
+
}
|
|
79008
|
+
function describeNoRepoSkipReason(policy, platform2 = process.platform) {
|
|
79009
|
+
if (policy.mode !== "org-repos") return "with no working directory recorded, stayed local";
|
|
79010
|
+
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
79011
|
}
|
|
78922
79012
|
function normalizeScopePath(raw) {
|
|
78923
79013
|
const expanded = raw === "~" || raw.startsWith("~/") ? path3.join(os5.homedir(), raw.slice(1)) : raw;
|
|
@@ -78939,6 +79029,37 @@ function isPathWithinScopes(resolved, scopePaths) {
|
|
|
78939
79029
|
);
|
|
78940
79030
|
}
|
|
78941
79031
|
|
|
79032
|
+
// ../../apps/collector/src/syncWindow.ts
|
|
79033
|
+
var STEADY_SYNC_WINDOW_DAYS = 3;
|
|
79034
|
+
var DEEP_FIRST_SYNC_DAYS = 90;
|
|
79035
|
+
var DEEP_FIRST_SYNC_MAX_SESSIONS = 100;
|
|
79036
|
+
function resolveSyncWindow(explicitSince, deepSyncedAt) {
|
|
79037
|
+
if (explicitSince !== void 0) {
|
|
79038
|
+
const days = Number(explicitSince);
|
|
79039
|
+
if (!Number.isFinite(days) || days <= 0) {
|
|
79040
|
+
throw new ExpectedCliError(`--since must be a positive number of days, got "${explicitSince}"`);
|
|
79041
|
+
}
|
|
79042
|
+
return { days, deepSweep: false };
|
|
79043
|
+
}
|
|
79044
|
+
if (deepSyncedAt === void 0) return { days: DEEP_FIRST_SYNC_DAYS, deepSweep: true };
|
|
79045
|
+
return { days: STEADY_SYNC_WINDOW_DAYS, deepSweep: false };
|
|
79046
|
+
}
|
|
79047
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
79048
|
+
function resolveScanFloor(window2, nowMs, narrowSinceMs) {
|
|
79049
|
+
if (window2.deepSweep || narrowSinceMs === void 0) return nowMs - window2.days * DAY_MS;
|
|
79050
|
+
return narrowSinceMs;
|
|
79051
|
+
}
|
|
79052
|
+
function rawShipFloorMs(appEnv, nowMs, explicitSince) {
|
|
79053
|
+
if (appEnv !== "development" || explicitSince !== void 0) return null;
|
|
79054
|
+
return nowMs - STEADY_SYNC_WINDOW_DAYS * DAY_MS;
|
|
79055
|
+
}
|
|
79056
|
+
function describeScanReach(window2, narrowSinceMs) {
|
|
79057
|
+
if (window2.deepSweep || narrowSinceMs === void 0) {
|
|
79058
|
+
return `from the last ${window2.days} day(s)`;
|
|
79059
|
+
}
|
|
79060
|
+
return `changed since ${new Date(narrowSinceMs).toISOString()}`;
|
|
79061
|
+
}
|
|
79062
|
+
|
|
78942
79063
|
// ../../apps/collector/src/upload.ts
|
|
78943
79064
|
import { readFileSync as readFileSync4 } from "node:fs";
|
|
78944
79065
|
import { gzipSync } from "node:zlib";
|
|
@@ -78996,7 +79117,16 @@ async function fetchCapturePolicy(apiUrl, machineToken) {
|
|
|
78996
79117
|
data: external_exports.object({
|
|
78997
79118
|
owners: external_exports.array(external_exports.string()),
|
|
78998
79119
|
repos: external_exports.array(external_exports.string()).nullable(),
|
|
78999
|
-
integration: external_exports.enum(["none", "linked"])
|
|
79120
|
+
integration: external_exports.enum(["none", "linked"]),
|
|
79121
|
+
workspaces: external_exports.array(
|
|
79122
|
+
external_exports.object({
|
|
79123
|
+
orgId: external_exports.string().min(1),
|
|
79124
|
+
name: external_exports.string(),
|
|
79125
|
+
owners: external_exports.array(external_exports.string()),
|
|
79126
|
+
repos: external_exports.array(external_exports.string()).nullable(),
|
|
79127
|
+
integration: external_exports.enum(["none", "linked"])
|
|
79128
|
+
})
|
|
79129
|
+
).optional()
|
|
79000
79130
|
})
|
|
79001
79131
|
}),
|
|
79002
79132
|
await response.json().catch(() => null),
|
|
@@ -79008,6 +79138,7 @@ async function uploadSessions(apiUrl, machineToken, items) {
|
|
|
79008
79138
|
let upserted = 0;
|
|
79009
79139
|
let unchanged = 0;
|
|
79010
79140
|
let outOfPolicy = 0;
|
|
79141
|
+
let deferred = 0;
|
|
79011
79142
|
for (let offset = 0; offset < items.length; offset += MAX_INGEST_BATCH) {
|
|
79012
79143
|
const batch = items.slice(offset, offset + MAX_INGEST_BATCH);
|
|
79013
79144
|
const response = await fetchWithRetry("uploading session metadata", `${apiUrl}/api/ingest/sessions`, {
|
|
@@ -79026,7 +79157,8 @@ async function uploadSessions(apiUrl, machineToken, items) {
|
|
|
79026
79157
|
data: external_exports.object({
|
|
79027
79158
|
upserted: external_exports.number(),
|
|
79028
79159
|
unchanged: external_exports.number(),
|
|
79029
|
-
outOfPolicy: external_exports.number()
|
|
79160
|
+
outOfPolicy: external_exports.number(),
|
|
79161
|
+
deferred: external_exports.number().optional()
|
|
79030
79162
|
})
|
|
79031
79163
|
}),
|
|
79032
79164
|
await response.json().catch(() => null),
|
|
@@ -79035,8 +79167,9 @@ async function uploadSessions(apiUrl, machineToken, items) {
|
|
|
79035
79167
|
upserted += result.data.upserted;
|
|
79036
79168
|
unchanged += result.data.unchanged;
|
|
79037
79169
|
outOfPolicy += result.data.outOfPolicy;
|
|
79170
|
+
deferred += result.data.deferred ?? 0;
|
|
79038
79171
|
}
|
|
79039
|
-
return { upserted, unchanged, outOfPolicy };
|
|
79172
|
+
return { upserted, unchanged, outOfPolicy, deferred };
|
|
79040
79173
|
}
|
|
79041
79174
|
async function uploadBranchFacts(apiUrl, machineToken, facts) {
|
|
79042
79175
|
let recorded = 0;
|
|
@@ -79086,6 +79219,7 @@ async function uploadRawSessions(apiUrl, machineToken, collected, readTranscript
|
|
|
79086
79219
|
uploaded: 0,
|
|
79087
79220
|
skippedTooLarge: 0,
|
|
79088
79221
|
skippedUnchanged: 0,
|
|
79222
|
+
missingMetadata: 0,
|
|
79089
79223
|
failed: [],
|
|
79090
79224
|
unconfigured: false
|
|
79091
79225
|
};
|
|
@@ -79118,6 +79252,10 @@ async function uploadRawSessions(apiUrl, machineToken, collected, readTranscript
|
|
|
79118
79252
|
summary.unconfigured = true;
|
|
79119
79253
|
return summary;
|
|
79120
79254
|
}
|
|
79255
|
+
if (response.status === 404) {
|
|
79256
|
+
summary.missingMetadata += 1;
|
|
79257
|
+
continue;
|
|
79258
|
+
}
|
|
79121
79259
|
if (!response.ok) {
|
|
79122
79260
|
throw new Error(`${response.status}: ${await errorDetail(response)}`);
|
|
79123
79261
|
}
|
|
@@ -79201,7 +79339,8 @@ async function runEnrollment(config2, options) {
|
|
|
79201
79339
|
log: log3
|
|
79202
79340
|
});
|
|
79203
79341
|
let orgPolicy = captureMode === "org-repos" ? await fetchCapturePolicy(config2.apiUrl, result.token) : void 0;
|
|
79204
|
-
|
|
79342
|
+
const anyWorkspaceClaims = (orgPolicy?.workspaces ?? []).some(workspaceClaimsAnyRepo);
|
|
79343
|
+
if (orgPolicy !== void 0 && orgPolicy.owners.length === 0 && orgPolicy.repos === null && !anyWorkspaceClaims) {
|
|
79205
79344
|
if (config2.appEnv === "development") {
|
|
79206
79345
|
const home = normalizeScopePath(os6.homedir());
|
|
79207
79346
|
log3(
|
|
@@ -79233,10 +79372,20 @@ async function runEnrollment(config2, options) {
|
|
|
79233
79372
|
);
|
|
79234
79373
|
}
|
|
79235
79374
|
}
|
|
79375
|
+
const workspacePolicies = orgPolicy?.workspaces?.map((workspace) => ({
|
|
79376
|
+
orgId: workspace.orgId,
|
|
79377
|
+
name: workspace.name,
|
|
79378
|
+
owners: workspace.owners,
|
|
79379
|
+
repos: workspace.repos,
|
|
79380
|
+
// Cached unknownness survives enrollment: an unresolved workspace's
|
|
79381
|
+
// claims are unknown, and the first sweep must not stamp over them.
|
|
79382
|
+
...workspace.integration === "linked" && workspace.owners.length === 0 && workspace.repos === null && { unresolved: true }
|
|
79383
|
+
}));
|
|
79236
79384
|
const policy = captureMode === "org-repos" ? {
|
|
79237
79385
|
mode: "org-repos",
|
|
79238
79386
|
allowedOwners: orgPolicy?.owners ?? [],
|
|
79239
79387
|
allowedRepos: orgPolicy?.repos ?? null,
|
|
79388
|
+
...workspacePolicies !== void 0 && { workspaceClaims: workspacePolicies },
|
|
79240
79389
|
memberFolders: [],
|
|
79241
79390
|
repoOf: () => null
|
|
79242
79391
|
} : { mode: "scope", scopePaths, repoOf: () => null };
|
|
@@ -79251,12 +79400,16 @@ async function runEnrollment(config2, options) {
|
|
|
79251
79400
|
captureMode,
|
|
79252
79401
|
...orgPolicy && { allowedOwners: orgPolicy.owners },
|
|
79253
79402
|
...orgPolicy?.repos != null && { allowedRepos: orgPolicy.repos },
|
|
79403
|
+
...captureMode === "org-repos" && workspacePolicies !== void 0 && { workspacePolicies },
|
|
79254
79404
|
scopePaths
|
|
79255
79405
|
};
|
|
79256
79406
|
const location = saveCredentials(config2.appEnv, credentials);
|
|
79257
79407
|
log3(`Enrolled "${result.machine.name}" (${result.machine.id}) into org "${result.org.name}".`);
|
|
79258
79408
|
log3(`Machine token stored in ${location}.`);
|
|
79259
79409
|
log3(`Syncing: ${describeCapturePolicy(policy)}.`);
|
|
79410
|
+
log3(
|
|
79411
|
+
`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.`
|
|
79412
|
+
);
|
|
79260
79413
|
return credentials;
|
|
79261
79414
|
}
|
|
79262
79415
|
async function enroll(config2, options, createAuthClient = gotrueAuthClient) {
|
|
@@ -79453,7 +79606,7 @@ function addFolder(config2, rawPath, enrollCommand, log3, deps = defaultDeps) {
|
|
|
79453
79606
|
});
|
|
79454
79607
|
const org = credentials.orgName ?? "your org";
|
|
79455
79608
|
log3(
|
|
79456
|
-
credentials.captureMode === "org-repos" ? `Sessions under ${folder} will now upload to ${org} \u2014 even outside the org's
|
|
79609
|
+
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
79610
|
);
|
|
79458
79611
|
log3("Takes effect on the next sync pass; run the sync command to apply now.");
|
|
79459
79612
|
}
|
|
@@ -79570,6 +79723,10 @@ function readIfExists2(file2) {
|
|
|
79570
79723
|
throw error52;
|
|
79571
79724
|
}
|
|
79572
79725
|
}
|
|
79726
|
+
var MAX_TRANSCRIPT_FILE_BYTES = 256 * 1024 * 1024;
|
|
79727
|
+
function describeReadError(error52) {
|
|
79728
|
+
return error52 instanceof Error ? error52.message : String(error52);
|
|
79729
|
+
}
|
|
79573
79730
|
|
|
79574
79731
|
// ../../apps/collector/src/lifecycleJournal.ts
|
|
79575
79732
|
var LIFECYCLE_JOURNAL_FILENAME = "session-lifecycle.jsonl";
|
|
@@ -79611,6 +79768,7 @@ function parseClaudeCodeTranscript(text) {
|
|
|
79611
79768
|
let cwd;
|
|
79612
79769
|
let gitBranch;
|
|
79613
79770
|
let anonCounter = 0;
|
|
79771
|
+
let speedMode;
|
|
79614
79772
|
for (const record2 of parseJsonl(text)) {
|
|
79615
79773
|
if (record2.type === "custom-title" && typeof record2.customTitle === "string") {
|
|
79616
79774
|
title = record2.customTitle;
|
|
@@ -79629,25 +79787,42 @@ function parseClaudeCodeTranscript(text) {
|
|
|
79629
79787
|
const message = record2.message;
|
|
79630
79788
|
if (!message || message.model === "<synthetic>") continue;
|
|
79631
79789
|
const id = message.id ?? (typeof record2.requestId === "string" ? record2.requestId : void 0) ?? (typeof record2.uuid === "string" ? record2.uuid : void 0) ?? `anon-${anonCounter++}`;
|
|
79632
|
-
const
|
|
79633
|
-
|
|
79634
|
-
|
|
79790
|
+
const previousMessage = usageByMessage.get(id);
|
|
79791
|
+
const isNewMessage = previousMessage === void 0;
|
|
79792
|
+
const speedValue = message.usage?.speed;
|
|
79793
|
+
const observedSpeed = speedModeOf(speedValue);
|
|
79794
|
+
const messageSpeed = observedSpeed ?? previousMessage?.speedMode;
|
|
79795
|
+
if (speedValue !== void 0) speedMode = observedSpeed ?? "unknown";
|
|
79796
|
+
if (message.usage) {
|
|
79797
|
+
usageByMessage.set(id, {
|
|
79798
|
+
usage: toUsage(message.usage),
|
|
79799
|
+
...messageSpeed && { speedMode: messageSpeed }
|
|
79800
|
+
});
|
|
79801
|
+
} else if (isNewMessage) {
|
|
79802
|
+
usageByMessage.set(id, {
|
|
79803
|
+
usage: { input: 0, cacheRead: 0, cacheCreation: 0, output: 0 }
|
|
79804
|
+
});
|
|
79805
|
+
}
|
|
79635
79806
|
if (isNewMessage && typeof message.model === "string") {
|
|
79636
79807
|
modelCounts.set(message.model, (modelCounts.get(message.model) ?? 0) + 1);
|
|
79637
79808
|
}
|
|
79638
79809
|
}
|
|
79639
79810
|
if (usageByMessage.size === 0 && timestamps.length === 0) return null;
|
|
79640
|
-
const summed = [...usageByMessage.values()].reduce(addUsage, {
|
|
79811
|
+
const summed = [...usageByMessage.values()].map((entry) => entry.usage).reduce(addUsage, {
|
|
79641
79812
|
input: 0,
|
|
79642
79813
|
cacheRead: 0,
|
|
79643
79814
|
cacheCreation: 0,
|
|
79644
79815
|
output: 0
|
|
79645
79816
|
});
|
|
79817
|
+
const speedFullyMeasured = usageByMessage.size > 0 && [...usageByMessage.values()].every((entry) => entry.speedMode !== void 0);
|
|
79818
|
+
const fastUsage = speedFullyMeasured ? [...usageByMessage.values()].filter((entry) => entry.speedMode === "fast").map((entry) => entry.usage).reduce(addUsage, { input: 0, cacheRead: 0, cacheCreation: 0, output: 0 }) : null;
|
|
79646
79819
|
return {
|
|
79647
79820
|
startedAt: timestamps[0],
|
|
79648
79821
|
endedAt: timestamps[timestamps.length - 1],
|
|
79649
79822
|
model: dominantModel(modelCounts),
|
|
79650
79823
|
usage: usageByMessage.size > 0 ? summed : null,
|
|
79824
|
+
fastUsage,
|
|
79825
|
+
...speedMode && { speedMode },
|
|
79651
79826
|
title,
|
|
79652
79827
|
turnCount: usageByMessage.size,
|
|
79653
79828
|
cwd,
|
|
@@ -79658,9 +79833,13 @@ async function collectClaudeCodeSessions(options = {}) {
|
|
|
79658
79833
|
const claudeDir = options.claudeDir ?? path5.join(os7.homedir(), ".claude");
|
|
79659
79834
|
const now = options.now ?? Date.now();
|
|
79660
79835
|
const capture = options.capture ?? captureEverythingUnresolved;
|
|
79836
|
+
const maxBytes = options.maxTranscriptBytes ?? MAX_TRANSCRIPT_FILE_BYTES;
|
|
79661
79837
|
const projectsDir = path5.join(claudeDir, "projects");
|
|
79662
79838
|
const items = [];
|
|
79663
|
-
|
|
79839
|
+
const skips = { skippedOutOfScope: 0, skippedNoRepo: 0 };
|
|
79840
|
+
const candidates = [];
|
|
79841
|
+
const unreadable = [];
|
|
79842
|
+
let skippedTooLarge = 0;
|
|
79664
79843
|
const home = path5.dirname(claudeDir);
|
|
79665
79844
|
const lifecycle = options.lifecycle ?? readLifecycleJournal(home);
|
|
79666
79845
|
for (const projectDir of listDirs(projectsDir)) {
|
|
@@ -79670,39 +79849,76 @@ async function collectClaudeCodeSessions(options = {}) {
|
|
|
79670
79849
|
const stat = statIfExists(transcriptPath);
|
|
79671
79850
|
if (!stat) continue;
|
|
79672
79851
|
if (options.sinceMs !== void 0 && stat.mtimeMs < options.sinceMs) continue;
|
|
79852
|
+
if (stat.size > maxBytes) {
|
|
79853
|
+
skippedTooLarge += 1;
|
|
79854
|
+
continue;
|
|
79855
|
+
}
|
|
79673
79856
|
const sessionId = path5.basename(entry, ".jsonl");
|
|
79674
79857
|
const endObservedAt = observedSessionEnd(lifecycle.get(sessionId), stat.mtimeMs);
|
|
79675
79858
|
const status = endObservedAt === null && now - stat.mtimeMs < ACTIVE_GRACE_MS ? "active" : "inactive";
|
|
79676
|
-
|
|
79859
|
+
try {
|
|
79677
79860
|
const cwd = transcriptCwd(transcriptPath);
|
|
79678
|
-
const
|
|
79679
|
-
if (!
|
|
79680
|
-
|
|
79861
|
+
const decision = captureDecision(cwd, capture);
|
|
79862
|
+
if (!decision.capture) {
|
|
79863
|
+
countCaptureSkip(skips, decision.reason);
|
|
79681
79864
|
continue;
|
|
79682
79865
|
}
|
|
79683
|
-
|
|
79684
|
-
|
|
79866
|
+
if (options.locationsOnly) {
|
|
79867
|
+
items.push({
|
|
79868
|
+
mtimeMs: stat.mtimeMs,
|
|
79869
|
+
transcriptPath,
|
|
79870
|
+
...cwd && { cwd },
|
|
79871
|
+
item: { source: "claude-code", externalId: sessionId, status, capturedVia: decision.via }
|
|
79872
|
+
});
|
|
79873
|
+
continue;
|
|
79874
|
+
}
|
|
79875
|
+
candidates.push({
|
|
79685
79876
|
transcriptPath,
|
|
79877
|
+
projectDir,
|
|
79878
|
+
sessionId,
|
|
79879
|
+
mtimeMs: stat.mtimeMs,
|
|
79880
|
+
status,
|
|
79881
|
+
endObservedAt,
|
|
79686
79882
|
...cwd && { cwd },
|
|
79687
|
-
|
|
79883
|
+
decision
|
|
79688
79884
|
});
|
|
79689
|
-
|
|
79885
|
+
} catch (error52) {
|
|
79886
|
+
unreadable.push(`${sessionId}: ${describeReadError(error52)}`);
|
|
79690
79887
|
}
|
|
79888
|
+
}
|
|
79889
|
+
}
|
|
79890
|
+
if (options.locationsOnly) {
|
|
79891
|
+
return {
|
|
79892
|
+
sessions: items.sort((a, b2) => b2.mtimeMs - a.mtimeMs),
|
|
79893
|
+
...skips,
|
|
79894
|
+
skippedTooLarge,
|
|
79895
|
+
unreadable,
|
|
79896
|
+
heldBack: 0
|
|
79897
|
+
};
|
|
79898
|
+
}
|
|
79899
|
+
candidates.sort((a, b2) => b2.mtimeMs - a.mtimeMs);
|
|
79900
|
+
const chosen = options.maxSessions === void 0 ? candidates : candidates.slice(0, options.maxSessions);
|
|
79901
|
+
for (const candidate of chosen) {
|
|
79902
|
+
await yieldToEventLoop();
|
|
79903
|
+
const { transcriptPath, projectDir, sessionId, mtimeMs, status, endObservedAt, cwd, decision } = candidate;
|
|
79904
|
+
try {
|
|
79691
79905
|
const text = readIfExists2(transcriptPath);
|
|
79692
79906
|
if (text === null) continue;
|
|
79693
79907
|
const summary = parseClaudeCodeTranscript(text);
|
|
79694
79908
|
if (!summary) continue;
|
|
79695
|
-
const
|
|
79696
|
-
|
|
79697
|
-
|
|
79698
|
-
|
|
79699
|
-
|
|
79700
|
-
|
|
79909
|
+
const usage = addSubagentUsage(
|
|
79910
|
+
summary.usage,
|
|
79911
|
+
summary.fastUsage,
|
|
79912
|
+
path5.join(projectDir, sessionId, "subagents"),
|
|
79913
|
+
{ maxBytes, sessionId }
|
|
79914
|
+
);
|
|
79915
|
+
skippedTooLarge += usage.skippedTooLarge;
|
|
79916
|
+
unreadable.push(...usage.unreadable);
|
|
79701
79917
|
const endedAt = endObservedAt ?? summary.endedAt;
|
|
79702
79918
|
items.push({
|
|
79703
|
-
mtimeMs
|
|
79919
|
+
mtimeMs,
|
|
79704
79920
|
transcriptPath,
|
|
79705
|
-
...
|
|
79921
|
+
...cwd && { cwd },
|
|
79706
79922
|
item: {
|
|
79707
79923
|
source: "claude-code",
|
|
79708
79924
|
externalId: sessionId,
|
|
@@ -79710,28 +79926,42 @@ async function collectClaudeCodeSessions(options = {}) {
|
|
|
79710
79926
|
...summary.startedAt && { startedAt: summary.startedAt },
|
|
79711
79927
|
...endedAt && { endedAt },
|
|
79712
79928
|
...summary.model && { model: summary.model },
|
|
79929
|
+
...summary.speedMode && { speedMode: summary.speedMode },
|
|
79713
79930
|
// Total input-class tokens the source reported (uncached + cache
|
|
79714
79931
|
// reads + cache writes) — a real reported total, not an estimate.
|
|
79715
79932
|
// The cache breakdown rides along: pricing bills reads at 0.1×
|
|
79716
79933
|
// and writes at 1.25×, so collapsing it would inflate estimates.
|
|
79717
|
-
...usage && {
|
|
79718
|
-
|
|
79719
|
-
|
|
79720
|
-
...usage && {
|
|
79934
|
+
...usage.total && {
|
|
79935
|
+
inputTokens: usage.total.input + usage.total.cacheRead + usage.total.cacheCreation
|
|
79936
|
+
},
|
|
79937
|
+
...usage.total && { outputTokens: usage.total.output },
|
|
79938
|
+
...usage.total && { cacheReadTokens: usage.total.cacheRead },
|
|
79939
|
+
...usage.total && { cacheWriteTokens: usage.total.cacheCreation },
|
|
79940
|
+
...usage.fast && {
|
|
79941
|
+
fastInputTokens: usage.fast.input + usage.fast.cacheRead + usage.fast.cacheCreation,
|
|
79942
|
+
fastOutputTokens: usage.fast.output,
|
|
79943
|
+
fastCacheReadTokens: usage.fast.cacheRead,
|
|
79944
|
+
fastCacheWriteTokens: usage.fast.cacheCreation
|
|
79945
|
+
},
|
|
79721
79946
|
...summary.title && { title: redactText(summary.title).slice(0, 500) },
|
|
79722
79947
|
// The PR-join coordinates: the checkout's directory name (bare
|
|
79723
79948
|
// repo, not the local path) and the branch the work started on.
|
|
79724
|
-
...
|
|
79949
|
+
...cwd && { repoDir: path5.basename(cwd) },
|
|
79725
79950
|
...summary.gitBranch && { gitBranch: summary.gitBranch },
|
|
79726
79951
|
...decision.repoFullName && { repoFullName: decision.repoFullName },
|
|
79727
79952
|
capturedVia: decision.via
|
|
79728
79953
|
}
|
|
79729
79954
|
});
|
|
79955
|
+
} catch (error52) {
|
|
79956
|
+
unreadable.push(`${sessionId}: ${describeReadError(error52)}`);
|
|
79730
79957
|
}
|
|
79731
79958
|
}
|
|
79732
79959
|
return {
|
|
79733
79960
|
sessions: items.sort((a, b2) => b2.mtimeMs - a.mtimeMs),
|
|
79734
|
-
|
|
79961
|
+
...skips,
|
|
79962
|
+
skippedTooLarge,
|
|
79963
|
+
unreadable,
|
|
79964
|
+
heldBack: candidates.length - chosen.length
|
|
79735
79965
|
};
|
|
79736
79966
|
}
|
|
79737
79967
|
function transcriptCwd(transcriptPath) {
|
|
@@ -79750,6 +79980,9 @@ function toUsage(u) {
|
|
|
79750
79980
|
output: n(u.output_tokens)
|
|
79751
79981
|
};
|
|
79752
79982
|
}
|
|
79983
|
+
function speedModeOf(value) {
|
|
79984
|
+
return value === "fast" || value === "standard" ? value : void 0;
|
|
79985
|
+
}
|
|
79753
79986
|
function addUsage(a, b2) {
|
|
79754
79987
|
return {
|
|
79755
79988
|
input: a.input + b2.input,
|
|
@@ -79758,15 +79991,36 @@ function addUsage(a, b2) {
|
|
|
79758
79991
|
output: a.output + b2.output
|
|
79759
79992
|
};
|
|
79760
79993
|
}
|
|
79761
|
-
function addSubagentUsage(main2, subagentsDir) {
|
|
79762
|
-
let
|
|
79994
|
+
function addSubagentUsage(main2, mainFast, subagentsDir, bounds) {
|
|
79995
|
+
let total = main2;
|
|
79996
|
+
let fast = mainFast;
|
|
79997
|
+
let fastCoverageComplete = main2 === null || mainFast !== null;
|
|
79998
|
+
let skippedTooLarge = 0;
|
|
79999
|
+
const unreadable = [];
|
|
79763
80000
|
for (const entry of listFiles(subagentsDir, ".jsonl")) {
|
|
79764
|
-
const
|
|
79765
|
-
|
|
79766
|
-
|
|
79767
|
-
|
|
80001
|
+
const sidecarPath = path5.join(subagentsDir, entry);
|
|
80002
|
+
try {
|
|
80003
|
+
const stat = statIfExists(sidecarPath);
|
|
80004
|
+
if (stat === null) continue;
|
|
80005
|
+
if (stat.size > bounds.maxBytes) {
|
|
80006
|
+
skippedTooLarge += 1;
|
|
80007
|
+
continue;
|
|
80008
|
+
}
|
|
80009
|
+
const text = readIfExists2(sidecarPath);
|
|
80010
|
+
if (text === null) continue;
|
|
80011
|
+
const summary = parseClaudeCodeTranscript(text);
|
|
80012
|
+
if (summary?.usage) {
|
|
80013
|
+
total = addUsage(total ?? { input: 0, cacheRead: 0, cacheCreation: 0, output: 0 }, summary.usage);
|
|
80014
|
+
if (summary.fastUsage === null) fastCoverageComplete = false;
|
|
80015
|
+
}
|
|
80016
|
+
if (summary?.fastUsage) {
|
|
80017
|
+
fast = addUsage(fast ?? { input: 0, cacheRead: 0, cacheCreation: 0, output: 0 }, summary.fastUsage);
|
|
80018
|
+
}
|
|
80019
|
+
} catch (error52) {
|
|
80020
|
+
unreadable.push(`${bounds.sessionId} (subagent ${entry}): ${describeReadError(error52)}`);
|
|
80021
|
+
}
|
|
79768
80022
|
}
|
|
79769
|
-
return
|
|
80023
|
+
return { total, fast: fastCoverageComplete ? fast : null, skippedTooLarge, unreadable };
|
|
79770
80024
|
}
|
|
79771
80025
|
function dominantModel(counts) {
|
|
79772
80026
|
let best;
|
|
@@ -79785,6 +80039,10 @@ function parseCodexRollout(text) {
|
|
|
79785
80039
|
let meta3 = null;
|
|
79786
80040
|
let model;
|
|
79787
80041
|
let totals = null;
|
|
80042
|
+
let fastTotals = { input: 0, cached: 0, output: 0 };
|
|
80043
|
+
let speedMode;
|
|
80044
|
+
let fastCoverageComplete = true;
|
|
80045
|
+
let classifiedUsage = false;
|
|
79788
80046
|
const timestamps = [];
|
|
79789
80047
|
for (const line of parseJsonl(text)) {
|
|
79790
80048
|
if (typeof line.timestamp === "string") timestamps.push(line.timestamp);
|
|
@@ -79795,15 +80053,38 @@ function parseCodexRollout(text) {
|
|
|
79795
80053
|
meta3 ??= record2;
|
|
79796
80054
|
} else if (line.type === "turn_context" && typeof record2.model === "string") {
|
|
79797
80055
|
model = record2.model;
|
|
80056
|
+
} else if (line.type === "event_msg" && record2.type === "thread_settings_applied") {
|
|
80057
|
+
const settings = record2.thread_settings;
|
|
80058
|
+
if (settings && typeof settings === "object" && !Array.isArray(settings)) {
|
|
80059
|
+
const tier = settings.service_tier;
|
|
80060
|
+
if (tier !== void 0) speedMode = codexSpeedMode(tier) ?? "unknown";
|
|
80061
|
+
}
|
|
79798
80062
|
} else if (line.type === "event_msg" && record2.type === "token_count") {
|
|
79799
80063
|
const info = record2.info;
|
|
79800
80064
|
const usage = info?.total_token_usage;
|
|
79801
80065
|
if (usage) {
|
|
79802
80066
|
const n = (value) => typeof value === "number" ? value : 0;
|
|
79803
|
-
|
|
79804
|
-
|
|
79805
|
-
|
|
79806
|
-
|
|
80067
|
+
const next = {
|
|
80068
|
+
input: n(usage.input_tokens),
|
|
80069
|
+
cached: n(usage.cached_input_tokens),
|
|
80070
|
+
output: n(usage.output_tokens)
|
|
80071
|
+
};
|
|
80072
|
+
const previous = totals ?? { input: 0, cached: 0, output: 0 };
|
|
80073
|
+
const delta = {
|
|
80074
|
+
input: Math.max(0, next.input - previous.input),
|
|
80075
|
+
cached: Math.max(0, next.cached - previous.cached),
|
|
80076
|
+
output: Math.max(0, next.output - previous.output)
|
|
80077
|
+
};
|
|
80078
|
+
if (delta.input > 0 || delta.cached > 0 || delta.output > 0) {
|
|
80079
|
+
if (speedMode === "fast" || speedMode === "standard") classifiedUsage = true;
|
|
80080
|
+
else fastCoverageComplete = false;
|
|
80081
|
+
if (speedMode === "fast") fastTotals = addCodexTotals(fastTotals, delta);
|
|
80082
|
+
}
|
|
80083
|
+
totals = {
|
|
80084
|
+
input: Math.max(previous.input, next.input),
|
|
80085
|
+
cached: Math.max(previous.cached, next.cached),
|
|
80086
|
+
output: Math.max(previous.output, next.output)
|
|
80087
|
+
};
|
|
79807
80088
|
}
|
|
79808
80089
|
}
|
|
79809
80090
|
}
|
|
@@ -79817,6 +80098,8 @@ function parseCodexRollout(text) {
|
|
|
79817
80098
|
endedAt: timestamps[timestamps.length - 1],
|
|
79818
80099
|
...model && { model },
|
|
79819
80100
|
totals,
|
|
80101
|
+
fastTotals: fastCoverageComplete && classifiedUsage ? fastTotals : null,
|
|
80102
|
+
...speedMode && { speedMode },
|
|
79820
80103
|
...typeof meta3?.cwd === "string" && meta3.cwd !== "" && { cwd: meta3.cwd },
|
|
79821
80104
|
...typeof git?.branch === "string" && git.branch !== "" && { gitBranch: git.branch },
|
|
79822
80105
|
...repoNameFromUrl(git?.repository_url) ?? {},
|
|
@@ -79830,14 +80113,23 @@ function parseRolloutFile(rolloutPath) {
|
|
|
79830
80113
|
function rolloutLocation(rolloutPath) {
|
|
79831
80114
|
const record2 = readFirstRecord(rolloutPath, (candidate) => candidate.type === "session_meta");
|
|
79832
80115
|
const meta3 = record2?.payload;
|
|
79833
|
-
if (!meta3 || typeof meta3 !== "object" || Array.isArray(meta3)) return { kind: "session", totals: null };
|
|
80116
|
+
if (!meta3 || typeof meta3 !== "object" || Array.isArray(meta3)) return { kind: "session", totals: null, fastTotals: null };
|
|
79834
80117
|
const { thread_source: threadSource, cwd } = meta3;
|
|
79835
80118
|
return {
|
|
79836
80119
|
kind: threadSource === "subagent" ? "subagent" : "session",
|
|
79837
80120
|
totals: null,
|
|
80121
|
+
fastTotals: null,
|
|
79838
80122
|
...typeof cwd === "string" && cwd !== "" && { cwd }
|
|
79839
80123
|
};
|
|
79840
80124
|
}
|
|
80125
|
+
function codexSpeedMode(value) {
|
|
80126
|
+
if (value === "priority" || value === "fast") return "fast";
|
|
80127
|
+
if (value === "default") return "standard";
|
|
80128
|
+
return void 0;
|
|
80129
|
+
}
|
|
80130
|
+
function addCodexTotals(a, b2) {
|
|
80131
|
+
return { input: a.input + b2.input, cached: a.cached + b2.cached, output: a.output + b2.output };
|
|
80132
|
+
}
|
|
79841
80133
|
function repoFullNameFrom(url2) {
|
|
79842
80134
|
const fullName = parseGithubRepo(url2);
|
|
79843
80135
|
return fullName ? { repoFullName: fullName } : {};
|
|
@@ -79875,58 +80167,100 @@ async function collectCodexSessions(options = {}) {
|
|
|
79875
80167
|
const sessionsDir = options.codexDir ?? path6.join(os8.homedir(), ".codex", "sessions");
|
|
79876
80168
|
const now = options.now ?? Date.now();
|
|
79877
80169
|
const capture = options.capture ?? captureEverythingUnresolved;
|
|
79878
|
-
const
|
|
80170
|
+
const maxBytes = options.maxTranscriptBytes ?? MAX_TRANSCRIPT_FILE_BYTES;
|
|
79879
80171
|
const items = [];
|
|
79880
|
-
|
|
80172
|
+
const candidates = [];
|
|
80173
|
+
const unreadable = [];
|
|
80174
|
+
const skips = { skippedOutOfScope: 0, skippedNoRepo: 0 };
|
|
79881
80175
|
let skippedSubagentThreads = 0;
|
|
79882
|
-
|
|
80176
|
+
let skippedTooLarge = 0;
|
|
80177
|
+
const externalIdOf = (rolloutPath) => path6.basename(rolloutPath, ".jsonl");
|
|
80178
|
+
const admitFrom = async (rootDir, archived) => {
|
|
79883
80179
|
for (const rolloutPath of rolloutFiles(rootDir)) {
|
|
79884
80180
|
await yieldToEventLoop2();
|
|
79885
80181
|
const stat = statIfExists(rolloutPath);
|
|
79886
80182
|
if (!stat) continue;
|
|
79887
80183
|
if (options.sinceMs !== void 0 && Math.max(stat.mtimeMs, stat.ctimeMs) < options.sinceMs)
|
|
79888
80184
|
continue;
|
|
79889
|
-
|
|
79890
|
-
|
|
79891
|
-
if (summary.kind === "subagent") {
|
|
79892
|
-
skippedSubagentThreads += 1;
|
|
79893
|
-
continue;
|
|
79894
|
-
}
|
|
79895
|
-
const decision = captureDecision(summary.cwd, capture);
|
|
79896
|
-
if (!decision.capture) {
|
|
79897
|
-
skippedOutOfScope += 1;
|
|
80185
|
+
if (stat.size > maxBytes) {
|
|
80186
|
+
skippedTooLarge += 1;
|
|
79898
80187
|
continue;
|
|
79899
80188
|
}
|
|
79900
|
-
|
|
79901
|
-
|
|
80189
|
+
try {
|
|
80190
|
+
const location = rolloutLocation(rolloutPath);
|
|
80191
|
+
if (location.kind === "subagent") {
|
|
80192
|
+
skippedSubagentThreads += 1;
|
|
80193
|
+
continue;
|
|
80194
|
+
}
|
|
80195
|
+
const decision = captureDecision(location.cwd, capture);
|
|
80196
|
+
if (!decision.capture) {
|
|
80197
|
+
countCaptureSkip(skips, decision.reason);
|
|
80198
|
+
continue;
|
|
80199
|
+
}
|
|
80200
|
+
if (options.locationsOnly) {
|
|
80201
|
+
items.push({
|
|
80202
|
+
mtimeMs: stat.mtimeMs,
|
|
80203
|
+
transcriptPath: rolloutPath,
|
|
80204
|
+
...location.cwd && { cwd: location.cwd },
|
|
80205
|
+
item: {
|
|
80206
|
+
source: "codex",
|
|
80207
|
+
externalId: externalIdOf(rolloutPath),
|
|
80208
|
+
status: now - stat.mtimeMs < ACTIVE_GRACE_MS ? "active" : "inactive",
|
|
80209
|
+
archived,
|
|
80210
|
+
capturedVia: decision.via
|
|
80211
|
+
}
|
|
80212
|
+
});
|
|
80213
|
+
continue;
|
|
80214
|
+
}
|
|
80215
|
+
candidates.push({
|
|
80216
|
+
rolloutPath,
|
|
80217
|
+
archived,
|
|
79902
80218
|
mtimeMs: stat.mtimeMs,
|
|
79903
|
-
|
|
79904
|
-
|
|
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
|
-
}
|
|
80219
|
+
...location.cwd && { cwd: location.cwd },
|
|
80220
|
+
decision
|
|
79912
80221
|
});
|
|
79913
|
-
|
|
80222
|
+
} catch (error52) {
|
|
80223
|
+
unreadable.push(`${externalIdOf(rolloutPath)}: ${describeReadError(error52)}`);
|
|
79914
80224
|
}
|
|
79915
|
-
|
|
80225
|
+
}
|
|
80226
|
+
};
|
|
80227
|
+
await admitFrom(sessionsDir, false);
|
|
80228
|
+
await admitFrom(codexArchivedSessionsDir(sessionsDir), true);
|
|
80229
|
+
if (options.locationsOnly) {
|
|
80230
|
+
return {
|
|
80231
|
+
sessions: items.sort((a, b2) => b2.mtimeMs - a.mtimeMs),
|
|
80232
|
+
...skips,
|
|
80233
|
+
skippedSubagentThreads,
|
|
80234
|
+
skippedTooLarge,
|
|
80235
|
+
unreadable,
|
|
80236
|
+
heldBack: 0
|
|
80237
|
+
};
|
|
80238
|
+
}
|
|
80239
|
+
candidates.sort((a, b2) => b2.mtimeMs - a.mtimeMs);
|
|
80240
|
+
const chosen = options.maxSessions === void 0 ? candidates : candidates.slice(0, options.maxSessions);
|
|
80241
|
+
const threadNames = chosen.length > 0 ? loadThreadNames(sessionsDir) : /* @__PURE__ */ new Map();
|
|
80242
|
+
for (const candidate of chosen) {
|
|
80243
|
+
await yieldToEventLoop2();
|
|
80244
|
+
const { rolloutPath, archived, mtimeMs, cwd, decision } = candidate;
|
|
80245
|
+
try {
|
|
80246
|
+
const summary = parseRolloutFile(rolloutPath);
|
|
80247
|
+
if (!summary) continue;
|
|
80248
|
+
const repoDir = summary.repoName ?? (cwd ? path6.basename(cwd) : void 0);
|
|
79916
80249
|
const repoFullName = decision.repoFullName ?? summary.repoFullName;
|
|
79917
80250
|
const title = summary.sessionId ? threadNames.get(summary.sessionId) : void 0;
|
|
79918
80251
|
items.push({
|
|
79919
|
-
mtimeMs
|
|
80252
|
+
mtimeMs,
|
|
79920
80253
|
transcriptPath: rolloutPath,
|
|
79921
|
-
...
|
|
80254
|
+
...cwd && { cwd },
|
|
79922
80255
|
item: {
|
|
79923
80256
|
source: "codex",
|
|
79924
|
-
externalId:
|
|
79925
|
-
status: now -
|
|
80257
|
+
externalId: externalIdOf(rolloutPath),
|
|
80258
|
+
status: now - mtimeMs < ACTIVE_GRACE_MS ? "active" : "inactive",
|
|
79926
80259
|
archived,
|
|
79927
80260
|
...summary.startedAt && { startedAt: summary.startedAt },
|
|
79928
80261
|
...summary.endedAt && { endedAt: summary.endedAt },
|
|
79929
80262
|
...summary.model && { model: summary.model },
|
|
80263
|
+
...summary.speedMode && { speedMode: summary.speedMode },
|
|
79930
80264
|
...title && { title: redactText(title).slice(0, 500) },
|
|
79931
80265
|
// input_tokens already includes the cached subset — it IS the
|
|
79932
80266
|
// all-in input-class total. No cache-write figure exists, so
|
|
@@ -79934,20 +80268,29 @@ async function collectCodexSessions(options = {}) {
|
|
|
79934
80268
|
...summary.totals && { inputTokens: summary.totals.input },
|
|
79935
80269
|
...summary.totals && { outputTokens: summary.totals.output },
|
|
79936
80270
|
...summary.totals && { cacheReadTokens: summary.totals.cached },
|
|
80271
|
+
...summary.fastTotals && { fastInputTokens: summary.fastTotals.input },
|
|
80272
|
+
...summary.fastTotals && { fastOutputTokens: summary.fastTotals.output },
|
|
80273
|
+
...summary.fastTotals && { fastCacheReadTokens: summary.fastTotals.cached },
|
|
80274
|
+
// OpenAI prompt-cache writes have no separate price in the current
|
|
80275
|
+
// session cost model; zero is measured here, not an invented count.
|
|
80276
|
+
...summary.fastTotals && { fastCacheWriteTokens: 0 },
|
|
79937
80277
|
...repoDir && { repoDir },
|
|
79938
80278
|
...summary.gitBranch && { gitBranch: summary.gitBranch },
|
|
79939
80279
|
...repoFullName && { repoFullName },
|
|
79940
80280
|
capturedVia: decision.via
|
|
79941
80281
|
}
|
|
79942
80282
|
});
|
|
80283
|
+
} catch (error52) {
|
|
80284
|
+
unreadable.push(`${externalIdOf(rolloutPath)}: ${describeReadError(error52)}`);
|
|
79943
80285
|
}
|
|
79944
|
-
}
|
|
79945
|
-
await collectFrom(sessionsDir, false);
|
|
79946
|
-
await collectFrom(codexArchivedSessionsDir(sessionsDir), true);
|
|
80286
|
+
}
|
|
79947
80287
|
return {
|
|
79948
80288
|
sessions: items.sort((a, b2) => b2.mtimeMs - a.mtimeMs),
|
|
79949
|
-
|
|
79950
|
-
skippedSubagentThreads
|
|
80289
|
+
...skips,
|
|
80290
|
+
skippedSubagentThreads,
|
|
80291
|
+
skippedTooLarge,
|
|
80292
|
+
unreadable,
|
|
80293
|
+
heldBack: candidates.length - chosen.length
|
|
79951
80294
|
};
|
|
79952
80295
|
}
|
|
79953
80296
|
|
|
@@ -79975,9 +80318,12 @@ function cursorSpoolDir(home) {
|
|
|
79975
80318
|
var emptyResult = () => ({
|
|
79976
80319
|
sessions: [],
|
|
79977
80320
|
skippedOutOfScope: 0,
|
|
80321
|
+
skippedNoRepo: 0,
|
|
79978
80322
|
skippedSubagentComposers: 0,
|
|
79979
80323
|
skippedNonLocalComposers: 0,
|
|
79980
80324
|
skippedUnreadable: 0,
|
|
80325
|
+
skippedTooLarge: 0,
|
|
80326
|
+
truncatedTranscripts: 0,
|
|
79981
80327
|
unavailable: null
|
|
79982
80328
|
});
|
|
79983
80329
|
var SQLITE_EXPERIMENTAL_WARNING = "SQLite is an experimental feature and might change at any time";
|
|
@@ -80005,9 +80351,6 @@ async function openCursorDatabase(dbPath) {
|
|
|
80005
80351
|
const sqlite = await loadSqlite();
|
|
80006
80352
|
return new sqlite.DatabaseSync(dbPath, { readOnly: true });
|
|
80007
80353
|
}
|
|
80008
|
-
function describeError(error52) {
|
|
80009
|
-
return error52 instanceof Error ? error52.message : String(error52);
|
|
80010
|
-
}
|
|
80011
80354
|
function isRecord3(value) {
|
|
80012
80355
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
80013
80356
|
}
|
|
@@ -80082,6 +80425,8 @@ function toolFilePathOf(args) {
|
|
|
80082
80425
|
}
|
|
80083
80426
|
return void 0;
|
|
80084
80427
|
}
|
|
80428
|
+
var SPOOL_META_RESERVE_BYTES = 64 * 1024;
|
|
80429
|
+
var lineBytes = (line) => Buffer.byteLength(JSON.stringify(line)) + 1;
|
|
80085
80430
|
function bubbleLines(bubble) {
|
|
80086
80431
|
const lines = [];
|
|
80087
80432
|
const createdAt = typeof bubble.createdAt === "string" ? { createdAt: bubble.createdAt } : {};
|
|
@@ -80121,43 +80466,50 @@ function bubbleLines(bubble) {
|
|
|
80121
80466
|
}
|
|
80122
80467
|
return lines;
|
|
80123
80468
|
}
|
|
80124
|
-
function extractTranscript(db, composerId) {
|
|
80469
|
+
function extractTranscript(db, composerId, maxBytes) {
|
|
80125
80470
|
const valueByKey = db.prepare("select value from cursorDiskKV where key = ?");
|
|
80126
80471
|
const dataRow = valueByKey.get(`composerData:${composerId}`);
|
|
80127
80472
|
const dataText = isRecord3(dataRow) && typeof dataRow.value === "string" ? dataRow.value : null;
|
|
80128
80473
|
const data = dataText === null ? null : safeJsonRecord2(dataText);
|
|
80129
80474
|
const spine = Array.isArray(data?.fullConversationHeadersOnly) ? data.fullConversationHeadersOnly : [];
|
|
80130
80475
|
if (spine.length === 0) return null;
|
|
80476
|
+
const budget = maxBytes - SPOOL_META_RESERVE_BYTES;
|
|
80131
80477
|
const lines = [];
|
|
80132
80478
|
const modelCounts = /* @__PURE__ */ new Map();
|
|
80479
|
+
let bytes = 0;
|
|
80133
80480
|
let bubbleCount = 0;
|
|
80134
|
-
|
|
80481
|
+
let bubblesOmitted = 0;
|
|
80482
|
+
for (const [index, entry] of spine.entries()) {
|
|
80135
80483
|
if (!isRecord3(entry) || typeof entry.bubbleId !== "string") continue;
|
|
80136
80484
|
const row = valueByKey.get(`bubbleId:${composerId}:${entry.bubbleId}`);
|
|
80137
80485
|
const text = isRecord3(row) && typeof row.value === "string" ? row.value : null;
|
|
80138
80486
|
const bubble = text === null ? null : safeJsonRecord2(text);
|
|
80139
80487
|
if (bubble === null) continue;
|
|
80488
|
+
const bubbleLinesOut = bubbleLines(bubble);
|
|
80489
|
+
const bubbleBytes = bubbleLinesOut.reduce((sum2, line) => sum2 + lineBytes(line), 0);
|
|
80490
|
+
if (bytes + bubbleBytes > budget) {
|
|
80491
|
+
bubblesOmitted = spine.length - index;
|
|
80492
|
+
break;
|
|
80493
|
+
}
|
|
80494
|
+
bytes += bubbleBytes;
|
|
80140
80495
|
bubbleCount += 1;
|
|
80141
|
-
for (const line of
|
|
80496
|
+
for (const line of bubbleLinesOut) {
|
|
80142
80497
|
lines.push(line);
|
|
80143
80498
|
if (line.type === "agent" && line.model !== void 0) {
|
|
80144
80499
|
modelCounts.set(line.model, (modelCounts.get(line.model) ?? 0) + 1);
|
|
80145
80500
|
}
|
|
80146
80501
|
}
|
|
80147
80502
|
}
|
|
80148
|
-
if (bubbleCount === 0) return null;
|
|
80503
|
+
if (bubbleCount === 0 && bubblesOmitted === 0) return null;
|
|
80149
80504
|
let model;
|
|
80150
80505
|
let best = 0;
|
|
80151
80506
|
for (const [name, count] of modelCounts) {
|
|
80152
80507
|
if (count > best) [model, best] = [name, count];
|
|
80153
80508
|
}
|
|
80154
|
-
return { lines, ...model && { model }, bubbleCount };
|
|
80509
|
+
return { lines, ...model && { model }, bubbleCount, bubblesOmitted };
|
|
80155
80510
|
}
|
|
80156
80511
|
function readSpoolModel(spoolPath) {
|
|
80157
|
-
const
|
|
80158
|
-
if (text === null) return void 0;
|
|
80159
|
-
const newline = text.indexOf("\n");
|
|
80160
|
-
const record2 = safeJsonRecord2(newline === -1 ? text : text.slice(0, newline));
|
|
80512
|
+
const record2 = readFirstRecord(spoolPath, () => true);
|
|
80161
80513
|
if (record2 === null || record2.type !== "meta" || record2.format !== "cursor-local-transcript") {
|
|
80162
80514
|
return void 0;
|
|
80163
80515
|
}
|
|
@@ -80170,20 +80522,20 @@ async function collectCursorSessions(options = {}) {
|
|
|
80170
80522
|
try {
|
|
80171
80523
|
if (statIfExists(dbPath) === null) return result;
|
|
80172
80524
|
} catch (error52) {
|
|
80173
|
-
result.unavailable = `Cursor's session store isn't readable (${
|
|
80525
|
+
result.unavailable = `Cursor's session store isn't readable (${describeReadError(error52)})`;
|
|
80174
80526
|
return result;
|
|
80175
80527
|
}
|
|
80176
80528
|
let db;
|
|
80177
80529
|
try {
|
|
80178
80530
|
db = await openCursorDatabase(dbPath);
|
|
80179
80531
|
} catch (error52) {
|
|
80180
|
-
result.unavailable = `Cursor's session store couldn't be opened (${
|
|
80532
|
+
result.unavailable = `Cursor's session store couldn't be opened (${describeReadError(error52)})`;
|
|
80181
80533
|
return result;
|
|
80182
80534
|
}
|
|
80183
80535
|
try {
|
|
80184
80536
|
collectFromStore(db, userDir, options, result);
|
|
80185
80537
|
} catch (error52) {
|
|
80186
|
-
result.unavailable = `reading Cursor's session store failed (${
|
|
80538
|
+
result.unavailable = `reading Cursor's session store failed (${describeReadError(error52)})`;
|
|
80187
80539
|
} finally {
|
|
80188
80540
|
try {
|
|
80189
80541
|
db.close();
|
|
@@ -80197,6 +80549,7 @@ function collectFromStore(db, userDir, options, result) {
|
|
|
80197
80549
|
const now = options.now ?? Date.now();
|
|
80198
80550
|
const capture = options.capture ?? captureEverythingUnresolved;
|
|
80199
80551
|
const spoolDir = options.spoolDir ?? cursorSpoolDir(os9.homedir());
|
|
80552
|
+
const maxBytes = options.maxTranscriptBytes ?? MAX_TRANSCRIPT_FILE_BYTES;
|
|
80200
80553
|
let spoolDirReady = false;
|
|
80201
80554
|
const headerRows = db.prepare(
|
|
80202
80555
|
"select composerId, workspaceId, createdAt, lastUpdatedAt, isArchived, isSubagent, value from composerHeaders where lastUpdatedAt >= ? order by lastUpdatedAt desc"
|
|
@@ -80225,7 +80578,7 @@ function collectFromStore(db, userDir, options, result) {
|
|
|
80225
80578
|
const cwd = facts.workspacePath ?? workspaceFolderOf(userDir, workspaceId);
|
|
80226
80579
|
const decision = captureDecision(cwd, capture);
|
|
80227
80580
|
if (!decision.capture) {
|
|
80228
|
-
result.
|
|
80581
|
+
countCaptureSkip(result, decision.reason);
|
|
80229
80582
|
continue;
|
|
80230
80583
|
}
|
|
80231
80584
|
if (options.locationsOnly) {
|
|
@@ -80246,10 +80599,15 @@ function collectFromStore(db, userDir, options, result) {
|
|
|
80246
80599
|
const spoolStat = statIfExists(spoolPath);
|
|
80247
80600
|
let model;
|
|
80248
80601
|
if (spoolStat !== null && Math.abs(spoolStat.mtimeMs - lastUpdatedAt) < 1) {
|
|
80602
|
+
if (spoolStat.size > maxBytes) {
|
|
80603
|
+
result.skippedTooLarge += 1;
|
|
80604
|
+
continue;
|
|
80605
|
+
}
|
|
80249
80606
|
model = readSpoolModel(spoolPath);
|
|
80250
80607
|
} else {
|
|
80251
|
-
const transcript = extractTranscript(db, raw.composerId);
|
|
80608
|
+
const transcript = extractTranscript(db, raw.composerId, maxBytes);
|
|
80252
80609
|
if (transcript === null) continue;
|
|
80610
|
+
if (transcript.bubblesOmitted > 0) result.truncatedTranscripts += 1;
|
|
80253
80611
|
model = transcript.model;
|
|
80254
80612
|
const meta3 = {
|
|
80255
80613
|
type: "meta",
|
|
@@ -80261,7 +80619,8 @@ function collectFromStore(db, userDir, options, result) {
|
|
|
80261
80619
|
...model && { model },
|
|
80262
80620
|
...typeof raw.createdAt === "number" && { createdAt: raw.createdAt },
|
|
80263
80621
|
lastUpdatedAt,
|
|
80264
|
-
...cwd && { workspacePath: cwd }
|
|
80622
|
+
...cwd && { workspacePath: cwd },
|
|
80623
|
+
...transcript.bubblesOmitted > 0 && { bubblesOmitted: transcript.bubblesOmitted }
|
|
80265
80624
|
};
|
|
80266
80625
|
if (!spoolDirReady) {
|
|
80267
80626
|
mkdirSync2(spoolDir, { recursive: true });
|
|
@@ -80306,36 +80665,6 @@ function collectFromStore(db, userDir, options, result) {
|
|
|
80306
80665
|
}
|
|
80307
80666
|
}
|
|
80308
80667
|
|
|
80309
|
-
// ../../apps/collector/src/syncWindow.ts
|
|
80310
|
-
var STEADY_SYNC_WINDOW_DAYS = 3;
|
|
80311
|
-
var DEEP_FIRST_SYNC_DAYS = 90;
|
|
80312
|
-
function resolveSyncWindow(explicitSince, deepSyncedAt) {
|
|
80313
|
-
if (explicitSince !== void 0) {
|
|
80314
|
-
const days = Number(explicitSince);
|
|
80315
|
-
if (!Number.isFinite(days) || days <= 0) {
|
|
80316
|
-
throw new ExpectedCliError(`--since must be a positive number of days, got "${explicitSince}"`);
|
|
80317
|
-
}
|
|
80318
|
-
return { days, deepSweep: false };
|
|
80319
|
-
}
|
|
80320
|
-
if (deepSyncedAt === void 0) return { days: DEEP_FIRST_SYNC_DAYS, deepSweep: true };
|
|
80321
|
-
return { days: STEADY_SYNC_WINDOW_DAYS, deepSweep: false };
|
|
80322
|
-
}
|
|
80323
|
-
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
80324
|
-
function resolveScanFloor(window2, nowMs, narrowSinceMs) {
|
|
80325
|
-
if (window2.deepSweep || narrowSinceMs === void 0) return nowMs - window2.days * DAY_MS;
|
|
80326
|
-
return narrowSinceMs;
|
|
80327
|
-
}
|
|
80328
|
-
function rawShipFloorMs(appEnv, nowMs, explicitSince) {
|
|
80329
|
-
if (appEnv !== "development" || explicitSince !== void 0) return null;
|
|
80330
|
-
return nowMs - STEADY_SYNC_WINDOW_DAYS * DAY_MS;
|
|
80331
|
-
}
|
|
80332
|
-
function describeScanReach(window2, narrowSinceMs) {
|
|
80333
|
-
if (window2.deepSweep || narrowSinceMs === void 0) {
|
|
80334
|
-
return `from the last ${window2.days} day(s)`;
|
|
80335
|
-
}
|
|
80336
|
-
return `changed since ${new Date(narrowSinceMs).toISOString()}`;
|
|
80337
|
-
}
|
|
80338
|
-
|
|
80339
80668
|
// ../../apps/collector/src/protectedRepoDiscovery.ts
|
|
80340
80669
|
var defaultDiscoveryDeps = {
|
|
80341
80670
|
collectClaude: collectClaudeCodeSessions,
|
|
@@ -80512,7 +80841,7 @@ function installCollectorHooks(homeDir = os11.homedir()) {
|
|
|
80512
80841
|
}
|
|
80513
80842
|
|
|
80514
80843
|
// ../../apps/collector/src/service.ts
|
|
80515
|
-
import { execFileSync as
|
|
80844
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
80516
80845
|
import { chmodSync as chmodSync2, existsSync as existsSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync7, rmSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
80517
80846
|
import os12 from "node:os";
|
|
80518
80847
|
import path10 from "node:path";
|
|
@@ -80660,7 +80989,7 @@ function restartService(config2) {
|
|
|
80660
80989
|
}
|
|
80661
80990
|
function runLaunchctl(args) {
|
|
80662
80991
|
try {
|
|
80663
|
-
|
|
80992
|
+
execFileSync3("launchctl", args, { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
|
|
80664
80993
|
} catch (cause) {
|
|
80665
80994
|
throw new Error(launchctlFailureMessage(args[0] ?? "", cause), { cause });
|
|
80666
80995
|
}
|
|
@@ -80675,7 +81004,7 @@ function launchctlFailureMessage(subcommand, cause) {
|
|
|
80675
81004
|
}
|
|
80676
81005
|
function isLoaded(appEnv) {
|
|
80677
81006
|
try {
|
|
80678
|
-
|
|
81007
|
+
execFileSync3("launchctl", ["print", `gui/${uid()}/${label(appEnv)}`], { stdio: "ignore" });
|
|
80679
81008
|
return true;
|
|
80680
81009
|
} catch {
|
|
80681
81010
|
return false;
|
|
@@ -80683,7 +81012,7 @@ function isLoaded(appEnv) {
|
|
|
80683
81012
|
}
|
|
80684
81013
|
function bootout(appEnv) {
|
|
80685
81014
|
try {
|
|
80686
|
-
|
|
81015
|
+
execFileSync3("launchctl", ["bootout", `gui/${uid()}/${label(appEnv)}`], { stdio: "ignore" });
|
|
80687
81016
|
return true;
|
|
80688
81017
|
} catch {
|
|
80689
81018
|
return false;
|
|
@@ -80984,12 +81313,33 @@ async function resolveCapturePolicy(config2, credentials, log3, deps = defaultPo
|
|
|
80984
81313
|
}
|
|
80985
81314
|
let owners = credentials.allowedOwners ?? [];
|
|
80986
81315
|
let repos = credentials.allowedRepos ?? null;
|
|
81316
|
+
let workspacePolicies = credentials.workspacePolicies;
|
|
80987
81317
|
let ownersUnresolved = false;
|
|
81318
|
+
let claimsUnresolved = false;
|
|
80988
81319
|
try {
|
|
80989
81320
|
const fresh = await deps.fetchPolicy(credentials.apiUrl, credentials.token);
|
|
80990
81321
|
ownersUnresolved = fresh.integration === "linked" && fresh.owners.length === 0;
|
|
80991
81322
|
const nextOwners = ownersUnresolved ? owners : fresh.owners;
|
|
80992
|
-
const
|
|
81323
|
+
const nextWorkspaces = fresh.workspaces?.map((workspace) => {
|
|
81324
|
+
const unresolved = workspace.integration === "linked" && workspace.owners.length === 0 && workspace.repos === null;
|
|
81325
|
+
const cached3 = workspacePolicies?.find((entry) => entry.orgId === workspace.orgId) ?? (credentials.orgId !== void 0 && workspace.orgId === credentials.orgId ? { orgId: workspace.orgId, name: workspace.name, owners, repos } : void 0);
|
|
81326
|
+
const usableCache = cached3 !== void 0 && workspaceClaimsAnyRepo(cached3);
|
|
81327
|
+
const effective = unresolved && usableCache ? cached3 : unresolved ? {
|
|
81328
|
+
orgId: workspace.orgId,
|
|
81329
|
+
name: workspace.name,
|
|
81330
|
+
owners: workspace.owners,
|
|
81331
|
+
repos: workspace.repos,
|
|
81332
|
+
unresolved: true
|
|
81333
|
+
} : {
|
|
81334
|
+
orgId: workspace.orgId,
|
|
81335
|
+
name: workspace.name,
|
|
81336
|
+
owners: workspace.owners,
|
|
81337
|
+
repos: workspace.repos
|
|
81338
|
+
};
|
|
81339
|
+
if (effective.unresolved) claimsUnresolved = true;
|
|
81340
|
+
return effective;
|
|
81341
|
+
});
|
|
81342
|
+
const changed = nextOwners.join(",") !== owners.join(",") || (fresh.repos?.join(",") ?? null) !== (repos?.join(",") ?? null) || JSON.stringify(nextWorkspaces ?? null) !== JSON.stringify(workspacePolicies ?? null);
|
|
80993
81343
|
if (changed) {
|
|
80994
81344
|
const next = {
|
|
80995
81345
|
...credentials,
|
|
@@ -80997,34 +81347,48 @@ async function resolveCapturePolicy(config2, credentials, log3, deps = defaultPo
|
|
|
80997
81347
|
};
|
|
80998
81348
|
if (fresh.repos !== null) next.allowedRepos = fresh.repos;
|
|
80999
81349
|
else delete next.allowedRepos;
|
|
81350
|
+
if (nextWorkspaces !== void 0) next.workspacePolicies = nextWorkspaces;
|
|
81351
|
+
else delete next.workspacePolicies;
|
|
81000
81352
|
deps.persist(config2.appEnv, next);
|
|
81001
81353
|
}
|
|
81002
81354
|
owners = nextOwners;
|
|
81003
81355
|
repos = fresh.repos;
|
|
81356
|
+
workspacePolicies = nextWorkspaces;
|
|
81004
81357
|
} catch (cause) {
|
|
81358
|
+
const cachedCoversSomething = owners.length > 0 || repos !== null || (workspacePolicies ?? []).some((entry) => workspaceClaimsAnyRepo(entry));
|
|
81359
|
+
if (!cachedCoversSomething || (workspacePolicies ?? []).some((entry) => entry.unresolved === true)) {
|
|
81360
|
+
claimsUnresolved = true;
|
|
81361
|
+
}
|
|
81005
81362
|
log3(
|
|
81006
|
-
`\u26A0 Couldn't refresh the org's repo sync policy (${describeSyncError(cause)}) \u2014 ` + (
|
|
81363
|
+
`\u26A0 Couldn't refresh the org's repo sync policy (${describeSyncError(cause)}) \u2014 ` + (cachedCoversSomething ? "using the cached policy." : "nothing will upload this pass.")
|
|
81007
81364
|
);
|
|
81008
81365
|
}
|
|
81009
81366
|
const memberFolders = credentials.scopePaths ?? [];
|
|
81367
|
+
if (ownersUnresolved && owners.length === 0 && repos === null) claimsUnresolved = true;
|
|
81010
81368
|
if (ownersUnresolved) {
|
|
81011
81369
|
log3(
|
|
81012
81370
|
"\u26A0 The org's GitHub is connected, but its account couldn't be resolved just now \u2014 " + (owners.length > 0 ? `using the cached owner list: ${owners.join(", ")}.` : "no owner-based uploads this pass. This is usually temporary; syncing resumes when it resolves.")
|
|
81013
81371
|
);
|
|
81014
81372
|
}
|
|
81373
|
+
const homeOrgId = credentials.orgId ?? workspacePolicies?.[0]?.orgId;
|
|
81374
|
+
const foreignClaims = (workspacePolicies ?? []).filter(
|
|
81375
|
+
(entry) => entry.orgId !== homeOrgId && workspaceClaimsAnyRepo(entry)
|
|
81376
|
+
);
|
|
81015
81377
|
if (repos !== null && repos.length === 0) {
|
|
81016
81378
|
log3(
|
|
81017
|
-
"\u26A0 The org's
|
|
81379
|
+
"\u26A0 The org's repo allowlist is empty \u2014 no repo-based uploads for it" + (foreignClaims.length > 0 ? `; repos synced by ${foreignClaims.map((entry) => entry.name).join(", ")} still upload.` : memberFolders.length > 0 ? "; only folders you added upload." : ".")
|
|
81018
81380
|
);
|
|
81019
81381
|
} else if (repos === null && owners.length === 0 && !ownersUnresolved) {
|
|
81020
81382
|
log3(
|
|
81021
|
-
"\u26A0 Syncing is gated on org-owned GitHub repos, but the org has no GitHub integration \u2014 nothing uploads. Connect GitHub in Settings, or add folders with `powerplant folders add`."
|
|
81383
|
+
foreignClaims.length > 0 ? `Your home workspace has no GitHub integration \u2014 syncing repos for ${foreignClaims.map((entry) => entry.name).join(", ")}.` : "\u26A0 Syncing is gated on org-owned GitHub repos, but the org has no GitHub integration \u2014 nothing uploads. Connect GitHub in Settings, or add folders with `powerplant folders add`."
|
|
81022
81384
|
);
|
|
81023
81385
|
}
|
|
81024
81386
|
return {
|
|
81025
81387
|
mode: "org-repos",
|
|
81026
81388
|
allowedOwners: owners,
|
|
81027
81389
|
allowedRepos: repos,
|
|
81390
|
+
...workspacePolicies !== void 0 && { workspaceClaims: workspacePolicies },
|
|
81391
|
+
...claimsUnresolved && { claimsUnresolved },
|
|
81028
81392
|
memberFolders,
|
|
81029
81393
|
repoOf,
|
|
81030
81394
|
environment
|
|
@@ -81041,26 +81405,33 @@ async function runSyncPass(config2, options = {}) {
|
|
|
81041
81405
|
const window2 = resolveSyncWindow(options.explicitSince, credentials.deepSyncedAt);
|
|
81042
81406
|
if (window2.deepSweep) {
|
|
81043
81407
|
log3(
|
|
81044
|
-
`First sync for this enrollment \u2014 sweeping the last ${window2.days} days to
|
|
81408
|
+
`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).`
|
|
81045
81409
|
);
|
|
81046
81410
|
}
|
|
81047
81411
|
const capture = await resolveCapturePolicy(config2, credentials, log3);
|
|
81048
81412
|
const sinceMs = resolveScanFloor(window2, Date.now(), options.narrowSinceMs);
|
|
81049
|
-
const
|
|
81050
|
-
const
|
|
81413
|
+
const maxSessions = window2.deepSweep ? DEEP_FIRST_SYNC_MAX_SESSIONS : void 0;
|
|
81414
|
+
const claude = await collectClaudeCodeSessions({ sinceMs, capture, maxSessions });
|
|
81415
|
+
const codex = await collectCodexSessions({ sinceMs, capture, maxSessions });
|
|
81051
81416
|
const cursor = await collectCursorSessions({ sinceMs, capture });
|
|
81052
81417
|
if (cursor.unavailable !== null) {
|
|
81053
81418
|
log3(`\u26A0 Cursor sessions skipped this pass \u2014 ${cursor.unavailable}. Other syncing is unaffected.`);
|
|
81054
81419
|
}
|
|
81055
|
-
const
|
|
81420
|
+
const inWindow = [...claude.sessions, ...codex.sessions, ...cursor.sessions].sort(
|
|
81056
81421
|
(a, b2) => b2.mtimeMs - a.mtimeMs
|
|
81057
81422
|
);
|
|
81423
|
+
const collected = maxSessions === void 0 ? inWindow : inWindow.slice(0, maxSessions);
|
|
81424
|
+
const heldBack = inWindow.length - collected.length + claude.heldBack + codex.heldBack;
|
|
81058
81425
|
const skippedOutOfScope = claude.skippedOutOfScope + codex.skippedOutOfScope + cursor.skippedOutOfScope;
|
|
81426
|
+
const skippedNoRepo = claude.skippedNoRepo + codex.skippedNoRepo + cursor.skippedNoRepo;
|
|
81059
81427
|
const items = collected.map((c) => c.item);
|
|
81060
81428
|
const active = items.filter((s) => s.status === "active").length;
|
|
81429
|
+
const tooLarge = (count, what) => count > 0 ? ` \xB7 ${count} ${what} skipped (over ${MAX_TRANSCRIPT_FILE_BYTES / (1024 * 1024)}MB \u2014 too large to read)` : "";
|
|
81061
81430
|
log3(
|
|
81062
|
-
`Found ${
|
|
81431
|
+
`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` : "") + tooLarge(claude.skippedTooLarge, "claude transcript(s)") + (claude.unreadable.length > 0 ? ` \xB7 ${claude.unreadable.length} claude transcript(s) unreadable, skipped` : "") + tooLarge(codex.skippedTooLarge, "codex rollout(s)") + (codex.unreadable.length > 0 ? ` \xB7 ${codex.unreadable.length} codex rollout(s) unreadable, skipped` : "") + tooLarge(cursor.skippedTooLarge, "cursor transcript(s)") + (cursor.truncatedTranscripts > 0 ? ` \xB7 ${cursor.truncatedTranscripts} cursor transcript(s) cut at ${MAX_TRANSCRIPT_FILE_BYTES / (1024 * 1024)}MB (the rest of the conversation stays local)` : "") + (heldBack > 0 ? ` \xB7 ${heldBack} older session(s) held back \u2014 the first sync backfills the ${DEEP_FIRST_SYNC_MAX_SESSIONS} most recently active` : "") + "."
|
|
81063
81432
|
);
|
|
81433
|
+
for (const failure of claude.unreadable) log3(` claude transcript skipped \u2014 ${failure}`);
|
|
81434
|
+
for (const failure of codex.unreadable) log3(` codex rollout skipped \u2014 ${failure}`);
|
|
81064
81435
|
if (options.dryRun) {
|
|
81065
81436
|
for (const s of items.slice(0, 10)) {
|
|
81066
81437
|
log3(` ${s.externalId} ${s.model ?? "model unmeasured"} ${s.title ?? "(untitled)"}`);
|
|
@@ -81085,11 +81456,18 @@ async function runSyncPass(config2, options = {}) {
|
|
|
81085
81456
|
saveCredentials(config2.appEnv, { ...current, deepSyncedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
81086
81457
|
log3(`Deep sync complete \u2014 future syncs cover the last ${STEADY_SYNC_WINDOW_DAYS} days.`);
|
|
81087
81458
|
};
|
|
81459
|
+
const claimsUnresolved = capture.mode === "org-repos" && capture.claimsUnresolved === true;
|
|
81088
81460
|
if (items.length === 0) {
|
|
81089
|
-
|
|
81461
|
+
if (claimsUnresolved) {
|
|
81462
|
+
if (window2.deepSweep) {
|
|
81463
|
+
log3("Deep sync not stamped \u2014 a workspace's claims are unresolved; the sweep re-runs next pass.");
|
|
81464
|
+
}
|
|
81465
|
+
} else {
|
|
81466
|
+
markDeepSynced();
|
|
81467
|
+
}
|
|
81090
81468
|
return { sessions: collected, upserted: 0 };
|
|
81091
81469
|
}
|
|
81092
|
-
const { upserted, unchanged, outOfPolicy } = await uploadSessions(
|
|
81470
|
+
const { upserted, unchanged, outOfPolicy, deferred } = await uploadSessions(
|
|
81093
81471
|
credentials.apiUrl,
|
|
81094
81472
|
credentials.token,
|
|
81095
81473
|
items
|
|
@@ -81097,6 +81475,11 @@ async function runSyncPass(config2, options = {}) {
|
|
|
81097
81475
|
log3(
|
|
81098
81476
|
`Uploaded: ${upserted} session(s) upserted` + (unchanged > 0 ? `, ${unchanged} unchanged (no writes issued)` : "") + "."
|
|
81099
81477
|
);
|
|
81478
|
+
if (deferred > 0) {
|
|
81479
|
+
log3(
|
|
81480
|
+
`\u26A0 ${deferred} session(s) deferred \u2014 a workspace's GitHub owners couldn't be resolved just now, so routing waited rather than guessing a workspace. They upload on a later pass.`
|
|
81481
|
+
);
|
|
81482
|
+
}
|
|
81100
81483
|
if (outOfPolicy > 0) {
|
|
81101
81484
|
log3(
|
|
81102
81485
|
`\u26A0 ${outOfPolicy} session(s) refused by the org's repo sync policy \u2014 they stayed out of Powerplant. The next pass refreshes the policy and stops offering them.`
|
|
@@ -81152,11 +81535,24 @@ async function runSyncPass(config2, options = {}) {
|
|
|
81152
81535
|
if (raw.failed.length > 0) parts.push(`${raw.failed.length} failed`);
|
|
81153
81536
|
log3(`Raw content: ${parts.join(", ")}.`);
|
|
81154
81537
|
for (const failure of raw.failed) log3(` raw upload failed \u2014 ${failure}`);
|
|
81155
|
-
|
|
81538
|
+
if (raw.missingMetadata > 0) {
|
|
81539
|
+
log3(
|
|
81540
|
+
`${raw.missingMetadata} raw transcript(s) waiting on their session metadata \u2014 they re-send once it lands.`
|
|
81541
|
+
);
|
|
81542
|
+
}
|
|
81543
|
+
const nothingElseAccountedFor = raw.skippedUnchanged === 0 && raw.skippedTooLarge === 0 && rawWindowSkipped === 0 && raw.missingMetadata === 0;
|
|
81156
81544
|
if (raw.uploaded === 0 && (raw.failed.length > 1 || raw.failed.length === 1 && nothingElseAccountedFor)) {
|
|
81157
81545
|
throw new Error(`all ${raw.failed.length} raw upload(s) failed this pass \u2014 ${raw.failed[0]}`);
|
|
81158
81546
|
}
|
|
81159
|
-
|
|
81547
|
+
if (deferred > 0 || claimsUnresolved) {
|
|
81548
|
+
if (window2.deepSweep) {
|
|
81549
|
+
log3(
|
|
81550
|
+
deferred > 0 ? "Deep sync not stamped \u2014 deferred sessions still owe their upload; the sweep re-runs next pass." : "Deep sync not stamped \u2014 a workspace's claims are unresolved; the sweep re-runs next pass."
|
|
81551
|
+
);
|
|
81552
|
+
}
|
|
81553
|
+
} else {
|
|
81554
|
+
markDeepSynced();
|
|
81555
|
+
}
|
|
81160
81556
|
}
|
|
81161
81557
|
return { sessions: collected, upserted };
|
|
81162
81558
|
}
|
|
@@ -81198,7 +81594,7 @@ import os14 from "node:os";
|
|
|
81198
81594
|
import path14 from "node:path";
|
|
81199
81595
|
|
|
81200
81596
|
// ../../apps/collector/src/wake.ts
|
|
81201
|
-
import { execFile as execFile2, execFileSync as
|
|
81597
|
+
import { execFile as execFile2, execFileSync as execFileSync4 } from "node:child_process";
|
|
81202
81598
|
import { existsSync as existsSync3 } from "node:fs";
|
|
81203
81599
|
import path13 from "node:path";
|
|
81204
81600
|
var WAKE_POLL_MS = 5e3;
|
|
@@ -81210,14 +81606,11 @@ function findClaudeTranscript(homeDir, externalId) {
|
|
|
81210
81606
|
const transcript = path13.join(projectDir, `${externalId}.jsonl`);
|
|
81211
81607
|
const stat = statIfExists(transcript);
|
|
81212
81608
|
if (stat === null) continue;
|
|
81213
|
-
const
|
|
81214
|
-
|
|
81215
|
-
|
|
81216
|
-
|
|
81217
|
-
|
|
81218
|
-
}
|
|
81219
|
-
}
|
|
81220
|
-
return null;
|
|
81609
|
+
const record2 = readFirstRecord(
|
|
81610
|
+
transcript,
|
|
81611
|
+
(candidate) => typeof candidate.cwd === "string" && candidate.cwd !== ""
|
|
81612
|
+
);
|
|
81613
|
+
return typeof record2?.cwd === "string" ? { cwd: record2.cwd, mtimeMs: stat.mtimeMs } : null;
|
|
81221
81614
|
}
|
|
81222
81615
|
return null;
|
|
81223
81616
|
}
|
|
@@ -81242,7 +81635,7 @@ function createWakePoller(deps) {
|
|
|
81242
81635
|
try {
|
|
81243
81636
|
for (const target of await deps.listPending()) {
|
|
81244
81637
|
if (inFlight.has(target.externalId)) continue;
|
|
81245
|
-
const home = deps
|
|
81638
|
+
const home = inspectTarget(deps, target);
|
|
81246
81639
|
if (home === null) continue;
|
|
81247
81640
|
if (deps.now() - home.mtimeMs < WAKE_QUIET_MS) continue;
|
|
81248
81641
|
const messages = await deps.claim(target);
|
|
@@ -81252,13 +81645,13 @@ function createWakePoller(deps) {
|
|
|
81252
81645
|
`waking session ${target.externalId} with ${messages.length} message(s) in ${home.cwd}`
|
|
81253
81646
|
);
|
|
81254
81647
|
void deps.runClaude(target, formatWakePrompt(messages), home.cwd).then(() => deps.log(`wake finished for session ${target.externalId}`)).catch(
|
|
81255
|
-
(error52) => reportFailedWake(deps, target, messages,
|
|
81648
|
+
(error52) => reportFailedWake(deps, target, messages, describeError(error52))
|
|
81256
81649
|
).finally(() => inFlight.delete(target.externalId));
|
|
81257
81650
|
}
|
|
81258
81651
|
consecutivePollFailures = 0;
|
|
81259
81652
|
} catch (error52) {
|
|
81260
81653
|
consecutivePollFailures += 1;
|
|
81261
|
-
deps.log(`\u26A0 wake poll failed (${
|
|
81654
|
+
deps.log(`\u26A0 wake poll failed (${describeError(error52)}) \u2014 next poll retries`);
|
|
81262
81655
|
if (consecutivePollFailures === REPORT_AFTER_CONSECUTIVE_FAILURES) {
|
|
81263
81656
|
deps.reportError?.(error52, "wake-poll", { consecutivePollFailures });
|
|
81264
81657
|
}
|
|
@@ -81268,6 +81661,16 @@ function createWakePoller(deps) {
|
|
|
81268
81661
|
}
|
|
81269
81662
|
};
|
|
81270
81663
|
}
|
|
81664
|
+
function inspectTarget(deps, target) {
|
|
81665
|
+
try {
|
|
81666
|
+
return deps.inspect(target);
|
|
81667
|
+
} catch (error52) {
|
|
81668
|
+
deps.log(
|
|
81669
|
+
`\u26A0 wake skipped for session ${target.externalId} \u2014 its transcript could not be read (${describeError(error52)}); next poll retries`
|
|
81670
|
+
);
|
|
81671
|
+
return null;
|
|
81672
|
+
}
|
|
81673
|
+
}
|
|
81271
81674
|
async function reportFailedWake(deps, target, messages, reason) {
|
|
81272
81675
|
deps.log(
|
|
81273
81676
|
`\u26A0 wake failed for session ${target.externalId} AFTER its messages were claimed (${reason})`
|
|
@@ -81280,7 +81683,7 @@ async function reportFailedWake(deps, target, messages, reason) {
|
|
|
81280
81683
|
);
|
|
81281
81684
|
} catch (reportError) {
|
|
81282
81685
|
deps.log(
|
|
81283
|
-
`\u26A0 wake failure report for session ${target.externalId} ALSO failed \u2014 its messages read Delivered but no agent ran (${
|
|
81686
|
+
`\u26A0 wake failure report for session ${target.externalId} ALSO failed \u2014 its messages read Delivered but no agent ran (${describeError(reportError)})`
|
|
81284
81687
|
);
|
|
81285
81688
|
deps.reportError?.(reportError, "wake-failure-report", {
|
|
81286
81689
|
externalId: target.externalId,
|
|
@@ -81300,7 +81703,7 @@ function resolveClaudeBin(homeDir, env = process.env) {
|
|
|
81300
81703
|
const found = candidates.find((candidate) => existsSync3(candidate));
|
|
81301
81704
|
if (found) return found;
|
|
81302
81705
|
try {
|
|
81303
|
-
const fromPath =
|
|
81706
|
+
const fromPath = execFileSync4("which", ["claude"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
81304
81707
|
return fromPath === "" ? null : fromPath;
|
|
81305
81708
|
} catch {
|
|
81306
81709
|
return null;
|
|
@@ -81322,7 +81725,7 @@ function versionAtLeast(version2, floor) {
|
|
|
81322
81725
|
return true;
|
|
81323
81726
|
}
|
|
81324
81727
|
function execClaudeVersion(claudeBin) {
|
|
81325
|
-
return
|
|
81728
|
+
return execFileSync4(claudeBin, ["--version"], {
|
|
81326
81729
|
stdio: ["ignore", "pipe", "ignore"],
|
|
81327
81730
|
timeout: VERSION_TIMEOUT_MS
|
|
81328
81731
|
}).toString();
|
|
@@ -81332,7 +81735,7 @@ function preflightClaudeVersion(claudeBin, readVersionOutput = execClaudeVersion
|
|
|
81332
81735
|
try {
|
|
81333
81736
|
output = readVersionOutput(claudeBin);
|
|
81334
81737
|
} catch (error52) {
|
|
81335
|
-
return { ok: false, problem: `\`${claudeBin} --version\` failed (${
|
|
81738
|
+
return { ok: false, problem: `\`${claudeBin} --version\` failed (${describeError(error52)})` };
|
|
81336
81739
|
}
|
|
81337
81740
|
const version2 = /\d+\.\d+\.\d+/.exec(output)?.[0] ?? null;
|
|
81338
81741
|
if (version2 === null) {
|
|
@@ -81477,7 +81880,7 @@ function startWakeService(config2, homeDir, log3) {
|
|
|
81477
81880
|
log3(`wake-on-message armed: polling every ${WAKE_POLL_MS / 1e3}s (${config2.appEnv})`);
|
|
81478
81881
|
return () => clearInterval(timer);
|
|
81479
81882
|
}
|
|
81480
|
-
function
|
|
81883
|
+
function describeError(error52) {
|
|
81481
81884
|
return error52 instanceof Error ? error52.message : String(error52);
|
|
81482
81885
|
}
|
|
81483
81886
|
|
|
@@ -81517,6 +81920,8 @@ var SAFETY_NET_INTERVAL_MS = 60 * 60 * 1e3;
|
|
|
81517
81920
|
var NARROW_OVERLAP_MS = 6e4;
|
|
81518
81921
|
var WATCHER_RETRY_MS = 6e4;
|
|
81519
81922
|
var FAILED_PASS_RETRY_MS = 6e4;
|
|
81923
|
+
var FAILED_PASS_BACKOFF_CAP_MS = 15 * 6e4;
|
|
81924
|
+
var STREAK_REREPORT_INTERVAL_MS = 60 * 6e4;
|
|
81520
81925
|
var VERSION_NOTICE_INTERVAL_MS = 60 * 6e4;
|
|
81521
81926
|
var COMPLETION_SLACK_MS = 3e4;
|
|
81522
81927
|
function createWatchLoop(deps) {
|
|
@@ -81529,6 +81934,10 @@ function createWatchLoop(deps) {
|
|
|
81529
81934
|
let completionTimer = null;
|
|
81530
81935
|
let running = false;
|
|
81531
81936
|
let consecutiveFailures = 0;
|
|
81937
|
+
let streakSignature = null;
|
|
81938
|
+
let streakCount = 0;
|
|
81939
|
+
let streakStartedAt = 0;
|
|
81940
|
+
let streakReportedAt = 0;
|
|
81532
81941
|
let pausedLogged = false;
|
|
81533
81942
|
let queued = null;
|
|
81534
81943
|
let retryNotBefore = 0;
|
|
@@ -81555,7 +81964,7 @@ function createWatchLoop(deps) {
|
|
|
81555
81964
|
if (stopped) return;
|
|
81556
81965
|
try {
|
|
81557
81966
|
closeWatcher = deps.watch(onEvent, (error52) => {
|
|
81558
|
-
deps.log(`watcher lost (${
|
|
81967
|
+
deps.log(`watcher lost (${describeError2(error52)}) \u2014 re-arming and re-scanning`);
|
|
81559
81968
|
closeWatcher?.();
|
|
81560
81969
|
closeWatcher = null;
|
|
81561
81970
|
arm();
|
|
@@ -81563,7 +81972,7 @@ function createWatchLoop(deps) {
|
|
|
81563
81972
|
});
|
|
81564
81973
|
} catch (error52) {
|
|
81565
81974
|
deps.log(
|
|
81566
|
-
`cannot watch yet (${
|
|
81975
|
+
`cannot watch yet (${describeError2(error52)}) \u2014 retrying in ${WATCHER_RETRY_MS / 1e3}s`
|
|
81567
81976
|
);
|
|
81568
81977
|
watcherRetry = setTimeout(arm, WATCHER_RETRY_MS);
|
|
81569
81978
|
}
|
|
@@ -81620,6 +82029,8 @@ function createWatchLoop(deps) {
|
|
|
81620
82029
|
}
|
|
81621
82030
|
}
|
|
81622
82031
|
consecutiveFailures = 0;
|
|
82032
|
+
streakSignature = null;
|
|
82033
|
+
streakCount = 0;
|
|
81623
82034
|
deps.onPassResult?.({
|
|
81624
82035
|
kind,
|
|
81625
82036
|
at: startedAt,
|
|
@@ -81629,17 +82040,33 @@ function createWatchLoop(deps) {
|
|
|
81629
82040
|
});
|
|
81630
82041
|
} catch (error52) {
|
|
81631
82042
|
consecutiveFailures += 1;
|
|
81632
|
-
|
|
82043
|
+
const signature = errorSignature(error52);
|
|
82044
|
+
if (signature === streakSignature) {
|
|
82045
|
+
streakCount += 1;
|
|
82046
|
+
} else {
|
|
82047
|
+
streakSignature = signature;
|
|
82048
|
+
streakCount = 1;
|
|
82049
|
+
streakStartedAt = startedAt;
|
|
82050
|
+
streakReportedAt = 0;
|
|
82051
|
+
}
|
|
82052
|
+
const holdMs = Math.max(
|
|
82053
|
+
Math.min(FAILED_PASS_RETRY_MS * 2 ** (streakCount - 1), FAILED_PASS_BACKOFF_CAP_MS),
|
|
82054
|
+
retryAfterMs(error52) ?? 0
|
|
82055
|
+
);
|
|
82056
|
+
retryNotBefore = now() + holdMs;
|
|
81633
82057
|
lifecycleChanged ||= consultLifecycle;
|
|
81634
82058
|
deps.onPassResult?.({
|
|
81635
82059
|
kind,
|
|
81636
82060
|
at: startedAt,
|
|
81637
82061
|
ok: false,
|
|
81638
|
-
error:
|
|
82062
|
+
error: describeError2(error52),
|
|
81639
82063
|
sessions: 0,
|
|
81640
82064
|
upserted: 0
|
|
81641
82065
|
});
|
|
81642
|
-
|
|
82066
|
+
const failLine = `${kind} pass failed (${describeError2(error52)})`;
|
|
82067
|
+
deps.log(
|
|
82068
|
+
streakCount > 1 ? `${failLine}: same failure ${streakCount} times in a row, next attempt in ${describeDelay(holdMs)}` : `${failLine}, next attempt in ${describeDelay(holdMs)} re-covers this window`
|
|
82069
|
+
);
|
|
81643
82070
|
if (error52 instanceof Error && !(error52 instanceof ExpectedCliError) && error52.stack) {
|
|
81644
82071
|
deps.log(error52.stack);
|
|
81645
82072
|
}
|
|
@@ -81650,10 +82077,13 @@ function createWatchLoop(deps) {
|
|
|
81650
82077
|
);
|
|
81651
82078
|
return;
|
|
81652
82079
|
}
|
|
81653
|
-
if (consecutiveFailures >= REPORT_AFTER_CONSECUTIVE_FAILURES) {
|
|
82080
|
+
if (consecutiveFailures >= REPORT_AFTER_CONSECUTIVE_FAILURES && (streakReportedAt === 0 || now() - streakReportedAt >= STREAK_REREPORT_INTERVAL_MS)) {
|
|
82081
|
+
streakReportedAt = now();
|
|
81654
82082
|
const report2 = error52 instanceof ExpectedCliError ? new Error(`${consecutiveFailures} consecutive failed passes: ${error52.message}`) : error52;
|
|
81655
82083
|
deps.reportError?.(report2, `watch-pass-${kind}`, {
|
|
81656
82084
|
consecutiveFailures,
|
|
82085
|
+
sameFailureCount: streakCount,
|
|
82086
|
+
streakMinutes: Math.round((now() - streakStartedAt) / 6e4),
|
|
81657
82087
|
pendingCompletions: pendingCompletions.size
|
|
81658
82088
|
});
|
|
81659
82089
|
}
|
|
@@ -81741,7 +82171,7 @@ function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
|
|
|
81741
82171
|
try {
|
|
81742
82172
|
return loadCredentials(config2.appEnv);
|
|
81743
82173
|
} catch (error52) {
|
|
81744
|
-
log3(`\u26A0 credential store unreadable (${
|
|
82174
|
+
log3(`\u26A0 credential store unreadable (${describeError2(error52)}) \u2014 identity omitted from status`);
|
|
81745
82175
|
return null;
|
|
81746
82176
|
}
|
|
81747
82177
|
})();
|
|
@@ -81755,14 +82185,14 @@ function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
|
|
|
81755
82185
|
);
|
|
81756
82186
|
} catch (error52) {
|
|
81757
82187
|
log3(
|
|
81758
|
-
`\u26A0 Claude Code hook install failed (${
|
|
82188
|
+
`\u26A0 Claude Code hook install failed (${describeError2(error52)}) \u2014 syncing continues without them`
|
|
81759
82189
|
);
|
|
81760
82190
|
reportCollectorError(error52, "watch-hook-install");
|
|
81761
82191
|
}
|
|
81762
82192
|
try {
|
|
81763
82193
|
startWakeService(config2, home, log3);
|
|
81764
82194
|
} catch (error52) {
|
|
81765
|
-
log3(`\u26A0 wake-on-message failed to start (${
|
|
82195
|
+
log3(`\u26A0 wake-on-message failed to start (${describeError2(error52)}) \u2014 syncing continues`);
|
|
81766
82196
|
reportCollectorError(error52, "watch-wake-start");
|
|
81767
82197
|
}
|
|
81768
82198
|
const presentRoots = storeRoots.filter(({ dir }) => existsSync4(dir)).map(({ dir }) => dir);
|
|
@@ -81793,7 +82223,7 @@ function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
|
|
|
81793
82223
|
statusWriteFailed = false;
|
|
81794
82224
|
} catch (error52) {
|
|
81795
82225
|
if (!statusWriteFailed) {
|
|
81796
|
-
log3(`\u26A0 status file write failed (${
|
|
82226
|
+
log3(`\u26A0 status file write failed (${describeError2(error52)}) \u2014 syncing continues`);
|
|
81797
82227
|
reportCollectorError(error52, "watch-status-write");
|
|
81798
82228
|
}
|
|
81799
82229
|
statusWriteFailed = true;
|
|
@@ -81891,12 +82321,24 @@ function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
|
|
|
81891
82321
|
return new Promise(() => {
|
|
81892
82322
|
});
|
|
81893
82323
|
}
|
|
81894
|
-
function
|
|
82324
|
+
function describeError2(error52) {
|
|
81895
82325
|
return error52 instanceof Error ? error52.message : String(error52);
|
|
81896
82326
|
}
|
|
82327
|
+
function errorSignature(error52) {
|
|
82328
|
+
const parts = [];
|
|
82329
|
+
for (let cause = error52; cause instanceof Error; cause = cause.cause) {
|
|
82330
|
+
parts.push(`${cause.name}: ${cause.message}`);
|
|
82331
|
+
}
|
|
82332
|
+
if (parts.length === 0) parts.push(String(error52));
|
|
82333
|
+
return parts.join(" | ").replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, "#").replace(/[0-9a-f]{8,}/gi, "#").replace(/\d+/g, "#");
|
|
82334
|
+
}
|
|
82335
|
+
function describeDelay(ms) {
|
|
82336
|
+
const seconds = Math.round(ms / 1e3);
|
|
82337
|
+
return seconds < 120 ? `${seconds}s` : `${Math.round(seconds / 60)} minutes`;
|
|
82338
|
+
}
|
|
81897
82339
|
|
|
81898
82340
|
// src/connect.ts
|
|
81899
|
-
import { execFileSync as
|
|
82341
|
+
import { execFileSync as execFileSync5, spawnSync } from "node:child_process";
|
|
81900
82342
|
import {
|
|
81901
82343
|
existsSync as existsSync5,
|
|
81902
82344
|
mkdirSync as mkdirSync6,
|
|
@@ -82122,7 +82564,7 @@ function desktopClaudeBinaries(homeDir) {
|
|
|
82122
82564
|
}
|
|
82123
82565
|
function defaultWhichAll(name) {
|
|
82124
82566
|
try {
|
|
82125
|
-
return
|
|
82567
|
+
return execFileSync5("which", ["-a", name], { encoding: "utf8" }).split("\n").map((line) => line.trim()).filter(Boolean);
|
|
82126
82568
|
} catch {
|
|
82127
82569
|
return [];
|
|
82128
82570
|
}
|
|
@@ -82915,7 +83357,7 @@ function realRunInteractive(bin, args, env) {
|
|
|
82915
83357
|
}
|
|
82916
83358
|
|
|
82917
83359
|
// src/connections.ts
|
|
82918
|
-
import { execFileSync as
|
|
83360
|
+
import { execFileSync as execFileSync6, spawnSync as spawnSync2 } from "node:child_process";
|
|
82919
83361
|
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync7 } from "node:fs";
|
|
82920
83362
|
import path16 from "node:path";
|
|
82921
83363
|
function probeConnections(clients, deps) {
|
|
@@ -83118,7 +83560,7 @@ function runWithPath(pathValue, budget = UNLIMITED_TIME_BUDGET) {
|
|
|
83118
83560
|
function whichAllWith(pathValue) {
|
|
83119
83561
|
return (name) => {
|
|
83120
83562
|
try {
|
|
83121
|
-
return
|
|
83563
|
+
return execFileSync6("which", ["-a", name], {
|
|
83122
83564
|
encoding: "utf8",
|
|
83123
83565
|
env: { ...process.env, PATH: pathValue }
|
|
83124
83566
|
}).split("\n").map((line) => line.trim()).filter(Boolean);
|
|
@@ -83136,7 +83578,7 @@ function renderStatus(i) {
|
|
|
83136
83578
|
const lines = [];
|
|
83137
83579
|
lines.push(`Environment: ${i.config.appEnv} (API ${i.config.apiUrl})`);
|
|
83138
83580
|
lines.push(
|
|
83139
|
-
`CLI: powerplant ${i.cliVersion}` + (i.latestVersion !== null && i.latestVersion !== i.cliVersion ? ` \u2014 ${i.latestVersion} is available:
|
|
83581
|
+
`CLI: powerplant ${i.cliVersion}` + (i.latestVersion !== null && i.latestVersion !== i.cliVersion ? ` \u2014 ${i.latestVersion} is available: powerplant update` : "")
|
|
83140
83582
|
);
|
|
83141
83583
|
const unsupported = unsupportedVersionNotice(i.cliVersion, i.oldestSupportedVersion);
|
|
83142
83584
|
if (unsupported !== null) lines.push(unsupported);
|
|
@@ -83607,7 +84049,7 @@ async function realPromptLine(question) {
|
|
|
83607
84049
|
}
|
|
83608
84050
|
|
|
83609
84051
|
// src/memberSessions.ts
|
|
83610
|
-
import { execFileSync as
|
|
84052
|
+
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
83611
84053
|
import { existsSync as existsSync6 } from "node:fs";
|
|
83612
84054
|
import os16 from "node:os";
|
|
83613
84055
|
import path17 from "node:path";
|
|
@@ -83682,7 +84124,7 @@ function detectCodexDesktop(probe = { exists: existsSync6, readUrlSchemes: realR
|
|
|
83682
84124
|
function realReadUrlSchemes(bundlePath) {
|
|
83683
84125
|
if (process.platform !== "darwin") return [];
|
|
83684
84126
|
try {
|
|
83685
|
-
const json2 =
|
|
84127
|
+
const json2 = execFileSync7(
|
|
83686
84128
|
"plutil",
|
|
83687
84129
|
["-extract", "CFBundleURLTypes", "json", "-o", "-", path17.join(bundlePath, "Contents", "Info.plist")],
|
|
83688
84130
|
{ stdio: ["ignore", "pipe", "ignore"] }
|
|
@@ -84139,8 +84581,38 @@ function createTerminalOutput(options) {
|
|
|
84139
84581
|
return { log: log3, activity };
|
|
84140
84582
|
}
|
|
84141
84583
|
|
|
84584
|
+
// src/update.ts
|
|
84585
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
84586
|
+
var npmLsSchema = external_exports.object({
|
|
84587
|
+
dependencies: external_exports.record(external_exports.string(), external_exports.object({ version: external_exports.string().min(1) })).optional()
|
|
84588
|
+
});
|
|
84589
|
+
function runCapturing(command, args) {
|
|
84590
|
+
const result = spawnSync4(command, args, {
|
|
84591
|
+
encoding: "utf8",
|
|
84592
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
84593
|
+
});
|
|
84594
|
+
return { stdout: result.stdout ?? "" };
|
|
84595
|
+
}
|
|
84596
|
+
function installedGlobalVersion(packageName, run = runCapturing) {
|
|
84597
|
+
const { stdout } = run("npm", ["ls", "-g", packageName, "--json"]);
|
|
84598
|
+
try {
|
|
84599
|
+
const parsed = npmLsSchema.safeParse(JSON.parse(stdout));
|
|
84600
|
+
return parsed.success ? parsed.data.dependencies?.[packageName]?.version ?? null : null;
|
|
84601
|
+
} catch {
|
|
84602
|
+
return null;
|
|
84603
|
+
}
|
|
84604
|
+
}
|
|
84605
|
+
var RESTART_NOTE = "Sync service will restart within minutes. (You can do this now with powerplant restart.)";
|
|
84606
|
+
function updatedMessage(version2) {
|
|
84607
|
+
if (version2 === null) {
|
|
84608
|
+
return `Updated, but could not read the installed version from npm. Check with: powerplant status
|
|
84609
|
+
${RESTART_NOTE}`;
|
|
84610
|
+
}
|
|
84611
|
+
return `Updated to v${version2}. ${RESTART_NOTE}`;
|
|
84612
|
+
}
|
|
84613
|
+
|
|
84142
84614
|
// src/version.ts
|
|
84143
|
-
var CLI_VERSION = true ? "0.
|
|
84615
|
+
var CLI_VERSION = true ? "0.4.0" : "0.0.0-checkout";
|
|
84144
84616
|
var CLI_PACKAGE_NAME = "@usepowerplant/cli";
|
|
84145
84617
|
|
|
84146
84618
|
// src/workspace.ts
|
|
@@ -84261,7 +84733,7 @@ Commands:
|
|
|
84261
84733
|
sync Run one sync pass in the foreground [--since <days>] [--dry-run].
|
|
84262
84734
|
folders The folders you explicitly sync on this machine:
|
|
84263
84735
|
powerplant folders [add <path> | remove <path>]. Additions on
|
|
84264
|
-
top of your org's
|
|
84736
|
+
top of your org's allowlisted repos \u2014 no re-enroll needed.
|
|
84265
84737
|
workspace Your workspaces, and which one Powerplant and your connected
|
|
84266
84738
|
agents use: powerplant workspace [<name>] switches to <name>.
|
|
84267
84739
|
resume-session Reopen a synced session in its own CLI, right here in
|
|
@@ -84573,7 +85045,7 @@ The access you granted these agents stays on your Powerplant account until you r
|
|
|
84573
85045
|
}
|
|
84574
85046
|
case "update": {
|
|
84575
85047
|
console.log(`Updating ${CLI_PACKAGE_NAME} via npm\u2026`);
|
|
84576
|
-
const npm =
|
|
85048
|
+
const npm = spawnSync5("npm", ["install", "-g", CLI_PACKAGE_NAME], { stdio: "inherit" });
|
|
84577
85049
|
if (npm.error) {
|
|
84578
85050
|
throw new ExpectedCliError(
|
|
84579
85051
|
`Could not run npm (${npm.error.message}) \u2014 update by hand: npm install -g ${CLI_PACKAGE_NAME}`
|
|
@@ -84582,9 +85054,7 @@ The access you granted these agents stays on your Powerplant account until you r
|
|
|
84582
85054
|
if (npm.status !== 0) {
|
|
84583
85055
|
throw new ExpectedCliError("npm install failed \u2014 see the output above.");
|
|
84584
85056
|
}
|
|
84585
|
-
console.log(
|
|
84586
|
-
"Updated. The background service restarts itself onto the new version within minutes (or now: powerplant restart)."
|
|
84587
|
-
);
|
|
85057
|
+
console.log(updatedMessage(installedGlobalVersion(CLI_PACKAGE_NAME)));
|
|
84588
85058
|
return;
|
|
84589
85059
|
}
|
|
84590
85060
|
case "restart": {
|