@usepowerplant/cli 0.3.2 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -6877,19 +6877,19 @@ var require_module_details_from_path = __commonJS({
6877
6877
  basedir += segments[i] + sep2;
6878
6878
  }
6879
6879
  }
6880
- var path19 = "";
6880
+ var path18 = "";
6881
6881
  var lastSegmentIndex = segments.length - 1;
6882
6882
  for (var i2 = index + offset; i2 <= lastSegmentIndex; i2++) {
6883
6883
  if (i2 === lastSegmentIndex) {
6884
- path19 += segments[i2];
6884
+ path18 += segments[i2];
6885
6885
  } else {
6886
- path19 += segments[i2] + sep2;
6886
+ path18 += segments[i2] + sep2;
6887
6887
  }
6888
6888
  }
6889
6889
  return {
6890
6890
  name,
6891
6891
  basedir,
6892
- path: path19
6892
+ path: path18
6893
6893
  };
6894
6894
  };
6895
6895
  }
@@ -6899,7 +6899,7 @@ var require_module_details_from_path = __commonJS({
6899
6899
  var require_require_in_the_middle = __commonJS({
6900
6900
  "../../node_modules/.pnpm/require-in-the-middle@8.0.1/node_modules/require-in-the-middle/index.js"(exports, module2) {
6901
6901
  "use strict";
6902
- var path19 = __require("path");
6902
+ var path18 = __require("path");
6903
6903
  var Module = __require("module");
6904
6904
  var debug2 = require_src3()("require-in-the-middle");
6905
6905
  var moduleDetailsFromPath = require_module_details_from_path();
@@ -7044,7 +7044,7 @@ var require_require_in_the_middle = __commonJS({
7044
7044
  }
7045
7045
  moduleName = filename;
7046
7046
  } else if (hasWhitelist === true && modules.includes(filename)) {
7047
- const parsedPath = path19.parse(filename);
7047
+ const parsedPath = path18.parse(filename);
7048
7048
  moduleName = parsedPath.name;
7049
7049
  basedir = parsedPath.dir;
7050
7050
  } else {
@@ -7082,7 +7082,7 @@ var require_require_in_the_middle = __commonJS({
7082
7082
  }
7083
7083
  if (res !== filename) {
7084
7084
  if (internals === true) {
7085
- moduleName = moduleName + path19.sep + path19.relative(basedir, filename);
7085
+ moduleName = moduleName + path18.sep + path18.relative(basedir, filename);
7086
7086
  debug2("preparing to process require of internal file: %s", moduleName);
7087
7087
  } else {
7088
7088
  debug2("ignoring require of non-main module file: %s", res);
@@ -7118,8 +7118,8 @@ var require_require_in_the_middle = __commonJS({
7118
7118
  }
7119
7119
  };
7120
7120
  function resolveModuleName(stat) {
7121
- const normalizedPath = path19.sep !== "/" ? stat.path.split(path19.sep).join("/") : stat.path;
7122
- return path19.posix.join(stat.name, normalizedPath).replace(normalize3, "");
7121
+ const normalizedPath = path18.sep !== "/" ? stat.path.split(path18.sep).join("/") : stat.path;
7122
+ return path18.posix.join(stat.name, normalizedPath).replace(normalize3, "");
7123
7123
  }
7124
7124
  }
7125
7125
  });
@@ -7204,7 +7204,7 @@ var require_RequireInTheMiddleSingleton = __commonJS({
7204
7204
  Object.defineProperty(exports, "__esModule", { value: true });
7205
7205
  exports.RequireInTheMiddleSingleton = void 0;
7206
7206
  var require_in_the_middle_1 = require_require_in_the_middle();
7207
- var path19 = __require("path");
7207
+ var path18 = __require("path");
7208
7208
  var ModuleNameTrie_1 = require_ModuleNameTrie();
7209
7209
  var isMocha = [
7210
7210
  "afterEach",
@@ -7268,7 +7268,7 @@ var require_RequireInTheMiddleSingleton = __commonJS({
7268
7268
  };
7269
7269
  exports.RequireInTheMiddleSingleton = RequireInTheMiddleSingleton;
7270
7270
  function normalizePathSeparators(moduleNameOrPath) {
7271
- return path19.sep !== ModuleNameTrie_1.ModuleNameSeparator ? moduleNameOrPath.split(path19.sep).join(ModuleNameTrie_1.ModuleNameSeparator) : moduleNameOrPath;
7271
+ return path18.sep !== ModuleNameTrie_1.ModuleNameSeparator ? moduleNameOrPath.split(path18.sep).join(ModuleNameTrie_1.ModuleNameSeparator) : moduleNameOrPath;
7272
7272
  }
7273
7273
  }
7274
7274
  });
@@ -7434,7 +7434,7 @@ var require_register = __commonJS({
7434
7434
  // ../../node_modules/.pnpm/import-in-the-middle@3.3.3/node_modules/import-in-the-middle/index.js
7435
7435
  var require_import_in_the_middle = __commonJS({
7436
7436
  "../../node_modules/.pnpm/import-in-the-middle@3.3.3/node_modules/import-in-the-middle/index.js"(exports, module2) {
7437
- var path19 = __require("path");
7437
+ var path18 = __require("path");
7438
7438
  var moduleDetailsFromPath = require_module_details_from_path();
7439
7439
  var { fileURLToPath: fileURLToPath3 } = __require("url");
7440
7440
  var { MessageChannel } = __require("worker_threads");
@@ -7553,7 +7553,7 @@ var require_import_in_the_middle = __commonJS({
7553
7553
  } else if (baseDir.endsWith(specifiers.get(loadUrl)) || isTurbopackSpecifier(specifiers.get(loadUrl), baseDir)) {
7554
7554
  callHookFn(hookFn, namespace, name, baseDir);
7555
7555
  } else if (internals) {
7556
- const internalPath = name + path19.sep + path19.relative(baseDir, filePath);
7556
+ const internalPath = name + path18.sep + path18.relative(baseDir, filePath);
7557
7557
  callHookFn(hookFn, namespace, internalPath, baseDir);
7558
7558
  }
7559
7559
  } else if (matchArg === specifier) {
@@ -7628,7 +7628,7 @@ var require_instrumentation2 = __commonJS({
7628
7628
  "use strict";
7629
7629
  Object.defineProperty(exports, "__esModule", { value: true });
7630
7630
  exports.InstrumentationBase = void 0;
7631
- var path19 = __require("path");
7631
+ var path18 = __require("path");
7632
7632
  var util_1 = __require("util");
7633
7633
  var semver_1 = require_semver();
7634
7634
  var shimmer_1 = require_shimmer();
@@ -7729,7 +7729,7 @@ var require_instrumentation2 = __commonJS({
7729
7729
  }
7730
7730
  _extractPackageVersion(baseDir) {
7731
7731
  try {
7732
- const json2 = (0, fs_1.readFileSync)(path19.join(baseDir, "package.json"), {
7732
+ const json2 = (0, fs_1.readFileSync)(path18.join(baseDir, "package.json"), {
7733
7733
  encoding: "utf8"
7734
7734
  });
7735
7735
  const version2 = JSON.parse(json2).version;
@@ -7771,7 +7771,7 @@ var require_instrumentation2 = __commonJS({
7771
7771
  return exports2;
7772
7772
  }
7773
7773
  const files = module3.files ?? [];
7774
- const normalizedName = path19.normalize(name);
7774
+ const normalizedName = path18.normalize(name);
7775
7775
  const supportedFileInstrumentations = files.filter((f) => f.name === normalizedName && isSupported(f.supportedVersions, version2, module3.includePrerelease));
7776
7776
  return supportedFileInstrumentations.reduce((patchedExports, file2) => {
7777
7777
  file2.moduleExports = patchedExports;
@@ -7817,8 +7817,8 @@ var require_instrumentation2 = __commonJS({
7817
7817
  this._warnOnPreloadedModules();
7818
7818
  for (const module3 of this._modules) {
7819
7819
  const hookFn = (exports2, name, baseDir) => {
7820
- if (!baseDir && path19.isAbsolute(name)) {
7821
- const parsedPath = path19.parse(name);
7820
+ if (!baseDir && path18.isAbsolute(name)) {
7821
+ const parsedPath = path18.parse(name);
7822
7822
  name = parsedPath.name;
7823
7823
  baseDir = parsedPath.dir;
7824
7824
  }
@@ -7827,7 +7827,7 @@ var require_instrumentation2 = __commonJS({
7827
7827
  const onRequire = (exports2, name, baseDir) => {
7828
7828
  return this._onRequire(module3, exports2, name, baseDir);
7829
7829
  };
7830
- const hook = path19.isAbsolute(module3.name) ? new require_in_the_middle_1.Hook([module3.name], { internals: true }, onRequire) : this._requireInTheMiddleSingleton.register(module3.name, onRequire);
7830
+ const hook = path18.isAbsolute(module3.name) ? new require_in_the_middle_1.Hook([module3.name], { internals: true }, onRequire) : this._requireInTheMiddleSingleton.register(module3.name, onRequire);
7831
7831
  this._hooks.push(hook);
7832
7832
  const esmHook = new import_in_the_middle_1.Hook([module3.name], { internals: true }, hookFn);
7833
7833
  this._hooks.push(esmHook);
@@ -8435,9 +8435,9 @@ var require_getMachineId_linux = __commonJS({
8435
8435
  var api_1 = (init_esm(), __toCommonJS(esm_exports));
8436
8436
  async function getMachineId() {
8437
8437
  const paths = ["/etc/machine-id", "/var/lib/dbus/machine-id"];
8438
- for (const path19 of paths) {
8438
+ for (const path18 of paths) {
8439
8439
  try {
8440
- const result = await fs_1.promises.readFile(path19, { encoding: "utf8" });
8440
+ const result = await fs_1.promises.readFile(path18, { encoding: "utf8" });
8441
8441
  return result.trim();
8442
8442
  } catch (e) {
8443
8443
  api_1.diag.debug(`error reading machine id: ${e}`);
@@ -11792,14 +11792,14 @@ var require_util = __commonJS({
11792
11792
  }
11793
11793
  const port = url2.port != null ? url2.port : url2.protocol === "https:" ? 443 : 80;
11794
11794
  let origin = url2.origin != null ? url2.origin : `${url2.protocol || ""}//${url2.hostname || ""}:${port}`;
11795
- let path19 = url2.path != null ? url2.path : `${url2.pathname || ""}${url2.search || ""}`;
11795
+ let path18 = url2.path != null ? url2.path : `${url2.pathname || ""}${url2.search || ""}`;
11796
11796
  if (origin[origin.length - 1] === "/") {
11797
11797
  origin = origin.slice(0, origin.length - 1);
11798
11798
  }
11799
- if (path19 && path19[0] !== "/") {
11800
- path19 = `/${path19}`;
11799
+ if (path18 && path18[0] !== "/") {
11800
+ path18 = `/${path18}`;
11801
11801
  }
11802
- return new URL(`${origin}${path19}`);
11802
+ return new URL(`${origin}${path18}`);
11803
11803
  }
11804
11804
  if (!isHttpOrHttpsPrefixed(url2.origin || url2.protocol)) {
11805
11805
  throw new InvalidArgumentError("Invalid URL protocol: the URL must start with `http:` or `https:`.");
@@ -12620,9 +12620,9 @@ var require_diagnostics = __commonJS({
12620
12620
  "undici:client:sendHeaders",
12621
12621
  (evt) => {
12622
12622
  const {
12623
- request: { method, path: path19, origin }
12623
+ request: { method, path: path18, origin }
12624
12624
  } = evt;
12625
- debugLog3("sending request to %s %s%s", method, origin, path19);
12625
+ debugLog3("sending request to %s %s%s", method, origin, path18);
12626
12626
  }
12627
12627
  );
12628
12628
  }
@@ -12640,14 +12640,14 @@ var require_diagnostics = __commonJS({
12640
12640
  "undici:request:headers",
12641
12641
  (evt) => {
12642
12642
  const {
12643
- request: { method, path: path19, origin },
12643
+ request: { method, path: path18, origin },
12644
12644
  response: { statusCode }
12645
12645
  } = evt;
12646
12646
  debugLog3(
12647
12647
  "received response to %s %s%s - HTTP %d",
12648
12648
  method,
12649
12649
  origin,
12650
- path19,
12650
+ path18,
12651
12651
  statusCode
12652
12652
  );
12653
12653
  }
@@ -12656,23 +12656,23 @@ var require_diagnostics = __commonJS({
12656
12656
  "undici:request:trailers",
12657
12657
  (evt) => {
12658
12658
  const {
12659
- request: { method, path: path19, origin }
12659
+ request: { method, path: path18, origin }
12660
12660
  } = evt;
12661
- debugLog3("trailers received from %s %s%s", method, origin, path19);
12661
+ debugLog3("trailers received from %s %s%s", method, origin, path18);
12662
12662
  }
12663
12663
  );
12664
12664
  diagnosticsChannel9.subscribe(
12665
12665
  "undici:request:error",
12666
12666
  (evt) => {
12667
12667
  const {
12668
- request: { method, path: path19, origin },
12668
+ request: { method, path: path18, origin },
12669
12669
  error: error52
12670
12670
  } = evt;
12671
12671
  debugLog3(
12672
12672
  "request to %s %s%s errored - %s",
12673
12673
  method,
12674
12674
  origin,
12675
- path19,
12675
+ path18,
12676
12676
  error52.message
12677
12677
  );
12678
12678
  }
@@ -12787,7 +12787,7 @@ var require_request = __commonJS({
12787
12787
  var kHandler = /* @__PURE__ */ Symbol("handler");
12788
12788
  var Request2 = class {
12789
12789
  constructor(origin, {
12790
- path: path19,
12790
+ path: path18,
12791
12791
  method,
12792
12792
  body,
12793
12793
  headers,
@@ -12804,11 +12804,11 @@ var require_request = __commonJS({
12804
12804
  maxRedirections,
12805
12805
  typeOfService
12806
12806
  }, handler) {
12807
- if (typeof path19 !== "string") {
12807
+ if (typeof path18 !== "string") {
12808
12808
  throw new InvalidArgumentError("path must be a string");
12809
- } else if (path19[0] !== "/" && !(path19.startsWith("http://") || path19.startsWith("https://")) && method !== "CONNECT") {
12809
+ } else if (path18[0] !== "/" && !(path18.startsWith("http://") || path18.startsWith("https://")) && method !== "CONNECT") {
12810
12810
  throw new InvalidArgumentError("path must be an absolute URL or start with a slash");
12811
- } else if (invalidPathRegex.test(path19)) {
12811
+ } else if (invalidPathRegex.test(path18)) {
12812
12812
  throw new InvalidArgumentError("invalid request path");
12813
12813
  }
12814
12814
  if (typeof method !== "string") {
@@ -12883,7 +12883,7 @@ var require_request = __commonJS({
12883
12883
  this.completed = false;
12884
12884
  this.aborted = false;
12885
12885
  this.upgrade = upgrade || null;
12886
- this.path = query ? serializePathWithQuery(path19, query) : path19;
12886
+ this.path = query ? serializePathWithQuery(path18, query) : path18;
12887
12887
  this.origin = origin;
12888
12888
  this.protocol = getProtocolFromUrlString(origin);
12889
12889
  this.idempotent = idempotent == null ? method === "HEAD" || method === "GET" : idempotent;
@@ -18066,7 +18066,7 @@ var require_client_h1 = __commonJS({
18066
18066
  return method !== "GET" && method !== "HEAD" && method !== "OPTIONS" && method !== "TRACE" && method !== "CONNECT";
18067
18067
  }
18068
18068
  function writeH1(client, request2) {
18069
- const { method, path: path19, host, upgrade, blocking, reset } = request2;
18069
+ const { method, path: path18, host, upgrade, blocking, reset } = request2;
18070
18070
  let { body, headers, contentLength } = request2;
18071
18071
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH" || method === "QUERY" || method === "PROPFIND" || method === "PROPPATCH";
18072
18072
  if (util2.isFormDataLike(body)) {
@@ -18144,7 +18144,7 @@ var require_client_h1 = __commonJS({
18144
18144
  if (socket.setTypeOfService) {
18145
18145
  socket.setTypeOfService(request2.typeOfService);
18146
18146
  }
18147
- let header = `${method} ${path19} HTTP/1.1\r
18147
+ let header = `${method} ${path18} HTTP/1.1\r
18148
18148
  `;
18149
18149
  if (typeof host === "string") {
18150
18150
  header += `host: ${host}\r
@@ -18797,7 +18797,7 @@ var require_client_h2 = __commonJS({
18797
18797
  function writeH2(client, request2) {
18798
18798
  const requestTimeout = request2.bodyTimeout ?? client[kBodyTimeout];
18799
18799
  const session = client[kHTTP2Session];
18800
- const { method, path: path19, host, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request2;
18800
+ const { method, path: path18, host, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request2;
18801
18801
  let { body } = request2;
18802
18802
  if (upgrade != null && upgrade !== "websocket") {
18803
18803
  util2.errorRequest(client, request2, new InvalidArgumentError(`Custom upgrade "${upgrade}" not supported over HTTP/2`));
@@ -18865,7 +18865,7 @@ var require_client_h2 = __commonJS({
18865
18865
  }
18866
18866
  headers[HTTP2_HEADER_METHOD] = "CONNECT";
18867
18867
  headers[HTTP2_HEADER_PROTOCOL] = "websocket";
18868
- headers[HTTP2_HEADER_PATH] = path19;
18868
+ headers[HTTP2_HEADER_PATH] = path18;
18869
18869
  if (protocol === "ws:" || protocol === "wss:") {
18870
18870
  headers[HTTP2_HEADER_SCHEME] = protocol === "ws:" ? "http" : "https";
18871
18871
  } else {
@@ -18906,7 +18906,7 @@ var require_client_h2 = __commonJS({
18906
18906
  stream.setTimeout(requestTimeout);
18907
18907
  return true;
18908
18908
  }
18909
- headers[HTTP2_HEADER_PATH] = path19;
18909
+ headers[HTTP2_HEADER_PATH] = path18;
18910
18910
  headers[HTTP2_HEADER_SCHEME] = protocol === "http:" ? "http" : "https";
18911
18911
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
18912
18912
  if (body && typeof body.read === "function") {
@@ -21249,10 +21249,10 @@ var require_proxy_agent = __commonJS({
21249
21249
  };
21250
21250
  const {
21251
21251
  origin,
21252
- path: path19 = "/",
21252
+ path: path18 = "/",
21253
21253
  headers = {}
21254
21254
  } = opts;
21255
- opts.path = origin + path19;
21255
+ opts.path = origin + path18;
21256
21256
  if (!("host" in headers) && !("Host" in headers)) {
21257
21257
  const { host } = new URL(origin);
21258
21258
  headers.host = host;
@@ -23335,20 +23335,20 @@ var require_mock_utils = __commonJS({
23335
23335
  }
23336
23336
  return normalizedQp;
23337
23337
  }
23338
- function safeUrl(path19) {
23339
- if (typeof path19 !== "string") {
23340
- return path19;
23338
+ function safeUrl(path18) {
23339
+ if (typeof path18 !== "string") {
23340
+ return path18;
23341
23341
  }
23342
- const pathSegments = path19.split("?", 3);
23342
+ const pathSegments = path18.split("?", 3);
23343
23343
  if (pathSegments.length !== 2) {
23344
- return path19;
23344
+ return path18;
23345
23345
  }
23346
23346
  const qp = new URLSearchParams(pathSegments.pop());
23347
23347
  qp.sort();
23348
23348
  return [...pathSegments, qp.toString()].join("?");
23349
23349
  }
23350
- function matchKey(mockDispatch2, { path: path19, method, body, headers }) {
23351
- const pathMatch = matchValue(mockDispatch2.path, path19);
23350
+ function matchKey(mockDispatch2, { path: path18, method, body, headers }) {
23351
+ const pathMatch = matchValue(mockDispatch2.path, path18);
23352
23352
  const methodMatch = matchValue(mockDispatch2.method, method);
23353
23353
  const bodyMatch = typeof mockDispatch2.body !== "undefined" ? matchValue(mockDispatch2.body, body) : true;
23354
23354
  const headersMatch = matchHeaders(mockDispatch2, headers);
@@ -23373,8 +23373,8 @@ var require_mock_utils = __commonJS({
23373
23373
  const basePath = key.query ? serializePathWithQuery(key.path, key.query) : key.path;
23374
23374
  const resolvedPath = typeof basePath === "string" ? safeUrl(basePath) : basePath;
23375
23375
  const resolvedPathWithoutTrailingSlash = removeTrailingSlash(resolvedPath);
23376
- let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path19, ignoreTrailingSlash }) => {
23377
- return ignoreTrailingSlash ? matchValue(removeTrailingSlash(safeUrl(path19)), resolvedPathWithoutTrailingSlash) : matchValue(safeUrl(path19), resolvedPath);
23376
+ let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path18, ignoreTrailingSlash }) => {
23377
+ return ignoreTrailingSlash ? matchValue(removeTrailingSlash(safeUrl(path18)), resolvedPathWithoutTrailingSlash) : matchValue(safeUrl(path18), resolvedPath);
23378
23378
  });
23379
23379
  if (matchedMockDispatches.length === 0) {
23380
23380
  throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`);
@@ -23413,19 +23413,19 @@ var require_mock_utils = __commonJS({
23413
23413
  mockDispatches.splice(index, 1);
23414
23414
  }
23415
23415
  }
23416
- function removeTrailingSlash(path19) {
23417
- while (path19.endsWith("/")) {
23418
- path19 = path19.slice(0, -1);
23416
+ function removeTrailingSlash(path18) {
23417
+ while (path18.endsWith("/")) {
23418
+ path18 = path18.slice(0, -1);
23419
23419
  }
23420
- if (path19.length === 0) {
23421
- path19 = "/";
23420
+ if (path18.length === 0) {
23421
+ path18 = "/";
23422
23422
  }
23423
- return path19;
23423
+ return path18;
23424
23424
  }
23425
23425
  function buildKey(opts) {
23426
- const { path: path19, method, body, headers, query } = opts;
23426
+ const { path: path18, method, body, headers, query } = opts;
23427
23427
  return {
23428
- path: path19,
23428
+ path: path18,
23429
23429
  method,
23430
23430
  body,
23431
23431
  headers,
@@ -24115,10 +24115,10 @@ var require_pending_interceptors_formatter = __commonJS({
24115
24115
  }
24116
24116
  format(pendingInterceptors) {
24117
24117
  const withPrettyHeaders = pendingInterceptors.map(
24118
- ({ method, path: path19, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
24118
+ ({ method, path: path18, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
24119
24119
  Method: method,
24120
24120
  Origin: origin,
24121
- Path: path19,
24121
+ Path: path18,
24122
24122
  "Status code": statusCode,
24123
24123
  Persistent: persist ? PERSISTENT : NOT_PERSISTENT,
24124
24124
  Invocations: timesInvoked,
@@ -24200,9 +24200,9 @@ var require_mock_agent = __commonJS({
24200
24200
  const acceptNonStandardSearchParameters = this[kMockAgentAcceptsNonStandardSearchParameters];
24201
24201
  const dispatchOpts = { ...opts };
24202
24202
  if (acceptNonStandardSearchParameters && dispatchOpts.path) {
24203
- const [path19, searchParams] = dispatchOpts.path.split("?");
24203
+ const [path18, searchParams] = dispatchOpts.path.split("?");
24204
24204
  const normalizedSearchParams = normalizeSearchParams(searchParams, acceptNonStandardSearchParameters);
24205
- dispatchOpts.path = `${path19}?${normalizedSearchParams}`;
24205
+ dispatchOpts.path = `${path18}?${normalizedSearchParams}`;
24206
24206
  }
24207
24207
  return this[kAgent].dispatch(dispatchOpts, handler);
24208
24208
  }
@@ -24603,12 +24603,12 @@ var require_snapshot_recorder = __commonJS({
24603
24603
  * @return {Promise<void>} - Resolves when snapshots are loaded
24604
24604
  */
24605
24605
  async loadSnapshots(filePath) {
24606
- const path19 = filePath || this.#snapshotPath;
24607
- if (!path19) {
24606
+ const path18 = filePath || this.#snapshotPath;
24607
+ if (!path18) {
24608
24608
  throw new InvalidArgumentError("Snapshot path is required");
24609
24609
  }
24610
24610
  try {
24611
- const data = await readFile2(resolve2(path19), "utf8");
24611
+ const data = await readFile2(resolve2(path18), "utf8");
24612
24612
  const parsed = JSON.parse(data);
24613
24613
  if (Array.isArray(parsed)) {
24614
24614
  this.#snapshots.clear();
@@ -24622,7 +24622,7 @@ var require_snapshot_recorder = __commonJS({
24622
24622
  if (error52.code === "ENOENT") {
24623
24623
  this.#snapshots.clear();
24624
24624
  } else {
24625
- throw new UndiciError(`Failed to load snapshots from ${path19}`, { cause: error52 });
24625
+ throw new UndiciError(`Failed to load snapshots from ${path18}`, { cause: error52 });
24626
24626
  }
24627
24627
  }
24628
24628
  }
@@ -24633,11 +24633,11 @@ var require_snapshot_recorder = __commonJS({
24633
24633
  * @returns {Promise<void>} - Resolves when snapshots are saved
24634
24634
  */
24635
24635
  async saveSnapshots(filePath) {
24636
- const path19 = filePath || this.#snapshotPath;
24637
- if (!path19) {
24636
+ const path18 = filePath || this.#snapshotPath;
24637
+ if (!path18) {
24638
24638
  throw new InvalidArgumentError("Snapshot path is required");
24639
24639
  }
24640
- const resolvedPath = resolve2(path19);
24640
+ const resolvedPath = resolve2(path18);
24641
24641
  await mkdir(dirname2(resolvedPath), { recursive: true });
24642
24642
  const data = Array.from(this.#snapshots.entries()).map(([hash2, snapshot]) => ({
24643
24643
  hash: hash2,
@@ -25269,15 +25269,15 @@ var require_redirect_handler = __commonJS({
25269
25269
  return;
25270
25270
  }
25271
25271
  const { origin, pathname, search } = util2.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)));
25272
- const path19 = search ? `${pathname}${search}` : pathname;
25273
- const redirectUrlString = `${origin}${path19}`;
25272
+ const path18 = search ? `${pathname}${search}` : pathname;
25273
+ const redirectUrlString = `${origin}${path18}`;
25274
25274
  for (const historyUrl of this.history) {
25275
25275
  if (historyUrl.toString() === redirectUrlString) {
25276
25276
  throw new InvalidArgumentError(`Redirect loop detected. Cannot redirect to ${origin}. This typically happens when using a Client or Pool with cross-origin redirects. Use an Agent for cross-origin redirects.`);
25277
25277
  }
25278
25278
  }
25279
25279
  this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin);
25280
- this.opts.path = path19;
25280
+ this.opts.path = path18;
25281
25281
  this.opts.origin = origin;
25282
25282
  this.opts.query = null;
25283
25283
  }
@@ -27046,10 +27046,10 @@ var require_cache_handler = __commonJS({
27046
27046
  }
27047
27047
  return locationUrl.pathname + locationUrl.search;
27048
27048
  }
27049
- function deleteCachedUri(store, cacheKey, path19) {
27049
+ function deleteCachedUri(store, cacheKey, path18) {
27050
27050
  deleteCachedValue(store, {
27051
27051
  ...cacheKey,
27052
- path: path19
27052
+ path: path18
27053
27053
  });
27054
27054
  for (let i = 0; i < util2.safeHTTPMethods.length; i++) {
27055
27055
  const method = util2.safeHTTPMethods[i];
@@ -27057,7 +27057,7 @@ var require_cache_handler = __commonJS({
27057
27057
  deleteCachedValue(store, {
27058
27058
  ...cacheKey,
27059
27059
  method,
27060
- path: path19
27060
+ path: path18
27061
27061
  });
27062
27062
  }
27063
27063
  }
@@ -27068,9 +27068,9 @@ var require_cache_handler = __commonJS({
27068
27068
  }
27069
27069
  const values = Array.isArray(headerValue) ? headerValue : [headerValue];
27070
27070
  for (let i = 0; i < values.length; i++) {
27071
- const path19 = getSameOriginPath(cacheKey, values[i]);
27072
- if (path19 !== void 0) {
27073
- deleteCachedUri(store, cacheKey, path19);
27071
+ const path18 = getSameOriginPath(cacheKey, values[i]);
27072
+ if (path18 !== void 0) {
27073
+ deleteCachedUri(store, cacheKey, path18);
27074
27074
  }
27075
27075
  }
27076
27076
  }
@@ -31948,11 +31948,11 @@ var require_fetch = __commonJS({
31948
31948
  function dispatch({ body }) {
31949
31949
  const url2 = requestCurrentURL(request2);
31950
31950
  const agent = fetchParams.controller.dispatcher;
31951
- const path19 = url2.pathname + url2.search;
31951
+ const path18 = url2.pathname + url2.search;
31952
31952
  const hasTrailingQuestionMark = url2.search.length === 0 && url2.href[url2.href.length - url2.hash.length - 1] === "?";
31953
31953
  return new Promise((resolve2, reject) => agent.dispatch(
31954
31954
  {
31955
- path: hasTrailingQuestionMark ? `${path19}?` : path19,
31955
+ path: hasTrailingQuestionMark ? `${path18}?` : path18,
31956
31956
  origin: url2.origin,
31957
31957
  method: request2.method,
31958
31958
  body: agent.isMockActive ? request2.body && (request2.body.source || request2.body.stream) : body,
@@ -32899,9 +32899,9 @@ var require_util4 = __commonJS({
32899
32899
  }
32900
32900
  }
32901
32901
  }
32902
- function validateCookiePath(path19) {
32903
- for (let i = 0; i < path19.length; ++i) {
32904
- const code = path19.charCodeAt(i);
32902
+ function validateCookiePath(path18) {
32903
+ for (let i = 0; i < path18.length; ++i) {
32904
+ const code = path18.charCodeAt(i);
32905
32905
  if (code < 32 || // exclude CTLs (0-31)
32906
32906
  code > 126 || // exclude DEL and non-ascii
32907
32907
  code === 59) {
@@ -36138,11 +36138,11 @@ var require_undici = __commonJS({
36138
36138
  if (typeof opts.path !== "string") {
36139
36139
  throw new InvalidArgumentError("invalid opts.path");
36140
36140
  }
36141
- let path19 = opts.path;
36141
+ let path18 = opts.path;
36142
36142
  if (!opts.path.startsWith("/")) {
36143
- path19 = `/${path19}`;
36143
+ path18 = `/${path18}`;
36144
36144
  }
36145
- url2 = new URL(util2.parseOrigin(url2).origin + path19);
36145
+ url2 = new URL(util2.parseOrigin(url2).origin + path18);
36146
36146
  } else {
36147
36147
  if (!opts) {
36148
36148
  opts = typeof url2 === "object" ? url2 : {};
@@ -36253,7 +36253,7 @@ ${captureLines}` : capture.stack;
36253
36253
 
36254
36254
  // src/cli.ts
36255
36255
  import { spawnSync as spawnSync5 } from "node:child_process";
36256
- import { existsSync as existsSync7, realpathSync as realpathSync2 } from "node:fs";
36256
+ import { existsSync as existsSync6, realpathSync as realpathSync2 } from "node:fs";
36257
36257
  import { parseArgs } from "node:util";
36258
36258
 
36259
36259
  // ../../apps/collector/src/credentials.ts
@@ -37028,10 +37028,10 @@ function mergeDefs(...defs) {
37028
37028
  function cloneDef(schema) {
37029
37029
  return mergeDefs(schema._zod.def);
37030
37030
  }
37031
- function getElementAtPath(obj, path19) {
37032
- if (!path19)
37031
+ function getElementAtPath(obj, path18) {
37032
+ if (!path18)
37033
37033
  return obj;
37034
- return path19.reduce((acc, key) => acc?.[key], obj);
37034
+ return path18.reduce((acc, key) => acc?.[key], obj);
37035
37035
  }
37036
37036
  function promiseAllObject(promisesObj) {
37037
37037
  const keys = Object.keys(promisesObj);
@@ -37440,11 +37440,11 @@ function explicitlyAborted(x, startIndex = 0) {
37440
37440
  }
37441
37441
  return false;
37442
37442
  }
37443
- function prefixIssues(path19, issues) {
37443
+ function prefixIssues(path18, issues) {
37444
37444
  return issues.map((iss) => {
37445
37445
  var _a4;
37446
37446
  (_a4 = iss).path ?? (_a4.path = []);
37447
- iss.path.unshift(path19);
37447
+ iss.path.unshift(path18);
37448
37448
  return iss;
37449
37449
  });
37450
37450
  }
@@ -37591,16 +37591,16 @@ function flattenError(error52, mapper = (issue2) => issue2.message) {
37591
37591
  }
37592
37592
  function formatError(error52, mapper = (issue2) => issue2.message) {
37593
37593
  const fieldErrors = { _errors: [] };
37594
- const processError = (error53, path19 = []) => {
37594
+ const processError = (error53, path18 = []) => {
37595
37595
  for (const issue2 of error53.issues) {
37596
37596
  if (issue2.code === "invalid_union" && issue2.errors.length) {
37597
- issue2.errors.map((issues) => processError({ issues }, [...path19, ...issue2.path]));
37597
+ issue2.errors.map((issues) => processError({ issues }, [...path18, ...issue2.path]));
37598
37598
  } else if (issue2.code === "invalid_key") {
37599
- processError({ issues: issue2.issues }, [...path19, ...issue2.path]);
37599
+ processError({ issues: issue2.issues }, [...path18, ...issue2.path]);
37600
37600
  } else if (issue2.code === "invalid_element") {
37601
- processError({ issues: issue2.issues }, [...path19, ...issue2.path]);
37601
+ processError({ issues: issue2.issues }, [...path18, ...issue2.path]);
37602
37602
  } else {
37603
- const fullpath = [...path19, ...issue2.path];
37603
+ const fullpath = [...path18, ...issue2.path];
37604
37604
  if (fullpath.length === 0) {
37605
37605
  fieldErrors._errors.push(mapper(issue2));
37606
37606
  } else {
@@ -37627,17 +37627,17 @@ function formatError(error52, mapper = (issue2) => issue2.message) {
37627
37627
  }
37628
37628
  function treeifyError(error52, mapper = (issue2) => issue2.message) {
37629
37629
  const result = { errors: [] };
37630
- const processError = (error53, path19 = []) => {
37630
+ const processError = (error53, path18 = []) => {
37631
37631
  var _a4, _b;
37632
37632
  for (const issue2 of error53.issues) {
37633
37633
  if (issue2.code === "invalid_union" && issue2.errors.length) {
37634
- issue2.errors.map((issues) => processError({ issues }, [...path19, ...issue2.path]));
37634
+ issue2.errors.map((issues) => processError({ issues }, [...path18, ...issue2.path]));
37635
37635
  } else if (issue2.code === "invalid_key") {
37636
- processError({ issues: issue2.issues }, [...path19, ...issue2.path]);
37636
+ processError({ issues: issue2.issues }, [...path18, ...issue2.path]);
37637
37637
  } else if (issue2.code === "invalid_element") {
37638
- processError({ issues: issue2.issues }, [...path19, ...issue2.path]);
37638
+ processError({ issues: issue2.issues }, [...path18, ...issue2.path]);
37639
37639
  } else {
37640
- const fullpath = [...path19, ...issue2.path];
37640
+ const fullpath = [...path18, ...issue2.path];
37641
37641
  if (fullpath.length === 0) {
37642
37642
  result.errors.push(mapper(issue2));
37643
37643
  continue;
@@ -37669,8 +37669,8 @@ function treeifyError(error52, mapper = (issue2) => issue2.message) {
37669
37669
  }
37670
37670
  function toDotPath(_path) {
37671
37671
  const segs = [];
37672
- const path19 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
37673
- for (const seg of path19) {
37672
+ const path18 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
37673
+ for (const seg of path18) {
37674
37674
  if (typeof seg === "number")
37675
37675
  segs.push(`[${seg}]`);
37676
37676
  else if (typeof seg === "symbol")
@@ -50362,13 +50362,13 @@ function resolveRef(ref, ctx) {
50362
50362
  if (!ref.startsWith("#")) {
50363
50363
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
50364
50364
  }
50365
- const path19 = ref.slice(1).split("/").filter(Boolean);
50366
- if (path19.length === 0) {
50365
+ const path18 = ref.slice(1).split("/").filter(Boolean);
50366
+ if (path18.length === 0) {
50367
50367
  return ctx.rootSchema;
50368
50368
  }
50369
50369
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
50370
- if (path19[0] === defsKey) {
50371
- const key = path19[1];
50370
+ if (path18[0] === defsKey) {
50371
+ const key = path18[1];
50372
50372
  if (!key || !ctx.defs[key]) {
50373
50373
  throw new Error(`Reference not found: ${ref}`);
50374
50374
  }
@@ -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) {
@@ -51398,8 +51409,8 @@ function primaryArg(args) {
51398
51409
  function readFilePath(name, primary) {
51399
51410
  if (!primary || !READ_TOOLS.has(name)) return null;
51400
51411
  if (ARTIFACT_PATH_RE.test(primary)) return null;
51401
- const path19 = primary.startsWith("/workspace/") ? primary.slice("/workspace/".length) : primary;
51402
- return path19.length > 0 ? path19 : null;
51412
+ const path18 = primary.startsWith("/workspace/") ? primary.slice("/workspace/".length) : primary;
51413
+ return path18.length > 0 ? path18 : null;
51403
51414
  }
51404
51415
  function asRecord3(value) {
51405
51416
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -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
  }
@@ -51906,8 +51919,8 @@ function getFunctionName(fn) {
51906
51919
  return defaultFunctionName;
51907
51920
  }
51908
51921
  }
51909
- function normalizeStackTracePath(path19) {
51910
- let filename = path19?.startsWith("file://") ? path19.slice(7) : path19;
51922
+ function normalizeStackTracePath(path18) {
51923
+ let filename = path18?.startsWith("file://") ? path18.slice(7) : path18;
51911
51924
  if (filename?.match(/\/[A-Z]:/)) {
51912
51925
  filename = filename.slice(1);
51913
51926
  }
@@ -53717,8 +53730,8 @@ function isValidProtocol(protocol) {
53717
53730
  return protocol === "http" || protocol === "https";
53718
53731
  }
53719
53732
  function dsnToString(dsn, withPassword = false) {
53720
- const { host, path: path19, pass, port, projectId, protocol, publicKey } = dsn;
53721
- return `${protocol}://${publicKey}${withPassword && pass ? `:${pass}` : ""}@${host}${port ? `:${port}` : ""}/${path19 ? `${path19}/` : path19}${projectId}`;
53733
+ const { host, path: path18, pass, port, projectId, protocol, publicKey } = dsn;
53734
+ return `${protocol}://${publicKey}${withPassword && pass ? `:${pass}` : ""}@${host}${port ? `:${port}` : ""}/${path18 ? `${path18}/` : path18}${projectId}`;
53722
53735
  }
53723
53736
  function dsnFromString(str) {
53724
53737
  const match = DSN_REGEX.exec(str);
@@ -53729,11 +53742,11 @@ function dsnFromString(str) {
53729
53742
  return void 0;
53730
53743
  }
53731
53744
  const [protocol, publicKey, pass = "", host = "", port = "", lastPath = ""] = match.slice(1);
53732
- let path19 = "";
53745
+ let path18 = "";
53733
53746
  let projectId = lastPath;
53734
53747
  const split = projectId.split("/");
53735
53748
  if (split.length > 1) {
53736
- path19 = split.slice(0, -1).join("/");
53749
+ path18 = split.slice(0, -1).join("/");
53737
53750
  projectId = split.pop();
53738
53751
  }
53739
53752
  if (projectId) {
@@ -53742,7 +53755,7 @@ function dsnFromString(str) {
53742
53755
  projectId = projectMatch[0];
53743
53756
  }
53744
53757
  }
53745
- return dsnFromComponents({ host, pass, path: path19, projectId, port, protocol, publicKey });
53758
+ return dsnFromComponents({ host, pass, path: path18, projectId, port, protocol, publicKey });
53746
53759
  }
53747
53760
  function dsnFromComponents(components) {
53748
53761
  return {
@@ -58276,9 +58289,9 @@ function stripUrlQueryAndFragment(urlPath) {
58276
58289
  return urlPath.split(/[?#]/, 1)[0];
58277
58290
  }
58278
58291
  function getSanitizedUrlString(url2) {
58279
- const { protocol, host, path: path19 } = url2;
58292
+ const { protocol, host, path: path18 } = url2;
58280
58293
  const filteredHost = host?.replace(/^.*@/, "[filtered]:[filtered]@").replace(/(:80)$/, "").replace(/(:443)$/, "") || "";
58281
- return `${protocol ? `${protocol}://` : ""}${filteredHost}${path19}`;
58294
+ return `${protocol ? `${protocol}://` : ""}${filteredHost}${path18}`;
58282
58295
  }
58283
58296
  function stripDataUrlContent(url2, includeDataPrefix = true) {
58284
58297
  if (url2.startsWith("data:")) {
@@ -59221,8 +59234,8 @@ function splitPath(filename) {
59221
59234
  const parts = splitPathRe.exec(truncated);
59222
59235
  return parts ? parts.slice(1) : [];
59223
59236
  }
59224
- function dirname(path19) {
59225
- const result = splitPath(path19);
59237
+ function dirname(path18) {
59238
+ const result = splitPath(path18);
59226
59239
  const root = result[0] || "";
59227
59240
  let dir = result[1];
59228
59241
  if (!root && !dir) {
@@ -63148,15 +63161,15 @@ var getLayerMetadata = (route, layer, layerPath) => {
63148
63161
  };
63149
63162
  }
63150
63163
  };
63151
- var getRouterPath = (path19, layer) => {
63164
+ var getRouterPath = (path18, layer) => {
63152
63165
  const stackLayer = Array.isArray(layer.handle?.stack) ? layer.handle?.stack?.[0] : void 0;
63153
63166
  if (stackLayer?.route?.path) {
63154
- return `${path19}${stackLayer.route.path}`;
63167
+ return `${path18}${stackLayer.route.path}`;
63155
63168
  }
63156
63169
  if (stackLayer && Array.isArray(stackLayer?.handle?.stack)) {
63157
- return getRouterPath(path19, stackLayer);
63170
+ return getRouterPath(path18, stackLayer);
63158
63171
  }
63159
- return path19;
63172
+ return path18;
63160
63173
  };
63161
63174
  var isLayerIgnored = (name, type, config2) => {
63162
63175
  if (Array.isArray(config2?.ignoreLayersType) && config2?.ignoreLayersType?.includes(type)) {
@@ -63176,7 +63189,7 @@ function getActualMatchedRoute(req, constructedRoute) {
63176
63189
  if (layersStore.length === 0) {
63177
63190
  return void 0;
63178
63191
  }
63179
- if (layersStore.every((path19) => path19 === "/")) {
63192
+ if (layersStore.every((path18) => path18 === "/")) {
63180
63193
  return req.originalUrl === "/" ? "/" : void 0;
63181
63194
  }
63182
63195
  if (constructedRoute === "*") {
@@ -63192,11 +63205,11 @@ function getActualMatchedRoute(req, constructedRoute) {
63192
63205
  function getConstructedRoute(req) {
63193
63206
  const layersStore = getStoredLayers(req);
63194
63207
  let constructedRoute = "";
63195
- for (const path19 of layersStore) {
63196
- if (path19 === "/" || path19 === "/*") {
63208
+ for (const path18 of layersStore) {
63209
+ if (path18 === "/" || path18 === "/*") {
63197
63210
  continue;
63198
63211
  }
63199
- constructedRoute += !constructedRoute || constructedRoute.endsWith("/") ? path19 : `/${path19}`;
63212
+ constructedRoute += !constructedRoute || constructedRoute.endsWith("/") ? path18 : `/${path18}`;
63200
63213
  }
63201
63214
  return constructedRoute.replace(/\/{2,}/g, "/");
63202
63215
  }
@@ -63701,9 +63714,9 @@ function getRequestUrlObject(requestOptions) {
63701
63714
  const hostHeader = requestOptions.headers?.host && String(requestOptions.headers?.host);
63702
63715
  const hostname4 = hostHeader || requestOptions.hostname || requestOptions.host || "";
63703
63716
  const port = !requestOptions.port || requestOptions.port === 80 || requestOptions.port === 443 || /^(.*):(\d+)$/.test(hostname4) ? "" : `:${requestOptions.port}`;
63704
- const path19 = requestOptions.path ? requestOptions.path : "/";
63717
+ const path18 = requestOptions.path ? requestOptions.path : "/";
63705
63718
  const base = `${protocol}//${hostname4}${port}`;
63706
- return new URL(path19.startsWith("//") ? `${base}${path19}` : path19, base);
63719
+ return new URL(path18.startsWith("//") ? `${base}${path18}` : path18, base);
63707
63720
  }
63708
63721
  function getRequestUrlFromClientRequest(request2) {
63709
63722
  return String(getRequestUrl(getRequestOptions(request2)));
@@ -64376,11 +64389,11 @@ function shouldIgnoreSpansForIncomingRequest(request2, {
64376
64389
  return false;
64377
64390
  }
64378
64391
  function isStaticAssetRequest(urlPath) {
64379
- const path19 = stripUrlQueryAndFragment(urlPath);
64380
- if (path19.match(/\.(ico|png|jpg|jpeg|gif|svg|css|js|woff|woff2|ttf|eot|webp|avif)$/)) {
64392
+ const path18 = stripUrlQueryAndFragment(urlPath);
64393
+ if (path18.match(/\.(ico|png|jpg|jpeg|gif|svg|css|js|woff|woff2|ttf|eot|webp|avif)$/)) {
64381
64394
  return true;
64382
64395
  }
64383
- if (path19.match(/^\/(robots\.txt|sitemap\.xml|manifest\.json|browserconfig\.xml)$/)) {
64396
+ if (path18.match(/^\/(robots\.txt|sitemap\.xml|manifest\.json|browserconfig\.xml)$/)) {
64384
64397
  return true;
64385
64398
  }
64386
64399
  return false;
@@ -64621,11 +64634,11 @@ function isKnownPrefetchRequest2(req) {
64621
64634
  return req.headers["next-router-prefetch"] === "1";
64622
64635
  }
64623
64636
  function isStaticAssetRequest2(urlPath) {
64624
- const path19 = stripUrlQueryAndFragment(urlPath);
64625
- if (path19.match(/\.(ico|png|jpg|jpeg|gif|svg|css|js|woff|woff2|ttf|eot|webp|avif)$/)) {
64637
+ const path18 = stripUrlQueryAndFragment(urlPath);
64638
+ if (path18.match(/\.(ico|png|jpg|jpeg|gif|svg|css|js|woff|woff2|ttf|eot|webp|avif)$/)) {
64626
64639
  return true;
64627
64640
  }
64628
- if (path19.match(/^\/(robots\.txt|sitemap\.xml|manifest\.json|browserconfig\.xml)$/)) {
64641
+ if (path18.match(/^\/(robots\.txt|sitemap\.xml|manifest\.json|browserconfig\.xml)$/)) {
64629
64642
  return true;
64630
64643
  }
64631
64644
  return false;
@@ -64985,19 +64998,19 @@ function getBreadcrumbData(request2) {
64985
64998
  return {};
64986
64999
  }
64987
65000
  }
64988
- function getAbsoluteUrl2(origin, path19 = "/") {
65001
+ function getAbsoluteUrl2(origin, path18 = "/") {
64989
65002
  try {
64990
- const url2 = new URL(path19, origin);
65003
+ const url2 = new URL(path18, origin);
64991
65004
  return url2.toString();
64992
65005
  } catch {
64993
65006
  const url2 = `${origin}`;
64994
- if (url2.endsWith("/") && path19.startsWith("/")) {
64995
- return `${url2}${path19.slice(1)}`;
65007
+ if (url2.endsWith("/") && path18.startsWith("/")) {
65008
+ return `${url2}${path18.slice(1)}`;
64996
65009
  }
64997
- if (!url2.endsWith("/") && !path19.startsWith("/")) {
64998
- return `${url2}/${path19}`;
65010
+ if (!url2.endsWith("/") && !path18.startsWith("/")) {
65011
+ return `${url2}/${path18}`;
64999
65012
  }
65000
- return `${url2}${path19}`;
65013
+ return `${url2}${path18}`;
65001
65014
  }
65002
65015
  }
65003
65016
 
@@ -65242,15 +65255,15 @@ function getRequestMethod(original) {
65242
65255
  }
65243
65256
  return "_OTHER";
65244
65257
  }
65245
- function getAbsoluteUrl3(origin, path19 = "/") {
65258
+ function getAbsoluteUrl3(origin, path18 = "/") {
65246
65259
  const url2 = `${origin}`;
65247
- if (url2.endsWith("/") && path19.startsWith("/")) {
65248
- return `${url2}${path19.slice(1)}`;
65260
+ if (url2.endsWith("/") && path18.startsWith("/")) {
65261
+ return `${url2}${path18.slice(1)}`;
65249
65262
  }
65250
- if (!url2.endsWith("/") && !path19.startsWith("/")) {
65251
- return `${url2}/${path19}`;
65263
+ if (!url2.endsWith("/") && !path18.startsWith("/")) {
65264
+ return `${url2}/${path18}`;
65252
65265
  }
65253
- return `${url2}${path19}`;
65266
+ return `${url2}${path18}`;
65254
65267
  }
65255
65268
 
65256
65269
  // ../../node_modules/.pnpm/@sentry+node-core@10.71.0_@opentelemetry+api@1.9.1_@opentelemetry+core@2.10.0_@opentele_e57ddc8c682f93a270df4265a143e64e/node_modules/@sentry/node-core/build/esm/integrations/node-fetch/index.js
@@ -67736,12 +67749,12 @@ function emplace(map2, key, contents) {
67736
67749
  }
67737
67750
  return value;
67738
67751
  }
67739
- function shouldSkipContextLinesForFile(path19) {
67740
- if (path19.startsWith("node:")) return true;
67741
- if (path19.endsWith(".min.js")) return true;
67742
- if (path19.endsWith(".min.cjs")) return true;
67743
- if (path19.endsWith(".min.mjs")) return true;
67744
- if (path19.startsWith("data:")) return true;
67752
+ function shouldSkipContextLinesForFile(path18) {
67753
+ if (path18.startsWith("node:")) return true;
67754
+ if (path18.endsWith(".min.js")) return true;
67755
+ if (path18.endsWith(".min.cjs")) return true;
67756
+ if (path18.endsWith(".min.mjs")) return true;
67757
+ if (path18.startsWith("data:")) return true;
67745
67758
  return false;
67746
67759
  }
67747
67760
  function shouldSkipContextLinesForFrame(frame) {
@@ -67789,9 +67802,9 @@ function makeLineReaderRanges(lines, linecontext) {
67789
67802
  }
67790
67803
  return out;
67791
67804
  }
67792
- function getContextLinesFromFile(path19, ranges, output) {
67805
+ function getContextLinesFromFile(path18, ranges, output) {
67793
67806
  return new Promise((resolve2, _reject) => {
67794
- const stream = createReadStream(path19);
67807
+ const stream = createReadStream(path18);
67795
67808
  const lineReaded = createInterface({
67796
67809
  input: stream
67797
67810
  });
@@ -67809,8 +67822,8 @@ function getContextLinesFromFile(path19, ranges, output) {
67809
67822
  let rangeStart = range[0];
67810
67823
  let rangeEnd = range[1];
67811
67824
  function onStreamError(e) {
67812
- LRU_FILE_CONTENTS_FS_READ_FAILED.set(path19, 1);
67813
- DEBUG_BUILD2 && debug.error(`Failed to read file: ${path19}. Error: ${e}`);
67825
+ LRU_FILE_CONTENTS_FS_READ_FAILED.set(path18, 1);
67826
+ DEBUG_BUILD2 && debug.error(`Failed to read file: ${path18}. Error: ${e}`);
67814
67827
  lineReaded.close();
67815
67828
  lineReaded.removeAllListeners();
67816
67829
  destroyStreamAndResolve();
@@ -69249,8 +69262,8 @@ function getSpotlightConfig(optionsSpotlight) {
69249
69262
 
69250
69263
  // ../../node_modules/.pnpm/@sentry+node-core@10.71.0_@opentelemetry+api@1.9.1_@opentelemetry+core@2.10.0_@opentele_e57ddc8c682f93a270df4265a143e64e/node_modules/@sentry/node-core/build/esm/utils/module.js
69251
69264
  import { posix, sep } from "node:path";
69252
- function normalizeWindowsPath(path19) {
69253
- return path19.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
69265
+ function normalizeWindowsPath(path18) {
69266
+ return path18.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
69254
69267
  }
69255
69268
  function createGetModuleFromFilename(basePath = process.argv[1] ? dirname(process.argv[1]) : process.cwd(), isWindows = sep === "\\") {
69256
69269
  const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;
@@ -72122,22 +72135,22 @@ function addSpanSource(span, loc, start, end) {
72122
72135
  span.setAttribute(AttributeNames2.SOURCE, source);
72123
72136
  }
72124
72137
  }
72125
- function createFieldIfNotExists(contextValue, info, path19) {
72126
- let field = getField(contextValue, path19);
72138
+ function createFieldIfNotExists(contextValue, info, path18) {
72139
+ let field = getField(contextValue, path18);
72127
72140
  if (field) {
72128
72141
  return { field, spanAdded: false };
72129
72142
  }
72130
- const parentSpan = getParentFieldSpan(contextValue, path19);
72143
+ const parentSpan = getParentFieldSpan(contextValue, path18);
72131
72144
  field = {
72132
- span: createResolverSpan(contextValue, info, path19, parentSpan)
72145
+ span: createResolverSpan(contextValue, info, path18, parentSpan)
72133
72146
  };
72134
- addField(contextValue, path19, field);
72147
+ addField(contextValue, path18, field);
72135
72148
  return { field, spanAdded: true };
72136
72149
  }
72137
- function createResolverSpan(contextValue, info, path19, parentSpan) {
72150
+ function createResolverSpan(contextValue, info, path18, parentSpan) {
72138
72151
  const attributes = {
72139
72152
  [AttributeNames2.FIELD_NAME]: info.fieldName,
72140
- [AttributeNames2.FIELD_PATH]: path19.join("."),
72153
+ [AttributeNames2.FIELD_PATH]: path18.join("."),
72141
72154
  [AttributeNames2.FIELD_TYPE]: info.returnType.toString(),
72142
72155
  [AttributeNames2.PARENT_NAME]: info.parentType.name
72143
72156
  };
@@ -72169,15 +72182,15 @@ function getOperation(document2, operationName) {
72169
72182
  return document2.definitions.find((definition) => OPERATION_VALUES.indexOf(definition?.operation) !== -1);
72170
72183
  }
72171
72184
  }
72172
- function addField(contextValue, path19, field) {
72173
- return contextValue[OTEL_GRAPHQL_DATA_SYMBOL].fields[path19.join(".")] = field;
72185
+ function addField(contextValue, path18, field) {
72186
+ return contextValue[OTEL_GRAPHQL_DATA_SYMBOL].fields[path18.join(".")] = field;
72174
72187
  }
72175
- function getField(contextValue, path19) {
72176
- return contextValue[OTEL_GRAPHQL_DATA_SYMBOL].fields[path19.join(".")];
72188
+ function getField(contextValue, path18) {
72189
+ return contextValue[OTEL_GRAPHQL_DATA_SYMBOL].fields[path18.join(".")];
72177
72190
  }
72178
- function getParentFieldSpan(contextValue, path19) {
72179
- for (let i = path19.length - 1; i > 0; i--) {
72180
- const field = getField(contextValue, path19.slice(0, i));
72191
+ function getParentFieldSpan(contextValue, path18) {
72192
+ for (let i = path18.length - 1; i > 0; i--) {
72193
+ const field = getField(contextValue, path18.slice(0, i));
72181
72194
  if (field) {
72182
72195
  return field.span;
72183
72196
  }
@@ -72187,9 +72200,9 @@ function getParentFieldSpan(contextValue, path19) {
72187
72200
  function getRootSpan2(contextValue) {
72188
72201
  return contextValue[OTEL_GRAPHQL_DATA_SYMBOL].span;
72189
72202
  }
72190
- function pathToArray(path19) {
72203
+ function pathToArray(path18) {
72191
72204
  const flattened = [];
72192
- let curr = path19;
72205
+ let curr = path18;
72193
72206
  while (curr) {
72194
72207
  flattened.push(String(curr.key));
72195
72208
  curr = curr.prev;
@@ -72330,8 +72343,8 @@ function wrapFieldResolver(getConfig3, fieldResolver, isDefaultResolver = false)
72330
72343
  if (!contextValue[OTEL_GRAPHQL_DATA_SYMBOL]) {
72331
72344
  return fieldResolver.call(this, source, args, contextValue, info);
72332
72345
  }
72333
- const path19 = pathToArray(info?.path);
72334
- const { field, spanAdded } = createFieldIfNotExists(contextValue, info, path19);
72346
+ const path18 = pathToArray(info?.path);
72347
+ const { field, spanAdded } = createFieldIfNotExists(contextValue, info, path18);
72335
72348
  const span = field.span;
72336
72349
  const shouldEndSpan = spanAdded;
72337
72350
  return withActiveSpan(span, () => {
@@ -75314,10 +75327,10 @@ var PostgresJsInstrumentation = class extends import_instrumentation24.Instrumen
75314
75327
  },
75315
75328
  (exports) => exports
75316
75329
  );
75317
- ["src", "cf/src", "cjs/src"].forEach((path19) => {
75330
+ ["src", "cf/src", "cjs/src"].forEach((path18) => {
75318
75331
  module2.files.push(
75319
75332
  new InstrumentationNodeModuleFile(
75320
- `postgres/${path19}/query.js`,
75333
+ `postgres/${path18}/query.js`,
75321
75334
  SUPPORTED_VERSIONS4,
75322
75335
  this._patchQueryPrototype.bind(this),
75323
75336
  this._unpatchQueryPrototype.bind(this)
@@ -75948,13 +75961,13 @@ var HonoInstrumentation = class extends import_instrumentation27.Instrumentation
75948
75961
  return function(original) {
75949
75962
  return function wrappedHandler(...args) {
75950
75963
  if (typeof args[0] === "string") {
75951
- const path19 = args[0];
75964
+ const path18 = args[0];
75952
75965
  if (args.length === 1) {
75953
- return original.apply(this, [path19]);
75966
+ return original.apply(this, [path18]);
75954
75967
  }
75955
75968
  const handlers2 = args.slice(1);
75956
75969
  return original.apply(this, [
75957
- path19,
75970
+ path18,
75958
75971
  ...handlers2.map((handler) => instrumentation._wrapHandler(handler))
75959
75972
  ]);
75960
75973
  }
@@ -75988,13 +76001,13 @@ var HonoInstrumentation = class extends import_instrumentation27.Instrumentation
75988
76001
  return function(original) {
75989
76002
  return function wrappedHandler(...args) {
75990
76003
  if (typeof args[0] === "string") {
75991
- const path19 = args[0];
76004
+ const path18 = args[0];
75992
76005
  if (args.length === 1) {
75993
- return original.apply(this, [path19]);
76006
+ return original.apply(this, [path18]);
75994
76007
  }
75995
76008
  const handlers2 = args.slice(1);
75996
76009
  return original.apply(this, [
75997
- path19,
76010
+ path18,
75998
76011
  ...handlers2.map((handler) => instrumentation._wrapHandler(handler))
75999
76012
  ]);
76000
76013
  }
@@ -76014,8 +76027,8 @@ var HonoInstrumentation = class extends import_instrumentation27.Instrumentation
76014
76027
  if (!instrumentation.isEnabled()) {
76015
76028
  return handler.apply(this, [c, next]);
76016
76029
  }
76017
- const path19 = c.req.path;
76018
- const span = instrumentation.tracer.startSpan(path19);
76030
+ const path18 = c.req.path;
76031
+ const span = instrumentation.tracer.startSpan(path18);
76019
76032
  return context.with(trace.setSpan(context.active(), span), () => {
76020
76033
  return instrumentation._safeExecute(
76021
76034
  () => {
@@ -76025,7 +76038,7 @@ var HonoInstrumentation = class extends import_instrumentation27.Instrumentation
76025
76038
  const type = instrumentation._determineHandlerType(result2);
76026
76039
  span.setAttributes({
76027
76040
  [AttributeNames5.HONO_TYPE]: type,
76028
- [AttributeNames5.HONO_NAME]: type === HonoTypes.REQUEST_HANDLER ? path19 : handler.name || "anonymous"
76041
+ [AttributeNames5.HONO_NAME]: type === HonoTypes.REQUEST_HANDLER ? path18 : handler.name || "anonymous"
76029
76042
  });
76030
76043
  instrumentation.getConfig().responseHook?.(span);
76031
76044
  return result2;
@@ -76034,7 +76047,7 @@ var HonoInstrumentation = class extends import_instrumentation27.Instrumentation
76034
76047
  const type = instrumentation._determineHandlerType(result);
76035
76048
  span.setAttributes({
76036
76049
  [AttributeNames5.HONO_TYPE]: type,
76037
- [AttributeNames5.HONO_NAME]: type === HonoTypes.REQUEST_HANDLER ? path19 : handler.name || "anonymous"
76050
+ [AttributeNames5.HONO_NAME]: type === HonoTypes.REQUEST_HANDLER ? path18 : handler.name || "anonymous"
76038
76051
  });
76039
76052
  instrumentation.getConfig().responseHook?.(span);
76040
76053
  return result;
@@ -76238,11 +76251,11 @@ var KoaInstrumentation = class extends import_instrumentation29.InstrumentationB
76238
76251
  const router = dispatchLayer.router;
76239
76252
  const routesStack = router?.stack ?? [];
76240
76253
  for (const pathLayer of routesStack) {
76241
- const path19 = pathLayer.path;
76254
+ const path18 = pathLayer.path;
76242
76255
  const pathStack = pathLayer.stack;
76243
76256
  for (let j = 0; j < pathStack.length; j++) {
76244
76257
  const routedMiddleware = pathStack[j];
76245
- pathStack[j] = this._patchLayer(routedMiddleware, true, path19);
76258
+ pathStack[j] = this._patchLayer(routedMiddleware, true, path18);
76246
76259
  }
76247
76260
  }
76248
76261
  return dispatchLayer;
@@ -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
@@ -78583,7 +78613,7 @@ function parseStoredCredentials(raw, appEnv) {
78583
78613
  const parsed = storedCredentialsSchema.safeParse(json2);
78584
78614
  if (!parsed.success) {
78585
78615
  throw new InvalidStoredCredentialsError(
78586
- `Stored sync service credentials for ${appEnv} are invalid (corrupted or from an incompatible version) \u2014 re-enroll this machine (powerplant enroll, or pnpm collector enroll from the checkout)`
78616
+ `Stored sync service credentials for ${appEnv} are invalid (corrupted or from an incompatible version), re-enroll this machine (powerplant enroll, or pnpm collector enroll from the checkout)`
78587
78617
  );
78588
78618
  }
78589
78619
  return parsed.data;
@@ -78645,14 +78675,14 @@ function fallbackFile(appEnv) {
78645
78675
 
78646
78676
  // ../../apps/collector/src/enroll.ts
78647
78677
  import { spawn } from "node:child_process";
78648
- import { randomBytes } from "node:crypto";
78678
+ import { createHash, randomBytes } from "node:crypto";
78649
78679
  import http5 from "node:http";
78650
78680
  import os6 from "node:os";
78651
78681
 
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.3.2" : "0.0.0-checkout";
78685
+ var cliVersion = true ? "0.4.1" : "0.0.0-checkout";
78656
78686
  function cliVersionHeaders() {
78657
78687
  return { [CLI_VERSION_HEADER]: cliVersion, "user-agent": `powerplant-cli/${cliVersion}` };
78658
78688
  }
@@ -78668,11 +78698,11 @@ function installHttp1Transport() {
78668
78698
  }
78669
78699
  async function fetchWithRetry(operation, url2, init3, options = {}) {
78670
78700
  const settled = (status) => status < 500 || status === 503 && options.treat503AsFinal === true;
78671
- const wrapped = (cause) => new Error(`${operation}: couldn't reach ${new URL(url2).host} \u2014 ${describeFetchFailure(cause)}`, {
78701
+ const wrapped = (cause) => new Error(`${operation}: couldn't reach ${new URL(url2).host}, ${describeFetchFailure(cause)}`, {
78672
78702
  cause
78673
78703
  });
78674
78704
  const request2 = withVersionHeader(init3);
78675
- const delaysMs = [250, 1e3];
78705
+ const delaysMs = options.retry === false ? [] : [250, 1e3];
78676
78706
  for (const delayMs of delaysMs) {
78677
78707
  try {
78678
78708
  const response = await fetch(url2, request2);
@@ -78917,9 +78947,10 @@ function captureDecision(cwd, policy) {
78917
78947
  }
78918
78948
  const repoFullName = policy.repoOf(cwd);
78919
78949
  if (repoFullName === null) return { capture: false, reason: "no-github-repo" };
78920
- const repo = repoFullName.toLowerCase();
78921
- const admitted = policy.allowedRepos !== null ? policy.allowedRepos.includes(repo) : policy.allowedOwners.length > 0 && policy.allowedOwners.includes(ownerOfRepo(repo));
78922
- return admitted ? { capture: true, via: "org-policy", repoFullName } : { capture: false, reason: "outside-policy" };
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" };
78923
78954
  }
78924
78955
  }
78925
78956
  }
@@ -78931,9 +78962,6 @@ function memberChoice(cwd, policy) {
78931
78962
  ...repoFullName !== null && { repoFullName }
78932
78963
  };
78933
78964
  }
78934
- function ownerOfRepo(repoFullName) {
78935
- return repoFullName.split("/")[0] ?? "";
78936
- }
78937
78965
  function listSome(values, max = 6) {
78938
78966
  return values.length <= max ? values.join(", ") : `${values.slice(0, max).join(", ")} and ${values.length - max} more`;
78939
78967
  }
@@ -78942,8 +78970,11 @@ function describeCapturePolicy(policy) {
78942
78970
  case "scope":
78943
78971
  return `sessions under ${policy.scopePaths.join(", ")}`;
78944
78972
  case "org-repos": {
78945
- const repoPart = policy.allowedRepos !== null ? policy.allowedRepos.length > 0 ? `sessions in the org's allowlisted repos: ${listSome(policy.allowedRepos)}` : "the org's repo allowlist is empty \u2014 no repo-based uploads" : policy.allowedOwners.length > 0 ? `sessions in GitHub repos owned by ${policy.allowedOwners.join(", ")}` : "org-owned GitHub repos \u2014 but the org has no GitHub integration, so nothing uploads";
78946
- return policy.memberFolders.length > 0 ? `${repoPart}, plus folders you added: ${policy.memberFolders.join(", ")}` : repoPart;
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, no repo-based uploads" : policy.allowedOwners.length > 0 ? `sessions in GitHub repos owned by ${policy.allowedOwners.join(", ")}` : "org-owned GitHub repos, 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;
78947
78978
  }
78948
78979
  }
78949
78980
  }
@@ -78959,6 +78990,9 @@ function describeStoredCapture(credentials) {
78959
78990
  mode: "org-repos",
78960
78991
  allowedOwners: credentials.allowedOwners ?? [],
78961
78992
  allowedRepos: credentials.allowedRepos ?? null,
78993
+ ...credentials.workspacePolicies !== void 0 && {
78994
+ workspaceClaims: credentials.workspacePolicies
78995
+ },
78962
78996
  memberFolders: credentials.scopePaths ?? [],
78963
78997
  repoOf: () => null
78964
78998
  });
@@ -79083,7 +79117,16 @@ async function fetchCapturePolicy(apiUrl, machineToken) {
79083
79117
  data: external_exports.object({
79084
79118
  owners: external_exports.array(external_exports.string()),
79085
79119
  repos: external_exports.array(external_exports.string()).nullable(),
79086
- 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()
79087
79130
  })
79088
79131
  }),
79089
79132
  await response.json().catch(() => null),
@@ -79095,6 +79138,7 @@ async function uploadSessions(apiUrl, machineToken, items) {
79095
79138
  let upserted = 0;
79096
79139
  let unchanged = 0;
79097
79140
  let outOfPolicy = 0;
79141
+ let deferred = 0;
79098
79142
  for (let offset = 0; offset < items.length; offset += MAX_INGEST_BATCH) {
79099
79143
  const batch = items.slice(offset, offset + MAX_INGEST_BATCH);
79100
79144
  const response = await fetchWithRetry("uploading session metadata", `${apiUrl}/api/ingest/sessions`, {
@@ -79113,7 +79157,8 @@ async function uploadSessions(apiUrl, machineToken, items) {
79113
79157
  data: external_exports.object({
79114
79158
  upserted: external_exports.number(),
79115
79159
  unchanged: external_exports.number(),
79116
- outOfPolicy: external_exports.number()
79160
+ outOfPolicy: external_exports.number(),
79161
+ deferred: external_exports.number().optional()
79117
79162
  })
79118
79163
  }),
79119
79164
  await response.json().catch(() => null),
@@ -79122,8 +79167,9 @@ async function uploadSessions(apiUrl, machineToken, items) {
79122
79167
  upserted += result.data.upserted;
79123
79168
  unchanged += result.data.unchanged;
79124
79169
  outOfPolicy += result.data.outOfPolicy;
79170
+ deferred += result.data.deferred ?? 0;
79125
79171
  }
79126
- return { upserted, unchanged, outOfPolicy };
79172
+ return { upserted, unchanged, outOfPolicy, deferred };
79127
79173
  }
79128
79174
  async function uploadBranchFacts(apiUrl, machineToken, facts) {
79129
79175
  let recorded = 0;
@@ -79173,6 +79219,7 @@ async function uploadRawSessions(apiUrl, machineToken, collected, readTranscript
79173
79219
  uploaded: 0,
79174
79220
  skippedTooLarge: 0,
79175
79221
  skippedUnchanged: 0,
79222
+ missingMetadata: 0,
79176
79223
  failed: [],
79177
79224
  unconfigured: false
79178
79225
  };
@@ -79205,6 +79252,10 @@ async function uploadRawSessions(apiUrl, machineToken, collected, readTranscript
79205
79252
  summary.unconfigured = true;
79206
79253
  return summary;
79207
79254
  }
79255
+ if (response.status === 404) {
79256
+ summary.missingMetadata += 1;
79257
+ continue;
79258
+ }
79208
79259
  if (!response.ok) {
79209
79260
  throw new Error(`${response.status}: ${await errorDetail(response)}`);
79210
79261
  }
@@ -79221,7 +79272,6 @@ async function uploadRawSessions(apiUrl, machineToken, collected, readTranscript
79221
79272
 
79222
79273
  // ../../apps/collector/src/enroll.ts
79223
79274
  var APPROVAL_TIMEOUT_MS = 5 * 60 * 1e3;
79224
- var MAX_DELIVERY_BYTES = 64 * 1024;
79225
79275
  var gotrueTokenSchema = external_exports.object({ access_token: external_exports.string().min(1) });
79226
79276
  var gotrueErrorSchema = external_exports.object({
79227
79277
  error_description: external_exports.string().optional(),
@@ -79288,11 +79338,12 @@ async function runEnrollment(config2, options) {
79288
79338
  log: log3
79289
79339
  });
79290
79340
  let orgPolicy = captureMode === "org-repos" ? await fetchCapturePolicy(config2.apiUrl, result.token) : void 0;
79291
- if (orgPolicy !== void 0 && orgPolicy.owners.length === 0 && orgPolicy.repos === null) {
79341
+ const anyWorkspaceClaims = (orgPolicy?.workspaces ?? []).some(workspaceClaimsAnyRepo);
79342
+ if (orgPolicy !== void 0 && orgPolicy.owners.length === 0 && orgPolicy.repos === null && !anyWorkspaceClaims) {
79292
79343
  if (config2.appEnv === "development") {
79293
79344
  const home = normalizeScopePath(os6.homedir());
79294
79345
  log3(
79295
- `No GitHub integration on org "${result.org.name}" \u2014 syncing sessions under ${home} (your home folder, local DB only). Re-enroll with --scope <path> to narrow.`
79346
+ `No GitHub integration on org "${result.org.name}", syncing sessions under ${home} (your home folder, local DB only). Re-enroll with --scope <path> to narrow.`
79296
79347
  );
79297
79348
  captureMode = "scope";
79298
79349
  scopePaths = [home];
@@ -79311,7 +79362,7 @@ async function runEnrollment(config2, options) {
79311
79362
  throw new ExpectedCliError(
79312
79363
  [
79313
79364
  `Your org "${result.org.name}" has no GitHub integration yet, so "work = org-owned repos" can't be resolved.`,
79314
- "If that isn't the org you expected, sign in with your work email \u2014 org membership follows the email's domain.",
79365
+ "If that isn't the org you expected, sign in with your work email, org membership follows the email's domain.",
79315
79366
  "Either:",
79316
79367
  ` \xB7 connect GitHub in Settings, then re-run: ${options.enrollCommand}`,
79317
79368
  " \xB7 or choose the upload boundary by hand:",
@@ -79320,10 +79371,20 @@ async function runEnrollment(config2, options) {
79320
79371
  );
79321
79372
  }
79322
79373
  }
79374
+ const workspacePolicies = orgPolicy?.workspaces?.map((workspace) => ({
79375
+ orgId: workspace.orgId,
79376
+ name: workspace.name,
79377
+ owners: workspace.owners,
79378
+ repos: workspace.repos,
79379
+ // Cached unknownness survives enrollment: an unresolved workspace's
79380
+ // claims are unknown, and the first sweep must not stamp over them.
79381
+ ...workspace.integration === "linked" && workspace.owners.length === 0 && workspace.repos === null && { unresolved: true }
79382
+ }));
79323
79383
  const policy = captureMode === "org-repos" ? {
79324
79384
  mode: "org-repos",
79325
79385
  allowedOwners: orgPolicy?.owners ?? [],
79326
79386
  allowedRepos: orgPolicy?.repos ?? null,
79387
+ ...workspacePolicies !== void 0 && { workspaceClaims: workspacePolicies },
79327
79388
  memberFolders: [],
79328
79389
  repoOf: () => null
79329
79390
  } : { mode: "scope", scopePaths, repoOf: () => null };
@@ -79338,6 +79399,7 @@ async function runEnrollment(config2, options) {
79338
79399
  captureMode,
79339
79400
  ...orgPolicy && { allowedOwners: orgPolicy.owners },
79340
79401
  ...orgPolicy?.repos != null && { allowedRepos: orgPolicy.repos },
79402
+ ...captureMode === "org-repos" && workspacePolicies !== void 0 && { workspacePolicies },
79341
79403
  scopePaths
79342
79404
  };
79343
79405
  const location = saveCredentials(config2.appEnv, credentials);
@@ -79349,24 +79411,73 @@ async function runEnrollment(config2, options) {
79349
79411
  );
79350
79412
  return credentials;
79351
79413
  }
79352
- async function enroll(config2, options, createAuthClient = gotrueAuthClient) {
79414
+ async function enroll(config2, options, createAuthClient = gotrueAuthClient, listener = {}) {
79353
79415
  if (options.email && options.password) {
79354
79416
  return enrollWithPassword(config2, options.name, options.email, options.password, createAuthClient);
79355
79417
  }
79356
79418
  const state = randomBytes(16).toString("base64url");
79419
+ const verifier = randomBytes(32).toString("base64url");
79357
79420
  return waitForApproval(
79358
- enrollApprovalUrl(config2.webUrl, options.name, state, options.capture),
79421
+ enrollApprovalUrl(config2.webUrl, {
79422
+ name: options.name,
79423
+ state,
79424
+ challenge: challengeFor(verifier),
79425
+ capture: options.capture
79426
+ }),
79359
79427
  state,
79360
- { log: options.log }
79428
+ { ...listener, log: options.log, redeem: (code) => redeemApproval(config2.apiUrl, code, verifier) }
79361
79429
  );
79362
79430
  }
79363
- function enrollApprovalUrl(webUrl, name, state, capture) {
79431
+ var challengeFor = (verifier) => createHash("sha256").update(verifier).digest("hex");
79432
+ function enrollApprovalUrl(webUrl, {
79433
+ name,
79434
+ state,
79435
+ challenge,
79436
+ capture
79437
+ }) {
79364
79438
  return `${webUrl}/enroll-machine?${new URLSearchParams({
79365
79439
  name,
79366
79440
  state,
79441
+ challenge,
79367
79442
  ...capture && { capture: capture.mode, folders: String(capture.folderCount) }
79368
79443
  }).toString()}`;
79369
79444
  }
79445
+ async function redeemApproval(apiUrl, code, verifier) {
79446
+ const response = await fetchWithRetry(
79447
+ "redeeming the enrollment approval",
79448
+ `${apiUrl}/api/machines/redeem`,
79449
+ {
79450
+ method: "POST",
79451
+ headers: { "content-type": "application/json" },
79452
+ body: JSON.stringify({ code, verifier })
79453
+ },
79454
+ { retry: false }
79455
+ );
79456
+ if (response.status === 404) {
79457
+ throw new ExpectedCliError(
79458
+ "the API doesn't know how to redeem an enrollment approval yet. It is likely older than this CLI (deploy in progress?). Try again in a minute."
79459
+ );
79460
+ }
79461
+ if (response.status >= 400 && response.status < 500) {
79462
+ const body = await response.json().catch(() => null);
79463
+ throw new ExpectedCliError(`enrollment failed: ${body?.error ?? `the API answered ${response.status}`}`);
79464
+ }
79465
+ return readEnrollmentResponse(response);
79466
+ }
79467
+ async function readEnrollmentResponse(response) {
79468
+ if (response.status !== 201) {
79469
+ const body2 = await response.json().catch(() => null);
79470
+ throw new Error(`enrollment failed (${response.status}): ${body2?.error ?? "no detail"}`);
79471
+ }
79472
+ const body = await response.json().catch(() => null);
79473
+ const enrollment = body?.data;
79474
+ if (!isEnrollResult(enrollment)) {
79475
+ throw new ExpectedCliError(
79476
+ "enrollment answered 201 without the expected machine/org/token. The API is likely older than this CLI (deploy in progress?). Try again in a minute."
79477
+ );
79478
+ }
79479
+ return enrollment;
79480
+ }
79370
79481
  async function enrollWithPassword(config2, name, email3, password, createAuthClient) {
79371
79482
  const client = createAuthClient(config2.supabaseUrl, config2.supabasePublishableKey);
79372
79483
  const { data, error: error52 } = await client.auth.signInWithPassword({ email: email3, password });
@@ -79378,18 +79489,7 @@ async function enrollWithPassword(config2, name, email3, password, createAuthCli
79378
79489
  headers: { authorization: `Bearer ${data.session.access_token}`, "content-type": "application/json" },
79379
79490
  body: JSON.stringify({ name })
79380
79491
  });
79381
- if (response.status !== 201) {
79382
- const body2 = await response.json().catch(() => null);
79383
- throw new Error(`enrollment failed (${response.status}): ${body2?.error ?? "no detail"}`);
79384
- }
79385
- const body = await response.json().catch(() => null);
79386
- const enrollment = body?.data;
79387
- if (!isEnrollResult(enrollment)) {
79388
- throw new ExpectedCliError(
79389
- "enrollment answered 201 without the expected machine/org/token \u2014 the API is likely older than this CLI (deploy in progress?). Try again in a minute."
79390
- );
79391
- }
79392
- return enrollment;
79492
+ return await readEnrollmentResponse(response);
79393
79493
  } finally {
79394
79494
  await client.auth.signOut({ scope: "local" });
79395
79495
  }
@@ -79398,61 +79498,84 @@ function isEnrollResult(value) {
79398
79498
  const candidate = value;
79399
79499
  return typeof candidate?.machine?.id === "string" && typeof candidate.machine.name === "string" && typeof candidate.org?.id === "string" && typeof candidate.org.name === "string" && typeof candidate.token === "string";
79400
79500
  }
79401
- function waitForApproval(approvalUrl, state, options = {}) {
79501
+ function waitForApproval(approvalUrl, state, options) {
79402
79502
  const {
79503
+ redeem,
79403
79504
  port = MACHINE_ENROLL_CALLBACK_PORT,
79404
79505
  open = openBrowser,
79506
+ onListening,
79405
79507
  timeoutMs = APPROVAL_TIMEOUT_MS,
79406
79508
  log: log3 = console.log
79407
79509
  } = options;
79408
79510
  return new Promise((resolve2, reject) => {
79409
79511
  const baseHeaders = { connection: "close" };
79512
+ let redeeming = false;
79410
79513
  const server = http5.createServer((request2, response) => {
79411
- if (request2.method !== "POST" || request2.url !== MACHINE_ENROLL_CALLBACK_PATH) {
79514
+ const url2 = new URL(request2.url ?? "/", "http://127.0.0.1");
79515
+ if (request2.method !== "GET" || url2.pathname !== MACHINE_ENROLL_CALLBACK_PATH) {
79412
79516
  response.writeHead(404, baseHeaders).end();
79413
79517
  return;
79414
79518
  }
79415
79519
  const answer = (status, html, onFlushed) => {
79416
79520
  response.writeHead(status, { ...baseHeaders, "content-type": "text/html; charset=utf-8" }).end(html, onFlushed);
79417
79521
  };
79418
- void readRawBody(request2).then((raw) => {
79419
- const delivery = parseDelivery(raw);
79420
- if (delivery?.state !== state) {
79421
- answer(
79422
- 403,
79423
- approvalAnswerPage(
79424
- "This isn't the enrollment the terminal is waiting for",
79425
- "Go back to the terminal that's waiting, open the link it printed, and approve from there."
79426
- )
79427
- );
79428
- return;
79429
- }
79430
- if (!isEnrollResult(delivery.data)) {
79431
- answer(
79432
- 400,
79433
- approvalAnswerPage(
79434
- "The approval couldn't be read",
79435
- "Close this tab and start over from the terminal."
79436
- )
79437
- );
79438
- return;
79439
- }
79440
- const data = delivery.data;
79522
+ if (url2.searchParams.get("state") !== state) {
79523
+ answer(
79524
+ 403,
79525
+ approvalAnswerPage(
79526
+ "This isn't the enrollment the terminal is waiting for",
79527
+ "Go back to the terminal that's waiting, open the link it printed, and approve from there."
79528
+ )
79529
+ );
79530
+ return;
79531
+ }
79532
+ const code = url2.searchParams.get("code");
79533
+ if (code === null || code === "") {
79534
+ answer(
79535
+ 400,
79536
+ approvalAnswerPage(
79537
+ "The approval couldn't be read",
79538
+ "Close this tab and start over from the terminal."
79539
+ )
79540
+ );
79541
+ return;
79542
+ }
79543
+ if (redeeming) {
79441
79544
  answer(
79545
+ 409,
79546
+ approvalAnswerPage(
79547
+ "This approval is already being handled",
79548
+ "The terminal is finishing this enrollment. You can close this tab."
79549
+ )
79550
+ );
79551
+ return;
79552
+ }
79553
+ redeeming = true;
79554
+ clearTimeout(timeout);
79555
+ void redeem(code).then(
79556
+ (data) => answer(
79442
79557
  200,
79443
79558
  approvalAnswerPage(
79444
79559
  `${data.machine.name} is enrolled in ${data.org.name}`,
79445
79560
  "You can close this tab and return to the terminal."
79446
79561
  ),
79447
79562
  () => finish(() => resolve2(data))
79448
- );
79449
- });
79563
+ ),
79564
+ (cause) => answer(
79565
+ 500,
79566
+ approvalAnswerPage(
79567
+ "The approval couldn't be completed",
79568
+ "Return to the terminal. It says what went wrong and how to retry."
79569
+ ),
79570
+ () => finish(() => reject(cause instanceof Error ? cause : new Error(String(cause))))
79571
+ )
79572
+ );
79450
79573
  });
79451
79574
  const timeout = setTimeout(
79452
79575
  () => finish(
79453
79576
  () => reject(
79454
79577
  new ExpectedCliError(
79455
- "enrollment approval timed out after 5 minutes \u2014 run the same command again to retry"
79578
+ "enrollment approval timed out after 5 minutes, run the same command again to retry"
79456
79579
  )
79457
79580
  )
79458
79581
  ),
@@ -79466,41 +79589,16 @@ function waitForApproval(approvalUrl, state, options = {}) {
79466
79589
  };
79467
79590
  server.once("error", (cause) => finish(() => reject(cause)));
79468
79591
  server.listen(port, "127.0.0.1", () => {
79592
+ const address = server.address();
79593
+ if (onListening && address !== null && typeof address === "object") onListening(address.port);
79469
79594
  log3(`Open approval page: ${approvalUrl}`);
79470
79595
  open(approvalUrl);
79471
79596
  });
79472
79597
  });
79473
79598
  }
79474
- function readRawBody(request2) {
79475
- return new Promise((resolve2) => {
79476
- const chunks = [];
79477
- let size = 0;
79478
- request2.on("data", (chunk) => {
79479
- size += chunk.length;
79480
- if (size > MAX_DELIVERY_BYTES) {
79481
- request2.destroy();
79482
- resolve2(null);
79483
- return;
79484
- }
79485
- chunks.push(chunk);
79486
- });
79487
- request2.on("end", () => resolve2(Buffer.concat(chunks).toString("utf8")));
79488
- request2.on("error", () => resolve2(null));
79489
- });
79490
- }
79491
- function parseDelivery(raw) {
79492
- if (raw === null) return null;
79493
- const fields = new URLSearchParams(raw);
79494
- let data;
79495
- try {
79496
- data = JSON.parse(fields.get("data") ?? "");
79497
- } catch {
79498
- data = void 0;
79499
- }
79500
- return { state: fields.get("state") ?? void 0, data };
79501
- }
79599
+ var PPLOGO_SVG = '<svg width="20" height="20" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M35.4167 7.5H17.5L3.75 30L17.5 52.5L30 30H17.0833L35.4167 7.5Z" fill="#FFD17A"/><path d="M24.5833 52.5H42.5L56.25 30L42.5 7.5L30 30H42.9167L24.5833 52.5Z" fill="#FFA600"/></svg>';
79502
79600
  function approvalAnswerPage(heading, detail) {
79503
- return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Powerplant</title></head><body style="margin:0;padding:4rem 1.5rem;display:flex;justify-content:center;font-family:system-ui,sans-serif"><main style="max-width:26rem"><h1 style="font-size:1.25rem">${escapeHtml(heading)}</h1><p style="line-height:1.5">${escapeHtml(detail)}</p></main></body></html>`;
79601
+ return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Powerplant</title><style>body{margin:0;min-height:100vh;display:flex;align-items:center;justify-content:center;background:oklch(98.7% 0.004 250);color:#232422;font-family:"Figtree",system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased}main{display:flex;flex-direction:column;align-items:center;gap:0.75rem;max-width:24rem;padding:1.5rem;text-align:center}.mark{display:flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;border:1px solid oklch(92.5% 0.006 250);border-radius:0.375rem;background:#ffffff}h1{margin:0;font-size:1.25rem;line-height:1.75rem;font-weight:700}p{margin:0;font-size:0.875rem;line-height:1.5;color:#6a6d69}</style></head><body><main><span class="mark">${PPLOGO_SVG}</span><h1>${escapeHtml(heading)}</h1><p>${escapeHtml(detail)}</p></main></body></html>`;
79504
79602
  }
79505
79603
  var escapeHtml = (text) => text.replace(/[&<>"']/g, (character) => `&#${character.charCodeAt(0)};`);
79506
79604
  function openBrowser(url2) {
@@ -79514,7 +79612,7 @@ function requireEnrolled(config2, enrollCommand, deps) {
79514
79612
  const credentials = deps.load(config2.appEnv);
79515
79613
  if (!credentials) {
79516
79614
  throw new ExpectedCliError(
79517
- `This machine isn't enrolled for ${config2.appEnv} \u2014 run: ${enrollCommand}`
79615
+ `This machine isn't enrolled for ${config2.appEnv}, run: ${enrollCommand}`
79518
79616
  );
79519
79617
  }
79520
79618
  return credentials;
@@ -79543,7 +79641,7 @@ function addFolder(config2, rawPath, enrollCommand, log3, deps = defaultDeps) {
79543
79641
  });
79544
79642
  const org = credentials.orgName ?? "your org";
79545
79643
  log3(
79546
- credentials.captureMode === "org-repos" ? `Sessions under ${folder} will now upload to ${org} \u2014 even outside the org's allowlisted repos.` : `Added ${folder} to this machine's sync boundary.`
79644
+ credentials.captureMode === "org-repos" ? `Sessions under ${folder} will now upload to ${org}, even outside the org's allowlisted repos.` : `Added ${folder} to this machine's sync boundary.`
79547
79645
  );
79548
79646
  log3("Takes effect on the next sync pass; run the sync command to apply now.");
79549
79647
  }
@@ -79559,14 +79657,14 @@ function removeFolder(config2, rawPath, enrollCommand, log3, deps = defaultDeps)
79559
79657
  const remaining = folders.filter((existing) => existing !== folder);
79560
79658
  if (credentials.captureMode === "scope" && remaining.length === 0) {
79561
79659
  throw new ExpectedCliError(
79562
- "A --scope enrollment needs at least one folder \u2014 re-enroll to change the sync boundary instead."
79660
+ "A --scope enrollment needs at least one folder, re-enroll to change the sync boundary instead."
79563
79661
  );
79564
79662
  }
79565
79663
  deps.persist(config2.appEnv, {
79566
79664
  ...credentials,
79567
79665
  scopePaths: remaining
79568
79666
  });
79569
- log3(`Removed ${folder} \u2014 sessions there stay local from the next sync pass.`);
79667
+ log3(`Removed ${folder}, sessions there stay local from the next sync pass.`);
79570
79668
  }
79571
79669
 
79572
79670
  // ../../apps/collector/src/protectedRepoDiscovery.ts
@@ -79705,6 +79803,7 @@ function parseClaudeCodeTranscript(text) {
79705
79803
  let cwd;
79706
79804
  let gitBranch;
79707
79805
  let anonCounter = 0;
79806
+ let speedMode;
79708
79807
  for (const record2 of parseJsonl(text)) {
79709
79808
  if (record2.type === "custom-title" && typeof record2.customTitle === "string") {
79710
79809
  title = record2.customTitle;
@@ -79723,25 +79822,42 @@ function parseClaudeCodeTranscript(text) {
79723
79822
  const message = record2.message;
79724
79823
  if (!message || message.model === "<synthetic>") continue;
79725
79824
  const id = message.id ?? (typeof record2.requestId === "string" ? record2.requestId : void 0) ?? (typeof record2.uuid === "string" ? record2.uuid : void 0) ?? `anon-${anonCounter++}`;
79726
- const isNewMessage = !usageByMessage.has(id);
79727
- if (message.usage) usageByMessage.set(id, toUsage(message.usage));
79728
- else if (isNewMessage) usageByMessage.set(id, { input: 0, cacheRead: 0, cacheCreation: 0, output: 0 });
79825
+ const previousMessage = usageByMessage.get(id);
79826
+ const isNewMessage = previousMessage === void 0;
79827
+ const speedValue = message.usage?.speed;
79828
+ const observedSpeed = speedModeOf(speedValue);
79829
+ const messageSpeed = observedSpeed ?? previousMessage?.speedMode;
79830
+ if (speedValue !== void 0) speedMode = observedSpeed ?? "unknown";
79831
+ if (message.usage) {
79832
+ usageByMessage.set(id, {
79833
+ usage: toUsage(message.usage),
79834
+ ...messageSpeed && { speedMode: messageSpeed }
79835
+ });
79836
+ } else if (isNewMessage) {
79837
+ usageByMessage.set(id, {
79838
+ usage: { input: 0, cacheRead: 0, cacheCreation: 0, output: 0 }
79839
+ });
79840
+ }
79729
79841
  if (isNewMessage && typeof message.model === "string") {
79730
79842
  modelCounts.set(message.model, (modelCounts.get(message.model) ?? 0) + 1);
79731
79843
  }
79732
79844
  }
79733
79845
  if (usageByMessage.size === 0 && timestamps.length === 0) return null;
79734
- const summed = [...usageByMessage.values()].reduce(addUsage, {
79846
+ const summed = [...usageByMessage.values()].map((entry) => entry.usage).reduce(addUsage, {
79735
79847
  input: 0,
79736
79848
  cacheRead: 0,
79737
79849
  cacheCreation: 0,
79738
79850
  output: 0
79739
79851
  });
79852
+ const speedFullyMeasured = usageByMessage.size > 0 && [...usageByMessage.values()].every((entry) => entry.speedMode !== void 0);
79853
+ 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;
79740
79854
  return {
79741
79855
  startedAt: timestamps[0],
79742
79856
  endedAt: timestamps[timestamps.length - 1],
79743
79857
  model: dominantModel(modelCounts),
79744
79858
  usage: usageByMessage.size > 0 ? summed : null,
79859
+ fastUsage,
79860
+ ...speedMode && { speedMode },
79745
79861
  title,
79746
79862
  turnCount: usageByMessage.size,
79747
79863
  cwd,
@@ -79752,9 +79868,13 @@ async function collectClaudeCodeSessions(options = {}) {
79752
79868
  const claudeDir = options.claudeDir ?? path5.join(os7.homedir(), ".claude");
79753
79869
  const now = options.now ?? Date.now();
79754
79870
  const capture = options.capture ?? captureEverythingUnresolved;
79871
+ const maxBytes = options.maxTranscriptBytes ?? MAX_TRANSCRIPT_FILE_BYTES;
79755
79872
  const projectsDir = path5.join(claudeDir, "projects");
79756
79873
  const items = [];
79757
79874
  const skips = { skippedOutOfScope: 0, skippedNoRepo: 0 };
79875
+ const candidates = [];
79876
+ const unreadable = [];
79877
+ let skippedTooLarge = 0;
79758
79878
  const home = path5.dirname(claudeDir);
79759
79879
  const lifecycle = options.lifecycle ?? readLifecycleJournal(home);
79760
79880
  for (const projectDir of listDirs(projectsDir)) {
@@ -79764,39 +79884,76 @@ async function collectClaudeCodeSessions(options = {}) {
79764
79884
  const stat = statIfExists(transcriptPath);
79765
79885
  if (!stat) continue;
79766
79886
  if (options.sinceMs !== void 0 && stat.mtimeMs < options.sinceMs) continue;
79887
+ if (stat.size > maxBytes) {
79888
+ skippedTooLarge += 1;
79889
+ continue;
79890
+ }
79767
79891
  const sessionId = path5.basename(entry, ".jsonl");
79768
79892
  const endObservedAt = observedSessionEnd(lifecycle.get(sessionId), stat.mtimeMs);
79769
79893
  const status = endObservedAt === null && now - stat.mtimeMs < ACTIVE_GRACE_MS ? "active" : "inactive";
79770
- if (options.locationsOnly) {
79894
+ try {
79771
79895
  const cwd = transcriptCwd(transcriptPath);
79772
- const decision2 = captureDecision(cwd, capture);
79773
- if (!decision2.capture) {
79774
- countCaptureSkip(skips, decision2.reason);
79896
+ const decision = captureDecision(cwd, capture);
79897
+ if (!decision.capture) {
79898
+ countCaptureSkip(skips, decision.reason);
79775
79899
  continue;
79776
79900
  }
79777
- items.push({
79778
- mtimeMs: stat.mtimeMs,
79901
+ if (options.locationsOnly) {
79902
+ items.push({
79903
+ mtimeMs: stat.mtimeMs,
79904
+ transcriptPath,
79905
+ ...cwd && { cwd },
79906
+ item: { source: "claude-code", externalId: sessionId, status, capturedVia: decision.via }
79907
+ });
79908
+ continue;
79909
+ }
79910
+ candidates.push({
79779
79911
  transcriptPath,
79912
+ projectDir,
79913
+ sessionId,
79914
+ mtimeMs: stat.mtimeMs,
79915
+ status,
79916
+ endObservedAt,
79780
79917
  ...cwd && { cwd },
79781
- item: { source: "claude-code", externalId: sessionId, status, capturedVia: decision2.via }
79918
+ decision
79782
79919
  });
79783
- continue;
79920
+ } catch (error52) {
79921
+ unreadable.push(`${sessionId}: ${describeReadError(error52)}`);
79784
79922
  }
79923
+ }
79924
+ }
79925
+ if (options.locationsOnly) {
79926
+ return {
79927
+ sessions: items.sort((a, b2) => b2.mtimeMs - a.mtimeMs),
79928
+ ...skips,
79929
+ skippedTooLarge,
79930
+ unreadable,
79931
+ heldBack: 0
79932
+ };
79933
+ }
79934
+ candidates.sort((a, b2) => b2.mtimeMs - a.mtimeMs);
79935
+ const chosen = options.maxSessions === void 0 ? candidates : candidates.slice(0, options.maxSessions);
79936
+ for (const candidate of chosen) {
79937
+ await yieldToEventLoop();
79938
+ const { transcriptPath, projectDir, sessionId, mtimeMs, status, endObservedAt, cwd, decision } = candidate;
79939
+ try {
79785
79940
  const text = readIfExists2(transcriptPath);
79786
79941
  if (text === null) continue;
79787
79942
  const summary = parseClaudeCodeTranscript(text);
79788
79943
  if (!summary) continue;
79789
- const decision = captureDecision(summary.cwd, capture);
79790
- if (!decision.capture) {
79791
- countCaptureSkip(skips, decision.reason);
79792
- continue;
79793
- }
79794
- const usage = addSubagentUsage(summary.usage, path5.join(projectDir, sessionId, "subagents"));
79944
+ const usage = addSubagentUsage(
79945
+ summary.usage,
79946
+ summary.fastUsage,
79947
+ path5.join(projectDir, sessionId, "subagents"),
79948
+ { maxBytes, sessionId }
79949
+ );
79950
+ skippedTooLarge += usage.skippedTooLarge;
79951
+ unreadable.push(...usage.unreadable);
79795
79952
  const endedAt = endObservedAt ?? summary.endedAt;
79796
79953
  items.push({
79797
- mtimeMs: stat.mtimeMs,
79954
+ mtimeMs,
79798
79955
  transcriptPath,
79799
- ...summary.cwd && { cwd: summary.cwd },
79956
+ ...cwd && { cwd },
79800
79957
  item: {
79801
79958
  source: "claude-code",
79802
79959
  externalId: sessionId,
@@ -79804,28 +79961,42 @@ async function collectClaudeCodeSessions(options = {}) {
79804
79961
  ...summary.startedAt && { startedAt: summary.startedAt },
79805
79962
  ...endedAt && { endedAt },
79806
79963
  ...summary.model && { model: summary.model },
79964
+ ...summary.speedMode && { speedMode: summary.speedMode },
79807
79965
  // Total input-class tokens the source reported (uncached + cache
79808
79966
  // reads + cache writes) — a real reported total, not an estimate.
79809
79967
  // The cache breakdown rides along: pricing bills reads at 0.1×
79810
79968
  // and writes at 1.25×, so collapsing it would inflate estimates.
79811
- ...usage && { inputTokens: usage.input + usage.cacheRead + usage.cacheCreation },
79812
- ...usage && { outputTokens: usage.output },
79813
- ...usage && { cacheReadTokens: usage.cacheRead },
79814
- ...usage && { cacheWriteTokens: usage.cacheCreation },
79969
+ ...usage.total && {
79970
+ inputTokens: usage.total.input + usage.total.cacheRead + usage.total.cacheCreation
79971
+ },
79972
+ ...usage.total && { outputTokens: usage.total.output },
79973
+ ...usage.total && { cacheReadTokens: usage.total.cacheRead },
79974
+ ...usage.total && { cacheWriteTokens: usage.total.cacheCreation },
79975
+ ...usage.fast && {
79976
+ fastInputTokens: usage.fast.input + usage.fast.cacheRead + usage.fast.cacheCreation,
79977
+ fastOutputTokens: usage.fast.output,
79978
+ fastCacheReadTokens: usage.fast.cacheRead,
79979
+ fastCacheWriteTokens: usage.fast.cacheCreation
79980
+ },
79815
79981
  ...summary.title && { title: redactText(summary.title).slice(0, 500) },
79816
79982
  // The PR-join coordinates: the checkout's directory name (bare
79817
79983
  // repo, not the local path) and the branch the work started on.
79818
- ...summary.cwd && { repoDir: path5.basename(summary.cwd) },
79984
+ ...cwd && { repoDir: path5.basename(cwd) },
79819
79985
  ...summary.gitBranch && { gitBranch: summary.gitBranch },
79820
79986
  ...decision.repoFullName && { repoFullName: decision.repoFullName },
79821
79987
  capturedVia: decision.via
79822
79988
  }
79823
79989
  });
79990
+ } catch (error52) {
79991
+ unreadable.push(`${sessionId}: ${describeReadError(error52)}`);
79824
79992
  }
79825
79993
  }
79826
79994
  return {
79827
79995
  sessions: items.sort((a, b2) => b2.mtimeMs - a.mtimeMs),
79828
- ...skips
79996
+ ...skips,
79997
+ skippedTooLarge,
79998
+ unreadable,
79999
+ heldBack: candidates.length - chosen.length
79829
80000
  };
79830
80001
  }
79831
80002
  function transcriptCwd(transcriptPath) {
@@ -79844,6 +80015,9 @@ function toUsage(u) {
79844
80015
  output: n(u.output_tokens)
79845
80016
  };
79846
80017
  }
80018
+ function speedModeOf(value) {
80019
+ return value === "fast" || value === "standard" ? value : void 0;
80020
+ }
79847
80021
  function addUsage(a, b2) {
79848
80022
  return {
79849
80023
  input: a.input + b2.input,
@@ -79852,15 +80026,36 @@ function addUsage(a, b2) {
79852
80026
  output: a.output + b2.output
79853
80027
  };
79854
80028
  }
79855
- function addSubagentUsage(main2, subagentsDir) {
79856
- let usage = main2;
80029
+ function addSubagentUsage(main2, mainFast, subagentsDir, bounds) {
80030
+ let total = main2;
80031
+ let fast = mainFast;
80032
+ let fastCoverageComplete = main2 === null || mainFast !== null;
80033
+ let skippedTooLarge = 0;
80034
+ const unreadable = [];
79857
80035
  for (const entry of listFiles(subagentsDir, ".jsonl")) {
79858
- const text = readIfExists2(path5.join(subagentsDir, entry));
79859
- if (text === null) continue;
79860
- const summary = parseClaudeCodeTranscript(text);
79861
- if (summary?.usage) usage = addUsage(usage ?? { input: 0, cacheRead: 0, cacheCreation: 0, output: 0 }, summary.usage);
80036
+ const sidecarPath = path5.join(subagentsDir, entry);
80037
+ try {
80038
+ const stat = statIfExists(sidecarPath);
80039
+ if (stat === null) continue;
80040
+ if (stat.size > bounds.maxBytes) {
80041
+ skippedTooLarge += 1;
80042
+ continue;
80043
+ }
80044
+ const text = readIfExists2(sidecarPath);
80045
+ if (text === null) continue;
80046
+ const summary = parseClaudeCodeTranscript(text);
80047
+ if (summary?.usage) {
80048
+ total = addUsage(total ?? { input: 0, cacheRead: 0, cacheCreation: 0, output: 0 }, summary.usage);
80049
+ if (summary.fastUsage === null) fastCoverageComplete = false;
80050
+ }
80051
+ if (summary?.fastUsage) {
80052
+ fast = addUsage(fast ?? { input: 0, cacheRead: 0, cacheCreation: 0, output: 0 }, summary.fastUsage);
80053
+ }
80054
+ } catch (error52) {
80055
+ unreadable.push(`${bounds.sessionId} (subagent ${entry}): ${describeReadError(error52)}`);
80056
+ }
79862
80057
  }
79863
- return usage;
80058
+ return { total, fast: fastCoverageComplete ? fast : null, skippedTooLarge, unreadable };
79864
80059
  }
79865
80060
  function dominantModel(counts) {
79866
80061
  let best;
@@ -79879,6 +80074,10 @@ function parseCodexRollout(text) {
79879
80074
  let meta3 = null;
79880
80075
  let model;
79881
80076
  let totals = null;
80077
+ let fastTotals = { input: 0, cached: 0, output: 0 };
80078
+ let speedMode;
80079
+ let fastCoverageComplete = true;
80080
+ let classifiedUsage = false;
79882
80081
  const timestamps = [];
79883
80082
  for (const line of parseJsonl(text)) {
79884
80083
  if (typeof line.timestamp === "string") timestamps.push(line.timestamp);
@@ -79889,15 +80088,38 @@ function parseCodexRollout(text) {
79889
80088
  meta3 ??= record2;
79890
80089
  } else if (line.type === "turn_context" && typeof record2.model === "string") {
79891
80090
  model = record2.model;
80091
+ } else if (line.type === "event_msg" && record2.type === "thread_settings_applied") {
80092
+ const settings = record2.thread_settings;
80093
+ if (settings && typeof settings === "object" && !Array.isArray(settings)) {
80094
+ const tier = settings.service_tier;
80095
+ if (tier !== void 0) speedMode = codexSpeedMode(tier) ?? "unknown";
80096
+ }
79892
80097
  } else if (line.type === "event_msg" && record2.type === "token_count") {
79893
80098
  const info = record2.info;
79894
80099
  const usage = info?.total_token_usage;
79895
80100
  if (usage) {
79896
80101
  const n = (value) => typeof value === "number" ? value : 0;
79897
- totals ??= { input: 0, cached: 0, output: 0 };
79898
- totals.input = Math.max(totals.input, n(usage.input_tokens));
79899
- totals.cached = Math.max(totals.cached, n(usage.cached_input_tokens));
79900
- totals.output = Math.max(totals.output, n(usage.output_tokens));
80102
+ const next = {
80103
+ input: n(usage.input_tokens),
80104
+ cached: n(usage.cached_input_tokens),
80105
+ output: n(usage.output_tokens)
80106
+ };
80107
+ const previous = totals ?? { input: 0, cached: 0, output: 0 };
80108
+ const delta = {
80109
+ input: Math.max(0, next.input - previous.input),
80110
+ cached: Math.max(0, next.cached - previous.cached),
80111
+ output: Math.max(0, next.output - previous.output)
80112
+ };
80113
+ if (delta.input > 0 || delta.cached > 0 || delta.output > 0) {
80114
+ if (speedMode === "fast" || speedMode === "standard") classifiedUsage = true;
80115
+ else fastCoverageComplete = false;
80116
+ if (speedMode === "fast") fastTotals = addCodexTotals(fastTotals, delta);
80117
+ }
80118
+ totals = {
80119
+ input: Math.max(previous.input, next.input),
80120
+ cached: Math.max(previous.cached, next.cached),
80121
+ output: Math.max(previous.output, next.output)
80122
+ };
79901
80123
  }
79902
80124
  }
79903
80125
  }
@@ -79911,6 +80133,8 @@ function parseCodexRollout(text) {
79911
80133
  endedAt: timestamps[timestamps.length - 1],
79912
80134
  ...model && { model },
79913
80135
  totals,
80136
+ fastTotals: fastCoverageComplete && classifiedUsage ? fastTotals : null,
80137
+ ...speedMode && { speedMode },
79914
80138
  ...typeof meta3?.cwd === "string" && meta3.cwd !== "" && { cwd: meta3.cwd },
79915
80139
  ...typeof git?.branch === "string" && git.branch !== "" && { gitBranch: git.branch },
79916
80140
  ...repoNameFromUrl(git?.repository_url) ?? {},
@@ -79924,14 +80148,23 @@ function parseRolloutFile(rolloutPath) {
79924
80148
  function rolloutLocation(rolloutPath) {
79925
80149
  const record2 = readFirstRecord(rolloutPath, (candidate) => candidate.type === "session_meta");
79926
80150
  const meta3 = record2?.payload;
79927
- if (!meta3 || typeof meta3 !== "object" || Array.isArray(meta3)) return { kind: "session", totals: null };
80151
+ if (!meta3 || typeof meta3 !== "object" || Array.isArray(meta3)) return { kind: "session", totals: null, fastTotals: null };
79928
80152
  const { thread_source: threadSource, cwd } = meta3;
79929
80153
  return {
79930
80154
  kind: threadSource === "subagent" ? "subagent" : "session",
79931
80155
  totals: null,
80156
+ fastTotals: null,
79932
80157
  ...typeof cwd === "string" && cwd !== "" && { cwd }
79933
80158
  };
79934
80159
  }
80160
+ function codexSpeedMode(value) {
80161
+ if (value === "priority" || value === "fast") return "fast";
80162
+ if (value === "default") return "standard";
80163
+ return void 0;
80164
+ }
80165
+ function addCodexTotals(a, b2) {
80166
+ return { input: a.input + b2.input, cached: a.cached + b2.cached, output: a.output + b2.output };
80167
+ }
79935
80168
  function repoFullNameFrom(url2) {
79936
80169
  const fullName = parseGithubRepo(url2);
79937
80170
  return fullName ? { repoFullName: fullName } : {};
@@ -80062,6 +80295,7 @@ async function collectCodexSessions(options = {}) {
80062
80295
  ...summary.startedAt && { startedAt: summary.startedAt },
80063
80296
  ...summary.endedAt && { endedAt: summary.endedAt },
80064
80297
  ...summary.model && { model: summary.model },
80298
+ ...summary.speedMode && { speedMode: summary.speedMode },
80065
80299
  ...title && { title: redactText(title).slice(0, 500) },
80066
80300
  // input_tokens already includes the cached subset — it IS the
80067
80301
  // all-in input-class total. No cache-write figure exists, so
@@ -80069,6 +80303,12 @@ async function collectCodexSessions(options = {}) {
80069
80303
  ...summary.totals && { inputTokens: summary.totals.input },
80070
80304
  ...summary.totals && { outputTokens: summary.totals.output },
80071
80305
  ...summary.totals && { cacheReadTokens: summary.totals.cached },
80306
+ ...summary.fastTotals && { fastInputTokens: summary.fastTotals.input },
80307
+ ...summary.fastTotals && { fastOutputTokens: summary.fastTotals.output },
80308
+ ...summary.fastTotals && { fastCacheReadTokens: summary.fastTotals.cached },
80309
+ // OpenAI prompt-cache writes have no separate price in the current
80310
+ // session cost model; zero is measured here, not an invented count.
80311
+ ...summary.fastTotals && { fastCacheWriteTokens: 0 },
80072
80312
  ...repoDir && { repoDir },
80073
80313
  ...summary.gitBranch && { gitBranch: summary.gitBranch },
80074
80314
  ...repoFullName && { repoFullName },
@@ -80090,7 +80330,7 @@ async function collectCodexSessions(options = {}) {
80090
80330
  }
80091
80331
 
80092
80332
  // ../../apps/collector/src/cursorLocal.ts
80093
- import { createHash } from "node:crypto";
80333
+ import { createHash as createHash2 } from "node:crypto";
80094
80334
  import { mkdirSync as mkdirSync2, utimesSync, writeFileSync as writeFileSync2 } from "node:fs";
80095
80335
  import os9 from "node:os";
80096
80336
  import path7 from "node:path";
@@ -80117,6 +80357,8 @@ var emptyResult = () => ({
80117
80357
  skippedSubagentComposers: 0,
80118
80358
  skippedNonLocalComposers: 0,
80119
80359
  skippedUnreadable: 0,
80360
+ skippedTooLarge: 0,
80361
+ truncatedTranscripts: 0,
80120
80362
  unavailable: null
80121
80363
  });
80122
80364
  var SQLITE_EXPERIMENTAL_WARNING = "SQLite is an experimental feature and might change at any time";
@@ -80144,9 +80386,6 @@ async function openCursorDatabase(dbPath) {
80144
80386
  const sqlite = await loadSqlite();
80145
80387
  return new sqlite.DatabaseSync(dbPath, { readOnly: true });
80146
80388
  }
80147
- function describeError(error52) {
80148
- return error52 instanceof Error ? error52.message : String(error52);
80149
- }
80150
80389
  function isRecord3(value) {
80151
80390
  return value !== null && typeof value === "object" && !Array.isArray(value);
80152
80391
  }
@@ -80160,7 +80399,7 @@ function safeJsonRecord2(text) {
80160
80399
  }
80161
80400
  function spoolFileName(composerId) {
80162
80401
  const sanitized = composerId.replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 80);
80163
- const hash2 = createHash("sha256").update(composerId).digest("hex").slice(0, 8);
80402
+ const hash2 = createHash2("sha256").update(composerId).digest("hex").slice(0, 8);
80164
80403
  return `${sanitized}-${hash2}.jsonl`;
80165
80404
  }
80166
80405
  function workspaceFolderOf(cursorUserDir, workspaceId) {
@@ -80221,6 +80460,8 @@ function toolFilePathOf(args) {
80221
80460
  }
80222
80461
  return void 0;
80223
80462
  }
80463
+ var SPOOL_META_RESERVE_BYTES = 64 * 1024;
80464
+ var lineBytes = (line) => Buffer.byteLength(JSON.stringify(line)) + 1;
80224
80465
  function bubbleLines(bubble) {
80225
80466
  const lines = [];
80226
80467
  const createdAt = typeof bubble.createdAt === "string" ? { createdAt: bubble.createdAt } : {};
@@ -80260,43 +80501,50 @@ function bubbleLines(bubble) {
80260
80501
  }
80261
80502
  return lines;
80262
80503
  }
80263
- function extractTranscript(db, composerId) {
80504
+ function extractTranscript(db, composerId, maxBytes) {
80264
80505
  const valueByKey = db.prepare("select value from cursorDiskKV where key = ?");
80265
80506
  const dataRow = valueByKey.get(`composerData:${composerId}`);
80266
80507
  const dataText = isRecord3(dataRow) && typeof dataRow.value === "string" ? dataRow.value : null;
80267
80508
  const data = dataText === null ? null : safeJsonRecord2(dataText);
80268
80509
  const spine = Array.isArray(data?.fullConversationHeadersOnly) ? data.fullConversationHeadersOnly : [];
80269
80510
  if (spine.length === 0) return null;
80511
+ const budget = maxBytes - SPOOL_META_RESERVE_BYTES;
80270
80512
  const lines = [];
80271
80513
  const modelCounts = /* @__PURE__ */ new Map();
80514
+ let bytes = 0;
80272
80515
  let bubbleCount = 0;
80273
- for (const entry of spine) {
80516
+ let bubblesOmitted = 0;
80517
+ for (const [index, entry] of spine.entries()) {
80274
80518
  if (!isRecord3(entry) || typeof entry.bubbleId !== "string") continue;
80275
80519
  const row = valueByKey.get(`bubbleId:${composerId}:${entry.bubbleId}`);
80276
80520
  const text = isRecord3(row) && typeof row.value === "string" ? row.value : null;
80277
80521
  const bubble = text === null ? null : safeJsonRecord2(text);
80278
80522
  if (bubble === null) continue;
80523
+ const bubbleLinesOut = bubbleLines(bubble);
80524
+ const bubbleBytes = bubbleLinesOut.reduce((sum2, line) => sum2 + lineBytes(line), 0);
80525
+ if (bytes + bubbleBytes > budget) {
80526
+ bubblesOmitted = spine.length - index;
80527
+ break;
80528
+ }
80529
+ bytes += bubbleBytes;
80279
80530
  bubbleCount += 1;
80280
- for (const line of bubbleLines(bubble)) {
80531
+ for (const line of bubbleLinesOut) {
80281
80532
  lines.push(line);
80282
80533
  if (line.type === "agent" && line.model !== void 0) {
80283
80534
  modelCounts.set(line.model, (modelCounts.get(line.model) ?? 0) + 1);
80284
80535
  }
80285
80536
  }
80286
80537
  }
80287
- if (bubbleCount === 0) return null;
80538
+ if (bubbleCount === 0 && bubblesOmitted === 0) return null;
80288
80539
  let model;
80289
80540
  let best = 0;
80290
80541
  for (const [name, count] of modelCounts) {
80291
80542
  if (count > best) [model, best] = [name, count];
80292
80543
  }
80293
- return { lines, ...model && { model }, bubbleCount };
80544
+ return { lines, ...model && { model }, bubbleCount, bubblesOmitted };
80294
80545
  }
80295
80546
  function readSpoolModel(spoolPath) {
80296
- const text = readIfExists2(spoolPath);
80297
- if (text === null) return void 0;
80298
- const newline = text.indexOf("\n");
80299
- const record2 = safeJsonRecord2(newline === -1 ? text : text.slice(0, newline));
80547
+ const record2 = readFirstRecord(spoolPath, () => true);
80300
80548
  if (record2 === null || record2.type !== "meta" || record2.format !== "cursor-local-transcript") {
80301
80549
  return void 0;
80302
80550
  }
@@ -80309,20 +80557,20 @@ async function collectCursorSessions(options = {}) {
80309
80557
  try {
80310
80558
  if (statIfExists(dbPath) === null) return result;
80311
80559
  } catch (error52) {
80312
- result.unavailable = `Cursor's session store isn't readable (${describeError(error52)})`;
80560
+ result.unavailable = `Cursor's session store isn't readable (${describeReadError(error52)})`;
80313
80561
  return result;
80314
80562
  }
80315
80563
  let db;
80316
80564
  try {
80317
80565
  db = await openCursorDatabase(dbPath);
80318
80566
  } catch (error52) {
80319
- result.unavailable = `Cursor's session store couldn't be opened (${describeError(error52)})`;
80567
+ result.unavailable = `Cursor's session store couldn't be opened (${describeReadError(error52)})`;
80320
80568
  return result;
80321
80569
  }
80322
80570
  try {
80323
80571
  collectFromStore(db, userDir, options, result);
80324
80572
  } catch (error52) {
80325
- result.unavailable = `reading Cursor's session store failed (${describeError(error52)})`;
80573
+ result.unavailable = `reading Cursor's session store failed (${describeReadError(error52)})`;
80326
80574
  } finally {
80327
80575
  try {
80328
80576
  db.close();
@@ -80336,6 +80584,7 @@ function collectFromStore(db, userDir, options, result) {
80336
80584
  const now = options.now ?? Date.now();
80337
80585
  const capture = options.capture ?? captureEverythingUnresolved;
80338
80586
  const spoolDir = options.spoolDir ?? cursorSpoolDir(os9.homedir());
80587
+ const maxBytes = options.maxTranscriptBytes ?? MAX_TRANSCRIPT_FILE_BYTES;
80339
80588
  let spoolDirReady = false;
80340
80589
  const headerRows = db.prepare(
80341
80590
  "select composerId, workspaceId, createdAt, lastUpdatedAt, isArchived, isSubagent, value from composerHeaders where lastUpdatedAt >= ? order by lastUpdatedAt desc"
@@ -80385,10 +80634,15 @@ function collectFromStore(db, userDir, options, result) {
80385
80634
  const spoolStat = statIfExists(spoolPath);
80386
80635
  let model;
80387
80636
  if (spoolStat !== null && Math.abs(spoolStat.mtimeMs - lastUpdatedAt) < 1) {
80637
+ if (spoolStat.size > maxBytes) {
80638
+ result.skippedTooLarge += 1;
80639
+ continue;
80640
+ }
80388
80641
  model = readSpoolModel(spoolPath);
80389
80642
  } else {
80390
- const transcript = extractTranscript(db, raw.composerId);
80643
+ const transcript = extractTranscript(db, raw.composerId, maxBytes);
80391
80644
  if (transcript === null) continue;
80645
+ if (transcript.bubblesOmitted > 0) result.truncatedTranscripts += 1;
80392
80646
  model = transcript.model;
80393
80647
  const meta3 = {
80394
80648
  type: "meta",
@@ -80400,7 +80654,8 @@ function collectFromStore(db, userDir, options, result) {
80400
80654
  ...model && { model },
80401
80655
  ...typeof raw.createdAt === "number" && { createdAt: raw.createdAt },
80402
80656
  lastUpdatedAt,
80403
- ...cwd && { workspacePath: cwd }
80657
+ ...cwd && { workspacePath: cwd },
80658
+ ...transcript.bubblesOmitted > 0 && { bubblesOmitted: transcript.bubblesOmitted }
80404
80659
  };
80405
80660
  if (!spoolDirReady) {
80406
80661
  mkdirSync2(spoolDir, { recursive: true });
@@ -80518,7 +80773,7 @@ function allowProtectedRepoDiscovery(config2, found, environment = {}, deps = de
80518
80773
  const credentials = deps.load(config2.appEnv);
80519
80774
  if (credentials === null || credentials.token !== enrollment.token || credentials.enrolledAt !== enrollment.enrolledAt) {
80520
80775
  throw new ExpectedCliError(
80521
- "This machine's enrollment changed while the folders were being reviewed, so the approval was not saved \u2014 run `powerplant init` again to review them."
80776
+ "This machine's enrollment changed while the folders were being reviewed, so the approval was not saved, run `powerplant init` again to review them."
80522
80777
  );
80523
80778
  }
80524
80779
  const allowedProtectedRoots = [
@@ -80534,17 +80789,16 @@ function allowProtectedRepoDiscovery(config2, found, environment = {}, deps = de
80534
80789
  }
80535
80790
 
80536
80791
  // ../../apps/collector/src/hookInstall.ts
80537
- import { existsSync, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "node:fs";
80792
+ import { existsSync, mkdirSync as mkdirSync3, readFileSync as readFileSync6, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
80538
80793
  import os11 from "node:os";
80539
80794
  import path9 from "node:path";
80540
80795
  import { fileURLToPath as fileURLToPath2 } from "node:url";
80541
- var INBOX_HOOK_FILENAME = "powerplant-inbox.mjs";
80542
80796
  var LIFECYCLE_HOOK_FILENAME = "powerplant-session-lifecycle.mjs";
80543
80797
  var HOOK_WIRINGS = [
80544
- { event: "UserPromptSubmit", script: INBOX_HOOK_FILENAME },
80545
80798
  { event: "SessionStart", script: LIFECYCLE_HOOK_FILENAME },
80546
80799
  { event: "SessionEnd", script: LIFECYCLE_HOOK_FILENAME }
80547
80800
  ];
80801
+ var RETIRED_HOOK_WIRINGS = [{ event: "UserPromptSubmit", script: "powerplant-inbox.mjs" }];
80548
80802
  var HOOK_SCRIPTS = [...new Set(HOOK_WIRINGS.map((wiring) => wiring.script))];
80549
80803
  function isOurGroup(group, script) {
80550
80804
  return Array.isArray(group.hooks) && group.hooks.some((hook) => typeof hook.command === "string" && hook.command.includes(script));
@@ -80565,9 +80819,10 @@ function mergeCollectorHooks(settingsText, commandFor) {
80565
80819
  }
80566
80820
  }
80567
80821
  const hooks = settings.hooks !== null && typeof settings.hooks === "object" && !Array.isArray(settings.hooks) ? { ...settings.hooks } : {};
80822
+ const groupsOf = (event) => Array.isArray(hooks[event]) ? hooks[event] : [];
80568
80823
  let changed = false;
80569
80824
  for (const wiring of HOOK_WIRINGS) {
80570
- const groups = Array.isArray(hooks[wiring.event]) ? hooks[wiring.event] : [];
80825
+ const groups = groupsOf(wiring.event);
80571
80826
  const ourGroup = {
80572
80827
  hooks: [{ type: "command", command: commandFor(wiring.script), timeout: 5 }]
80573
80828
  };
@@ -80576,6 +80831,14 @@ function mergeCollectorHooks(settingsText, commandFor) {
80576
80831
  hooks[wiring.event] = [...groups.filter((group) => !isOurGroup(group, wiring.script)), ourGroup];
80577
80832
  changed = true;
80578
80833
  }
80834
+ for (const retired of RETIRED_HOOK_WIRINGS) {
80835
+ const groups = groupsOf(retired.event);
80836
+ const kept = groups.filter((group) => !isOurGroup(group, retired.script));
80837
+ if (kept.length === groups.length) continue;
80838
+ if (kept.length === 0) delete hooks[retired.event];
80839
+ else hooks[retired.event] = kept;
80840
+ changed = true;
80841
+ }
80579
80842
  if (!changed) return { action: "unchanged" };
80580
80843
  const next = { ...settings, hooks };
80581
80844
  return { action: "write", text: `${JSON.stringify(next, null, 2)}
@@ -80613,6 +80876,11 @@ function installCollectorHooks(homeDir = os11.homedir()) {
80613
80876
  mkdirSync3(settingsDir, { recursive: true });
80614
80877
  writeFileSync3(settingsPath, merged.text);
80615
80878
  }
80879
+ if (merged.action !== "skip") {
80880
+ for (const retired of RETIRED_HOOK_WIRINGS) {
80881
+ rmSync(path9.join(hookDir, retired.script), { force: true });
80882
+ }
80883
+ }
80616
80884
  return {
80617
80885
  hookDir,
80618
80886
  settings: merged.action,
@@ -80622,7 +80890,7 @@ function installCollectorHooks(homeDir = os11.homedir()) {
80622
80890
 
80623
80891
  // ../../apps/collector/src/service.ts
80624
80892
  import { execFileSync as execFileSync3 } from "node:child_process";
80625
- import { chmodSync as chmodSync2, existsSync as existsSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync7, rmSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
80893
+ import { chmodSync as chmodSync2, existsSync as existsSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync7, rmSync as rmSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
80626
80894
  import os12 from "node:os";
80627
80895
  import path10 from "node:path";
80628
80896
  var RESTART_THROTTLE_SECONDS = 60;
@@ -80653,7 +80921,7 @@ function refuseStaleAppEnvLauncher(env, readLauncher = readLauncherIfExists) {
80653
80921
  const script = readLauncher(launcherPath(appEnv));
80654
80922
  if (script === null || !script.includes("APP_ENV=") || script.includes("POWERPLANT_ENV=")) return;
80655
80923
  throw new ExpectedCliError(
80656
- `This background service was installed before the POWERPLANT_ENV rename and is no longer supported. Remove it with: powerplant uninstall --env ${appEnv}` + (appEnv === "production" ? " \u2014 then powerplant init reinstalls it." : ` \u2014 then powerplant init --env ${appEnv} reinstalls it if you still need it.`)
80924
+ `This background service was installed before the POWERPLANT_ENV rename and is no longer supported. Remove it with: powerplant uninstall --env ${appEnv}` + (appEnv === "production" ? ", then powerplant init reinstalls it." : `, then powerplant init --env ${appEnv} reinstalls it if you still need it.`)
80657
80925
  );
80658
80926
  }
80659
80927
  function readLauncherIfExists(launcherFile) {
@@ -80698,7 +80966,7 @@ function installService(config2, command) {
80698
80966
  if (!waitForUnload(() => isLoaded(config2.appEnv))) {
80699
80967
  const service = `gui/${uid()}/${label(config2.appEnv)}`;
80700
80968
  throw new Error(
80701
- `launchd is still unloading ${label(config2.appEnv)} after ${UNLOAD_WAIT_MS / 1e3}s \u2014 re-run powerplant init, or unload it by hand first: launchctl bootout ${service}`
80969
+ `launchd is still unloading ${label(config2.appEnv)} after ${UNLOAD_WAIT_MS / 1e3}s, re-run powerplant init, or unload it by hand first: launchctl bootout ${service}`
80702
80970
  );
80703
80971
  }
80704
80972
  const launcher = launcherPath(config2.appEnv);
@@ -80744,7 +81012,7 @@ function serviceUpToDate(config2, command) {
80744
81012
  function uninstallService(config2) {
80745
81013
  requireMacos();
80746
81014
  const existed = bootout(config2.appEnv);
80747
- rmSync(launcherPath(config2.appEnv), { force: true });
81015
+ rmSync2(launcherPath(config2.appEnv), { force: true });
80748
81016
  if (existsSync2(plistPath(config2.appEnv))) {
80749
81017
  unlinkSync2(plistPath(config2.appEnv));
80750
81018
  return true;
@@ -80814,7 +81082,7 @@ function xmlEscape(value) {
80814
81082
  }
80815
81083
 
80816
81084
  // ../../apps/collector/src/status.ts
80817
- import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "node:fs";
81085
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, renameSync, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "node:fs";
80818
81086
  import os13 from "node:os";
80819
81087
  import path11 from "node:path";
80820
81088
  var HEARTBEAT_INTERVAL_MS = 6e4;
@@ -80917,10 +81185,10 @@ function writeCollectorControl(appEnv, control, homeDir) {
80917
81185
  writeAtomically(controlFilePath(appEnv, homeDir), JSON.stringify(control, null, 2));
80918
81186
  }
80919
81187
  function clearCollectorControl(appEnv, homeDir) {
80920
- rmSync2(controlFilePath(appEnv, homeDir), { force: true });
81188
+ rmSync3(controlFilePath(appEnv, homeDir), { force: true });
80921
81189
  }
80922
81190
  function clearCollectorStatus(appEnv, homeDir) {
80923
- rmSync2(statusFilePath(appEnv, homeDir), { force: true });
81191
+ rmSync3(statusFilePath(appEnv, homeDir), { force: true });
80924
81192
  }
80925
81193
  function describeDaemonState(read, probes = {}) {
80926
81194
  if (read.file === "invalid") return { state: "unreadable" };
@@ -81093,12 +81361,33 @@ async function resolveCapturePolicy(config2, credentials, log3, deps = defaultPo
81093
81361
  }
81094
81362
  let owners = credentials.allowedOwners ?? [];
81095
81363
  let repos = credentials.allowedRepos ?? null;
81364
+ let workspacePolicies = credentials.workspacePolicies;
81096
81365
  let ownersUnresolved = false;
81366
+ let claimsUnresolved = false;
81097
81367
  try {
81098
81368
  const fresh = await deps.fetchPolicy(credentials.apiUrl, credentials.token);
81099
81369
  ownersUnresolved = fresh.integration === "linked" && fresh.owners.length === 0;
81100
81370
  const nextOwners = ownersUnresolved ? owners : fresh.owners;
81101
- const changed = nextOwners.join(",") !== owners.join(",") || (fresh.repos?.join(",") ?? null) !== (repos?.join(",") ?? null);
81371
+ const nextWorkspaces = fresh.workspaces?.map((workspace) => {
81372
+ const unresolved = workspace.integration === "linked" && workspace.owners.length === 0 && workspace.repos === null;
81373
+ 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);
81374
+ const usableCache = cached3 !== void 0 && workspaceClaimsAnyRepo(cached3);
81375
+ const effective = unresolved && usableCache ? cached3 : unresolved ? {
81376
+ orgId: workspace.orgId,
81377
+ name: workspace.name,
81378
+ owners: workspace.owners,
81379
+ repos: workspace.repos,
81380
+ unresolved: true
81381
+ } : {
81382
+ orgId: workspace.orgId,
81383
+ name: workspace.name,
81384
+ owners: workspace.owners,
81385
+ repos: workspace.repos
81386
+ };
81387
+ if (effective.unresolved) claimsUnresolved = true;
81388
+ return effective;
81389
+ });
81390
+ const changed = nextOwners.join(",") !== owners.join(",") || (fresh.repos?.join(",") ?? null) !== (repos?.join(",") ?? null) || JSON.stringify(nextWorkspaces ?? null) !== JSON.stringify(workspacePolicies ?? null);
81102
81391
  if (changed) {
81103
81392
  const next = {
81104
81393
  ...credentials,
@@ -81106,34 +81395,48 @@ async function resolveCapturePolicy(config2, credentials, log3, deps = defaultPo
81106
81395
  };
81107
81396
  if (fresh.repos !== null) next.allowedRepos = fresh.repos;
81108
81397
  else delete next.allowedRepos;
81398
+ if (nextWorkspaces !== void 0) next.workspacePolicies = nextWorkspaces;
81399
+ else delete next.workspacePolicies;
81109
81400
  deps.persist(config2.appEnv, next);
81110
81401
  }
81111
81402
  owners = nextOwners;
81112
81403
  repos = fresh.repos;
81404
+ workspacePolicies = nextWorkspaces;
81113
81405
  } catch (cause) {
81406
+ const cachedCoversSomething = owners.length > 0 || repos !== null || (workspacePolicies ?? []).some((entry) => workspaceClaimsAnyRepo(entry));
81407
+ if (!cachedCoversSomething || (workspacePolicies ?? []).some((entry) => entry.unresolved === true)) {
81408
+ claimsUnresolved = true;
81409
+ }
81114
81410
  log3(
81115
- `\u26A0 Couldn't refresh the org's repo sync policy (${describeSyncError(cause)}) \u2014 ` + (owners.length > 0 || repos !== null ? "using the cached policy." : "nothing will upload this pass.")
81411
+ `\u26A0 Couldn't refresh the org's repo sync policy (${describeSyncError(cause)}), ` + (cachedCoversSomething ? "using the cached policy." : "nothing will upload this pass.")
81116
81412
  );
81117
81413
  }
81118
81414
  const memberFolders = credentials.scopePaths ?? [];
81415
+ if (ownersUnresolved && owners.length === 0 && repos === null) claimsUnresolved = true;
81119
81416
  if (ownersUnresolved) {
81120
81417
  log3(
81121
- "\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.")
81418
+ "\u26A0 The org's GitHub is connected, but its account couldn't be resolved just now, " + (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.")
81122
81419
  );
81123
81420
  }
81421
+ const homeOrgId = credentials.orgId ?? workspacePolicies?.[0]?.orgId;
81422
+ const foreignClaims = (workspacePolicies ?? []).filter(
81423
+ (entry) => entry.orgId !== homeOrgId && workspaceClaimsAnyRepo(entry)
81424
+ );
81124
81425
  if (repos !== null && repos.length === 0) {
81125
81426
  log3(
81126
- "\u26A0 The org's repo allowlist is empty \u2014 no repo-based uploads" + (memberFolders.length > 0 ? "; only folders you added upload." : ".")
81427
+ "\u26A0 The org's repo allowlist is empty, 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." : ".")
81127
81428
  );
81128
81429
  } else if (repos === null && owners.length === 0 && !ownersUnresolved) {
81129
81430
  log3(
81130
- "\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`."
81431
+ foreignClaims.length > 0 ? `Your home workspace has no GitHub integration, 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, nothing uploads. Connect GitHub in Settings, or add folders with `powerplant folders add`."
81131
81432
  );
81132
81433
  }
81133
81434
  return {
81134
81435
  mode: "org-repos",
81135
81436
  allowedOwners: owners,
81136
81437
  allowedRepos: repos,
81438
+ ...workspacePolicies !== void 0 && { workspaceClaims: workspacePolicies },
81439
+ ...claimsUnresolved && { claimsUnresolved },
81137
81440
  memberFolders,
81138
81441
  repoOf,
81139
81442
  environment
@@ -81144,66 +81447,75 @@ async function runSyncPass(config2, options = {}) {
81144
81447
  const credentials = loadCredentials(config2.appEnv);
81145
81448
  if (!credentials) {
81146
81449
  throw new ExpectedCliError(
81147
- `This machine isn't enrolled for ${config2.appEnv} \u2014 run: ${options.enrollCommand ?? "pnpm collector enroll"}`
81450
+ `This machine isn't enrolled for ${config2.appEnv}, run: ${options.enrollCommand ?? "pnpm collector enroll"}`
81148
81451
  );
81149
81452
  }
81150
81453
  const window2 = resolveSyncWindow(options.explicitSince, credentials.deepSyncedAt);
81151
81454
  if (window2.deepSweep) {
81152
81455
  log3(
81153
- `First sync for this enrollment \u2014 sweeping the last ${window2.days} days and backfilling up to the ${DEEP_FIRST_SYNC_MAX_SESSIONS} most recently active sessions already on this machine (later syncs cover ${STEADY_SYNC_WINDOW_DAYS} days).`
81456
+ `First sync for this enrollment, 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).`
81154
81457
  );
81155
81458
  }
81156
81459
  const capture = await resolveCapturePolicy(config2, credentials, log3);
81157
81460
  const sinceMs = resolveScanFloor(window2, Date.now(), options.narrowSinceMs);
81158
81461
  const maxSessions = window2.deepSweep ? DEEP_FIRST_SYNC_MAX_SESSIONS : void 0;
81159
- const claude = await collectClaudeCodeSessions({ sinceMs, capture });
81462
+ const claude = await collectClaudeCodeSessions({ sinceMs, capture, maxSessions });
81160
81463
  const codex = await collectCodexSessions({ sinceMs, capture, maxSessions });
81161
81464
  const cursor = await collectCursorSessions({ sinceMs, capture });
81162
81465
  if (cursor.unavailable !== null) {
81163
- log3(`\u26A0 Cursor sessions skipped this pass \u2014 ${cursor.unavailable}. Other syncing is unaffected.`);
81466
+ log3(`\u26A0 Cursor sessions skipped this pass, ${cursor.unavailable}. Other syncing is unaffected.`);
81164
81467
  }
81165
81468
  const inWindow = [...claude.sessions, ...codex.sessions, ...cursor.sessions].sort(
81166
81469
  (a, b2) => b2.mtimeMs - a.mtimeMs
81167
81470
  );
81168
81471
  const collected = maxSessions === void 0 ? inWindow : inWindow.slice(0, maxSessions);
81169
- const heldBack = inWindow.length - collected.length + codex.heldBack;
81472
+ const heldBack = inWindow.length - collected.length + claude.heldBack + codex.heldBack;
81170
81473
  const skippedOutOfScope = claude.skippedOutOfScope + codex.skippedOutOfScope + cursor.skippedOutOfScope;
81171
81474
  const skippedNoRepo = claude.skippedNoRepo + codex.skippedNoRepo + cursor.skippedNoRepo;
81172
81475
  const items = collected.map((c) => c.item);
81173
81476
  const active = items.filter((s) => s.status === "active").length;
81477
+ const tooLarge = (count, what) => count > 0 ? ` \xB7 ${count} ${what} skipped (over ${MAX_TRANSCRIPT_FILE_BYTES / (1024 * 1024)}MB, too large to read)` : "";
81174
81478
  log3(
81175
- `Found ${inWindow.length} local session(s) (${claude.sessions.length} claude, ${codex.sessions.length} codex, ${cursor.sessions.length} cursor) ` + describeScanReach(window2, options.narrowSinceMs) + (active > 0 ? ` (${active} still active)` : "") + (skippedOutOfScope > 0 ? ` \xB7 ${skippedOutOfScope} ${describeSkipReason(capture)}` : "") + (skippedNoRepo > 0 ? ` \xB7 ${skippedNoRepo} ${describeNoRepoSkipReason(capture)}` : "") + (codex.skippedSubagentThreads > 0 ? ` \xB7 ${codex.skippedSubagentThreads} codex subagent thread(s) skipped (spend unmeasured)` : "") + (cursor.skippedSubagentComposers > 0 ? ` \xB7 ${cursor.skippedSubagentComposers} cursor subagent thread(s) skipped` : "") + (cursor.skippedNonLocalComposers > 0 ? ` \xB7 ${cursor.skippedNonLocalComposers} cursor session(s) skipped (not local to this machine)` : "") + (cursor.skippedUnreadable > 0 ? ` \xB7 ${cursor.skippedUnreadable} cursor session(s) unreadable, skipped` : "") + (codex.skippedTooLarge > 0 ? ` \xB7 ${codex.skippedTooLarge} codex rollout(s) skipped (over ${MAX_TRANSCRIPT_FILE_BYTES / (1024 * 1024)}MB \u2014 too large to read)` : "") + (codex.unreadable.length > 0 ? ` \xB7 ${codex.unreadable.length} codex rollout(s) unreadable, skipped` : "") + (heldBack > 0 ? ` \xB7 ${heldBack} older session(s) held back \u2014 the first sync backfills the ${DEEP_FIRST_SYNC_MAX_SESSIONS} most recently active` : "") + "."
81479
+ `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, the first sync backfills the ${DEEP_FIRST_SYNC_MAX_SESSIONS} most recently active` : "") + "."
81176
81480
  );
81177
- for (const failure of codex.unreadable) log3(` codex rollout skipped \u2014 ${failure}`);
81481
+ for (const failure of claude.unreadable) log3(` claude transcript skipped, ${failure}`);
81482
+ for (const failure of codex.unreadable) log3(` codex rollout skipped, ${failure}`);
81178
81483
  if (options.dryRun) {
81179
81484
  for (const s of items.slice(0, 10)) {
81180
81485
  log3(` ${s.externalId} ${s.model ?? "model unmeasured"} ${s.title ?? "(untitled)"}`);
81181
81486
  }
81182
81487
  if (items.length > 10) log3(` ... and ${items.length - 10} more`);
81183
- log3("Dry run \u2014 nothing uploaded.");
81488
+ log3("Dry run, nothing uploaded.");
81184
81489
  return { sessions: collected, upserted: 0 };
81185
81490
  }
81186
81491
  const markDeepSynced = () => {
81187
81492
  if (!window2.deepSweep) return;
81188
81493
  const current = loadCredentials(config2.appEnv);
81189
81494
  if (current === null) {
81190
- log3("Deep sync finished, but this machine's credentials are gone \u2014 nothing to stamp.");
81495
+ log3("Deep sync finished, but this machine's credentials are gone, nothing to stamp.");
81191
81496
  return;
81192
81497
  }
81193
81498
  if (current.token !== credentials.token || current.enrolledAt !== credentials.enrolledAt) {
81194
81499
  log3(
81195
- "Deep sync finished for a superseded enrollment \u2014 the current enrollment still owes its own deep sweep."
81500
+ "Deep sync finished for a superseded enrollment, the current enrollment still owes its own deep sweep."
81196
81501
  );
81197
81502
  return;
81198
81503
  }
81199
81504
  saveCredentials(config2.appEnv, { ...current, deepSyncedAt: (/* @__PURE__ */ new Date()).toISOString() });
81200
- log3(`Deep sync complete \u2014 future syncs cover the last ${STEADY_SYNC_WINDOW_DAYS} days.`);
81505
+ log3(`Deep sync complete, future syncs cover the last ${STEADY_SYNC_WINDOW_DAYS} days.`);
81201
81506
  };
81507
+ const claimsUnresolved = capture.mode === "org-repos" && capture.claimsUnresolved === true;
81202
81508
  if (items.length === 0) {
81203
- markDeepSynced();
81509
+ if (claimsUnresolved) {
81510
+ if (window2.deepSweep) {
81511
+ log3("Deep sync not stamped, a workspace's claims are unresolved; the sweep re-runs next pass.");
81512
+ }
81513
+ } else {
81514
+ markDeepSynced();
81515
+ }
81204
81516
  return { sessions: collected, upserted: 0 };
81205
81517
  }
81206
- const { upserted, unchanged, outOfPolicy } = await uploadSessions(
81518
+ const { upserted, unchanged, outOfPolicy, deferred } = await uploadSessions(
81207
81519
  credentials.apiUrl,
81208
81520
  credentials.token,
81209
81521
  items
@@ -81211,9 +81523,14 @@ async function runSyncPass(config2, options = {}) {
81211
81523
  log3(
81212
81524
  `Uploaded: ${upserted} session(s) upserted` + (unchanged > 0 ? `, ${unchanged} unchanged (no writes issued)` : "") + "."
81213
81525
  );
81526
+ if (deferred > 0) {
81527
+ log3(
81528
+ `\u26A0 ${deferred} session(s) deferred, 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.`
81529
+ );
81530
+ }
81214
81531
  if (outOfPolicy > 0) {
81215
81532
  log3(
81216
- `\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.`
81533
+ `\u26A0 ${outOfPolicy} session(s) refused by the org's repo sync policy, they stayed out of Powerplant. The next pass refreshes the policy and stops offering them.`
81217
81534
  );
81218
81535
  }
81219
81536
  const registerPreviews = options.registerPreviews ?? registerAnnouncedPreviews;
@@ -81228,13 +81545,13 @@ async function runSyncPass(config2, options = {}) {
81228
81545
  }
81229
81546
  for (const externalId of previewSummary.foreignOwned) {
81230
81547
  log3(
81231
- `Previews for ${externalId} are registered by another machine \u2014 leaving its list in place.`
81548
+ `Previews for ${externalId} are registered by another machine, leaving its list in place.`
81232
81549
  );
81233
81550
  }
81234
81551
  for (const externalId of previewSummary.previewsOff) {
81235
- log3(`Previews for ${externalId} refused \u2014 this organization has previews switched off.`);
81552
+ log3(`Previews for ${externalId} refused, this organization has previews switched off.`);
81236
81553
  }
81237
- for (const failure of previewSummary.failed) log3(` preview registration failed \u2014 ${failure}`);
81554
+ for (const failure of previewSummary.failed) log3(` preview registration failed, ${failure}`);
81238
81555
  try {
81239
81556
  const observe = options.observeBranch ? options.observeBranch() : branchObserver({ allowedProtectedRoots: credentials.repoDiscoveryRoots ?? [] });
81240
81557
  const facts = branchFactsOf(collected, observe);
@@ -81244,7 +81561,7 @@ async function runSyncPass(config2, options = {}) {
81244
81561
  log3(`Branch facts: ${recorded} branch state(s) recorded.`);
81245
81562
  }
81246
81563
  } catch (cause) {
81247
- log3(`\u26A0 Branch facts skipped this pass \u2014 ${describeSyncError(cause)}`);
81564
+ log3(`\u26A0 Branch facts skipped this pass, ${describeSyncError(cause)}`);
81248
81565
  }
81249
81566
  const rawFloor = rawShipFloorMs(config2.appEnv, Date.now(), options.explicitSince);
81250
81567
  const rawEligible = rawFloor === null ? collected : collected.filter((c) => c.mtimeMs >= rawFloor);
@@ -81257,7 +81574,7 @@ async function runSyncPass(config2, options = {}) {
81257
81574
  options.rawShipped
81258
81575
  );
81259
81576
  if (raw.unconfigured) {
81260
- log3("Raw content: this environment has no raw storage yet \u2014 skipped.");
81577
+ log3("Raw content: this environment has no raw storage yet, skipped.");
81261
81578
  } else {
81262
81579
  const parts = [`${raw.uploaded} uploaded`];
81263
81580
  if (raw.skippedUnchanged > 0) parts.push(`${raw.skippedUnchanged} unchanged`);
@@ -81265,12 +81582,25 @@ async function runSyncPass(config2, options = {}) {
81265
81582
  if (raw.skippedTooLarge > 0) parts.push(`${raw.skippedTooLarge} skipped (too large)`);
81266
81583
  if (raw.failed.length > 0) parts.push(`${raw.failed.length} failed`);
81267
81584
  log3(`Raw content: ${parts.join(", ")}.`);
81268
- for (const failure of raw.failed) log3(` raw upload failed \u2014 ${failure}`);
81269
- const nothingElseAccountedFor = raw.skippedUnchanged === 0 && raw.skippedTooLarge === 0 && rawWindowSkipped === 0;
81585
+ for (const failure of raw.failed) log3(` raw upload failed, ${failure}`);
81586
+ if (raw.missingMetadata > 0) {
81587
+ log3(
81588
+ `${raw.missingMetadata} raw transcript(s) waiting on their session metadata, they re-send once it lands.`
81589
+ );
81590
+ }
81591
+ const nothingElseAccountedFor = raw.skippedUnchanged === 0 && raw.skippedTooLarge === 0 && rawWindowSkipped === 0 && raw.missingMetadata === 0;
81270
81592
  if (raw.uploaded === 0 && (raw.failed.length > 1 || raw.failed.length === 1 && nothingElseAccountedFor)) {
81271
- throw new Error(`all ${raw.failed.length} raw upload(s) failed this pass \u2014 ${raw.failed[0]}`);
81593
+ throw new Error(`all ${raw.failed.length} raw upload(s) failed this pass, ${raw.failed[0]}`);
81594
+ }
81595
+ if (deferred > 0 || claimsUnresolved) {
81596
+ if (window2.deepSweep) {
81597
+ log3(
81598
+ deferred > 0 ? "Deep sync not stamped, deferred sessions still owe their upload; the sweep re-runs next pass." : "Deep sync not stamped, a workspace's claims are unresolved; the sweep re-runs next pass."
81599
+ );
81600
+ }
81601
+ } else {
81602
+ markDeepSynced();
81272
81603
  }
81273
- markDeepSynced();
81274
81604
  }
81275
81605
  return { sessions: collected, upserted };
81276
81606
  }
@@ -81307,293 +81637,9 @@ function unsupportedVersionNotice(cliVersion2, oldestSupportedVersion) {
81307
81637
  }
81308
81638
 
81309
81639
  // ../../apps/collector/src/watch.ts
81310
- import { existsSync as existsSync4, watch as fsWatch } from "node:fs";
81640
+ import { existsSync as existsSync3, watch as fsWatch } from "node:fs";
81311
81641
  import os14 from "node:os";
81312
- import path14 from "node:path";
81313
-
81314
- // ../../apps/collector/src/wake.ts
81315
- import { execFile as execFile2, execFileSync as execFileSync4 } from "node:child_process";
81316
- import { existsSync as existsSync3 } from "node:fs";
81317
81642
  import path13 from "node:path";
81318
- var WAKE_POLL_MS = 5e3;
81319
- var WAKE_QUIET_MS = 2 * 6e4;
81320
- var WAKE_TURN_TIMEOUT_MS = 10 * 6e4;
81321
- var FETCH_TIMEOUT_MS2 = 5e3;
81322
- function findClaudeTranscript(homeDir, externalId) {
81323
- for (const projectDir of listDirs(path13.join(homeDir, ".claude", "projects"))) {
81324
- const transcript = path13.join(projectDir, `${externalId}.jsonl`);
81325
- const stat = statIfExists(transcript);
81326
- if (stat === null) continue;
81327
- const text = readIfExists2(transcript);
81328
- if (text === null) continue;
81329
- for (const record2 of parseJsonl(text)) {
81330
- if (typeof record2.cwd === "string" && record2.cwd !== "") {
81331
- return { cwd: record2.cwd, mtimeMs: stat.mtimeMs };
81332
- }
81333
- }
81334
- return null;
81335
- }
81336
- return null;
81337
- }
81338
- function formatWakePrompt(messages) {
81339
- const lines = messages.map(
81340
- (message) => `- From ${message.authorEmail ?? "a teammate"} (sent ${message.createdAt}): ${message.body}`
81341
- );
81342
- return [
81343
- `While this session was idle, ${messages.length === 1 ? "a teammate sent a message" : `teammates sent ${messages.length} messages`} to it via Powerplant:`,
81344
- ...lines,
81345
- "This prompt was submitted automatically by Powerplant's message delivery \u2014 no one is at the keyboard. Acknowledge the message and act on it as an instruction from that teammate."
81346
- ].join("\n");
81347
- }
81348
- function createWakePoller(deps) {
81349
- const inFlight = /* @__PURE__ */ new Set();
81350
- let polling = false;
81351
- let consecutivePollFailures = 0;
81352
- return {
81353
- async poll() {
81354
- if (polling) return;
81355
- polling = true;
81356
- try {
81357
- for (const target of await deps.listPending()) {
81358
- if (inFlight.has(target.externalId)) continue;
81359
- const home = deps.inspect(target);
81360
- if (home === null) continue;
81361
- if (deps.now() - home.mtimeMs < WAKE_QUIET_MS) continue;
81362
- const messages = await deps.claim(target);
81363
- if (messages.length === 0) continue;
81364
- inFlight.add(target.externalId);
81365
- deps.log(
81366
- `waking session ${target.externalId} with ${messages.length} message(s) in ${home.cwd}`
81367
- );
81368
- void deps.runClaude(target, formatWakePrompt(messages), home.cwd).then(() => deps.log(`wake finished for session ${target.externalId}`)).catch(
81369
- (error52) => reportFailedWake(deps, target, messages, describeError2(error52))
81370
- ).finally(() => inFlight.delete(target.externalId));
81371
- }
81372
- consecutivePollFailures = 0;
81373
- } catch (error52) {
81374
- consecutivePollFailures += 1;
81375
- deps.log(`\u26A0 wake poll failed (${describeError2(error52)}) \u2014 next poll retries`);
81376
- if (consecutivePollFailures === REPORT_AFTER_CONSECUTIVE_FAILURES) {
81377
- deps.reportError?.(error52, "wake-poll", { consecutivePollFailures });
81378
- }
81379
- } finally {
81380
- polling = false;
81381
- }
81382
- }
81383
- };
81384
- }
81385
- async function reportFailedWake(deps, target, messages, reason) {
81386
- deps.log(
81387
- `\u26A0 wake failed for session ${target.externalId} AFTER its messages were claimed (${reason})`
81388
- );
81389
- const messageIds = messages.flatMap((message) => message.id === null ? [] : [message.id]);
81390
- try {
81391
- await deps.reportWakeFailure(target, messageIds, reason);
81392
- deps.log(
81393
- `wake failure reported for session ${target.externalId} \u2014 its ${messageIds.length} message(s) now read "Not delivered"`
81394
- );
81395
- } catch (reportError) {
81396
- deps.log(
81397
- `\u26A0 wake failure report for session ${target.externalId} ALSO failed \u2014 its messages read Delivered but no agent ran (${describeError2(reportError)})`
81398
- );
81399
- deps.reportError?.(reportError, "wake-failure-report", {
81400
- externalId: target.externalId,
81401
- messageCount: messageIds.length
81402
- });
81403
- }
81404
- }
81405
- function resolveClaudeBin(homeDir, env = process.env) {
81406
- if (env.POWERPLANT_CLAUDE_BIN) {
81407
- return existsSync3(env.POWERPLANT_CLAUDE_BIN) ? env.POWERPLANT_CLAUDE_BIN : null;
81408
- }
81409
- const candidates = [
81410
- path13.join(homeDir, ".local", "bin", "claude"),
81411
- "/opt/homebrew/bin/claude",
81412
- "/usr/local/bin/claude"
81413
- ];
81414
- const found = candidates.find((candidate) => existsSync3(candidate));
81415
- if (found) return found;
81416
- try {
81417
- const fromPath = execFileSync4("which", ["claude"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
81418
- return fromPath === "" ? null : fromPath;
81419
- } catch {
81420
- return null;
81421
- }
81422
- }
81423
- var WAKE_MIN_CLAUDE_VERSION = "2.1.0";
81424
- var VERSION_TIMEOUT_MS = 15e3;
81425
- function versionParts(version2) {
81426
- return version2.split(".").map(Number);
81427
- }
81428
- function versionAtLeast(version2, floor) {
81429
- const have = versionParts(version2);
81430
- const need = versionParts(floor);
81431
- for (let i = 0; i < need.length; i += 1) {
81432
- const a = have[i] ?? 0;
81433
- const b2 = need[i] ?? 0;
81434
- if (a !== b2) return a > b2;
81435
- }
81436
- return true;
81437
- }
81438
- function execClaudeVersion(claudeBin) {
81439
- return execFileSync4(claudeBin, ["--version"], {
81440
- stdio: ["ignore", "pipe", "ignore"],
81441
- timeout: VERSION_TIMEOUT_MS
81442
- }).toString();
81443
- }
81444
- function preflightClaudeVersion(claudeBin, readVersionOutput = execClaudeVersion) {
81445
- let output;
81446
- try {
81447
- output = readVersionOutput(claudeBin);
81448
- } catch (error52) {
81449
- return { ok: false, problem: `\`${claudeBin} --version\` failed (${describeError2(error52)})` };
81450
- }
81451
- const version2 = /\d+\.\d+\.\d+/.exec(output)?.[0] ?? null;
81452
- if (version2 === null) {
81453
- return {
81454
- ok: false,
81455
- problem: `\`${claudeBin} --version\` answered "${output.trim()}" \u2014 no version in that`
81456
- };
81457
- }
81458
- if (!versionAtLeast(version2, WAKE_MIN_CLAUDE_VERSION)) {
81459
- return {
81460
- ok: false,
81461
- problem: `claude ${version2} is older than ${WAKE_MIN_CLAUDE_VERSION} \u2014 too old to headlessly resume current transcripts (it hangs until the wake timeout)`
81462
- };
81463
- }
81464
- return { ok: true, version: version2 };
81465
- }
81466
- async function collectorApiList(credentials, pathAndQuery, key) {
81467
- const response = await fetch(`${credentials.apiUrl}${pathAndQuery}`, {
81468
- headers: { authorization: `Bearer ${credentials.token}`, ...cliVersionHeaders() },
81469
- signal: AbortSignal.timeout(FETCH_TIMEOUT_MS2)
81470
- });
81471
- const route = pathAndQuery.split("?")[0];
81472
- if (!response.ok) throw new Error(`${route} answered ${response.status}`);
81473
- const envelope = external_exports.object({ data: external_exports.record(external_exports.string(), external_exports.unknown()) }).safeParse(await response.json().catch(() => null));
81474
- if (!envelope.success) {
81475
- throw new Error(`${route} answered an unexpected shape: ${zodIssueSummary(envelope.error)}`, {
81476
- cause: envelope.error
81477
- });
81478
- }
81479
- const list = envelope.data.data[key];
81480
- if (!Array.isArray(list)) {
81481
- throw new Error(`${route} answered an unexpected shape: data.${key} is not a list`);
81482
- }
81483
- const values = list;
81484
- return values;
81485
- }
81486
- var wakeTargetSchema = external_exports.object({ source: external_exports.string().min(1), externalId: external_exports.string().min(1) });
81487
- async function fetchWakePending(credentials) {
81488
- const sessions = await collectorApiList(credentials, "/api/collector/wake-pending", "sessions");
81489
- return sessions.flatMap((entry) => {
81490
- const parsed = wakeTargetSchema.safeParse(entry);
81491
- return parsed.success ? [{ source: parsed.data.source, externalId: parsed.data.externalId }] : [];
81492
- });
81493
- }
81494
- var wakeMessageSchema = external_exports.object({
81495
- id: external_exports.string().nullable().catch(null),
81496
- authorEmail: external_exports.string().nullable().catch(null),
81497
- body: external_exports.string(),
81498
- createdAt: external_exports.string().catch("")
81499
- });
81500
- async function claimInbox(credentials, target) {
81501
- const messages = await collectorApiList(
81502
- credentials,
81503
- `/api/collector/inbox?source=${encodeURIComponent(target.source)}&externalId=${encodeURIComponent(target.externalId)}`,
81504
- "messages"
81505
- );
81506
- return messages.flatMap((entry) => {
81507
- const parsed = wakeMessageSchema.safeParse(entry);
81508
- return parsed.success ? [parsed.data] : [];
81509
- });
81510
- }
81511
- async function postWakeFailure(credentials, target, messageIds, reason) {
81512
- const response = await fetchWithRetry(
81513
- "reporting a failed wake",
81514
- `${credentials.apiUrl}/api/collector/wake-failed`,
81515
- {
81516
- method: "POST",
81517
- headers: {
81518
- authorization: `Bearer ${credentials.token}`,
81519
- "content-type": "application/json"
81520
- },
81521
- body: JSON.stringify({
81522
- source: target.source,
81523
- externalId: target.externalId,
81524
- messageIds,
81525
- reason
81526
- })
81527
- }
81528
- );
81529
- if (!response.ok) throw new Error(`/api/collector/wake-failed answered ${response.status}`);
81530
- }
81531
- function runClaudeResume(claudeBin, externalId, prompt, cwd) {
81532
- return new Promise((resolve2, reject) => {
81533
- const child = execFile2(
81534
- claudeBin,
81535
- ["-p", "--resume", externalId, prompt],
81536
- { cwd, timeout: WAKE_TURN_TIMEOUT_MS, maxBuffer: 16 * 1024 * 1024 },
81537
- (error52, _stdout, stderr) => error52 ? reject(new Error(describeWakeTurnFailure(error52, stderr))) : resolve2()
81538
- );
81539
- child.stdin?.end();
81540
- });
81541
- }
81542
- var STDERR_TAIL_CHARS = 300;
81543
- function describeWakeTurnFailure(error52, stderr) {
81544
- const tail = redactText(stderr.trim()).slice(-STDERR_TAIL_CHARS);
81545
- const base = baseWakeTurnReason(error52);
81546
- return tail === "" ? base : `${base} \u2014 stderr: ${tail}`;
81547
- }
81548
- function baseWakeTurnReason(error52) {
81549
- if (error52.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") return error52.message;
81550
- if (error52.killed) {
81551
- return `wake turn killed after ${WAKE_TURN_TIMEOUT_MS / 6e4} minutes \u2014 the resumed claude turn never finished`;
81552
- }
81553
- if (typeof error52.code === "number") return `claude exited with code ${error52.code}`;
81554
- if (error52.signal) return `claude died on signal ${error52.signal}`;
81555
- return error52.message;
81556
- }
81557
- function startWakeService(config2, homeDir, log3) {
81558
- if (process.env.POWERPLANT_WAKE_ON_MESSAGE === "0") {
81559
- log3("wake-on-message disabled (POWERPLANT_WAKE_ON_MESSAGE=0)");
81560
- return null;
81561
- }
81562
- const credentials = loadCredentials(config2.appEnv);
81563
- if (credentials === null) {
81564
- log3("wake-on-message off: machine not enrolled");
81565
- return null;
81566
- }
81567
- const claudeBin = resolveClaudeBin(homeDir);
81568
- if (claudeBin === null) {
81569
- log3("wake-on-message off: claude CLI not found (set POWERPLANT_CLAUDE_BIN)");
81570
- return null;
81571
- }
81572
- const preflight = preflightClaudeVersion(claudeBin);
81573
- if (!preflight.ok) {
81574
- log3(
81575
- `wake-on-message off: ${preflight.problem} \u2014 update Claude Code (npm install -g @anthropic-ai/claude-code) or point POWERPLANT_CLAUDE_BIN at a ${WAKE_MIN_CLAUDE_VERSION}+ CLI, then restart the sync service to re-arm wake`
81576
- );
81577
- return null;
81578
- }
81579
- log3(`wake-on-message claude binary: ${claudeBin} (version ${preflight.version})`);
81580
- const poller = createWakePoller({
81581
- listPending: () => fetchWakePending(credentials),
81582
- claim: (target) => claimInbox(credentials, target),
81583
- inspect: (target) => findClaudeTranscript(homeDir, target.externalId),
81584
- runClaude: (target, prompt, cwd) => runClaudeResume(claudeBin, target.externalId, prompt, cwd),
81585
- reportWakeFailure: (target, messageIds, reason) => postWakeFailure(credentials, target, messageIds, reason),
81586
- now: Date.now,
81587
- log: log3,
81588
- reportError: reportCollectorError
81589
- });
81590
- const timer = setInterval(() => void poller.poll(), WAKE_POLL_MS);
81591
- log3(`wake-on-message armed: polling every ${WAKE_POLL_MS / 1e3}s (${config2.appEnv})`);
81592
- return () => clearInterval(timer);
81593
- }
81594
- function describeError2(error52) {
81595
- return error52 instanceof Error ? error52.message : String(error52);
81596
- }
81597
81643
 
81598
81644
  // ../../apps/collector/src/selfRestart.ts
81599
81645
  import { statSync as statSync3 } from "node:fs";
@@ -81631,6 +81677,8 @@ var SAFETY_NET_INTERVAL_MS = 60 * 60 * 1e3;
81631
81677
  var NARROW_OVERLAP_MS = 6e4;
81632
81678
  var WATCHER_RETRY_MS = 6e4;
81633
81679
  var FAILED_PASS_RETRY_MS = 6e4;
81680
+ var FAILED_PASS_BACKOFF_CAP_MS = 15 * 6e4;
81681
+ var STREAK_REREPORT_INTERVAL_MS = 60 * 6e4;
81634
81682
  var VERSION_NOTICE_INTERVAL_MS = 60 * 6e4;
81635
81683
  var COMPLETION_SLACK_MS = 3e4;
81636
81684
  function createWatchLoop(deps) {
@@ -81643,6 +81691,10 @@ function createWatchLoop(deps) {
81643
81691
  let completionTimer = null;
81644
81692
  let running = false;
81645
81693
  let consecutiveFailures = 0;
81694
+ let streakSignature = null;
81695
+ let streakCount = 0;
81696
+ let streakStartedAt = 0;
81697
+ let streakReportedAt = 0;
81646
81698
  let pausedLogged = false;
81647
81699
  let queued = null;
81648
81700
  let retryNotBefore = 0;
@@ -81669,7 +81721,7 @@ function createWatchLoop(deps) {
81669
81721
  if (stopped) return;
81670
81722
  try {
81671
81723
  closeWatcher = deps.watch(onEvent, (error52) => {
81672
- deps.log(`watcher lost (${describeError3(error52)}) \u2014 re-arming and re-scanning`);
81724
+ deps.log(`watcher lost (${describeError(error52)}), re-arming and re-scanning`);
81673
81725
  closeWatcher?.();
81674
81726
  closeWatcher = null;
81675
81727
  arm();
@@ -81677,7 +81729,7 @@ function createWatchLoop(deps) {
81677
81729
  });
81678
81730
  } catch (error52) {
81679
81731
  deps.log(
81680
- `cannot watch yet (${describeError3(error52)}) \u2014 retrying in ${WATCHER_RETRY_MS / 1e3}s`
81732
+ `cannot watch yet (${describeError(error52)}), retrying in ${WATCHER_RETRY_MS / 1e3}s`
81681
81733
  );
81682
81734
  watcherRetry = setTimeout(arm, WATCHER_RETRY_MS);
81683
81735
  }
@@ -81686,7 +81738,7 @@ function createWatchLoop(deps) {
81686
81738
  if (stopped) return;
81687
81739
  if (deps.paused?.()) {
81688
81740
  if (!pausedLogged) {
81689
- deps.log("syncing paused \u2014 skipping sync passes until resumed");
81741
+ deps.log("syncing paused, skipping sync passes until resumed");
81690
81742
  pausedLogged = true;
81691
81743
  }
81692
81744
  return;
@@ -81734,6 +81786,8 @@ function createWatchLoop(deps) {
81734
81786
  }
81735
81787
  }
81736
81788
  consecutiveFailures = 0;
81789
+ streakSignature = null;
81790
+ streakCount = 0;
81737
81791
  deps.onPassResult?.({
81738
81792
  kind,
81739
81793
  at: startedAt,
@@ -81743,31 +81797,50 @@ function createWatchLoop(deps) {
81743
81797
  });
81744
81798
  } catch (error52) {
81745
81799
  consecutiveFailures += 1;
81746
- retryNotBefore = now() + Math.max(FAILED_PASS_RETRY_MS, retryAfterMs(error52) ?? 0);
81800
+ const signature = errorSignature(error52);
81801
+ if (signature === streakSignature) {
81802
+ streakCount += 1;
81803
+ } else {
81804
+ streakSignature = signature;
81805
+ streakCount = 1;
81806
+ streakStartedAt = startedAt;
81807
+ streakReportedAt = 0;
81808
+ }
81809
+ const holdMs = Math.max(
81810
+ Math.min(FAILED_PASS_RETRY_MS * 2 ** (streakCount - 1), FAILED_PASS_BACKOFF_CAP_MS),
81811
+ retryAfterMs(error52) ?? 0
81812
+ );
81813
+ retryNotBefore = now() + holdMs;
81747
81814
  lifecycleChanged ||= consultLifecycle;
81748
81815
  deps.onPassResult?.({
81749
81816
  kind,
81750
81817
  at: startedAt,
81751
81818
  ok: false,
81752
- error: describeError3(error52),
81819
+ error: describeError(error52),
81753
81820
  sessions: 0,
81754
81821
  upserted: 0
81755
81822
  });
81756
- deps.log(`${kind} pass failed (${describeError3(error52)}) \u2014 next pass re-covers this window`);
81823
+ const failLine = `${kind} pass failed (${describeError(error52)})`;
81824
+ deps.log(
81825
+ 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`
81826
+ );
81757
81827
  if (error52 instanceof Error && !(error52 instanceof ExpectedCliError) && error52.stack) {
81758
81828
  deps.log(error52.stack);
81759
81829
  }
81760
81830
  if (isPoisonedHttp2Session(error52)) {
81761
81831
  deps.reportError?.(error52, `watch-pass-${kind}-poisoned-pool`);
81762
81832
  deps.fatal?.(
81763
- "connection pool poisoned by a destroyed HTTP/2 session \u2014 exiting so the service restarts clean"
81833
+ "connection pool poisoned by a destroyed HTTP/2 session, exiting so the service restarts clean"
81764
81834
  );
81765
81835
  return;
81766
81836
  }
81767
- if (consecutiveFailures >= REPORT_AFTER_CONSECUTIVE_FAILURES) {
81837
+ if (consecutiveFailures >= REPORT_AFTER_CONSECUTIVE_FAILURES && (streakReportedAt === 0 || now() - streakReportedAt >= STREAK_REREPORT_INTERVAL_MS)) {
81838
+ streakReportedAt = now();
81768
81839
  const report2 = error52 instanceof ExpectedCliError ? new Error(`${consecutiveFailures} consecutive failed passes: ${error52.message}`) : error52;
81769
81840
  deps.reportError?.(report2, `watch-pass-${kind}`, {
81770
81841
  consecutiveFailures,
81842
+ sameFailureCount: streakCount,
81843
+ streakMinutes: Math.round((now() - streakStartedAt) / 6e4),
81771
81844
  pendingCompletions: pendingCompletions.size
81772
81845
  });
81773
81846
  }
@@ -81821,7 +81894,7 @@ function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
81821
81894
  const home = homeDir ?? os14.homedir();
81822
81895
  process.on("uncaughtException", (error52) => {
81823
81896
  console.error(
81824
- `${(/* @__PURE__ */ new Date()).toISOString()} \u26A0 uncaught exception \u2014 exiting for a clean restart`,
81897
+ `${(/* @__PURE__ */ new Date()).toISOString()} \u26A0 uncaught exception, exiting for a clean restart`,
81825
81898
  error52
81826
81899
  );
81827
81900
  reportCollectorError(error52, "watch-uncaught-exception");
@@ -81829,16 +81902,16 @@ function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
81829
81902
  });
81830
81903
  process.on("unhandledRejection", (reason) => {
81831
81904
  console.error(
81832
- `${(/* @__PURE__ */ new Date()).toISOString()} \u26A0 unhandled rejection \u2014 exiting for a clean restart`,
81905
+ `${(/* @__PURE__ */ new Date()).toISOString()} \u26A0 unhandled rejection, exiting for a clean restart`,
81833
81906
  reason
81834
81907
  );
81835
81908
  reportCollectorError(reason, "watch-unhandled-rejection");
81836
81909
  void flushReporting().then(() => process.exit(1));
81837
81910
  });
81838
81911
  const storeRoots = [
81839
- { dir: path14.join(home, ".claude", "projects"), recursive: true },
81840
- { dir: path14.join(home, ".codex", "sessions"), recursive: true },
81841
- { dir: path14.join(home, ".codex", "archived_sessions"), recursive: true },
81912
+ { dir: path13.join(home, ".claude", "projects"), recursive: true },
81913
+ { dir: path13.join(home, ".codex", "sessions"), recursive: true },
81914
+ { dir: path13.join(home, ".codex", "archived_sessions"), recursive: true },
81842
81915
  // Cursor keeps every local session in one SQLite store here; its WAL
81843
81916
  // file changes on session writes, so a non-recursive watch on the
81844
81917
  // directory is the freshness signal. Cursor's own unrelated writes to
@@ -81846,7 +81919,7 @@ function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
81846
81919
  { dir: cursorGlobalStorageDir(home), recursive: false }
81847
81920
  ];
81848
81921
  const lifecycleRoot = {
81849
- dir: path14.join(home, ".config", "powerplant"),
81922
+ dir: path13.join(home, ".config", "powerplant"),
81850
81923
  recursive: false,
81851
81924
  lifecycle: true
81852
81925
  };
@@ -81855,7 +81928,7 @@ function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
81855
81928
  try {
81856
81929
  return loadCredentials(config2.appEnv);
81857
81930
  } catch (error52) {
81858
- log3(`\u26A0 credential store unreadable (${describeError3(error52)}) \u2014 identity omitted from status`);
81931
+ log3(`\u26A0 credential store unreadable (${describeError(error52)}), identity omitted from status`);
81859
81932
  return null;
81860
81933
  }
81861
81934
  })();
@@ -81865,21 +81938,15 @@ function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
81865
81938
  try {
81866
81939
  const hook = installCollectorHooks(home);
81867
81940
  log3(
81868
- hook.settings === "skip" ? `\u26A0 Claude Code hooks not wired: ${hook.detail ?? "settings.json unusable"} \u2014 fix ~/.claude/settings.json and restart` : `Claude Code hooks ${hook.settings === "write" ? "installed" : "current"} at ${hook.hookDir} (new Claude sessions pick them up)`
81941
+ hook.settings === "skip" ? `\u26A0 Claude Code hooks not wired: ${hook.detail ?? "settings.json unusable"}, fix ~/.claude/settings.json and restart` : `Claude Code hooks ${hook.settings === "write" ? "installed" : "current"} at ${hook.hookDir} (new Claude sessions pick them up)`
81869
81942
  );
81870
81943
  } catch (error52) {
81871
81944
  log3(
81872
- `\u26A0 Claude Code hook install failed (${describeError3(error52)}) \u2014 syncing continues without them`
81945
+ `\u26A0 Claude Code hook install failed (${describeError(error52)}), syncing continues without them`
81873
81946
  );
81874
81947
  reportCollectorError(error52, "watch-hook-install");
81875
81948
  }
81876
- try {
81877
- startWakeService(config2, home, log3);
81878
- } catch (error52) {
81879
- log3(`\u26A0 wake-on-message failed to start (${describeError3(error52)}) \u2014 syncing continues`);
81880
- reportCollectorError(error52, "watch-wake-start");
81881
- }
81882
- const presentRoots = storeRoots.filter(({ dir }) => existsSync4(dir)).map(({ dir }) => dir);
81949
+ const presentRoots = storeRoots.filter(({ dir }) => existsSync3(dir)).map(({ dir }) => dir);
81883
81950
  const initialControl = readCollectorControl(config2.appEnv, home);
81884
81951
  const pausedAtMs = initialControl.paused ? Date.parse(initialControl.pausedAt ?? "") : Number.NaN;
81885
81952
  let status = {
@@ -81907,7 +81974,7 @@ function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
81907
81974
  statusWriteFailed = false;
81908
81975
  } catch (error52) {
81909
81976
  if (!statusWriteFailed) {
81910
- log3(`\u26A0 status file write failed (${describeError3(error52)}) \u2014 syncing continues`);
81977
+ log3(`\u26A0 status file write failed (${describeError(error52)}), syncing continues`);
81911
81978
  reportCollectorError(error52, "watch-status-write");
81912
81979
  }
81913
81980
  statusWriteFailed = true;
@@ -81961,7 +82028,7 @@ function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
81961
82028
  },
81962
82029
  ...Number.isFinite(pausedAtMs) && { initialCoveredSinceMs: pausedAtMs },
81963
82030
  watch: (onEvent, onLost) => {
81964
- const present = armableWatchRoots(storeRoots, lifecycleRoot, existsSync4);
82031
+ const present = armableWatchRoots(storeRoots, lifecycleRoot, existsSync3);
81965
82032
  if (present === null) throw new Error(`no session stores found under ${home}`);
81966
82033
  log3(
81967
82034
  `watching ${present.map(({ dir }) => dir).join(" + ")} for session changes (${config2.appEnv})`
@@ -81998,21 +82065,33 @@ function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
81998
82065
  controlPoll.unref?.();
81999
82066
  if (runtime.entryPath) {
82000
82067
  watchEntryForUpdate(runtime.entryPath, (detail) => {
82001
- log3(`${detail} \u2014 exiting so the service restarts on the new version`);
82068
+ log3(`${detail}, exiting so the service restarts on the new version`);
82002
82069
  void flushReporting().then(() => process.exit(1));
82003
82070
  });
82004
82071
  }
82005
82072
  return new Promise(() => {
82006
82073
  });
82007
82074
  }
82008
- function describeError3(error52) {
82075
+ function describeError(error52) {
82009
82076
  return error52 instanceof Error ? error52.message : String(error52);
82010
82077
  }
82078
+ function errorSignature(error52) {
82079
+ const parts = [];
82080
+ for (let cause = error52; cause instanceof Error; cause = cause.cause) {
82081
+ parts.push(`${cause.name}: ${cause.message}`);
82082
+ }
82083
+ if (parts.length === 0) parts.push(String(error52));
82084
+ 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, "#");
82085
+ }
82086
+ function describeDelay(ms) {
82087
+ const seconds = Math.round(ms / 1e3);
82088
+ return seconds < 120 ? `${seconds}s` : `${Math.round(seconds / 60)} minutes`;
82089
+ }
82011
82090
 
82012
82091
  // src/connect.ts
82013
- import { execFileSync as execFileSync5, spawnSync } from "node:child_process";
82092
+ import { execFileSync as execFileSync4, spawnSync } from "node:child_process";
82014
82093
  import {
82015
- existsSync as existsSync5,
82094
+ existsSync as existsSync4,
82016
82095
  mkdirSync as mkdirSync6,
82017
82096
  mkdtempSync,
82018
82097
  readdirSync as readdirSync2,
@@ -82022,7 +82101,7 @@ import {
82022
82101
  writeFileSync as writeFileSync6
82023
82102
  } from "node:fs";
82024
82103
  import os15 from "node:os";
82025
- import path15 from "node:path";
82104
+ import path14 from "node:path";
82026
82105
  import { emitKeypressEvents } from "node:readline";
82027
82106
  var MCP_SERVER_NAME = "powerplant";
82028
82107
  var AGENT_DISPLAY_NAMES = {
@@ -82036,7 +82115,7 @@ function parseClientFilter(values) {
82036
82115
  for (const value of values) {
82037
82116
  if (value === "claude" || value === "claude-code") filter.add("claude");
82038
82117
  else if (value === "codex" || value === "cursor") filter.add(value);
82039
- else throw new ExpectedCliError(`Unknown --client "${value}" \u2014 one of: claude, codex, cursor`);
82118
+ else throw new ExpectedCliError(`Unknown --client "${value}", one of: claude, codex, cursor`);
82040
82119
  }
82041
82120
  return filter;
82042
82121
  }
@@ -82060,7 +82139,7 @@ function detectClients(options = {}) {
82060
82139
  const homeDir = options.homeDir ?? os15.homedir();
82061
82140
  const whichAll = options.whichAll ?? defaultWhichAll;
82062
82141
  const env = options.env ?? process.env;
82063
- const applicationsDirs = options.applicationsDirs ?? (process.platform === "darwin" ? ["/Applications", path15.join(homeDir, "Applications")] : []);
82142
+ const applicationsDirs = options.applicationsDirs ?? (process.platform === "darwin" ? ["/Applications", path14.join(homeDir, "Applications")] : []);
82064
82143
  const bundleSeams = {
82065
82144
  readBundleInfo: options.readBundleInfo ?? realReadBundleInfo,
82066
82145
  verifySignature: options.verifySignature ?? realVerifySignature,
@@ -82082,20 +82161,20 @@ function detectClients(options = {}) {
82082
82161
  clients.push({ client, bin, ...configDir !== void 0 && { configDir } });
82083
82162
  };
82084
82163
  for (const bin of whichAll("claude")) add("claude", bin);
82085
- const localClaude = path15.join(homeDir, ".local", "bin", "claude");
82086
- if (existsSync5(localClaude)) add("claude", localClaude);
82164
+ const localClaude = path14.join(homeDir, ".local", "bin", "claude");
82165
+ if (existsSync4(localClaude)) add("claude", localClaude);
82087
82166
  for (const bin of desktopClaudeBinaries(homeDir)) add("claude", bin);
82088
82167
  const claudeBins = clients.filter((client) => client.client === "claude" && client.configDir === void 0).map((client) => client.bin);
82089
82168
  for (const configDir of claudeConfigHomes(homeDir, env)) {
82090
82169
  for (const bin of claudeBins) add("claude", bin, configDir);
82091
82170
  }
82092
82171
  for (const bin of whichAll("codex")) add("codex", bin);
82093
- const localCodex = path15.join(homeDir, ".local", "bin", "codex");
82094
- if (existsSync5(localCodex)) add("codex", localCodex);
82172
+ const localCodex = path14.join(homeDir, ".local", "bin", "codex");
82173
+ if (existsSync4(localCodex)) add("codex", localCodex);
82095
82174
  for (const bin of codexBundleBinaries(applicationsDirs, bundleSeams)) add("codex", bin);
82096
- const cursorDir = path15.join(homeDir, ".cursor");
82097
- if (existsSync5(cursorDir) || cursorAppInstalled(applicationsDirs, bundleSeams)) {
82098
- clients.push({ client: "cursor", bin: path15.join(cursorDir, "mcp.json") });
82175
+ const cursorDir = path14.join(homeDir, ".cursor");
82176
+ if (existsSync4(cursorDir) || cursorAppInstalled(applicationsDirs, bundleSeams)) {
82177
+ clients.push({ client: "cursor", bin: path14.join(cursorDir, "mcp.json") });
82099
82178
  }
82100
82179
  return clients;
82101
82180
  }
@@ -82103,7 +82182,7 @@ function claudeConfigHomes(homeDir, env) {
82103
82182
  const homes = [];
82104
82183
  const seen = /* @__PURE__ */ new Set();
82105
82184
  try {
82106
- seen.add(realpathSync(path15.join(homeDir, ".claude")));
82185
+ seen.add(realpathSync(path14.join(homeDir, ".claude")));
82107
82186
  } catch {
82108
82187
  }
82109
82188
  const add = (configDir) => {
@@ -82115,7 +82194,7 @@ function claudeConfigHomes(homeDir, env) {
82115
82194
  }
82116
82195
  if (seen.has(real)) return;
82117
82196
  seen.add(real);
82118
- homes.push(path15.resolve(configDir));
82197
+ homes.push(path14.resolve(configDir));
82119
82198
  };
82120
82199
  let entries = [];
82121
82200
  try {
@@ -82123,8 +82202,8 @@ function claudeConfigHomes(homeDir, env) {
82123
82202
  } catch {
82124
82203
  }
82125
82204
  for (const entry of entries.sort()) {
82126
- if (entry.startsWith(".claude-") && existsSync5(path15.join(homeDir, entry, ".claude.json"))) {
82127
- add(path15.join(homeDir, entry));
82205
+ if (entry.startsWith(".claude-") && existsSync4(path14.join(homeDir, entry, ".claude.json"))) {
82206
+ add(path14.join(homeDir, entry));
82128
82207
  }
82129
82208
  }
82130
82209
  if (env.CLAUDE_CONFIG_DIR) add(env.CLAUDE_CONFIG_DIR);
@@ -82135,7 +82214,7 @@ var CODESIGN_TIMEOUT_MS = 1e4;
82135
82214
  function budgetedTimeout(capMs, appDir, seams) {
82136
82215
  const remaining = seams.budget.remainingMs();
82137
82216
  if (remaining <= 0) {
82138
- seams.warn(`Skipping ${appDir} \u2014 out of time to verify app bundles this run.`);
82217
+ seams.warn(`Skipping ${appDir}, out of time to verify app bundles this run.`);
82139
82218
  return null;
82140
82219
  }
82141
82220
  return Math.min(capMs, remaining);
@@ -82152,7 +82231,7 @@ function appBundles(applicationsDirs, namePrefixes) {
82152
82231
  for (const entry of entries) {
82153
82232
  if (!entry.endsWith(".app")) continue;
82154
82233
  if (!namePrefixes.some((prefix) => entry.startsWith(prefix))) continue;
82155
- bundles.push(path15.join(dir, entry));
82234
+ bundles.push(path14.join(dir, entry));
82156
82235
  }
82157
82236
  }
82158
82237
  return bundles;
@@ -82162,7 +82241,7 @@ function signedByTeam(binPath, teamId, vendor, appDir, seams) {
82162
82241
  if (timeoutMs === null) return false;
82163
82242
  if (seams.verifySignature(binPath, teamId, timeoutMs)) return true;
82164
82243
  seams.warn(
82165
- `Skipping ${appDir} \u2014 its bundled binary is not verifiably signed by ${vendor} (the Apple-anchored signature check failed).`
82244
+ `Skipping ${appDir}, its bundled binary is not verifiably signed by ${vendor} (the Apple-anchored signature check failed).`
82166
82245
  );
82167
82246
  return false;
82168
82247
  }
@@ -82172,7 +82251,7 @@ function codexBundleBinaries(applicationsDirs, seams) {
82172
82251
  const infoTimeoutMs = budgetedTimeout(BUNDLE_INFO_TIMEOUT_MS, appDir, seams);
82173
82252
  if (infoTimeoutMs === null) continue;
82174
82253
  if (seams.readBundleInfo(appDir, infoTimeoutMs)?.bundleId !== CODEX_BUNDLE_ID) continue;
82175
- const bin = path15.join(appDir, "Contents", "Resources", "codex");
82254
+ const bin = path14.join(appDir, "Contents", "Resources", "codex");
82176
82255
  if (!isExecutableFile(bin)) continue;
82177
82256
  if (!signedByTeam(bin, OPENAI_TEAM_ID, "OpenAI", appDir, seams)) continue;
82178
82257
  found.push(bin);
@@ -82185,7 +82264,7 @@ function cursorAppInstalled(applicationsDirs, seams) {
82185
82264
  if (infoTimeoutMs === null) continue;
82186
82265
  const executable = seams.readBundleInfo(appDir, infoTimeoutMs)?.executable;
82187
82266
  if (executable === void 0) continue;
82188
- const bin = path15.join(appDir, "Contents", "MacOS", executable);
82267
+ const bin = path14.join(appDir, "Contents", "MacOS", executable);
82189
82268
  if (!isExecutableFile(bin)) continue;
82190
82269
  if (!signedByTeam(bin, CURSOR_TEAM_ID, "Cursor", appDir, seams)) continue;
82191
82270
  return true;
@@ -82207,7 +82286,7 @@ var bundleInfoSchema = external_exports.object({
82207
82286
  function realReadBundleInfo(appDir, timeoutMs) {
82208
82287
  const result = spawnSync(
82209
82288
  "plutil",
82210
- ["-convert", "json", "-o", "-", path15.join(appDir, "Contents", "Info.plist")],
82289
+ ["-convert", "json", "-o", "-", path14.join(appDir, "Contents", "Info.plist")],
82211
82290
  { encoding: "utf8", timeout: timeoutMs }
82212
82291
  );
82213
82292
  if (result.status !== 0) return null;
@@ -82225,18 +82304,18 @@ function realVerifySignature(binPath, teamId, timeoutMs) {
82225
82304
  }).status === 0;
82226
82305
  }
82227
82306
  function desktopClaudeBinaries(homeDir) {
82228
- const root = path15.join(homeDir, "Library", "Application Support", "Claude", "claude-code");
82307
+ const root = path14.join(homeDir, "Library", "Application Support", "Claude", "claude-code");
82229
82308
  let versions;
82230
82309
  try {
82231
82310
  versions = readdirSync2(root);
82232
82311
  } catch {
82233
82312
  return [];
82234
82313
  }
82235
- return versions.map((version2) => path15.join(root, version2, "claude.app", "Contents", "MacOS", "claude")).filter((bin) => existsSync5(bin));
82314
+ return versions.map((version2) => path14.join(root, version2, "claude.app", "Contents", "MacOS", "claude")).filter((bin) => existsSync4(bin));
82236
82315
  }
82237
82316
  function defaultWhichAll(name) {
82238
82317
  try {
82239
- return execFileSync5("which", ["-a", name], { encoding: "utf8" }).split("\n").map((line) => line.trim()).filter(Boolean);
82318
+ return execFileSync4("which", ["-a", name], { encoding: "utf8" }).split("\n").map((line) => line.trim()).filter(Boolean);
82240
82319
  } catch {
82241
82320
  return [];
82242
82321
  }
@@ -82253,7 +82332,7 @@ function runConnect(clients, deps) {
82253
82332
  failed: 0
82254
82333
  };
82255
82334
  if (clients.length === 0) {
82256
- deps.log("No coding agents found (looked for `claude`, `codex`, and Cursor) \u2014 nothing to connect.");
82335
+ deps.log("No coding agents found (looked for `claude`, `codex`, and Cursor), nothing to connect.");
82257
82336
  return outcome;
82258
82337
  }
82259
82338
  for (const [agent, candidates] of agents) {
@@ -82439,30 +82518,30 @@ function connectClaude(client, name, deps) {
82439
82518
  const env = claudeEnv(client);
82440
82519
  const probe = parseClaudeProbe(deps.run(bin, ["mcp", "get", MCP_SERVER_NAME], env));
82441
82520
  if (probe.broken) {
82442
- deps.log(`\u2717 ${name}: probing for the Powerplant MCP failed \u2014 ${probe.detail}`);
82521
+ deps.log(`\u2717 ${name}: probing for the Powerplant MCP failed, ${probe.detail}`);
82443
82522
  return { state: "failed", unchanged: false };
82444
82523
  }
82445
82524
  const { missing, registeredUrl } = probe;
82446
82525
  if (missing || registeredUrl !== void 0 && registeredUrl !== deps.mcpUrl) {
82447
82526
  if (!missing) {
82448
- deps.log(`${name}: registered against ${registeredUrl} \u2014 re-registering for ${deps.mcpUrl}.`);
82527
+ deps.log(`${name}: registered against ${registeredUrl}, re-registering for ${deps.mcpUrl}.`);
82449
82528
  deps.run(bin, ["mcp", "remove", "--scope", "user", MCP_SERVER_NAME], env);
82450
82529
  }
82451
82530
  const add = deps.run(bin, claudeAddArgs(deps), env);
82452
82531
  if (add.status !== 0) {
82453
- deps.log(`\u2717 ${name}: registering the Powerplant MCP failed \u2014 ${describeFailure(add)}`);
82532
+ deps.log(`\u2717 ${name}: registering the Powerplant MCP failed, ${describeFailure(add)}`);
82454
82533
  return { state: "failed", unchanged: false };
82455
82534
  }
82456
82535
  deps.log(`${name}: Powerplant MCP registered (user scope).`);
82457
82536
  } else if (probe.authenticated) {
82458
- deps.log(`\u2713 ${name} already connected`);
82537
+ deps.log(`\u2713 ${name} MCP already connected`);
82459
82538
  return { state: "connected", unchanged: true };
82460
82539
  } else if (deps.officialClients?.["claude-code"]) {
82461
82540
  deps.log(`${name}: re-registering with the pre-registered client before sign-in.`);
82462
82541
  deps.run(bin, ["mcp", "remove", "--scope", "user", MCP_SERVER_NAME], env);
82463
82542
  const add = deps.run(bin, claudeAddArgs(deps), env);
82464
82543
  if (add.status !== 0) {
82465
- deps.log(`\u2717 ${name}: registering the Powerplant MCP failed \u2014 ${describeFailure(add)}`);
82544
+ deps.log(`\u2717 ${name}: registering the Powerplant MCP failed, ${describeFailure(add)}`);
82466
82545
  return { state: "failed", unchanged: false };
82467
82546
  }
82468
82547
  } else {
@@ -82639,7 +82718,7 @@ function parseCodexProbe(result) {
82639
82718
  if (result.status !== 0) {
82640
82719
  return {
82641
82720
  broken: true,
82642
- detail: `listing MCP servers failed \u2014 ${describeFailure(result)}`,
82721
+ detail: `listing MCP servers failed, ${describeFailure(result)}`,
82643
82722
  missing: false,
82644
82723
  authenticated: false
82645
82724
  };
@@ -82648,7 +82727,7 @@ function parseCodexProbe(result) {
82648
82727
  if (servers === null) {
82649
82728
  return {
82650
82729
  broken: true,
82651
- detail: "could not read its MCP server list \u2014 is this codex version very old?",
82730
+ detail: "could not read its MCP server list, is this codex version very old?",
82652
82731
  missing: false,
82653
82732
  authenticated: false
82654
82733
  };
@@ -82686,7 +82765,7 @@ function healOrphanCodexOauthTable(name, deps) {
82686
82765
  if (!changed) return;
82687
82766
  deps.writeTextFile(deps.codexConfigPath, content);
82688
82767
  deps.log(
82689
- `${name}: removed leftover sign-in settings from an interrupted setup \u2014 codex can read its configuration again.`
82768
+ `${name}: removed leftover sign-in settings from an interrupted setup, codex can read its configuration again.`
82690
82769
  );
82691
82770
  }
82692
82771
  function connectCodex(bin, name, deps) {
@@ -82700,7 +82779,7 @@ function connectCodex(bin, name, deps) {
82700
82779
  const wrongUrl = registeredUrl !== void 0 && registeredUrl !== deps.mcpUrl;
82701
82780
  const alreadyConnected = !missing && !wrongUrl && probe.authenticated;
82702
82781
  if (missing || wrongUrl) {
82703
- if (wrongUrl) deps.log(`${name}: registered against ${registeredUrl} \u2014 re-registering for ${deps.mcpUrl}.`);
82782
+ if (wrongUrl) deps.log(`${name}: registered against ${registeredUrl}, re-registering for ${deps.mcpUrl}.`);
82704
82783
  if (codexAddLogsIn(bin, deps)) {
82705
82784
  ensureCodexCallbackPort(name, deps);
82706
82785
  const outcome = login(bin, name, codexAddArgs(deps, { withOfficialClientId: true }), deps);
@@ -82709,12 +82788,12 @@ function connectCodex(bin, name, deps) {
82709
82788
  }
82710
82789
  const add = deps.run(bin, codexAddArgs(deps, { withOfficialClientId: false }));
82711
82790
  if (add.status !== 0) {
82712
- deps.log(`\u2717 ${name}: registering the Powerplant MCP failed \u2014 ${describeFailure(add)}`);
82791
+ deps.log(`\u2717 ${name}: registering the Powerplant MCP failed, ${describeFailure(add)}`);
82713
82792
  return { state: "failed", unchanged: false };
82714
82793
  }
82715
82794
  deps.log(`${name}: Powerplant MCP registered.`);
82716
82795
  } else if (alreadyConnected) {
82717
- deps.log(`\u2713 ${name} already connected`);
82796
+ deps.log(`\u2713 ${name} MCP already connected`);
82718
82797
  } else {
82719
82798
  deps.log(`${name}: Powerplant MCP already registered.`);
82720
82799
  }
@@ -82725,7 +82804,7 @@ function connectCodex(bin, name, deps) {
82725
82804
  function applyCodexConfigPatch(name, deps, patch, successLine) {
82726
82805
  if (patch.conflict !== void 0) {
82727
82806
  deps.log(
82728
- `${name}: ${patch.conflict} \u2014 sign-in may fail until the port conflict is resolved; then re-run ${deps.connectCommand}.`
82807
+ `${name}: ${patch.conflict}, sign-in may fail until the port conflict is resolved; then re-run ${deps.connectCommand}.`
82729
82808
  );
82730
82809
  return;
82731
82810
  }
@@ -82769,18 +82848,18 @@ function connectCursor(mcpJsonPath, name, deps) {
82769
82848
  );
82770
82849
  if (patch.error !== void 0 || patch.entry === void 0) {
82771
82850
  deps.log(
82772
- `\u2717 ${name}: ${mcpJsonPath.replace(os15.homedir(), "~")} is ${patch.error} \u2014 fix it by hand, then re-run ${deps.connectCommand}.`
82851
+ `\u2717 ${name}: ${mcpJsonPath.replace(os15.homedir(), "~")} is ${patch.error}, fix it by hand, then re-run ${deps.connectCommand}.`
82773
82852
  );
82774
82853
  return { state: "failed", unchanged: false };
82775
82854
  }
82776
82855
  if (patch.changed) deps.writeTextFile(mcpJsonPath, patch.content);
82777
82856
  const configured = patch.changed ? "configured" : "already configured";
82778
82857
  if (deps.openUrl(cursorInstallDeeplink(MCP_SERVER_NAME, patch.entry))) {
82779
- deps.log(`\u2192 ${name} ${configured} \u2014 approve inside Cursor when it asks.`);
82858
+ deps.log(`\u2192 ${name} ${configured}, approve inside Cursor when it asks.`);
82780
82859
  return { state: "configured", unchanged: !patch.changed };
82781
82860
  }
82782
82861
  deps.log(
82783
- `\u2192 ${name} ${configured} \u2014 could not open Cursor; in Cursor's MCP settings, enable the powerplant server and sign in.`
82862
+ `\u2192 ${name} ${configured}, could not open Cursor; in Cursor's MCP settings, enable the powerplant server and sign in.`
82784
82863
  );
82785
82864
  return { state: "pending", unchanged: false };
82786
82865
  }
@@ -82788,10 +82867,10 @@ function login(bin, name, args, deps, env) {
82788
82867
  deps.log(`\u2192 ${name} approve in your browser\u2026`);
82789
82868
  const status = deps.runInteractive(bin, args, env);
82790
82869
  if (status === 0) {
82791
- deps.log(`\u2713 ${name} connected`);
82870
+ deps.log(`\u2713 ${name} MCP connected`);
82792
82871
  return { state: "connected", unchanged: false };
82793
82872
  }
82794
- deps.log(`\u2192 ${name} login not completed \u2014 finish later with: ${deps.connectCommand}`);
82873
+ deps.log(`\u2192 ${name} MCP sign-in not completed, finish later with: ${deps.connectCommand}`);
82795
82874
  return { state: "pending", unchanged: false };
82796
82875
  }
82797
82876
  function runDisconnect(clients, deps) {
@@ -82804,7 +82883,7 @@ function runDisconnect(clients, deps) {
82804
82883
  };
82805
82884
  if (clients.length === 0) {
82806
82885
  deps.log(
82807
- "No coding agents found (looked for `claude`, `codex`, and Cursor) \u2014 nothing to disconnect."
82886
+ "No coding agents found (looked for `claude`, `codex`, and Cursor), nothing to disconnect."
82808
82887
  );
82809
82888
  return outcome;
82810
82889
  }
@@ -82848,12 +82927,12 @@ function disconnectClaude(client, deps) {
82848
82927
  return "notConnected";
82849
82928
  }
82850
82929
  if (get.status !== 0) {
82851
- deps.log(`\u2717 ${name}: probing for the Powerplant MCP failed \u2014 ${describeFailure(get)}`);
82930
+ deps.log(`\u2717 ${name}: probing for the Powerplant MCP failed, ${describeFailure(get)}`);
82852
82931
  return "failed";
82853
82932
  }
82854
82933
  const remove = deps.run(bin, ["mcp", "remove", "--scope", "user", MCP_SERVER_NAME], env);
82855
82934
  if (remove.status !== 0) {
82856
- deps.log(`\u2717 ${name}: removing the Powerplant MCP failed \u2014 ${describeFailure(remove)}`);
82935
+ deps.log(`\u2717 ${name}: removing the Powerplant MCP failed, ${describeFailure(remove)}`);
82857
82936
  return "failed";
82858
82937
  }
82859
82938
  deps.log(`${name}: Powerplant MCP removed, sign-in included.`);
@@ -82863,12 +82942,12 @@ function disconnectCodex(bin, deps) {
82863
82942
  const name = describeBinary(bin);
82864
82943
  const list = deps.run(bin, ["mcp", "list", "--json"]);
82865
82944
  if (list.status !== 0) {
82866
- deps.log(`\u2717 ${name}: listing MCP servers failed \u2014 ${describeFailure(list)}`);
82945
+ deps.log(`\u2717 ${name}: listing MCP servers failed, ${describeFailure(list)}`);
82867
82946
  return "failed";
82868
82947
  }
82869
82948
  const servers = parseCodexServers(list.stdout);
82870
82949
  if (servers === null) {
82871
- deps.log(`\u2717 ${name}: could not read its MCP server list \u2014 is this codex version very old?`);
82950
+ deps.log(`\u2717 ${name}: could not read its MCP server list, is this codex version very old?`);
82872
82951
  return "failed";
82873
82952
  }
82874
82953
  const registered = servers.some((server) => server.name === MCP_SERVER_NAME);
@@ -82881,7 +82960,7 @@ function disconnectCodex(bin, deps) {
82881
82960
  deps.run(bin, ["mcp", "logout", MCP_SERVER_NAME]);
82882
82961
  const remove = deps.run(bin, ["mcp", "remove", MCP_SERVER_NAME]);
82883
82962
  if (remove.status !== 0) {
82884
- deps.log(`\u2717 ${name}: removing the Powerplant MCP failed \u2014 ${describeFailure(remove)}`);
82963
+ deps.log(`\u2717 ${name}: removing the Powerplant MCP failed, ${describeFailure(remove)}`);
82885
82964
  return "failed";
82886
82965
  }
82887
82966
  deps.log(`${name}: Powerplant MCP removed, sign-in included.`);
@@ -82891,7 +82970,7 @@ function disconnectCursor(mcpJsonPath, deps) {
82891
82970
  const name = `Cursor (${mcpJsonPath.replace(os15.homedir(), "~")})`;
82892
82971
  const patch = unpatchCursorMcpJson(deps.readTextFile(mcpJsonPath), MCP_SERVER_NAME);
82893
82972
  if (patch.error !== void 0) {
82894
- deps.log(`\u2717 ${name}: mcp.json is ${patch.error} \u2014 fix it by hand, then re-run powerplant disconnect.`);
82973
+ deps.log(`\u2717 ${name}: mcp.json is ${patch.error}, fix it by hand, then re-run powerplant disconnect.`);
82895
82974
  return "failed";
82896
82975
  }
82897
82976
  if (!patch.changed) {
@@ -82918,11 +82997,11 @@ function anyClientStillConnected(clients, deps) {
82918
82997
  });
82919
82998
  }
82920
82999
  function describeBinary(bin) {
82921
- return `${path15.basename(bin)} (${bin.replace(os15.homedir(), "~")})`;
83000
+ return `${path14.basename(bin)} (${bin.replace(os15.homedir(), "~")})`;
82922
83001
  }
82923
83002
  function describeClaudeTarget(client) {
82924
83003
  if (client.configDir === void 0) return describeBinary(client.bin);
82925
- return `${path15.basename(client.bin)} (${client.configDir.replace(os15.homedir(), "~")})`;
83004
+ return `${path14.basename(client.bin)} (${client.configDir.replace(os15.homedir(), "~")})`;
82926
83005
  }
82927
83006
  function describeFailure(result) {
82928
83007
  return result.error ?? lastLine(result.stdout + result.stderr);
@@ -82933,7 +83012,7 @@ function lastLine(output) {
82933
83012
  }
82934
83013
  var neutralDirMemo;
82935
83014
  function neutralWorkdir() {
82936
- neutralDirMemo ??= mkdtempSync(path15.join(os15.tmpdir(), "powerplant-connect-"));
83015
+ neutralDirMemo ??= mkdtempSync(path14.join(os15.tmpdir(), "powerplant-connect-"));
82937
83016
  return neutralDirMemo;
82938
83017
  }
82939
83018
  async function fetchOfficialClients(apiUrl, log3) {
@@ -82956,7 +83035,7 @@ async function fetchOfficialClients(apiUrl, log3) {
82956
83035
  return officialClientsDocumentSchema.parse(await response.json()).clients;
82957
83036
  } catch (error52) {
82958
83037
  log3(
82959
- `Official client ids unavailable (${error52 instanceof Error ? error52.message : String(error52)}) \u2014 agents will use dynamic registration where the environment allows it.`
83038
+ `Official client ids unavailable (${error52 instanceof Error ? error52.message : String(error52)}), agents will use dynamic registration where the environment allows it.`
82960
83039
  );
82961
83040
  return null;
82962
83041
  }
@@ -82977,11 +83056,11 @@ function realReadTextFileStrict(filePath) {
82977
83056
  }
82978
83057
  }
82979
83058
  function realWriteTextFile(filePath, content) {
82980
- mkdirSync6(path15.dirname(filePath), { recursive: true });
83059
+ mkdirSync6(path14.dirname(filePath), { recursive: true });
82981
83060
  writeFileSync6(filePath, content);
82982
83061
  }
82983
83062
  function codexConfigPath(homeDir = os15.homedir()) {
82984
- return path15.join(process.env.CODEX_HOME ?? path15.join(homeDir, ".codex"), "config.toml");
83063
+ return path14.join(process.env.CODEX_HOME ?? path14.join(homeDir, ".codex"), "config.toml");
82985
83064
  }
82986
83065
  function realOpenUrl(url2) {
82987
83066
  const bin = process.platform === "darwin" ? "open" : process.platform === "linux" ? "xdg-open" : null;
@@ -83029,9 +83108,9 @@ function realRunInteractive(bin, args, env) {
83029
83108
  }
83030
83109
 
83031
83110
  // src/connections.ts
83032
- import { execFileSync as execFileSync6, spawnSync as spawnSync2 } from "node:child_process";
83111
+ import { execFileSync as execFileSync5, spawnSync as spawnSync2 } from "node:child_process";
83033
83112
  import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync7 } from "node:fs";
83034
- import path16 from "node:path";
83113
+ import path15 from "node:path";
83035
83114
  function probeConnections(clients, deps) {
83036
83115
  const reports = [];
83037
83116
  for (const kind of ["claude", "codex", "cursor"]) {
@@ -83075,7 +83154,7 @@ function aggregateClaudeTargets(targets) {
83075
83154
  if (targets.length === 1 || worst.report.state === "signed-in") return worst.report;
83076
83155
  return {
83077
83156
  ...worst.report,
83078
- target: worst.configDir === void 0 ? "default profile" : path16.basename(worst.configDir)
83157
+ target: worst.configDir === void 0 ? "default profile" : path15.basename(worst.configDir)
83079
83158
  };
83080
83159
  }
83081
83160
  function probeClient(client, deps) {
@@ -83154,12 +83233,12 @@ var detectedClientsSchema = external_exports.object({
83154
83233
  )
83155
83234
  });
83156
83235
  function detectedClientsPath(homeDir) {
83157
- return path16.join(collectorStateDir(homeDir), "detected-clients.json");
83236
+ return path15.join(collectorStateDir(homeDir), "detected-clients.json");
83158
83237
  }
83159
83238
  function saveDetectedClients(clients, pathValue, homeDir) {
83160
83239
  try {
83161
83240
  const file2 = detectedClientsPath(homeDir);
83162
- mkdirSync7(path16.dirname(file2), { recursive: true });
83241
+ mkdirSync7(path15.dirname(file2), { recursive: true });
83163
83242
  const record2 = {
83164
83243
  detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
83165
83244
  path: pathValue,
@@ -83232,7 +83311,7 @@ function runWithPath(pathValue, budget = UNLIMITED_TIME_BUDGET) {
83232
83311
  function whichAllWith(pathValue) {
83233
83312
  return (name) => {
83234
83313
  try {
83235
- return execFileSync6("which", ["-a", name], {
83314
+ return execFileSync5("which", ["-a", name], {
83236
83315
  encoding: "utf8",
83237
83316
  env: { ...process.env, PATH: pathValue }
83238
83317
  }).split("\n").map((line) => line.trim()).filter(Boolean);
@@ -83250,72 +83329,72 @@ function renderStatus(i) {
83250
83329
  const lines = [];
83251
83330
  lines.push(`Environment: ${i.config.appEnv} (API ${i.config.apiUrl})`);
83252
83331
  lines.push(
83253
- `CLI: powerplant ${i.cliVersion}` + (i.latestVersion !== null && i.latestVersion !== i.cliVersion ? ` \u2014 ${i.latestVersion} is available: powerplant update` : "")
83332
+ `CLI: powerplant ${i.cliVersion}` + (i.latestVersion !== null && i.latestVersion !== i.cliVersion ? `, ${i.latestVersion} is available: powerplant update` : "")
83254
83333
  );
83255
83334
  const unsupported = unsupportedVersionNotice(i.cliVersion, i.oldestSupportedVersion);
83256
83335
  if (unsupported !== null) lines.push(unsupported);
83257
83336
  if (i.credentialsError !== void 0) {
83258
83337
  lines.push(`\u26A0 ${i.credentialsError}`);
83259
83338
  } else if (!i.credentials) {
83260
- lines.push("Not enrolled \u2014 run: powerplant init");
83339
+ lines.push("Not enrolled, run: powerplant init");
83261
83340
  } else {
83262
- const orgLine = i.credentials.orgName ? ` into org "${i.credentials.orgName}"` : " (org not recorded \u2014 re-enroll to see it)";
83341
+ const orgLine = i.credentials.orgName ? ` into org "${i.credentials.orgName}"` : " (org not recorded, re-enroll to see it)";
83263
83342
  lines.push(
83264
83343
  `Enrolled as "${i.credentials.machineName}" (${i.credentials.machineId})${orgLine} on ${i.credentials.enrolledAt}.`
83265
83344
  );
83266
83345
  lines.push(`Syncing: ${describeStoredCapture(i.credentials)}.`);
83267
83346
  for (const entry of i.protectedSessions ?? []) {
83268
83347
  lines.push(
83269
- `\u26A0 Repository matching is not enabled for ${entry.sessionCount} recent agent session${entry.sessionCount === 1 ? "" : "s"} under ${entry.displayRoot}. Sessions outside folders you explicitly added are staying local \u2014 run \`powerplant init\` to review Git repository scanning.`
83348
+ `\u26A0 Repository matching is not enabled for ${entry.sessionCount} recent agent session${entry.sessionCount === 1 ? "" : "s"} under ${entry.displayRoot}. Sessions outside folders you explicitly added are staying local, run \`powerplant init\` to review Git repository scanning.`
83270
83349
  );
83271
83350
  }
83272
83351
  }
83273
83352
  switch (i.service) {
83274
83353
  case "unsupported":
83275
83354
  lines.push(
83276
- "Background service: unsupported on this platform (macOS-only for now) \u2014 run `powerplant sync` on a schedule instead."
83355
+ "Background sync: unsupported on this platform (macOS-only for now), run `powerplant sync` on a schedule instead."
83277
83356
  );
83278
83357
  break;
83279
83358
  case "not-installed":
83280
- lines.push("Background service: not installed \u2014 run: powerplant init");
83359
+ lines.push("Background sync: not installed, run: powerplant init");
83281
83360
  break;
83282
83361
  case "not-loaded":
83283
- lines.push("Background service: installed but not loaded \u2014 run: powerplant restart");
83362
+ lines.push("Background sync: installed but not loaded, run: powerplant restart");
83284
83363
  break;
83285
83364
  case "loaded":
83286
83365
  lines.push(...renderDaemon(i));
83287
83366
  break;
83288
83367
  }
83289
83368
  if (i.control.paused) {
83290
- lines.push("\u23F8 Syncing is paused \u2014 resume with: powerplant resume");
83369
+ lines.push("\u23F8 Syncing is paused, resume with: powerplant resume");
83291
83370
  }
83292
83371
  return lines;
83293
83372
  }
83294
83373
  function renderDaemon(i) {
83295
83374
  if (i.daemon.state === "unreadable") {
83296
83375
  return [
83297
- "Background service: loaded, but its status file is unreadable (written by a different version?) \u2014 run: powerplant restart"
83376
+ "Background sync: loaded, but its status file is unreadable (written by a different version?), run: powerplant restart"
83298
83377
  ];
83299
83378
  }
83300
83379
  if (i.daemon.state === "not-running") {
83301
83380
  return [
83302
- `Background service: loaded, but no sync service status is available \u2014 starting up, or running a version from before status reporting. If this persists: powerplant restart. Log: ${serviceLogPath(i.config.appEnv)}`
83381
+ `Background sync: loaded, but no background sync status is available, starting up, or running a version from before status reporting. If this persists: powerplant restart. Log: ${serviceLogPath(i.config.appEnv)}`
83303
83382
  ];
83304
83383
  }
83305
83384
  const status = i.daemon.status;
83306
83385
  const lines = [];
83307
83386
  if (i.daemon.state === "stalled") {
83308
83387
  lines.push(
83309
- `\u26A0 Sync service stalled \u2014 no heartbeat for ${duration3(i.daemon.staleMs)} (pid ${status.pid}). Run: powerplant restart`
83388
+ `\u26A0 Background sync stalled, no heartbeat for ${duration3(i.daemon.staleMs)} (pid ${status.pid}). Run: powerplant restart`
83310
83389
  );
83311
83390
  } else {
83312
83391
  lines.push(
83313
- `Sync service: running (version ${status.version}, pid ${status.pid}, started ${status.startedAt}).`
83392
+ `Background sync: running (version ${status.version}, pid ${status.pid}, started ${status.startedAt}).`
83314
83393
  );
83315
83394
  }
83316
83395
  if (status.version !== i.cliVersion) {
83317
83396
  lines.push(
83318
- ` The daemon is on ${status.version}, this CLI is ${i.cliVersion} \u2014 it restarts onto the installed version within minutes, or now via: powerplant restart`
83397
+ ` The daemon is on ${status.version}, this CLI is ${i.cliVersion}, it restarts onto the installed version within minutes, or now via: powerplant restart`
83319
83398
  );
83320
83399
  }
83321
83400
  lines.push(
@@ -83399,13 +83478,13 @@ function redactedLogTail(raw) {
83399
83478
  while (lines.length > 0 && lines[lines.length - 1]?.trim() === "") lines.pop();
83400
83479
  return lines.slice(-DOCTOR_LOG_TAIL_LINES);
83401
83480
  }
83402
- function readDoctorLogTail(path19) {
83481
+ function readDoctorLogTail(path18) {
83403
83482
  let fd;
83404
83483
  try {
83405
- fd = openSync2(path19, "r");
83484
+ fd = openSync2(path18, "r");
83406
83485
  } catch (error52) {
83407
- if (error52.code === "ENOENT") return { path: path19, lines: null };
83408
- return { path: path19, lines: null, readError: error52 instanceof Error ? error52.message : String(error52) };
83486
+ if (error52.code === "ENOENT") return { path: path18, lines: null };
83487
+ return { path: path18, lines: null, readError: error52 instanceof Error ? error52.message : String(error52) };
83409
83488
  }
83410
83489
  try {
83411
83490
  const size = fstatSync(fd).size;
@@ -83414,9 +83493,9 @@ function readDoctorLogTail(path19) {
83414
83493
  readSync2(fd, buffer, 0, buffer.length, offset);
83415
83494
  let text = buffer.toString("utf8");
83416
83495
  if (offset > 0) text = text.slice(text.indexOf("\n") + 1);
83417
- return { path: path19, lines: redactedLogTail(text) };
83496
+ return { path: path18, lines: redactedLogTail(text) };
83418
83497
  } catch (error52) {
83419
- return { path: path19, lines: null, readError: error52 instanceof Error ? error52.message : String(error52) };
83498
+ return { path: path18, lines: null, readError: error52 instanceof Error ? error52.message : String(error52) };
83420
83499
  } finally {
83421
83500
  closeSync2(fd);
83422
83501
  }
@@ -83424,7 +83503,7 @@ function readDoctorLogTail(path19) {
83424
83503
  function renderDoctorReport(i) {
83425
83504
  const lines = [];
83426
83505
  lines.push(
83427
- "Powerplant diagnostics \u2014 paste everything below into your email to support@forestwalk.ai."
83506
+ "Powerplant diagnostics, paste everything below into your email to support@forestwalk.ai."
83428
83507
  );
83429
83508
  lines.push(
83430
83509
  `Collected ${new Date(i.now).toISOString()}. This command only prints; nothing was uploaded.`
@@ -83446,7 +83525,7 @@ function renderDoctorReport(i) {
83446
83525
  }
83447
83526
  function enrollmentLine(i) {
83448
83527
  if (i.credentialsError !== void 0) {
83449
- return `Enrollment: credential store unreadable \u2014 ${i.credentialsError}`;
83528
+ return `Enrollment: credential store unreadable, ${i.credentialsError}`;
83450
83529
  }
83451
83530
  if (!i.credentials) return "Enrollment: not enrolled";
83452
83531
  return `Enrollment: machine ${i.credentials.machineId}, org ${i.credentials.orgId ?? "not recorded"}, since ${i.credentials.enrolledAt}`;
@@ -83471,28 +83550,28 @@ function serviceLines(i) {
83471
83550
  const status = i.daemon.status;
83472
83551
  const out = [];
83473
83552
  out.push(
83474
- i.daemon.state === "stalled" ? `Sync service: stalled \u2014 no heartbeat for ${duration3(i.daemon.staleMs)} (version ${status.version}, pid ${status.pid})` : `Sync service: running (version ${status.version}, pid ${status.pid}, started ${status.startedAt})`
83553
+ i.daemon.state === "stalled" ? `Sync service: stalled, no heartbeat for ${duration3(i.daemon.staleMs)} (version ${status.version}, pid ${status.pid})` : `Sync service: running (version ${status.version}, pid ${status.pid}, started ${status.startedAt})`
83475
83554
  );
83476
83555
  out.push(
83477
83556
  status.lastSyncAt !== void 0 ? `Last successful sync: ${ago(i.now - Date.parse(status.lastSyncAt))} (${status.lastSyncAt})` : "No successful sync yet in this daemon run."
83478
83557
  );
83479
83558
  if (status.lastPass) {
83480
83559
  out.push(
83481
- status.lastPass.ok ? `Last pass: ok (${status.lastPass.kind}, ${status.lastPass.sessions} sessions, ${status.lastPass.upserted} uploaded, at ${status.lastPass.at})` : `Last pass: failed at ${status.lastPass.at} \u2014 ${redactText(status.lastPass.error ?? "unknown error")} (${status.consecutiveFailures} consecutive failure${status.consecutiveFailures === 1 ? "" : "s"})`
83560
+ status.lastPass.ok ? `Last pass: ok (${status.lastPass.kind}, ${status.lastPass.sessions} sessions, ${status.lastPass.upserted} uploaded, at ${status.lastPass.at})` : `Last pass: failed at ${status.lastPass.at}, ${redactText(status.lastPass.error ?? "unknown error")} (${status.consecutiveFailures} consecutive failure${status.consecutiveFailures === 1 ? "" : "s"})`
83482
83561
  );
83483
83562
  }
83484
83563
  return out;
83485
83564
  }
83486
83565
  function logLines(tail) {
83487
83566
  if (tail.readError !== void 0) {
83488
- return [`Sync service log: could not read ${tail.path} \u2014 ${tail.readError}`];
83567
+ return [`Sync service log: could not read ${tail.path}, ${tail.readError}`];
83489
83568
  }
83490
83569
  if (tail.lines === null) {
83491
83570
  return [`Sync service log: no file at ${tail.path} (the background service may never have run).`];
83492
83571
  }
83493
83572
  if (tail.lines.length === 0) return [`Sync service log: ${tail.path} is empty.`];
83494
83573
  return [
83495
- `Sync service log \u2014 last ${tail.lines.length} line${tail.lines.length === 1 ? "" : "s"} of ${tail.path}, credentials redacted:`,
83574
+ `Sync service log, last ${tail.lines.length} line${tail.lines.length === 1 ? "" : "s"} of ${tail.path}, credentials redacted:`,
83496
83575
  ...tail.lines
83497
83576
  ];
83498
83577
  }
@@ -83536,13 +83615,13 @@ function isCliEnv(value) {
83536
83615
  }
83537
83616
  function describeEnvironment(config2, env, explicitEnv) {
83538
83617
  const steeredByEnvVar = explicitEnv === void 0 && env.POWERPLANT_ENV !== void 0;
83539
- return `Environment: ${config2.appEnv} (API ${config2.apiUrl})${steeredByEnvVar ? " \u2014 set by POWERPLANT_ENV" : ""}`;
83618
+ return `Environment: ${config2.appEnv} (API ${config2.apiUrl})${steeredByEnvVar ? ", set by POWERPLANT_ENV" : ""}`;
83540
83619
  }
83541
83620
  function resolveCliConfig(env, explicitEnv) {
83542
83621
  const name = explicitEnv ?? env.POWERPLANT_ENV ?? "production";
83543
83622
  if (!isCliEnv(name)) {
83544
83623
  throw new ExpectedCliError(
83545
- explicitEnv === void 0 ? `Unknown environment "${name}" (from POWERPLANT_ENV in your shell) \u2014 unset POWERPLANT_ENV to use production.` : `Unknown environment "${name}" \u2014 drop --env to use production.`
83624
+ explicitEnv === void 0 ? `Unknown environment "${name}" (from POWERPLANT_ENV in your shell), unset POWERPLANT_ENV to use production.` : `Unknown environment "${name}", drop --env to use production.`
83546
83625
  );
83547
83626
  }
83548
83627
  const base = CLI_ENVIRONMENTS[name];
@@ -83584,7 +83663,7 @@ async function runInit(deps) {
83584
83663
  let repoDiscoveryEnabled = false;
83585
83664
  let protectedFoldersNeedAttention = false;
83586
83665
  let installed = false;
83587
- log3("2/3 Start background syncing");
83666
+ log3("2/3 Start background sync");
83588
83667
  if (deps.platform === "darwin") {
83589
83668
  const protectedSessions = await deps.activity(
83590
83669
  "Checking recent session locations\u2026",
@@ -83639,29 +83718,29 @@ async function runInit(deps) {
83639
83718
  }
83640
83719
  if (deps.platform !== "darwin") {
83641
83720
  log3(
83642
- " The background service is macOS-only for now \u2014 run `powerplant sync` on a schedule of your choosing instead."
83721
+ " Background sync is macOS-only for now. Run `powerplant sync` on a schedule of your choosing instead."
83643
83722
  );
83644
83723
  } else if (deps.serviceUpToDate()) {
83645
- log3("\u2713 Background syncing is running");
83724
+ log3("\u2713 Background sync is running");
83646
83725
  } else {
83647
- await deps.activity("Installing the sync service\u2026", deps.installService);
83648
- log3(" \u2713 Sync service is running");
83726
+ await deps.activity("Installing background sync\u2026", deps.installService);
83727
+ log3(" \u2713 Background sync is running");
83649
83728
  log3(
83650
83729
  ` Runs quietly and only uploads sessions inside ${orgName ? `${orgName}\u2019s` : "your organization\u2019s"} boundary.`
83651
83730
  );
83652
83731
  installed = true;
83653
83732
  }
83654
83733
  log3("");
83655
- log3("3/3 Add MCP to your agents");
83734
+ log3("3/3 Give your agents Powerplant MCP access");
83656
83735
  let outcome = null;
83657
83736
  try {
83658
83737
  outcome = await deps.connect();
83659
83738
  } catch (error52) {
83660
- log3(` \u2717 Adding MCP failed \u2014 ${error52 instanceof Error ? error52.message : String(error52)}`);
83739
+ log3(` \u2717 Could not give your agents Powerplant MCP access, ${error52 instanceof Error ? error52.message : String(error52)}`);
83661
83740
  }
83662
83741
  if (outcome?.found === 0) {
83663
83742
  log3(
83664
- deps.platform === "darwin" ? ` This machine still syncs once one is installed \u2014 then run: ${deps.connectCommand}` : ` Once one is installed, run: ${deps.connectCommand}`
83743
+ deps.platform === "darwin" ? ` This machine still syncs once one is installed, then run: ${deps.connectCommand}` : ` Once one is installed, run: ${deps.connectCommand}`
83665
83744
  );
83666
83745
  }
83667
83746
  log3("");
@@ -83689,7 +83768,7 @@ async function runInit(deps) {
83689
83768
  log3(` Open Powerplant: ${deps.webUrl}`);
83690
83769
  log3("");
83691
83770
  log3(
83692
- deps.platform === "darwin" ? ` Start a coding session in ${repository} \u2014 watch it appear in Powerplant.` : ` Start a coding session in ${repository}, then run \`powerplant sync\` to see it in Powerplant.`
83771
+ deps.platform === "darwin" ? ` Start a coding session in ${repository}, watch it appear in Powerplant.` : ` Start a coding session in ${repository}, then run \`powerplant sync\` to see it in Powerplant.`
83693
83772
  );
83694
83773
  }
83695
83774
  async function confirmProtectedRepoDiscovery(deps) {
@@ -83721,10 +83800,10 @@ async function realPromptLine(question) {
83721
83800
  }
83722
83801
 
83723
83802
  // src/memberSessions.ts
83724
- import { execFileSync as execFileSync7 } from "node:child_process";
83725
- import { existsSync as existsSync6 } from "node:fs";
83803
+ import { execFileSync as execFileSync6 } from "node:child_process";
83804
+ import { existsSync as existsSync5 } from "node:fs";
83726
83805
  import os16 from "node:os";
83727
- import path17 from "node:path";
83806
+ import path16 from "node:path";
83728
83807
  var memberSessionSchema = external_exports.object({
83729
83808
  id: external_exports.string().min(1),
83730
83809
  source: external_exports.string().min(1),
@@ -83735,12 +83814,12 @@ var memberSessionSchema = external_exports.object({
83735
83814
  lastActivityAt: external_exports.string().min(1),
83736
83815
  providerUrl: external_exports.string().nullable()
83737
83816
  });
83738
- var FETCH_TIMEOUT_MS3 = 3e3;
83817
+ var FETCH_TIMEOUT_MS2 = 3e3;
83739
83818
  async function fetchMemberSessions(credentials, fetchImpl = fetch) {
83740
83819
  try {
83741
83820
  const response = await fetchImpl(`${credentials.apiUrl}/api/collector/member-sessions`, {
83742
83821
  headers: { authorization: `Bearer ${credentials.token}`, ...cliVersionHeaders() },
83743
- signal: AbortSignal.timeout(FETCH_TIMEOUT_MS3)
83822
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS2)
83744
83823
  });
83745
83824
  if (!response.ok) return null;
83746
83825
  const envelope = external_exports.object({ data: external_exports.object({ sessions: external_exports.array(memberSessionSchema) }) }).safeParse(await response.json());
@@ -83784,11 +83863,11 @@ function codexThreadId(externalId) {
83784
83863
  }
83785
83864
  var CODEX_APP_BUNDLES = ["ChatGPT.app", "Codex.app"];
83786
83865
  var urlSchemesSchema = external_exports.array(external_exports.object({ CFBundleURLSchemes: external_exports.array(external_exports.string()).optional() }));
83787
- function detectCodexDesktop(probe = { exists: existsSync6, readUrlSchemes: realReadUrlSchemes }) {
83788
- const roots = ["/Applications", path17.join(os16.homedir(), "Applications")];
83866
+ function detectCodexDesktop(probe = { exists: existsSync5, readUrlSchemes: realReadUrlSchemes }) {
83867
+ const roots = ["/Applications", path16.join(os16.homedir(), "Applications")];
83789
83868
  return roots.some(
83790
83869
  (root) => CODEX_APP_BUNDLES.some((bundle) => {
83791
- const bundlePath = path17.join(root, bundle);
83870
+ const bundlePath = path16.join(root, bundle);
83792
83871
  return probe.exists(bundlePath) && probe.readUrlSchemes(bundlePath).includes("codex");
83793
83872
  })
83794
83873
  );
@@ -83796,9 +83875,9 @@ function detectCodexDesktop(probe = { exists: existsSync6, readUrlSchemes: realR
83796
83875
  function realReadUrlSchemes(bundlePath) {
83797
83876
  if (process.platform !== "darwin") return [];
83798
83877
  try {
83799
- const json2 = execFileSync7(
83878
+ const json2 = execFileSync6(
83800
83879
  "plutil",
83801
- ["-extract", "CFBundleURLTypes", "json", "-o", "-", path17.join(bundlePath, "Contents", "Info.plist")],
83880
+ ["-extract", "CFBundleURLTypes", "json", "-o", "-", path16.join(bundlePath, "Contents", "Info.plist")],
83802
83881
  { stdio: ["ignore", "pipe", "ignore"] }
83803
83882
  ).toString();
83804
83883
  const parsed = urlSchemesSchema.safeParse(JSON.parse(json2));
@@ -83810,7 +83889,7 @@ function realReadUrlSchemes(bundlePath) {
83810
83889
  }
83811
83890
 
83812
83891
  // src/menu.ts
83813
- import path18 from "node:path";
83892
+ import path17 from "node:path";
83814
83893
  var MENU_CONTRACT = 1;
83815
83894
  var CLIENT_NAMES = { claude: "Claude Code", codex: "Codex", cursor: "Cursor" };
83816
83895
  function renderMenu(i) {
@@ -83856,7 +83935,7 @@ function renderMenu(i) {
83856
83935
  }
83857
83936
  if (i.connections.length > 0) {
83858
83937
  items.push({ type: "separator" });
83859
- items.push({ type: "section", title: "Connections" });
83938
+ items.push({ type: "section", title: "Agents authorized for MCP" });
83860
83939
  for (const connection of i.connections) {
83861
83940
  const { row, action, warns } = connectionRow(connection, envArgs);
83862
83941
  if (warns) attention = true;
@@ -83877,7 +83956,7 @@ function renderMenu(i) {
83877
83956
  items.push({ type: "separator" });
83878
83957
  items.push({ type: "action", title: "Open Powerplant", url: i.config.webUrl });
83879
83958
  if (i.credentials !== null && i.service === "loaded" && !i.control.paused) {
83880
- items.push({ type: "action", title: "Pause Syncing", run: ["pause", ...envArgs] });
83959
+ items.push({ type: "action", title: "Pause syncing", run: ["pause", ...envArgs] });
83881
83960
  }
83882
83961
  const icon = attention ? "attention" : i.control.paused ? "paused" : "running";
83883
83962
  return {
@@ -83898,12 +83977,12 @@ function updateAction(i) {
83898
83977
  function collectorBlock(i, envArgs, problem) {
83899
83978
  const restart = {
83900
83979
  type: "action",
83901
- title: "Restart Sync Service",
83980
+ title: "Restart background sync",
83902
83981
  run: ["restart", ...envArgs]
83903
83982
  };
83904
83983
  const viewLog = {
83905
83984
  type: "action",
83906
- title: "View Sync Service Log",
83985
+ title: "View background sync log",
83907
83986
  file: serviceLogPath(i.config.appEnv)
83908
83987
  };
83909
83988
  switch (i.service) {
@@ -83912,19 +83991,19 @@ function collectorBlock(i, envArgs, problem) {
83912
83991
  {
83913
83992
  type: "status",
83914
83993
  mark: "muted",
83915
- title: "Background service isn't supported on this platform",
83994
+ title: "Background sync isn't supported on this platform",
83916
83995
  subtitle: "Sessions upload when you run: powerplant sync"
83917
83996
  }
83918
83997
  ];
83919
83998
  case "not-installed":
83920
83999
  return [
83921
- problem("Not uploading \u2014 the background service isn't set up"),
83922
- { type: "action", title: "Set Up Service\u2026", terminal: ["init", ...envArgs] }
84000
+ problem("Not uploading, the background sync isn't set up"),
84001
+ { type: "action", title: "Set up background sync\u2026", terminal: ["init", ...envArgs] }
83923
84002
  ];
83924
84003
  case "not-loaded":
83925
84004
  return [
83926
- problem("Not uploading \u2014 the background service is stopped"),
83927
- { type: "action", title: "Start Service", run: ["restart", ...envArgs] }
84005
+ problem("Not uploading, the background sync is stopped"),
84006
+ { type: "action", title: "Start background sync", run: ["restart", ...envArgs] }
83928
84007
  ];
83929
84008
  case "loaded":
83930
84009
  break;
@@ -83933,15 +84012,15 @@ function collectorBlock(i, envArgs, problem) {
83933
84012
  case "unreadable":
83934
84013
  return [
83935
84014
  problem(
83936
- "Sync service status can't be read",
83937
- "Its report was written by a different version \u2014 restarting usually clears this."
84015
+ "Background sync status can't be read",
84016
+ "Its report was written by a different version, restarting usually clears this."
83938
84017
  ),
83939
84018
  restart
83940
84019
  ];
83941
84020
  case "not-running":
83942
84021
  return [
83943
84022
  problem(
83944
- "Not uploading \u2014 the sync service isn't running",
84023
+ "Not uploading, the background sync isn't running",
83945
84024
  "It may be starting up. If this lasts, restart it."
83946
84025
  ),
83947
84026
  restart
@@ -83949,7 +84028,7 @@ function collectorBlock(i, envArgs, problem) {
83949
84028
  case "stalled":
83950
84029
  return [
83951
84030
  problem(
83952
- `Sync service quiet for ${duration3(i.daemon.staleMs)} \u2014 it may be stuck`,
84031
+ `Background sync quiet for ${duration3(i.daemon.staleMs)}, it may be stuck`,
83953
84032
  lastUploadLine(i)
83954
84033
  ),
83955
84034
  restart
@@ -83963,9 +84042,9 @@ function collectorBlock(i, envArgs, problem) {
83963
84042
  type: "status",
83964
84043
  mark: "paused",
83965
84044
  title: "Syncing paused",
83966
- subtitle: "The sync service is running but uploads nothing."
84045
+ subtitle: "The background sync is running but uploads nothing."
83967
84046
  },
83968
- { type: "action", title: "Resume Syncing", run: ["resume", ...envArgs] }
84047
+ { type: "action", title: "Resume syncing", run: ["resume", ...envArgs] }
83969
84048
  ];
83970
84049
  }
83971
84050
  const status = i.daemon.status;
@@ -83979,7 +84058,7 @@ function collectorBlock(i, envArgs, problem) {
83979
84058
  ];
83980
84059
  }
83981
84060
  return [
83982
- { type: "status", mark: "ok", title: "Sync service running", subtitle: lastUploadLine(i) }
84061
+ { type: "status", mark: "ok", title: "Background sync running", subtitle: lastUploadLine(i) }
83983
84062
  ];
83984
84063
  }
83985
84064
  var MAX_ACTIVE_AGENTS = 8;
@@ -84030,15 +84109,15 @@ function connectionRow(connection, envArgs) {
84030
84109
  type: "connection",
84031
84110
  mark,
84032
84111
  name,
84033
- state: connection.target === void 0 ? state : `${state} \u2014 ${connection.target}`
84112
+ state: connection.target === void 0 ? state : `${state}, ${connection.target}`
84034
84113
  });
84035
84114
  switch (connection.state) {
84036
84115
  case "signed-in":
84037
- return { row: row("ok", "Signed in"), warns: false };
84116
+ return { row: row("ok", "MCP access granted"), warns: false };
84038
84117
  case "sign-in-needed":
84039
84118
  return {
84040
- row: row("warn", "Sign-in needed"),
84041
- action: fix(`Sign In to ${name}\u2026`),
84119
+ row: row("warn", "MCP sign-in needed"),
84120
+ action: fix(`Give ${name} MCP access\u2026`),
84042
84121
  warns: true
84043
84122
  };
84044
84123
  case "different-url":
@@ -84049,12 +84128,12 @@ function connectionRow(connection, envArgs) {
84049
84128
  };
84050
84129
  case "not-connected":
84051
84130
  return {
84052
- row: row("warn", connection.client === "cursor" ? "Not set up" : "Not connected"),
84053
- action: fix(connection.client === "cursor" ? "Set Up Cursor\u2026" : `Connect ${name}\u2026`),
84131
+ row: row("warn", connection.client === "cursor" ? "MCP not configured" : "Not connected"),
84132
+ action: fix(connection.client === "cursor" ? "Add Powerplant MCP to Cursor\u2026" : `Give ${name} MCP access\u2026`),
84054
84133
  warns: true
84055
84134
  };
84056
84135
  case "configured":
84057
- return { row: row("muted", "Set up in Cursor"), warns: false };
84136
+ return { row: row("muted", "MCP configured in Cursor"), warns: false };
84058
84137
  case "check-failed":
84059
84138
  return { row: row("muted", "Can't check right now"), warns: false };
84060
84139
  }
@@ -84074,13 +84153,13 @@ function moreItems(i) {
84074
84153
  muted(`Enrolled ${i.credentials.enrolledAt.slice(0, 10)}`),
84075
84154
  muted(`CLI ${i.cliVersion}`, daemonVersionNote(i))
84076
84155
  ];
84077
- more.push({ type: "submenu", title: "Sync Service Details", items: details });
84156
+ more.push({ type: "submenu", title: "Background sync details", items: details });
84078
84157
  }
84079
84158
  more.push({ type: "action", title: "Copy Diagnostics", diagnostics: true });
84080
84159
  if (i.config.appEnv !== "production") {
84081
84160
  more.push({
84082
84161
  type: "action",
84083
- title: "View Sync Service Log",
84162
+ title: "View background sync log",
84084
84163
  file: serviceLogPath(i.config.appEnv)
84085
84164
  });
84086
84165
  }
@@ -84092,18 +84171,18 @@ function captureLine(credentials) {
84092
84171
  return `Syncs sessions under ${(credentials.scopePaths ?? []).join(", ")}`;
84093
84172
  case "org-repos": {
84094
84173
  const owners = credentials.allowedOwners ?? [];
84095
- return owners.length > 0 ? `Syncs sessions in ${owners.join(", ")} repositories` : "Set to sync your organization's repositories, but none are linked yet \u2014 nothing uploads";
84174
+ return owners.length > 0 ? `Syncs sessions in ${owners.join(", ")} repositories` : "Set to sync your organization's repositories, but none are linked yet, nothing uploads";
84096
84175
  }
84097
84176
  }
84098
84177
  }
84099
84178
  function daemonVersionNote(i) {
84100
84179
  if (!("status" in i.daemon)) return void 0;
84101
84180
  const version2 = i.daemon.status.version;
84102
- return version2 === i.cliVersion ? void 0 : `Sync service ${version2}`;
84181
+ return version2 === i.cliVersion ? void 0 : `Background sync ${version2}`;
84103
84182
  }
84104
84183
  var VERSION_CACHE_TTL_MS = 60 * 6e4;
84105
84184
  function versionCachePath(homeDir) {
84106
- return path18.join(collectorStateDir(homeDir), "version-check.json");
84185
+ return path17.join(collectorStateDir(homeDir), "version-check.json");
84107
84186
  }
84108
84187
  async function cachedLatestVersion(options) {
84109
84188
  const raw = options.readTextFile(options.cacheFile);
@@ -84138,7 +84217,7 @@ function resumeSessionArgv(source, sessionId) {
84138
84217
  const command = RESUME_COMMANDS[source];
84139
84218
  if (command === void 0) {
84140
84219
  throw new ExpectedCliError(
84141
- `Sessions from "${source}" have no terminal to resume in \u2014 open them in Powerplant instead.`
84220
+ `Sessions from "${source}" have no terminal to resume in, open them in Powerplant instead.`
84142
84221
  );
84143
84222
  }
84144
84223
  return command(sessionId);
@@ -84151,7 +84230,7 @@ function runResumeSession(source, sessionId, spawn2 = spawnSync3) {
84151
84230
  const result = spawn2(binary, args, { stdio: "inherit" });
84152
84231
  if (result.error !== void 0) {
84153
84232
  throw new ExpectedCliError(
84154
- `Could not run ${binary} (${result.error.message}) \u2014 is the ${binary} CLI installed on this machine?`
84233
+ `Could not run ${binary} (${result.error.message}), is the ${binary} CLI installed on this machine?`
84155
84234
  );
84156
84235
  }
84157
84236
  if (result.status !== null && result.status !== 0) {
@@ -84284,7 +84363,7 @@ ${RESTART_NOTE}`;
84284
84363
  }
84285
84364
 
84286
84365
  // src/version.ts
84287
- var CLI_VERSION = true ? "0.3.2" : "0.0.0-checkout";
84366
+ var CLI_VERSION = true ? "0.4.1" : "0.0.0-checkout";
84288
84367
  var CLI_PACKAGE_NAME = "@usepowerplant/cli";
84289
84368
 
84290
84369
  // src/workspace.ts
@@ -84294,18 +84373,18 @@ var workspaceSchema = external_exports.object({
84294
84373
  role: external_exports.string().min(1),
84295
84374
  active: external_exports.boolean()
84296
84375
  });
84297
- var FETCH_TIMEOUT_MS4 = 1e4;
84298
- async function callApi(credentials, path19, init3, fetchImpl) {
84376
+ var FETCH_TIMEOUT_MS3 = 1e4;
84377
+ async function callApi(credentials, path18, init3, fetchImpl) {
84299
84378
  let response;
84300
84379
  try {
84301
- response = await fetchImpl(`${credentials.apiUrl}${path19}`, {
84380
+ response = await fetchImpl(`${credentials.apiUrl}${path18}`, {
84302
84381
  ...init3,
84303
84382
  headers: {
84304
84383
  authorization: `Bearer ${credentials.token}`,
84305
84384
  "content-type": "application/json",
84306
84385
  ...cliVersionHeaders()
84307
84386
  },
84308
- signal: AbortSignal.timeout(FETCH_TIMEOUT_MS4)
84387
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS3)
84309
84388
  });
84310
84389
  } catch (error52) {
84311
84390
  const detail = error52 instanceof Error ? error52.message : String(error52);
@@ -84313,11 +84392,11 @@ async function callApi(credentials, path19, init3, fetchImpl) {
84313
84392
  }
84314
84393
  if (response.status === 401) {
84315
84394
  throw new ExpectedCliError(
84316
- "Powerplant refused this machine's credentials \u2014 run `powerplant enroll` to re-enroll it."
84395
+ "Powerplant refused this machine's credentials, run `powerplant enroll` to re-enroll it."
84317
84396
  );
84318
84397
  }
84319
84398
  if (!response.ok) {
84320
- throw new ExpectedCliError(`Powerplant answered ${response.status} for ${path19}.`);
84399
+ throw new ExpectedCliError(`Powerplant answered ${response.status} for ${path18}.`);
84321
84400
  }
84322
84401
  return response.json();
84323
84402
  }
@@ -84325,7 +84404,7 @@ async function fetchWorkspaces(credentials, fetchImpl = fetch) {
84325
84404
  const envelope = external_exports.object({ data: external_exports.object({ workspaces: external_exports.array(workspaceSchema) }) }).safeParse(await callApi(credentials, "/api/collector/workspaces", {}, fetchImpl));
84326
84405
  if (!envelope.success) {
84327
84406
  throw new ExpectedCliError(
84328
- "Powerplant answered in a shape this CLI can't read \u2014 run `powerplant update`."
84407
+ "Powerplant answered in a shape this CLI can't read, run `powerplant update`."
84329
84408
  );
84330
84409
  }
84331
84410
  return envelope.data.data.workspaces;
@@ -84340,7 +84419,7 @@ async function selectWorkspace(credentials, orgId, fetchImpl = fetch) {
84340
84419
  }
84341
84420
  function renderWorkspaces(workspaces) {
84342
84421
  const lines = workspaces.map(
84343
- (workspace) => `${workspace.active ? "*" : " "} ${workspace.name} (${workspace.role})${workspace.active ? " \u2014 active" : ""}`
84422
+ (workspace) => `${workspace.active ? "*" : " "} ${workspace.name} (${workspace.role})${workspace.active ? ", active" : ""}`
84344
84423
  );
84345
84424
  if (workspaces.length > 1) {
84346
84425
  lines.push("", "Switch with: powerplant workspace <name>");
@@ -84359,7 +84438,7 @@ function matchWorkspace(workspaces, target) {
84359
84438
  }
84360
84439
  const ids = matches.map((workspace) => `${workspace.name} = ${workspace.id}`).join(", ");
84361
84440
  throw new ExpectedCliError(
84362
- `"${target}" names ${matches.length} of your workspaces \u2014 switch by id instead: ${ids}.`
84441
+ `"${target}" names ${matches.length} of your workspaces, switch by id instead: ${ids}.`
84363
84442
  );
84364
84443
  }
84365
84444
  async function runWorkspaceCommand(targetWords, credentials, print, fetchImpl = fetch) {
@@ -84380,39 +84459,39 @@ async function runWorkspaceCommand(targetWords, credentials, print, fetchImpl =
84380
84459
  }
84381
84460
 
84382
84461
  // src/cli.ts
84383
- var USAGE = `powerplant \u2014 the Powerplant sync service for this machine
84462
+ var USAGE = `powerplant, the Powerplant background sync for this machine
84384
84463
 
84385
84464
  Usage: powerplant <command> [options]
84386
84465
 
84387
84466
  Commands:
84388
- init Set up this machine: enroll it into your org, install the
84389
- background service, and add Powerplant MCP to your agents.
84390
- Safe to re-run \u2014 finished steps are skipped.
84391
- connect Add Powerplant MCP to installed agents (Claude Code, Codex,
84392
- Cursor) \u2014 also for later installs and re-authentication.
84467
+ init Enroll this machine, start background sync, and give your
84468
+ coding agents Powerplant MCP access.
84469
+ Safe to re-run, finished steps are skipped.
84470
+ connect Give installed coding agents Powerplant MCP access (Claude Code,
84471
+ Codex, Cursor), also for later installs and re-authentication.
84393
84472
  --client claude|codex|cursor adds just those (repeatable).
84394
84473
  disconnect The inverse of connect: remove the Powerplant MCP entry (and
84395
84474
  its sign-in) from every installed coding agent.
84396
84475
  enroll (Re-)enroll this machine into your org (browser approval).
84397
- status What the sync service is doing on this machine (--json for tools).
84476
+ status What the background sync is doing on this machine (--json for tools).
84398
84477
  doctor A diagnostics summary for support: status plus the redacted
84399
- tail of the sync service log. Prints only \u2014 nothing is uploaded.
84478
+ tail of the background sync log. Prints only, nothing is uploaded.
84400
84479
  Paste the output into your email to support@forestwalk.ai.
84401
84480
  update Update this CLI to the latest published version (via npm).
84402
- restart Restart the background service.
84403
- pause Pause syncing \u2014 the service keeps running, nothing uploads.
84481
+ restart Restart the background sync.
84482
+ pause Pause syncing, the service keeps running, nothing uploads.
84404
84483
  resume Resume syncing (runs a catch-up pass over the pause).
84405
84484
  sync Run one sync pass in the foreground [--since <days>] [--dry-run].
84406
84485
  folders The folders you explicitly sync on this machine:
84407
84486
  powerplant folders [add <path> | remove <path>]. Additions on
84408
- top of your org's allowlisted repos \u2014 no re-enroll needed.
84487
+ top of your org's allowlisted repos, no re-enroll needed.
84409
84488
  workspace Your workspaces, and which one Powerplant and your connected
84410
84489
  agents use: powerplant workspace [<name>] switches to <name>.
84411
84490
  resume-session Reopen a synced session in its own CLI, right here in
84412
84491
  this terminal: powerplant resume-session <claude-code|codex>
84413
84492
  <session-id>.
84414
- watch Run the sync service in the foreground.
84415
- uninstall Remove the background service [--forget also deletes this
84493
+ watch Run the background sync in the foreground.
84494
+ uninstall Remove the background sync [--forget also deletes this
84416
84495
  machine's stored credentials].
84417
84496
 
84418
84497
  Options:
@@ -84500,7 +84579,7 @@ ${USAGE}`);
84500
84579
  let clients = clientFilter === void 0 ? detected : detected.filter((client) => clientFilter.has(client.client));
84501
84580
  if (clientFilter !== void 0 && clients.length === 0) {
84502
84581
  log3(
84503
- `No ${[...clientFilter].join(" or ")} installation found on this machine \u2014 nothing to connect.`
84582
+ `No ${[...clientFilter].join(" or ")} installation found on this machine, nothing to connect.`
84504
84583
  );
84505
84584
  return {
84506
84585
  found: 0,
@@ -84517,7 +84596,7 @@ ${USAGE}`);
84517
84596
  clients = await pickAgents(clients, { log: log3, promptCheckboxes: realCheckboxPrompt });
84518
84597
  skipped = agentCount(detected) - agentCount(clients);
84519
84598
  if (clients.length === 0) {
84520
- log3(`MCP wasn't added \u2014 run any time: ${connectCommand}`);
84599
+ log3(`Powerplant MCP wasn't added to any agents. Run any time: ${connectCommand}`);
84521
84600
  return {
84522
84601
  found: agentCount(detected),
84523
84602
  connected: 0,
@@ -84575,7 +84654,7 @@ ${USAGE}`);
84575
84654
  case "connect": {
84576
84655
  const outcome = await connect3(console.log);
84577
84656
  if (outcome.pending > 0) {
84578
- console.log(`Some logins are unfinished \u2014 re-run \`${connectCommand}\` any time.`);
84657
+ console.log(`Some logins are unfinished, re-run \`${connectCommand}\` any time.`);
84579
84658
  }
84580
84659
  if (outcome.failed > 0) process.exitCode = 1;
84581
84660
  return;
@@ -84662,7 +84741,7 @@ The access you granted these agents stays on your Powerplant account until you r
84662
84741
  const clients = mergeDetectedClients(
84663
84742
  detectClients({ whichAll: whichAllWith(probePath), budget }),
84664
84743
  saved,
84665
- existsSync7
84744
+ existsSync6
84666
84745
  );
84667
84746
  const [latestVersion, oldestSupportedVersion, memberSessions, grantedClientIds] = await Promise.all([
84668
84747
  cachedLatestVersion({
@@ -84705,7 +84784,7 @@ The access you granted these agents stays on your Powerplant account until you r
84705
84784
  const stored = loadCredentialsSafely(config2);
84706
84785
  if (stored.credentials === null) {
84707
84786
  throw new ExpectedCliError(
84708
- `This machine isn't enrolled yet \u2014 run \`powerplant init\` first.${stored.credentialsError ? ` (${stored.credentialsError})` : ""}`
84787
+ `This machine isn't enrolled yet, run \`powerplant init\` first.${stored.credentialsError ? ` (${stored.credentialsError})` : ""}`
84709
84788
  );
84710
84789
  }
84711
84790
  await runWorkspaceCommand(positionals.slice(1), stored.credentials, console.log);
@@ -84720,31 +84799,31 @@ The access you granted these agents stays on your Powerplant account until you r
84720
84799
  const npm = spawnSync5("npm", ["install", "-g", CLI_PACKAGE_NAME], { stdio: "inherit" });
84721
84800
  if (npm.error) {
84722
84801
  throw new ExpectedCliError(
84723
- `Could not run npm (${npm.error.message}) \u2014 update by hand: npm install -g ${CLI_PACKAGE_NAME}`
84802
+ `Could not run npm (${npm.error.message}), update by hand: npm install -g ${CLI_PACKAGE_NAME}`
84724
84803
  );
84725
84804
  }
84726
84805
  if (npm.status !== 0) {
84727
- throw new ExpectedCliError("npm install failed \u2014 see the output above.");
84806
+ throw new ExpectedCliError("npm install failed, see the output above.");
84728
84807
  }
84729
84808
  console.log(updatedMessage(installedGlobalVersion(CLI_PACKAGE_NAME)));
84730
84809
  return;
84731
84810
  }
84732
84811
  case "restart": {
84733
84812
  if (restartService(config2) === "not-installed") {
84734
- throw new ExpectedCliError("No background service installed \u2014 run: powerplant init");
84813
+ throw new ExpectedCliError("No background sync installed, run: powerplant init");
84735
84814
  }
84736
- console.log("Background service restarted.");
84815
+ console.log("Background sync restarted.");
84737
84816
  return;
84738
84817
  }
84739
84818
  case "pause": {
84740
84819
  writeCollectorControl(config2.appEnv, { paused: true, pausedAt: (/* @__PURE__ */ new Date()).toISOString() });
84741
- console.log("Syncing paused \u2014 the sync service keeps running but uploads nothing.");
84820
+ console.log("Syncing paused, the background sync keeps running but uploads nothing.");
84742
84821
  console.log("Resume with: powerplant resume");
84743
84822
  return;
84744
84823
  }
84745
84824
  case "resume": {
84746
84825
  writeCollectorControl(config2.appEnv, { paused: false, resumedAt: (/* @__PURE__ */ new Date()).toISOString() });
84747
- console.log("Syncing resumed \u2014 the sync service runs a catch-up pass covering the pause.");
84826
+ console.log("Syncing resumed, the background sync runs a catch-up pass covering the pause.");
84748
84827
  return;
84749
84828
  }
84750
84829
  case "sync": {
@@ -84783,18 +84862,18 @@ The access you granted these agents stays on your Powerplant account until you r
84783
84862
  const removed = uninstallService(config2);
84784
84863
  clearCollectorStatus(config2.appEnv);
84785
84864
  clearCollectorControl(config2.appEnv);
84786
- console.log(removed ? "Background service removed." : "No background service was installed.");
84865
+ console.log(removed ? "Background sync removed." : "No background sync was installed.");
84787
84866
  if (values.forget) {
84788
84867
  deleteCredentials(config2.appEnv);
84789
- console.log("Stored credentials deleted \u2014 this machine is no longer enrolled.");
84868
+ console.log("Stored credentials deleted, this machine is no longer enrolled.");
84790
84869
  } else {
84791
84870
  console.log(
84792
- "Credentials kept \u2014 `powerplant init` re-installs without re-enrolling. (--forget deletes them.)"
84871
+ "Credentials kept, `powerplant init` re-installs without re-enrolling. (--forget deletes them.)"
84793
84872
  );
84794
84873
  }
84795
84874
  if (anyClientStillConnected(detectClients(), { run: realRun, readTextFile: realReadTextFile })) {
84796
84875
  console.log(
84797
- "Your coding agents are still connected to the Powerplant MCP server \u2014 remove those connections with: powerplant disconnect"
84876
+ "Your coding agents are still connected to the Powerplant MCP server, remove those connections with: powerplant disconnect"
84798
84877
  );
84799
84878
  }
84800
84879
  return;