@fml-inc/fml 0.8.12 → 0.8.13-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +58 -0
- package/dist/chunk-3RKICLMI.js +34 -0
- package/dist/{chunk-JK5JGPUN.js → chunk-56ABBKUE.js} +2 -2
- package/dist/chunk-74KOMQZO.js +505 -0
- package/dist/{chunk-4YLCUJ6C.js → chunk-AZSAAPRC.js} +20 -2
- package/dist/{chunk-IUFDFBU7.js → chunk-C4Z6RMQ5.js} +1 -1
- package/dist/chunk-F57IFY3K.js +21 -0
- package/dist/{chunk-5VP4C72Z.js → chunk-K2ANDHRQ.js} +1 -1
- package/dist/{chunk-VFZWTVPD.js → chunk-LYKMU7QR.js} +252 -343
- package/dist/{chunk-Z7MXNJ6X.js → chunk-PVOZYFMP.js} +1 -1
- package/dist/cli.js +12 -21
- package/dist/hook-handler.js +32 -13
- package/dist/mcp/server.js +61 -2
- package/dist/observer-server.js +9 -8
- package/dist/pi/extension.js +802 -354
- package/dist/sync-cycle.js +4 -3
- package/package.json +1 -1
- package/dist/chunk-CSOFP7MT.js +0 -67
- package/dist/chunk-EX5J3MPG.js +0 -142
package/dist/pi/extension.js
CHANGED
|
@@ -10,7 +10,7 @@ var __export = (target, all) => {
|
|
|
10
10
|
|
|
11
11
|
// src/dirs.ts
|
|
12
12
|
import os from "node:os";
|
|
13
|
-
import
|
|
13
|
+
import path16 from "node:path";
|
|
14
14
|
function isPresent(value) {
|
|
15
15
|
return value !== void 0 && value.trim().length > 0;
|
|
16
16
|
}
|
|
@@ -31,7 +31,7 @@ function platformBaseDirectory(value, pathApi) {
|
|
|
31
31
|
function resolveFmlDirectories(options = {}) {
|
|
32
32
|
const platform = options.platform ?? process.platform;
|
|
33
33
|
const env = options.env ?? process.env;
|
|
34
|
-
const pathApi = platform === "win32" ?
|
|
34
|
+
const pathApi = platform === "win32" ? path16.win32 : path16.posix;
|
|
35
35
|
const homeDir = normalizeAbsolutePath(
|
|
36
36
|
options.homeDir ?? os.homedir(),
|
|
37
37
|
"home directory",
|
|
@@ -123,41 +123,41 @@ var init_dirs = __esm({
|
|
|
123
123
|
FML_LOG_DIR = DIRECTORIES.logDir;
|
|
124
124
|
FML_OBSERVER_DIR = DIRECTORIES.observerDir;
|
|
125
125
|
FML_OBSERVER_PORT = observerPort();
|
|
126
|
-
FML_OBSERVER_STATE_FILE =
|
|
126
|
+
FML_OBSERVER_STATE_FILE = path16.join(
|
|
127
127
|
FML_OBSERVER_DIR,
|
|
128
128
|
"observer.pid.json"
|
|
129
129
|
);
|
|
130
|
-
FML_OBSERVER_LOCK_FILE =
|
|
130
|
+
FML_OBSERVER_LOCK_FILE = path16.join(
|
|
131
131
|
FML_OBSERVER_DIR,
|
|
132
132
|
"observer.lock"
|
|
133
133
|
);
|
|
134
|
-
FML_OBSERVER_AUTH_TOKEN_FILE =
|
|
134
|
+
FML_OBSERVER_AUTH_TOKEN_FILE = path16.join(
|
|
135
135
|
FML_OBSERVER_DIR,
|
|
136
136
|
"auth-token"
|
|
137
137
|
);
|
|
138
|
-
FML_OBSERVER_ENDPOINT_FILE =
|
|
138
|
+
FML_OBSERVER_ENDPOINT_FILE = path16.join(
|
|
139
139
|
FML_OBSERVER_DIR,
|
|
140
140
|
"endpoint.json"
|
|
141
141
|
);
|
|
142
|
-
FML_OBSERVER_LOG_FILE =
|
|
143
|
-
FML_UPDATE_LOG_FILE =
|
|
144
|
-
FML_INSTALLATION_ID_FILE =
|
|
142
|
+
FML_OBSERVER_LOG_FILE = path16.join(FML_LOG_DIR, "observer.log");
|
|
143
|
+
FML_UPDATE_LOG_FILE = path16.join(FML_LOG_DIR, "update.log");
|
|
144
|
+
FML_INSTALLATION_ID_FILE = path16.join(
|
|
145
145
|
FML_STATE_DIR,
|
|
146
146
|
"installation_id"
|
|
147
147
|
);
|
|
148
|
-
FML_PANOPTICON_DB_FILE =
|
|
148
|
+
FML_PANOPTICON_DB_FILE = path16.join(FML_STATE_DIR, "panopticon.db");
|
|
149
149
|
FML_PANOPTICON_DB_OBSERVER_STATE_FILE = `${FML_PANOPTICON_DB_FILE}.observer.pid.json`;
|
|
150
150
|
FML_PANOPTICON_DB_OBSERVER_LOCK_FILE = `${FML_PANOPTICON_DB_FILE}.observer.lock`;
|
|
151
|
-
FML_LEGACY_OBSERVER_DB_FILE =
|
|
151
|
+
FML_LEGACY_OBSERVER_DB_FILE = path16.join(
|
|
152
152
|
FML_OBSERVER_DIR,
|
|
153
153
|
"observer.db"
|
|
154
154
|
);
|
|
155
|
-
FML_HOOK_INBOX_DIR =
|
|
156
|
-
FML_HOOK_CONTEXT_STATE_DIR =
|
|
155
|
+
FML_HOOK_INBOX_DIR = path16.join(FML_OBSERVER_DIR, "hook-inbox");
|
|
156
|
+
FML_HOOK_CONTEXT_STATE_DIR = path16.join(
|
|
157
157
|
FML_OBSERVER_DIR,
|
|
158
158
|
"hook-context-state"
|
|
159
159
|
);
|
|
160
|
-
FML_HOOK_CAPTURE_DISABLED_FILE =
|
|
160
|
+
FML_HOOK_CAPTURE_DISABLED_FILE = path16.join(
|
|
161
161
|
FML_OBSERVER_DIR,
|
|
162
162
|
"hook-capture.disabled"
|
|
163
163
|
);
|
|
@@ -165,8 +165,8 @@ var init_dirs = __esm({
|
|
|
165
165
|
});
|
|
166
166
|
|
|
167
167
|
// src/config.ts
|
|
168
|
-
import
|
|
169
|
-
import
|
|
168
|
+
import fs7 from "node:fs";
|
|
169
|
+
import path17 from "node:path";
|
|
170
170
|
function isHostedBackendUrl(url2) {
|
|
171
171
|
let normalized;
|
|
172
172
|
try {
|
|
@@ -195,7 +195,7 @@ function normalizeAppUrl(value) {
|
|
|
195
195
|
function readEnvConfig() {
|
|
196
196
|
try {
|
|
197
197
|
const parsed = JSON.parse(
|
|
198
|
-
|
|
198
|
+
fs7.readFileSync(ENV_CONFIG_PATH, "utf8")
|
|
199
199
|
);
|
|
200
200
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
201
201
|
throw new Error("invalid environment config");
|
|
@@ -256,7 +256,7 @@ function getActiveEnv() {
|
|
|
256
256
|
return { name, convexUrl, appUrl };
|
|
257
257
|
}
|
|
258
258
|
function authStorePathFor(envName) {
|
|
259
|
-
return
|
|
259
|
+
return path17.join(FML_DATA_DIR, `auth.${envName}.json`);
|
|
260
260
|
}
|
|
261
261
|
function getSiteUrlForConvexUrl(convexUrl) {
|
|
262
262
|
const explicit = process.env.CONVEX_SITE_URL;
|
|
@@ -274,7 +274,7 @@ var init_config = __esm({
|
|
|
274
274
|
LEGACY_HOSTED_BACKEND_URLS = [
|
|
275
275
|
"https://trustworthy-chihuahua-382.convex.site"
|
|
276
276
|
];
|
|
277
|
-
ENV_CONFIG_PATH =
|
|
277
|
+
ENV_CONFIG_PATH = path17.join(FML_DATA_DIR, "env.json");
|
|
278
278
|
ENV_NAME_RE = /^[A-Za-z0-9_-]+$/;
|
|
279
279
|
CONVEX_URL = (process.env.FML_CONVEX_URL ?? getActiveEnv().convexUrl ?? DEFAULT_PROD_URL).replace(/\/$/, "");
|
|
280
280
|
WORKOS_API_URL = "https://api.workos.com";
|
|
@@ -307,9 +307,9 @@ __export(telemetry_config_exports, {
|
|
|
307
307
|
refreshCliTelemetryConfig: () => refreshCliTelemetryConfig,
|
|
308
308
|
resolveCliSentryConfig: () => resolveCliSentryConfig
|
|
309
309
|
});
|
|
310
|
-
import { createHash as
|
|
311
|
-
import
|
|
312
|
-
import
|
|
310
|
+
import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
|
|
311
|
+
import fs8 from "node:fs";
|
|
312
|
+
import path18 from "node:path";
|
|
313
313
|
function parseSentryConfig(value) {
|
|
314
314
|
if (value === null) return null;
|
|
315
315
|
if (!value || typeof value !== "object")
|
|
@@ -330,16 +330,16 @@ function parseSentryConfig(value) {
|
|
|
330
330
|
}
|
|
331
331
|
function cachePathFor(siteUrl) {
|
|
332
332
|
const key = isHostedBackendUrl(siteUrl) ? DEFAULT_SYNC_URL : siteUrl;
|
|
333
|
-
return
|
|
333
|
+
return path18.join(
|
|
334
334
|
FML_DATA_DIR,
|
|
335
335
|
"telemetry-config",
|
|
336
|
-
`${
|
|
336
|
+
`${createHash4("sha256").update(key).digest("hex")}.json`
|
|
337
337
|
);
|
|
338
338
|
}
|
|
339
339
|
function readCache(siteUrl) {
|
|
340
340
|
try {
|
|
341
341
|
const value = JSON.parse(
|
|
342
|
-
|
|
342
|
+
fs8.readFileSync(cachePathFor(siteUrl), "utf8")
|
|
343
343
|
);
|
|
344
344
|
if (!value || typeof value !== "object") return void 0;
|
|
345
345
|
const entry = value;
|
|
@@ -356,15 +356,15 @@ function readCache(siteUrl) {
|
|
|
356
356
|
}
|
|
357
357
|
function writeCache(entry) {
|
|
358
358
|
const cachePath = cachePathFor(entry.siteUrl);
|
|
359
|
-
const temporaryPath = `${cachePath}.${
|
|
359
|
+
const temporaryPath = `${cachePath}.${randomUUID3()}.tmp`;
|
|
360
360
|
try {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
361
|
+
fs8.mkdirSync(path18.dirname(cachePath), { recursive: true, mode: 448 });
|
|
362
|
+
fs8.writeFileSync(temporaryPath, JSON.stringify(entry), { mode: 384 });
|
|
363
|
+
fs8.renameSync(temporaryPath, cachePath);
|
|
364
364
|
} catch {
|
|
365
365
|
} finally {
|
|
366
366
|
try {
|
|
367
|
-
|
|
367
|
+
fs8.rmSync(temporaryPath, { force: true });
|
|
368
368
|
} catch {
|
|
369
369
|
}
|
|
370
370
|
}
|
|
@@ -450,10 +450,10 @@ var init_telemetry_config = __esm({
|
|
|
450
450
|
});
|
|
451
451
|
|
|
452
452
|
// src/auth/token-store.ts
|
|
453
|
-
import { createHash as
|
|
454
|
-
import { execFileSync } from "node:child_process";
|
|
455
|
-
import
|
|
456
|
-
import
|
|
453
|
+
import { createHash as createHash5, randomUUID as randomUUID4 } from "node:crypto";
|
|
454
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
455
|
+
import fs9 from "node:fs";
|
|
456
|
+
import path19 from "node:path";
|
|
457
457
|
function getCurrentProcessIdentity() {
|
|
458
458
|
let cache = processGlobals[PROCESS_IDENTITY_KEY];
|
|
459
459
|
if (!cache) {
|
|
@@ -473,36 +473,36 @@ function resolveEnvName(envName) {
|
|
|
473
473
|
return envName ?? getActiveEnv().name;
|
|
474
474
|
}
|
|
475
475
|
function ensureDir(envName) {
|
|
476
|
-
const dir =
|
|
477
|
-
|
|
476
|
+
const dir = path19.dirname(storePathFor(envName));
|
|
477
|
+
fs9.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
478
478
|
try {
|
|
479
|
-
|
|
479
|
+
fs9.chmodSync(dir, 448);
|
|
480
480
|
} catch {
|
|
481
481
|
}
|
|
482
482
|
}
|
|
483
483
|
function writeSecretFile(filePath, data) {
|
|
484
|
-
const temporaryFile = `${filePath}.${process.pid}.${
|
|
484
|
+
const temporaryFile = `${filePath}.${process.pid}.${randomUUID4()}.tmp`;
|
|
485
485
|
let descriptor = null;
|
|
486
486
|
try {
|
|
487
|
-
descriptor =
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
487
|
+
descriptor = fs9.openSync(temporaryFile, "wx", 384);
|
|
488
|
+
fs9.writeFileSync(descriptor, data, { encoding: "utf8" });
|
|
489
|
+
fs9.fsyncSync(descriptor);
|
|
490
|
+
fs9.closeSync(descriptor);
|
|
491
491
|
descriptor = null;
|
|
492
|
-
|
|
492
|
+
fs9.renameSync(temporaryFile, filePath);
|
|
493
493
|
try {
|
|
494
|
-
|
|
494
|
+
fs9.chmodSync(filePath, 384);
|
|
495
495
|
} catch {
|
|
496
496
|
}
|
|
497
497
|
} finally {
|
|
498
498
|
if (descriptor !== null) {
|
|
499
499
|
try {
|
|
500
|
-
|
|
500
|
+
fs9.closeSync(descriptor);
|
|
501
501
|
} catch {
|
|
502
502
|
}
|
|
503
503
|
}
|
|
504
504
|
try {
|
|
505
|
-
|
|
505
|
+
fs9.unlinkSync(temporaryFile);
|
|
506
506
|
} catch {
|
|
507
507
|
}
|
|
508
508
|
}
|
|
@@ -510,7 +510,7 @@ function writeSecretFile(filePath, data) {
|
|
|
510
510
|
function readTokens(envName) {
|
|
511
511
|
let raw;
|
|
512
512
|
try {
|
|
513
|
-
raw =
|
|
513
|
+
raw = fs9.readFileSync(storePathFor(envName), "utf-8");
|
|
514
514
|
} catch {
|
|
515
515
|
return null;
|
|
516
516
|
}
|
|
@@ -534,7 +534,7 @@ function parseStoredAuthShape(parsed) {
|
|
|
534
534
|
function readTokensForReconciliation(envName) {
|
|
535
535
|
let raw;
|
|
536
536
|
try {
|
|
537
|
-
raw =
|
|
537
|
+
raw = fs9.readFileSync(storePathFor(envName), "utf8");
|
|
538
538
|
} catch (error62) {
|
|
539
539
|
if (error62.code === "ENOENT") return null;
|
|
540
540
|
throw error62;
|
|
@@ -561,7 +561,7 @@ function writeTokensUnlocked(auth, envName) {
|
|
|
561
561
|
writeSecretFile(storePathFor(envName), JSON.stringify(auth, null, 2));
|
|
562
562
|
}
|
|
563
563
|
function credentialHash(value) {
|
|
564
|
-
return
|
|
564
|
+
return createHash5("sha256").update(value).digest("hex");
|
|
565
565
|
}
|
|
566
566
|
function describeError(error62) {
|
|
567
567
|
return error62 instanceof Error ? error62.message : String(error62);
|
|
@@ -639,7 +639,7 @@ function refreshLockPath(envName) {
|
|
|
639
639
|
return `${storePathFor(envName)}.refresh.lock`;
|
|
640
640
|
}
|
|
641
641
|
function pendingOAuthRefreshPrefix(envName) {
|
|
642
|
-
return `${
|
|
642
|
+
return `${path19.basename(storePathFor(envName))}.refresh-result.`;
|
|
643
643
|
}
|
|
644
644
|
function parsePendingOAuthRefresh(raw) {
|
|
645
645
|
try {
|
|
@@ -655,7 +655,7 @@ function parsePendingOAuthRefresh(raw) {
|
|
|
655
655
|
}
|
|
656
656
|
function stagePendingOAuthRefresh(previousRefreshToken, refreshed, envName) {
|
|
657
657
|
ensureDir(envName);
|
|
658
|
-
const resultPath = `${storePathFor(envName)}.refresh-result.${Date.now()}.${
|
|
658
|
+
const resultPath = `${storePathFor(envName)}.refresh-result.${Date.now()}.${randomUUID4()}.json`;
|
|
659
659
|
const pending = {
|
|
660
660
|
version: 1,
|
|
661
661
|
previousRefreshTokenHash: credentialHash(previousRefreshToken),
|
|
@@ -668,7 +668,7 @@ function stagePendingOAuthRefresh(previousRefreshToken, refreshed, envName) {
|
|
|
668
668
|
}
|
|
669
669
|
function removePendingOAuthRefresh(resultPath) {
|
|
670
670
|
try {
|
|
671
|
-
|
|
671
|
+
fs9.unlinkSync(resultPath);
|
|
672
672
|
} catch (error62) {
|
|
673
673
|
if (error62.code !== "ENOENT") {
|
|
674
674
|
console.error(
|
|
@@ -679,11 +679,11 @@ function removePendingOAuthRefresh(resultPath) {
|
|
|
679
679
|
}
|
|
680
680
|
function reconcilePendingOAuthRefreshes(envName, unreadableStorePolicy = "preserve-staged") {
|
|
681
681
|
const storePath = storePathFor(envName);
|
|
682
|
-
const directory =
|
|
682
|
+
const directory = path19.dirname(storePath);
|
|
683
683
|
const prefix = pendingOAuthRefreshPrefix(envName);
|
|
684
684
|
let resultPaths;
|
|
685
685
|
try {
|
|
686
|
-
resultPaths =
|
|
686
|
+
resultPaths = fs9.readdirSync(directory).filter((entry) => entry.startsWith(prefix) && entry.endsWith(".json")).map((entry) => path19.join(directory, entry));
|
|
687
687
|
} catch (error62) {
|
|
688
688
|
if (error62.code === "ENOENT") return;
|
|
689
689
|
throw error62;
|
|
@@ -691,7 +691,7 @@ function reconcilePendingOAuthRefreshes(envName, unreadableStorePolicy = "preser
|
|
|
691
691
|
const pendingResults = resultPaths.map((resultPath) => {
|
|
692
692
|
let pending = null;
|
|
693
693
|
try {
|
|
694
|
-
pending = parsePendingOAuthRefresh(
|
|
694
|
+
pending = parsePendingOAuthRefresh(fs9.readFileSync(resultPath, "utf8"));
|
|
695
695
|
} catch (error62) {
|
|
696
696
|
if (error62.code !== "ENOENT") throw error62;
|
|
697
697
|
}
|
|
@@ -746,13 +746,13 @@ function parseRefreshLockOwner(raw) {
|
|
|
746
746
|
function getProcessIdentity(pid) {
|
|
747
747
|
try {
|
|
748
748
|
if (process.platform === "linux") {
|
|
749
|
-
const stat =
|
|
749
|
+
const stat = fs9.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
750
750
|
const fieldsAfterCommand = stat.slice(stat.lastIndexOf(")") + 2).split(" ");
|
|
751
751
|
const startTimeTicks = fieldsAfterCommand[19];
|
|
752
752
|
return startTimeTicks ? `linux:${startTimeTicks}` : null;
|
|
753
753
|
}
|
|
754
754
|
if (process.platform === "darwin") {
|
|
755
|
-
const startedAt =
|
|
755
|
+
const startedAt = execFileSync2(
|
|
756
756
|
"ps",
|
|
757
757
|
["-p", String(pid), "-o", "lstart="],
|
|
758
758
|
{
|
|
@@ -764,7 +764,7 @@ function getProcessIdentity(pid) {
|
|
|
764
764
|
return startedAt ? `darwin:${startedAt}` : null;
|
|
765
765
|
}
|
|
766
766
|
if (process.platform === "win32") {
|
|
767
|
-
const startedAt =
|
|
767
|
+
const startedAt = execFileSync2(
|
|
768
768
|
"powershell.exe",
|
|
769
769
|
[
|
|
770
770
|
"-NoProfile",
|
|
@@ -798,7 +798,7 @@ function parseRecoveryMarkerOwner(raw) {
|
|
|
798
798
|
function snapshotLeaseFile(filePath) {
|
|
799
799
|
let stat;
|
|
800
800
|
try {
|
|
801
|
-
stat =
|
|
801
|
+
stat = fs9.lstatSync(filePath);
|
|
802
802
|
} catch (error62) {
|
|
803
803
|
if (error62.code === "ENOENT") return null;
|
|
804
804
|
throw error62;
|
|
@@ -807,7 +807,7 @@ function snapshotLeaseFile(filePath) {
|
|
|
807
807
|
try {
|
|
808
808
|
return {
|
|
809
809
|
stat,
|
|
810
|
-
raw:
|
|
810
|
+
raw: fs9.readFileSync(path19.join(filePath, "owner.json"), "utf8")
|
|
811
811
|
};
|
|
812
812
|
} catch (error62) {
|
|
813
813
|
if (error62.code === "ENOENT") {
|
|
@@ -817,7 +817,7 @@ function snapshotLeaseFile(filePath) {
|
|
|
817
817
|
}
|
|
818
818
|
}
|
|
819
819
|
try {
|
|
820
|
-
return { stat, raw:
|
|
820
|
+
return { stat, raw: fs9.readFileSync(filePath, "utf8") };
|
|
821
821
|
} catch (error62) {
|
|
822
822
|
if (error62.code === "ENOENT") return null;
|
|
823
823
|
throw error62;
|
|
@@ -829,21 +829,21 @@ function snapshotsMatch(observed, current) {
|
|
|
829
829
|
function restoreMovedLease(movedPath, originalPath) {
|
|
830
830
|
let movedStat;
|
|
831
831
|
try {
|
|
832
|
-
movedStat =
|
|
832
|
+
movedStat = fs9.lstatSync(movedPath);
|
|
833
833
|
} catch (error62) {
|
|
834
834
|
if (error62.code === "ENOENT") return;
|
|
835
835
|
throw error62;
|
|
836
836
|
}
|
|
837
837
|
if (!movedStat.isFile()) return;
|
|
838
838
|
try {
|
|
839
|
-
|
|
839
|
+
fs9.linkSync(movedPath, originalPath);
|
|
840
840
|
} catch (error62) {
|
|
841
841
|
const code = error62.code;
|
|
842
842
|
if (code === "EEXIST" || code === "ENOENT") return;
|
|
843
843
|
throw error62;
|
|
844
844
|
}
|
|
845
845
|
try {
|
|
846
|
-
|
|
846
|
+
fs9.unlinkSync(movedPath);
|
|
847
847
|
} catch (error62) {
|
|
848
848
|
if (error62.code !== "ENOENT") throw error62;
|
|
849
849
|
}
|
|
@@ -857,27 +857,27 @@ function pathMatchesClaim(filePath, claimedStat, ownerId, parseOwner) {
|
|
|
857
857
|
function markClaimReleased(filePath, claimedStat, owner, parseOwner) {
|
|
858
858
|
let descriptor;
|
|
859
859
|
try {
|
|
860
|
-
descriptor =
|
|
860
|
+
descriptor = fs9.openSync(filePath, "r+");
|
|
861
861
|
} catch (error62) {
|
|
862
862
|
if (error62.code === "ENOENT") return false;
|
|
863
863
|
throw error62;
|
|
864
864
|
}
|
|
865
865
|
try {
|
|
866
|
-
const currentStat =
|
|
866
|
+
const currentStat = fs9.fstatSync(descriptor);
|
|
867
867
|
if (currentStat.dev !== claimedStat.dev || currentStat.ino !== claimedStat.ino) {
|
|
868
868
|
return false;
|
|
869
869
|
}
|
|
870
|
-
const currentOwner = parseOwner(
|
|
870
|
+
const currentOwner = parseOwner(fs9.readFileSync(descriptor, "utf8"));
|
|
871
871
|
if (currentOwner?.ownerId !== owner.ownerId) return false;
|
|
872
872
|
const serialized = Buffer.from(
|
|
873
873
|
`${JSON.stringify({ ...owner, releasedAtMs: Date.now() })}
|
|
874
874
|
`,
|
|
875
875
|
"utf8"
|
|
876
876
|
);
|
|
877
|
-
|
|
877
|
+
fs9.ftruncateSync(descriptor, 0);
|
|
878
878
|
let offset = 0;
|
|
879
879
|
while (offset < serialized.length) {
|
|
880
|
-
offset +=
|
|
880
|
+
offset += fs9.writeSync(
|
|
881
881
|
descriptor,
|
|
882
882
|
serialized,
|
|
883
883
|
offset,
|
|
@@ -885,19 +885,19 @@ function markClaimReleased(filePath, claimedStat, owner, parseOwner) {
|
|
|
885
885
|
offset
|
|
886
886
|
);
|
|
887
887
|
}
|
|
888
|
-
|
|
888
|
+
fs9.fsyncSync(descriptor);
|
|
889
889
|
return true;
|
|
890
890
|
} finally {
|
|
891
|
-
|
|
891
|
+
fs9.closeSync(descriptor);
|
|
892
892
|
}
|
|
893
893
|
}
|
|
894
894
|
function releaseOwnedLease(filePath, claimedStat, owner, parseOwner, description) {
|
|
895
|
-
const releasedPath = `${filePath}.released.${
|
|
895
|
+
const releasedPath = `${filePath}.released.${randomUUID4()}`;
|
|
896
896
|
let moved = false;
|
|
897
897
|
try {
|
|
898
898
|
if (!markClaimReleased(filePath, claimedStat, owner, parseOwner)) return;
|
|
899
899
|
try {
|
|
900
|
-
|
|
900
|
+
fs9.renameSync(filePath, releasedPath);
|
|
901
901
|
moved = true;
|
|
902
902
|
} catch (error62) {
|
|
903
903
|
if (error62.code === "ENOENT") return;
|
|
@@ -910,7 +910,7 @@ function releaseOwnedLease(filePath, claimedStat, owner, parseOwner, description
|
|
|
910
910
|
moved = false;
|
|
911
911
|
return;
|
|
912
912
|
}
|
|
913
|
-
|
|
913
|
+
fs9.rmSync(releasedPath, {
|
|
914
914
|
recursive: released.stat.isDirectory(),
|
|
915
915
|
force: true
|
|
916
916
|
});
|
|
@@ -984,9 +984,9 @@ function ownerPidWasReused(owner, lockMtimeMs) {
|
|
|
984
984
|
return currentIdentity !== null && currentIdentity !== owner.processIdentity;
|
|
985
985
|
}
|
|
986
986
|
function writeClaimToDescriptor(descriptor, serializedOwner) {
|
|
987
|
-
const claimedStat =
|
|
988
|
-
|
|
989
|
-
|
|
987
|
+
const claimedStat = fs9.fstatSync(descriptor);
|
|
988
|
+
fs9.writeFileSync(descriptor, serializedOwner, { encoding: "utf8" });
|
|
989
|
+
fs9.fsyncSync(descriptor);
|
|
990
990
|
return claimedStat;
|
|
991
991
|
}
|
|
992
992
|
function recoverAbandonedRecoveryMarker(recoveryPath) {
|
|
@@ -996,15 +996,15 @@ function recoverAbandonedRecoveryMarker(recoveryPath) {
|
|
|
996
996
|
const ownerAlive = owner ? isProcessAlive(owner.pid) : false;
|
|
997
997
|
const reclaimable = owner ? owner.releasedAtMs !== void 0 || !ownerAlive || ownerAlive && ownerPidWasReused(owner, observed.stat.mtimeMs) : Date.now() - observed.stat.mtimeMs > OAUTH_REFRESH_RECOVERY_STALE_MS;
|
|
998
998
|
if (!reclaimable) return;
|
|
999
|
-
const abandonedPath = `${recoveryPath}.stale.${
|
|
999
|
+
const abandonedPath = `${recoveryPath}.stale.${randomUUID4()}`;
|
|
1000
1000
|
try {
|
|
1001
|
-
|
|
1001
|
+
fs9.renameSync(recoveryPath, abandonedPath);
|
|
1002
1002
|
const moved = snapshotLeaseFile(abandonedPath);
|
|
1003
1003
|
if (!moved || !snapshotsMatch(observed, moved)) {
|
|
1004
1004
|
restoreMovedLease(abandonedPath, recoveryPath);
|
|
1005
1005
|
return;
|
|
1006
1006
|
}
|
|
1007
|
-
|
|
1007
|
+
fs9.rmSync(abandonedPath, {
|
|
1008
1008
|
recursive: moved.stat.isDirectory(),
|
|
1009
1009
|
force: true
|
|
1010
1010
|
});
|
|
@@ -1013,7 +1013,7 @@ function recoverAbandonedRecoveryMarker(recoveryPath) {
|
|
|
1013
1013
|
}
|
|
1014
1014
|
}
|
|
1015
1015
|
function acquireRecoveryMarker(recoveryPath) {
|
|
1016
|
-
const ownerId =
|
|
1016
|
+
const ownerId = randomUUID4();
|
|
1017
1017
|
const owner = {
|
|
1018
1018
|
version: 1,
|
|
1019
1019
|
ownerId,
|
|
@@ -1024,7 +1024,7 @@ function acquireRecoveryMarker(recoveryPath) {
|
|
|
1024
1024
|
};
|
|
1025
1025
|
let descriptor;
|
|
1026
1026
|
try {
|
|
1027
|
-
descriptor =
|
|
1027
|
+
descriptor = fs9.openSync(recoveryPath, "wx", 384);
|
|
1028
1028
|
} catch (error62) {
|
|
1029
1029
|
if (error62.code === "EEXIST") return null;
|
|
1030
1030
|
throw error62;
|
|
@@ -1037,7 +1037,7 @@ function acquireRecoveryMarker(recoveryPath) {
|
|
|
1037
1037
|
`
|
|
1038
1038
|
);
|
|
1039
1039
|
} finally {
|
|
1040
|
-
|
|
1040
|
+
fs9.closeSync(descriptor);
|
|
1041
1041
|
}
|
|
1042
1042
|
const isOwned = () => pathMatchesClaim(
|
|
1043
1043
|
recoveryPath,
|
|
@@ -1081,15 +1081,15 @@ function recoverStaleRefreshLock(lockPath) {
|
|
|
1081
1081
|
}
|
|
1082
1082
|
if (!reclaimable) return;
|
|
1083
1083
|
if (!recovery.isOwned()) return;
|
|
1084
|
-
const abandonedPath = `${lockPath}.stale.${
|
|
1084
|
+
const abandonedPath = `${lockPath}.stale.${randomUUID4()}`;
|
|
1085
1085
|
try {
|
|
1086
|
-
|
|
1086
|
+
fs9.renameSync(lockPath, abandonedPath);
|
|
1087
1087
|
const moved = snapshotLeaseFile(abandonedPath);
|
|
1088
1088
|
if (!moved || !snapshotsMatch(observed, moved)) {
|
|
1089
1089
|
restoreMovedLease(abandonedPath, lockPath);
|
|
1090
1090
|
return;
|
|
1091
1091
|
}
|
|
1092
|
-
|
|
1092
|
+
fs9.rmSync(abandonedPath, {
|
|
1093
1093
|
recursive: moved.stat.isDirectory(),
|
|
1094
1094
|
force: true
|
|
1095
1095
|
});
|
|
@@ -1116,7 +1116,7 @@ async function acquireRefreshLock(envName, signal) {
|
|
|
1116
1116
|
if (signal?.aborted) {
|
|
1117
1117
|
throw signal.reason ?? new DOMException("The operation was aborted", "AbortError");
|
|
1118
1118
|
}
|
|
1119
|
-
if (
|
|
1119
|
+
if (fs9.existsSync(recoveryPath)) {
|
|
1120
1120
|
try {
|
|
1121
1121
|
recoverAbandonedRecoveryMarker(recoveryPath);
|
|
1122
1122
|
} catch (recoveryError) {
|
|
@@ -1130,7 +1130,7 @@ async function acquireRefreshLock(envName, signal) {
|
|
|
1130
1130
|
}
|
|
1131
1131
|
let descriptor;
|
|
1132
1132
|
try {
|
|
1133
|
-
descriptor =
|
|
1133
|
+
descriptor = fs9.openSync(lockPath, "wx", 384);
|
|
1134
1134
|
} catch (error62) {
|
|
1135
1135
|
if (error62.code !== "EEXIST") {
|
|
1136
1136
|
console.error(
|
|
@@ -1149,7 +1149,7 @@ async function acquireRefreshLock(envName, signal) {
|
|
|
1149
1149
|
await sleep(OAUTH_REFRESH_LOCK_POLL_MS, signal);
|
|
1150
1150
|
continue;
|
|
1151
1151
|
}
|
|
1152
|
-
const ownerId =
|
|
1152
|
+
const ownerId = randomUUID4();
|
|
1153
1153
|
const owner = {
|
|
1154
1154
|
version: 1,
|
|
1155
1155
|
ownerId,
|
|
@@ -1166,13 +1166,13 @@ async function acquireRefreshLock(envName, signal) {
|
|
|
1166
1166
|
`
|
|
1167
1167
|
);
|
|
1168
1168
|
} catch (error62) {
|
|
1169
|
-
|
|
1169
|
+
fs9.closeSync(descriptor);
|
|
1170
1170
|
console.error(
|
|
1171
1171
|
`[fml] Auth: unable to initialize refresh lock: ${error62 instanceof Error ? error62.message : String(error62)}`
|
|
1172
1172
|
);
|
|
1173
1173
|
return null;
|
|
1174
1174
|
}
|
|
1175
|
-
|
|
1175
|
+
fs9.closeSync(descriptor);
|
|
1176
1176
|
const isOwned = () => pathMatchesClaim(lockPath, claimedStat, ownerId, parseRefreshLockOwner);
|
|
1177
1177
|
const releaseClaim = () => releaseOwnedLease(
|
|
1178
1178
|
lockPath,
|
|
@@ -1187,7 +1187,7 @@ async function acquireRefreshLock(envName, signal) {
|
|
|
1187
1187
|
}
|
|
1188
1188
|
let entryBlocked;
|
|
1189
1189
|
try {
|
|
1190
|
-
entryBlocked =
|
|
1190
|
+
entryBlocked = fs9.existsSync(recoveryPath) || !isOwned();
|
|
1191
1191
|
} catch (error62) {
|
|
1192
1192
|
console.error(
|
|
1193
1193
|
`[fml] Auth: unable to verify refresh lock ownership: ${describeError(error62)}`
|
|
@@ -1600,7 +1600,7 @@ var init_token_store = __esm({
|
|
|
1600
1600
|
PROCESS_IDENTITY_KEY = /* @__PURE__ */ Symbol.for("@fml-inc/fml:auth-process-identity");
|
|
1601
1601
|
processGlobals = globalThis;
|
|
1602
1602
|
existingProcessInstanceId = processGlobals[PROCESS_INSTANCE_KEY];
|
|
1603
|
-
CURRENT_PROCESS_INSTANCE_ID = typeof existingProcessInstanceId === "string" ? existingProcessInstanceId :
|
|
1603
|
+
CURRENT_PROCESS_INSTANCE_ID = typeof existingProcessInstanceId === "string" ? existingProcessInstanceId : randomUUID4();
|
|
1604
1604
|
processGlobals[PROCESS_INSTANCE_KEY] = CURRENT_PROCESS_INSTANCE_ID;
|
|
1605
1605
|
CorruptStoredCredentialsError = class extends Error {
|
|
1606
1606
|
};
|
|
@@ -1612,30 +1612,537 @@ var init_token_store = __esm({
|
|
|
1612
1612
|
}
|
|
1613
1613
|
});
|
|
1614
1614
|
|
|
1615
|
+
// ../../packages/agent-targets/src/git-state.ts
|
|
1616
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
1617
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
1618
|
+
import fs from "node:fs";
|
|
1619
|
+
import path from "node:path";
|
|
1620
|
+
|
|
1621
|
+
// ../../packages/shared/src/github-repository.ts
|
|
1622
|
+
var MAX_REPOSITORY_BYTES = 201;
|
|
1623
|
+
var UTF8_ENCODER = new TextEncoder();
|
|
1624
|
+
function utf8Bytes(value) {
|
|
1625
|
+
return UTF8_ENCODER.encode(value).byteLength;
|
|
1626
|
+
}
|
|
1627
|
+
function githubRepositoryPath(pathname) {
|
|
1628
|
+
const parts = pathname.replace(/^\/+|\/+$/g, "").split("/");
|
|
1629
|
+
if (parts.length !== 2) return null;
|
|
1630
|
+
const owner = parts[0];
|
|
1631
|
+
const repository = parts[1]?.replace(/\.git$/i, "");
|
|
1632
|
+
if (!owner || !repository) return null;
|
|
1633
|
+
const segmentPattern = /^[A-Za-z0-9_.-]+$/;
|
|
1634
|
+
if (!segmentPattern.test(owner) || !segmentPattern.test(repository)) {
|
|
1635
|
+
return null;
|
|
1636
|
+
}
|
|
1637
|
+
const canonical = `${owner.toLowerCase()}/${repository.toLowerCase()}`;
|
|
1638
|
+
if (canonical.startsWith("./") || canonical.startsWith("../") || repository === "." || repository === ".." || repository.toLowerCase().endsWith(".git")) {
|
|
1639
|
+
return null;
|
|
1640
|
+
}
|
|
1641
|
+
return utf8Bytes(canonical) <= MAX_REPOSITORY_BYTES ? canonical : null;
|
|
1642
|
+
}
|
|
1643
|
+
function canonicalGitHubRepository(remote) {
|
|
1644
|
+
const trimmed = remote.trim();
|
|
1645
|
+
if (!trimmed || trimmed.includes("\0")) return null;
|
|
1646
|
+
const scp = trimmed.match(/^git@github\.com:([^?#]+)$/i);
|
|
1647
|
+
if (scp?.[1]) return githubRepositoryPath(scp[1]);
|
|
1648
|
+
let url2;
|
|
1649
|
+
try {
|
|
1650
|
+
url2 = new URL(trimmed);
|
|
1651
|
+
} catch {
|
|
1652
|
+
return null;
|
|
1653
|
+
}
|
|
1654
|
+
if (url2.hostname.toLowerCase() !== "github.com") return null;
|
|
1655
|
+
if (url2.search || url2.hash || url2.password || url2.port) return null;
|
|
1656
|
+
if (url2.protocol === "https:" || url2.protocol === "http:") {
|
|
1657
|
+
if (url2.username) return null;
|
|
1658
|
+
} else if (url2.protocol === "ssh:") {
|
|
1659
|
+
if (url2.username && url2.username !== "git") return null;
|
|
1660
|
+
} else {
|
|
1661
|
+
return null;
|
|
1662
|
+
}
|
|
1663
|
+
return githubRepositoryPath(url2.pathname);
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
// ../../packages/shared/src/git-state.ts
|
|
1667
|
+
function isRecord(value) {
|
|
1668
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1669
|
+
}
|
|
1670
|
+
function validGitText(value) {
|
|
1671
|
+
return typeof value === "string" && value.length > 0 && value.length <= 4096 && !new RegExp("\\p{Cc}", "u").test(value);
|
|
1672
|
+
}
|
|
1673
|
+
function parseGitState(value) {
|
|
1674
|
+
if (!isRecord(value)) return null;
|
|
1675
|
+
const {
|
|
1676
|
+
version: version2,
|
|
1677
|
+
observedAtMs,
|
|
1678
|
+
repository,
|
|
1679
|
+
worktreeRoot,
|
|
1680
|
+
gitDir,
|
|
1681
|
+
commonDir,
|
|
1682
|
+
headRef,
|
|
1683
|
+
branch,
|
|
1684
|
+
commitHash,
|
|
1685
|
+
detached,
|
|
1686
|
+
unborn,
|
|
1687
|
+
isLinkedWorktree
|
|
1688
|
+
} = value;
|
|
1689
|
+
if (version2 !== 1 || typeof observedAtMs !== "number" || !Number.isSafeInteger(observedAtMs) || observedAtMs < 0 || !(repository === null || typeof repository === "string" && repository.length <= 201 && canonicalGitHubRepository(`https://github.com/${repository}`) === repository) || !validGitText(worktreeRoot) || !validGitText(gitDir) || !validGitText(commonDir) || !(headRef === null || validGitText(headRef)) || !(branch === null || validGitText(branch)) || !(commitHash === null || typeof commitHash === "string" && /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(commitHash)) || typeof detached !== "boolean" || typeof unborn !== "boolean" || typeof isLinkedWorktree !== "boolean")
|
|
1690
|
+
return null;
|
|
1691
|
+
return {
|
|
1692
|
+
version: version2,
|
|
1693
|
+
observedAtMs,
|
|
1694
|
+
repository,
|
|
1695
|
+
worktreeRoot,
|
|
1696
|
+
gitDir,
|
|
1697
|
+
commonDir,
|
|
1698
|
+
headRef,
|
|
1699
|
+
branch,
|
|
1700
|
+
commitHash,
|
|
1701
|
+
detached,
|
|
1702
|
+
unborn,
|
|
1703
|
+
isLinkedWorktree
|
|
1704
|
+
};
|
|
1705
|
+
}
|
|
1706
|
+
var MAX_GIT_STATES_BYTES = 16 * 1024;
|
|
1707
|
+
|
|
1708
|
+
// ../../packages/agent-targets/src/git-state.ts
|
|
1709
|
+
function isRecord2(value) {
|
|
1710
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1711
|
+
}
|
|
1712
|
+
var GIT_STATE_CACHE_FILENAME = "git-state.cache.json";
|
|
1713
|
+
var GIT_STATE_PAYLOAD_KEY = "_fmlGitState";
|
|
1714
|
+
var DEFAULT_TTL_MS = 5e3;
|
|
1715
|
+
var DEFAULT_TIMEOUT_MS = 1e3;
|
|
1716
|
+
var DEFAULT_MAX_ENTRIES = 512;
|
|
1717
|
+
var MAX_OUTPUT_BYTES = 16 * 1024;
|
|
1718
|
+
var GIT_DISCOVERY_OVERRIDES = [
|
|
1719
|
+
"GIT_DIR",
|
|
1720
|
+
"GIT_COMMON_DIR",
|
|
1721
|
+
"GIT_WORK_TREE",
|
|
1722
|
+
"GIT_CEILING_DIRECTORIES",
|
|
1723
|
+
"GIT_DISCOVERY_ACROSS_FILESYSTEM"
|
|
1724
|
+
];
|
|
1725
|
+
function* readState(cwd, knownHeadRef) {
|
|
1726
|
+
const layout = yield {
|
|
1727
|
+
args: [
|
|
1728
|
+
"rev-parse",
|
|
1729
|
+
"--show-toplevel",
|
|
1730
|
+
"--absolute-git-dir",
|
|
1731
|
+
"--git-common-dir",
|
|
1732
|
+
"--verify",
|
|
1733
|
+
"HEAD"
|
|
1734
|
+
],
|
|
1735
|
+
// An unborn HEAD still emits the three validated checkout paths.
|
|
1736
|
+
allowedExit: 128,
|
|
1737
|
+
keepOutputOnAllowedExit: true
|
|
1738
|
+
};
|
|
1739
|
+
const [worktreePath, gitPath, commonPath, revision] = layout?.split("\n") ?? [];
|
|
1740
|
+
if (!worktreePath || !gitPath || !commonPath) return null;
|
|
1741
|
+
const worktreeRoot = path.resolve(cwd, worktreePath);
|
|
1742
|
+
const gitDir = path.resolve(cwd, gitPath);
|
|
1743
|
+
const commonDir = path.resolve(cwd, commonPath);
|
|
1744
|
+
const headRef = knownHeadRef === void 0 ? yield { args: ["symbolic-ref", "--quiet", "HEAD"], allowedExit: 1 } : knownHeadRef;
|
|
1745
|
+
const commitHash = revision ?? null;
|
|
1746
|
+
if (!headRef && !commitHash) return null;
|
|
1747
|
+
const remote = yield {
|
|
1748
|
+
args: ["remote", "get-url", "origin"],
|
|
1749
|
+
allowedExit: 2
|
|
1750
|
+
};
|
|
1751
|
+
return {
|
|
1752
|
+
version: 1,
|
|
1753
|
+
repository: remote ? canonicalGitHubRepository(remote) : null,
|
|
1754
|
+
worktreeRoot,
|
|
1755
|
+
gitDir,
|
|
1756
|
+
commonDir,
|
|
1757
|
+
headRef,
|
|
1758
|
+
branch: headRef?.startsWith("refs/heads/") ? headRef.slice(11) : null,
|
|
1759
|
+
commitHash: commitHash?.toLowerCase() ?? null,
|
|
1760
|
+
detached: headRef === null,
|
|
1761
|
+
unborn: headRef !== null && commitHash === null,
|
|
1762
|
+
isLinkedWorktree: gitDir !== commonDir
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1765
|
+
function remaining(deadline) {
|
|
1766
|
+
const milliseconds = deadline - performance.now();
|
|
1767
|
+
if (milliseconds <= 0) throw new Error("Git lookup timed out");
|
|
1768
|
+
return Math.max(1, Math.ceil(milliseconds));
|
|
1769
|
+
}
|
|
1770
|
+
function runSync(cwd, command, deadline) {
|
|
1771
|
+
try {
|
|
1772
|
+
return execFileSync("git", ["-C", cwd, ...command.args], {
|
|
1773
|
+
encoding: "utf8",
|
|
1774
|
+
timeout: remaining(deadline),
|
|
1775
|
+
maxBuffer: MAX_OUTPUT_BYTES,
|
|
1776
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1777
|
+
windowsHide: true
|
|
1778
|
+
}).trim() || null;
|
|
1779
|
+
} catch (error62) {
|
|
1780
|
+
if (command.allowedExit !== void 0 && error62 && typeof error62 === "object" && "status" in error62 && error62.status === command.allowedExit) {
|
|
1781
|
+
return command.keepOutputOnAllowedExit && "stdout" in error62 && typeof error62.stdout === "string" ? error62.stdout.trim() || null : null;
|
|
1782
|
+
}
|
|
1783
|
+
throw error62;
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
async function runAsync(cwd, command, deadline, signal) {
|
|
1787
|
+
return await new Promise((resolve, reject) => {
|
|
1788
|
+
execFile(
|
|
1789
|
+
"git",
|
|
1790
|
+
["-C", cwd, ...command.args],
|
|
1791
|
+
{
|
|
1792
|
+
encoding: "utf8",
|
|
1793
|
+
timeout: remaining(deadline),
|
|
1794
|
+
maxBuffer: MAX_OUTPUT_BYTES,
|
|
1795
|
+
windowsHide: true,
|
|
1796
|
+
signal
|
|
1797
|
+
},
|
|
1798
|
+
(error62, stdout) => {
|
|
1799
|
+
if (signal?.aborted)
|
|
1800
|
+
reject(signal.reason ?? new Error("Git lookup aborted"));
|
|
1801
|
+
else if (error62 && error62.code !== command.allowedExit) reject(error62);
|
|
1802
|
+
else
|
|
1803
|
+
resolve(
|
|
1804
|
+
error62 && !command.keepOutputOnAllowedExit ? null : stdout.trim() || null
|
|
1805
|
+
);
|
|
1806
|
+
}
|
|
1807
|
+
);
|
|
1808
|
+
});
|
|
1809
|
+
}
|
|
1810
|
+
function inspectCheckout(cwd) {
|
|
1811
|
+
if (!cwd || cwd.trim() !== cwd || new RegExp("\\p{Cc}", "u").test(cwd) || Buffer.byteLength(cwd) > 4096)
|
|
1812
|
+
return null;
|
|
1813
|
+
try {
|
|
1814
|
+
const directory = fs.realpathSync(cwd);
|
|
1815
|
+
if (!fs.statSync(directory).isDirectory()) return null;
|
|
1816
|
+
if (GIT_DISCOVERY_OVERRIDES.some((key) => process.env[key] !== void 0))
|
|
1817
|
+
return { key: directory, signature: null };
|
|
1818
|
+
const layout = findGitDirectory(directory);
|
|
1819
|
+
if (!layout) return null;
|
|
1820
|
+
if (process.env.GIT_CONFIG !== void 0)
|
|
1821
|
+
return { key: layout.worktreeRoot, signature: null };
|
|
1822
|
+
const local = checkoutSignature(layout);
|
|
1823
|
+
const environment = Object.keys(process.env).filter(
|
|
1824
|
+
(key) => key.startsWith("GIT_") || key === "HOME" || key === "XDG_CONFIG_HOME"
|
|
1825
|
+
).sort().map((key) => [key, process.env[key]]);
|
|
1826
|
+
return {
|
|
1827
|
+
key: layout.worktreeRoot,
|
|
1828
|
+
headRef: local.headRef,
|
|
1829
|
+
signature: local.signature === null ? null : createHash("sha256").update(JSON.stringify([local.signature, environment])).digest("hex")
|
|
1830
|
+
};
|
|
1831
|
+
} catch {
|
|
1832
|
+
return null;
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
function createGitStateResolver(options = {}) {
|
|
1836
|
+
const entries = /* @__PURE__ */ new Map();
|
|
1837
|
+
const pending = /* @__PURE__ */ new Map();
|
|
1838
|
+
const now = options.nowMs ?? Date.now;
|
|
1839
|
+
const maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
1840
|
+
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
1841
|
+
const readDisk = () => {
|
|
1842
|
+
if (!options.cacheFile) return;
|
|
1843
|
+
try {
|
|
1844
|
+
const file2 = readRegularFile(options.cacheFile, 8 * 1024 * 1024).toString(
|
|
1845
|
+
"utf8"
|
|
1846
|
+
);
|
|
1847
|
+
const parsed = JSON.parse(file2);
|
|
1848
|
+
if (!isRecord2(parsed) || parsed.version !== 1 || !Array.isArray(parsed.entries) || parsed.entries.length > maxEntries)
|
|
1849
|
+
return;
|
|
1850
|
+
const timestamp = now();
|
|
1851
|
+
for (const pair of parsed.entries) {
|
|
1852
|
+
if (!Array.isArray(pair) || pair.length !== 2) continue;
|
|
1853
|
+
const cwd = pair[0];
|
|
1854
|
+
const entry = pair[1];
|
|
1855
|
+
if (typeof cwd !== "string" || !isRecord2(entry)) continue;
|
|
1856
|
+
const state = parseGitState(entry.state);
|
|
1857
|
+
if (state && typeof entry.signature === "string" && typeof entry.expiresAtMs === "number" && Number.isFinite(entry.expiresAtMs) && entry.expiresAtMs > timestamp && state.observedAtMs <= timestamp && entry.expiresAtMs <= state.observedAtMs + ttlMs) {
|
|
1858
|
+
entries.set(cwd, {
|
|
1859
|
+
signature: entry.signature,
|
|
1860
|
+
state,
|
|
1861
|
+
expiresAtMs: entry.expiresAtMs
|
|
1862
|
+
});
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
trim();
|
|
1866
|
+
} catch {
|
|
1867
|
+
}
|
|
1868
|
+
};
|
|
1869
|
+
const trim = () => {
|
|
1870
|
+
while (entries.size > Math.max(0, maxEntries)) {
|
|
1871
|
+
const key = entries.keys().next().value;
|
|
1872
|
+
if (key === void 0) break;
|
|
1873
|
+
entries.delete(key);
|
|
1874
|
+
}
|
|
1875
|
+
};
|
|
1876
|
+
const cached2 = (cwd, signature) => {
|
|
1877
|
+
if (!signature || ttlMs <= 0) return void 0;
|
|
1878
|
+
if (!entries.has(cwd)) readDisk();
|
|
1879
|
+
const entry = entries.get(cwd);
|
|
1880
|
+
if (!entry || entry.signature !== signature || entry.expiresAtMs <= now() || entry.state.observedAtMs > now())
|
|
1881
|
+
return void 0;
|
|
1882
|
+
entries.delete(cwd);
|
|
1883
|
+
entries.set(cwd, entry);
|
|
1884
|
+
return entry.state;
|
|
1885
|
+
};
|
|
1886
|
+
const finish = (cwd, signature, value) => {
|
|
1887
|
+
const state = parseGitState(value && { ...value, observedAtMs: now() });
|
|
1888
|
+
if (!state) return null;
|
|
1889
|
+
const after = inspectCheckout(cwd)?.signature ?? null;
|
|
1890
|
+
if (signature !== after) return null;
|
|
1891
|
+
if (signature && maxEntries > 0 && ttlMs > 0) {
|
|
1892
|
+
entries.delete(cwd);
|
|
1893
|
+
entries.set(cwd, {
|
|
1894
|
+
signature,
|
|
1895
|
+
state,
|
|
1896
|
+
expiresAtMs: state.observedAtMs + ttlMs
|
|
1897
|
+
});
|
|
1898
|
+
trim();
|
|
1899
|
+
if (options.cacheFile) {
|
|
1900
|
+
const temporary = `${options.cacheFile}.${process.pid}.${randomUUID()}.tmp`;
|
|
1901
|
+
try {
|
|
1902
|
+
fs.mkdirSync(path.dirname(options.cacheFile), {
|
|
1903
|
+
recursive: true,
|
|
1904
|
+
mode: 448
|
|
1905
|
+
});
|
|
1906
|
+
fs.writeFileSync(
|
|
1907
|
+
temporary,
|
|
1908
|
+
JSON.stringify({ version: 1, entries: [...entries] }),
|
|
1909
|
+
{ mode: 384 }
|
|
1910
|
+
);
|
|
1911
|
+
fs.renameSync(temporary, options.cacheFile);
|
|
1912
|
+
} catch {
|
|
1913
|
+
try {
|
|
1914
|
+
fs.rmSync(temporary, { force: true });
|
|
1915
|
+
} catch {
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
return state;
|
|
1921
|
+
};
|
|
1922
|
+
return {
|
|
1923
|
+
resolve(cwd, lookup = {}) {
|
|
1924
|
+
lookup.signal?.throwIfAborted();
|
|
1925
|
+
const snapshot = inspectCheckout(cwd);
|
|
1926
|
+
if (!snapshot) return null;
|
|
1927
|
+
const { key, signature } = snapshot;
|
|
1928
|
+
const hit = cached2(key, signature);
|
|
1929
|
+
if (hit) return hit;
|
|
1930
|
+
try {
|
|
1931
|
+
const deadline = performance.now() + (lookup.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
1932
|
+
const recipe = readState(key, snapshot.headRef);
|
|
1933
|
+
let step = recipe.next();
|
|
1934
|
+
while (!step.done) {
|
|
1935
|
+
lookup.signal?.throwIfAborted();
|
|
1936
|
+
step = recipe.next(runSync(key, step.value, deadline));
|
|
1937
|
+
}
|
|
1938
|
+
return finish(key, signature, step.value);
|
|
1939
|
+
} catch {
|
|
1940
|
+
lookup.signal?.throwIfAborted();
|
|
1941
|
+
return null;
|
|
1942
|
+
}
|
|
1943
|
+
},
|
|
1944
|
+
async resolveAsync(cwd, lookup = {}) {
|
|
1945
|
+
lookup.signal?.throwIfAborted();
|
|
1946
|
+
const snapshot = inspectCheckout(cwd);
|
|
1947
|
+
if (!snapshot) return null;
|
|
1948
|
+
const { key, signature } = snapshot;
|
|
1949
|
+
const hit = cached2(key, signature);
|
|
1950
|
+
if (hit) return hit;
|
|
1951
|
+
const existing = !lookup.signal ? pending.get(key) : void 0;
|
|
1952
|
+
if (existing) return await existing;
|
|
1953
|
+
const work = (async () => {
|
|
1954
|
+
try {
|
|
1955
|
+
const deadline = performance.now() + (lookup.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
1956
|
+
const recipe = readState(key, snapshot.headRef);
|
|
1957
|
+
let step = recipe.next();
|
|
1958
|
+
while (!step.done)
|
|
1959
|
+
step = recipe.next(
|
|
1960
|
+
await runAsync(key, step.value, deadline, lookup.signal)
|
|
1961
|
+
);
|
|
1962
|
+
return finish(key, signature, step.value);
|
|
1963
|
+
} catch {
|
|
1964
|
+
lookup.signal?.throwIfAborted();
|
|
1965
|
+
return null;
|
|
1966
|
+
}
|
|
1967
|
+
})();
|
|
1968
|
+
if (!lookup.signal) pending.set(key, work);
|
|
1969
|
+
try {
|
|
1970
|
+
return await work;
|
|
1971
|
+
} finally {
|
|
1972
|
+
if (pending.get(key) === work) pending.delete(key);
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
};
|
|
1976
|
+
}
|
|
1977
|
+
function readRegularFile(filePath, maxBytes) {
|
|
1978
|
+
const fd = fs.openSync(
|
|
1979
|
+
filePath,
|
|
1980
|
+
fs.constants.O_RDONLY | fs.constants.O_NONBLOCK
|
|
1981
|
+
);
|
|
1982
|
+
try {
|
|
1983
|
+
const stat = fs.fstatSync(fd);
|
|
1984
|
+
if (!stat.isFile() || stat.size > maxBytes)
|
|
1985
|
+
throw new Error("Git metadata is not a bounded regular file");
|
|
1986
|
+
const buffer = Buffer.alloc(stat.size + 1);
|
|
1987
|
+
let length = 0;
|
|
1988
|
+
while (length < buffer.length) {
|
|
1989
|
+
const bytes = fs.readSync(
|
|
1990
|
+
fd,
|
|
1991
|
+
buffer,
|
|
1992
|
+
length,
|
|
1993
|
+
buffer.length - length,
|
|
1994
|
+
null
|
|
1995
|
+
);
|
|
1996
|
+
if (bytes === 0) break;
|
|
1997
|
+
length += bytes;
|
|
1998
|
+
}
|
|
1999
|
+
if (length > stat.size) throw new Error("Git metadata grew during read");
|
|
2000
|
+
return buffer.subarray(0, length);
|
|
2001
|
+
} finally {
|
|
2002
|
+
fs.closeSync(fd);
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
function fileContentIdentity(filePath) {
|
|
2006
|
+
try {
|
|
2007
|
+
const contents = readRegularFile(filePath, 4 * 1024 * 1024);
|
|
2008
|
+
return `sha256:${createHash("sha256").update(contents).digest("hex")}`;
|
|
2009
|
+
} catch (error62) {
|
|
2010
|
+
const code = error62 && typeof error62 === "object" && "code" in error62 ? error62.code : void 0;
|
|
2011
|
+
return code === "ENOENT" || code === "ENOTDIR" ? "missing" : null;
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
function fileStatIdentity(filePath) {
|
|
2015
|
+
try {
|
|
2016
|
+
const stat = fs.statSync(filePath, { bigint: true });
|
|
2017
|
+
if (!stat.isFile() || stat.ino === 0n) return null;
|
|
2018
|
+
return [stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].join(
|
|
2019
|
+
":"
|
|
2020
|
+
);
|
|
2021
|
+
} catch (error62) {
|
|
2022
|
+
const code = error62 && typeof error62 === "object" && "code" in error62 ? error62.code : void 0;
|
|
2023
|
+
return code === "ENOENT" || code === "ENOTDIR" ? "missing" : null;
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
function readSmallText(filePath) {
|
|
2027
|
+
try {
|
|
2028
|
+
return readRegularFile(filePath, MAX_OUTPUT_BYTES).toString("utf8").trim();
|
|
2029
|
+
} catch {
|
|
2030
|
+
return null;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
function findGitDirectory(cwd) {
|
|
2034
|
+
let current = path.resolve(cwd);
|
|
2035
|
+
for (; ; ) {
|
|
2036
|
+
const candidate = path.join(current, ".git");
|
|
2037
|
+
let stat;
|
|
2038
|
+
try {
|
|
2039
|
+
stat = fs.statSync(candidate);
|
|
2040
|
+
} catch {
|
|
2041
|
+
stat = void 0;
|
|
2042
|
+
}
|
|
2043
|
+
if (stat?.isDirectory()) {
|
|
2044
|
+
return { worktreeRoot: current, gitDir: candidate, commonDir: candidate };
|
|
2045
|
+
}
|
|
2046
|
+
if (stat?.isFile()) {
|
|
2047
|
+
const pointer = readSmallText(candidate);
|
|
2048
|
+
const match = pointer ? /^gitdir:\s*(.+)$/u.exec(pointer) : null;
|
|
2049
|
+
if (!match?.[1]) return null;
|
|
2050
|
+
const gitDir = path.resolve(current, match[1]);
|
|
2051
|
+
const common = readSmallText(path.join(gitDir, "commondir"));
|
|
2052
|
+
return {
|
|
2053
|
+
worktreeRoot: current,
|
|
2054
|
+
gitDir,
|
|
2055
|
+
commonDir: common ? path.resolve(gitDir, common) : gitDir
|
|
2056
|
+
};
|
|
2057
|
+
}
|
|
2058
|
+
const parent = path.dirname(current);
|
|
2059
|
+
if (parent === current) return null;
|
|
2060
|
+
try {
|
|
2061
|
+
if (fs.statSync(current).dev !== fs.statSync(parent).dev) return null;
|
|
2062
|
+
} catch {
|
|
2063
|
+
return null;
|
|
2064
|
+
}
|
|
2065
|
+
current = parent;
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
function checkoutSignature(layout) {
|
|
2069
|
+
const head = readSmallText(path.join(layout.gitDir, "HEAD"));
|
|
2070
|
+
const configIdentity = [
|
|
2071
|
+
fileStatIdentity(path.join(layout.commonDir, "config")),
|
|
2072
|
+
fileStatIdentity(path.join(layout.gitDir, "config.worktree"))
|
|
2073
|
+
];
|
|
2074
|
+
if (head === null || configIdentity.some((value) => value === null))
|
|
2075
|
+
return { signature: null };
|
|
2076
|
+
const parts = [layout.gitDir, layout.commonDir, head, ...configIdentity];
|
|
2077
|
+
const ref = /^ref:\s*(.+)$/u.exec(head)?.[1];
|
|
2078
|
+
if (ref) {
|
|
2079
|
+
const looseRefPath = path.join(layout.commonDir, ref);
|
|
2080
|
+
const looseRef = readSmallText(looseRefPath);
|
|
2081
|
+
if (/^ref:/u.test(looseRef ?? "")) return { signature: null };
|
|
2082
|
+
const packedIdentity = fileStatIdentity(
|
|
2083
|
+
path.join(layout.commonDir, "packed-refs")
|
|
2084
|
+
);
|
|
2085
|
+
const reftableIdentity = fileContentIdentity(
|
|
2086
|
+
path.join(layout.commonDir, "reftable", "tables.list")
|
|
2087
|
+
);
|
|
2088
|
+
const looseIdentity = looseRef ?? fileStatIdentity(looseRefPath);
|
|
2089
|
+
if (looseIdentity === null || packedIdentity === null || reftableIdentity === null)
|
|
2090
|
+
return { signature: null };
|
|
2091
|
+
if (looseRef === null && looseIdentity !== "missing")
|
|
2092
|
+
return { signature: null };
|
|
2093
|
+
parts.push(looseIdentity, packedIdentity, reftableIdentity);
|
|
2094
|
+
return {
|
|
2095
|
+
// A fresh unborn checkout has no refs yet. Cache its missing-file
|
|
2096
|
+
// identities after Git validates the snapshot; the first ref invalidates it.
|
|
2097
|
+
signature: parts.join("\0"),
|
|
2098
|
+
...reftableIdentity === "missing" ? { headRef: ref } : {}
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2101
|
+
return {
|
|
2102
|
+
signature: parts.join("\0"),
|
|
2103
|
+
.../^(?:[a-f0-9]{40}|[a-f0-9]{64})$/i.test(head) ? { headRef: null } : {}
|
|
2104
|
+
};
|
|
2105
|
+
}
|
|
2106
|
+
|
|
1615
2107
|
// src/pi/extension.ts
|
|
1616
|
-
import { randomUUID as
|
|
1617
|
-
import
|
|
2108
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
2109
|
+
import fs10 from "node:fs";
|
|
1618
2110
|
import os2 from "node:os";
|
|
1619
|
-
import
|
|
2111
|
+
import path22 from "node:path";
|
|
2112
|
+
|
|
2113
|
+
// ../../packages/agent-targets/src/pi-agent-dir.ts
|
|
2114
|
+
import path2 from "node:path";
|
|
2115
|
+
function resolvePiAgentDir(context) {
|
|
2116
|
+
const pathApi = (context.platform ?? process.platform) === "win32" ? path2.win32 : path2.posix;
|
|
2117
|
+
const configured = context.env.PI_CODING_AGENT_DIR?.trim();
|
|
2118
|
+
if (!configured) {
|
|
2119
|
+
return pathApi.join(context.homeDir, ".pi", "agent");
|
|
2120
|
+
}
|
|
2121
|
+
if (configured === "~") return context.homeDir;
|
|
2122
|
+
if (configured.startsWith("~/") || configured.startsWith("~\\")) {
|
|
2123
|
+
return pathApi.join(context.homeDir, configured.slice(2));
|
|
2124
|
+
}
|
|
2125
|
+
return pathApi.normalize(configured);
|
|
2126
|
+
}
|
|
1620
2127
|
|
|
1621
2128
|
// ../../packages/agent-targets/src/artifacts/targets/claude.ts
|
|
1622
|
-
import
|
|
2129
|
+
import path5 from "node:path";
|
|
1623
2130
|
|
|
1624
2131
|
// ../../packages/agent-targets/src/config-paths.ts
|
|
1625
|
-
import
|
|
2132
|
+
import path3 from "node:path";
|
|
1626
2133
|
function claudeConfigDirectory(context) {
|
|
1627
2134
|
const configuredDir = context.claudeConfigDir ?? context.env?.CLAUDE_CONFIG_DIR;
|
|
1628
|
-
return typeof configuredDir === "string" && configuredDir.trim().length > 0 ?
|
|
2135
|
+
return typeof configuredDir === "string" && configuredDir.trim().length > 0 ? path3.resolve(configuredDir) : path3.join(context.homeDir, ".claude");
|
|
1629
2136
|
}
|
|
1630
2137
|
function geminiConfigDirectory(context) {
|
|
1631
2138
|
const configuredHome = context.env.GEMINI_CLI_HOME;
|
|
1632
|
-
const homeRoot = typeof configuredHome === "string" && configuredHome.trim().length > 0 ?
|
|
1633
|
-
return
|
|
2139
|
+
const homeRoot = typeof configuredHome === "string" && configuredHome.trim().length > 0 ? path3.resolve(configuredHome) : context.homeDir;
|
|
2140
|
+
return path3.join(homeRoot, ".gemini");
|
|
1634
2141
|
}
|
|
1635
2142
|
|
|
1636
2143
|
// ../../packages/agent-targets/src/artifacts/filesystem.ts
|
|
1637
|
-
import
|
|
1638
|
-
import
|
|
2144
|
+
import fs2 from "node:fs";
|
|
2145
|
+
import path4 from "node:path";
|
|
1639
2146
|
var DEFAULT_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
1640
2147
|
"node_modules",
|
|
1641
2148
|
"dist",
|
|
@@ -1652,11 +2159,11 @@ var DEFAULT_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
1652
2159
|
"designqc-captures"
|
|
1653
2160
|
]);
|
|
1654
2161
|
function normalizeFilePath(filePath) {
|
|
1655
|
-
return
|
|
2162
|
+
return path4.resolve(filePath);
|
|
1656
2163
|
}
|
|
1657
2164
|
function safeReaddir(dir) {
|
|
1658
2165
|
try {
|
|
1659
|
-
return
|
|
2166
|
+
return fs2.readdirSync(dir, { withFileTypes: true });
|
|
1660
2167
|
} catch {
|
|
1661
2168
|
return [];
|
|
1662
2169
|
}
|
|
@@ -1669,7 +2176,7 @@ function walkFiles(input2) {
|
|
|
1669
2176
|
function walk(dir, depth) {
|
|
1670
2177
|
if (depth > input2.maxDepth) return;
|
|
1671
2178
|
for (const entry of safeReaddir(dir)) {
|
|
1672
|
-
const entryPath =
|
|
2179
|
+
const entryPath = path4.join(dir, entry.name);
|
|
1673
2180
|
if (entry.isDirectory()) {
|
|
1674
2181
|
if (!skipDirs.has(entry.name)) walk(entryPath, depth + 1);
|
|
1675
2182
|
continue;
|
|
@@ -1705,7 +2212,7 @@ function transcriptCandidate(input2) {
|
|
|
1705
2212
|
}
|
|
1706
2213
|
|
|
1707
2214
|
// ../../packages/agent-targets/src/artifacts/harness-install-id.ts
|
|
1708
|
-
import
|
|
2215
|
+
import fs3 from "node:fs";
|
|
1709
2216
|
var MAX_HARNESS_INSTALL_ID_LENGTH = 128;
|
|
1710
2217
|
function normalizeHarnessInstallId(value) {
|
|
1711
2218
|
if (typeof value !== "string") return null;
|
|
@@ -1716,7 +2223,7 @@ function normalizeHarnessInstallId(value) {
|
|
|
1716
2223
|
}
|
|
1717
2224
|
function readHarnessInstallIdFile(filePath) {
|
|
1718
2225
|
try {
|
|
1719
|
-
return normalizeHarnessInstallId(
|
|
2226
|
+
return normalizeHarnessInstallId(fs3.readFileSync(filePath, "utf8"));
|
|
1720
2227
|
} catch {
|
|
1721
2228
|
return null;
|
|
1722
2229
|
}
|
|
@@ -1725,7 +2232,7 @@ var claudeMachineIdCache = /* @__PURE__ */ new Map();
|
|
|
1725
2232
|
function readClaudeMachineId(configFile) {
|
|
1726
2233
|
let stat;
|
|
1727
2234
|
try {
|
|
1728
|
-
stat =
|
|
2235
|
+
stat = fs3.statSync(configFile);
|
|
1729
2236
|
} catch {
|
|
1730
2237
|
claudeMachineIdCache.delete(configFile);
|
|
1731
2238
|
return null;
|
|
@@ -1736,7 +2243,7 @@ function readClaudeMachineId(configFile) {
|
|
|
1736
2243
|
}
|
|
1737
2244
|
let machineId = null;
|
|
1738
2245
|
try {
|
|
1739
|
-
const parsed = JSON.parse(
|
|
2246
|
+
const parsed = JSON.parse(fs3.readFileSync(configFile, "utf8"));
|
|
1740
2247
|
if (parsed && typeof parsed === "object") {
|
|
1741
2248
|
machineId = normalizeHarnessInstallId(
|
|
1742
2249
|
parsed.machineID
|
|
@@ -1754,55 +2261,8 @@ function readClaudeMachineId(configFile) {
|
|
|
1754
2261
|
}
|
|
1755
2262
|
|
|
1756
2263
|
// ../../packages/agent-targets/src/artifacts/repository-context.ts
|
|
1757
|
-
import { createHash } from "node:crypto";
|
|
1758
|
-
import
|
|
1759
|
-
|
|
1760
|
-
// ../../packages/shared/src/github-repository.ts
|
|
1761
|
-
var MAX_REPOSITORY_BYTES = 201;
|
|
1762
|
-
var UTF8_ENCODER = new TextEncoder();
|
|
1763
|
-
function utf8Bytes(value) {
|
|
1764
|
-
return UTF8_ENCODER.encode(value).byteLength;
|
|
1765
|
-
}
|
|
1766
|
-
function githubRepositoryPath(pathname) {
|
|
1767
|
-
const parts = pathname.replace(/^\/+|\/+$/g, "").split("/");
|
|
1768
|
-
if (parts.length !== 2) return null;
|
|
1769
|
-
const owner = parts[0];
|
|
1770
|
-
const repository = parts[1]?.replace(/\.git$/i, "");
|
|
1771
|
-
if (!owner || !repository) return null;
|
|
1772
|
-
const segmentPattern = /^[A-Za-z0-9_.-]+$/;
|
|
1773
|
-
if (!segmentPattern.test(owner) || !segmentPattern.test(repository)) {
|
|
1774
|
-
return null;
|
|
1775
|
-
}
|
|
1776
|
-
const canonical = `${owner.toLowerCase()}/${repository.toLowerCase()}`;
|
|
1777
|
-
if (canonical.startsWith("./") || canonical.startsWith("../") || repository === "." || repository === ".." || repository.toLowerCase().endsWith(".git")) {
|
|
1778
|
-
return null;
|
|
1779
|
-
}
|
|
1780
|
-
return utf8Bytes(canonical) <= MAX_REPOSITORY_BYTES ? canonical : null;
|
|
1781
|
-
}
|
|
1782
|
-
function canonicalGitHubRepository(remote) {
|
|
1783
|
-
const trimmed = remote.trim();
|
|
1784
|
-
if (!trimmed || trimmed.includes("\0")) return null;
|
|
1785
|
-
const scp = trimmed.match(/^git@github\.com:([^?#]+)$/i);
|
|
1786
|
-
if (scp?.[1]) return githubRepositoryPath(scp[1]);
|
|
1787
|
-
let url2;
|
|
1788
|
-
try {
|
|
1789
|
-
url2 = new URL(trimmed);
|
|
1790
|
-
} catch {
|
|
1791
|
-
return null;
|
|
1792
|
-
}
|
|
1793
|
-
if (url2.hostname.toLowerCase() !== "github.com") return null;
|
|
1794
|
-
if (url2.search || url2.hash || url2.password || url2.port) return null;
|
|
1795
|
-
if (url2.protocol === "https:" || url2.protocol === "http:") {
|
|
1796
|
-
if (url2.username) return null;
|
|
1797
|
-
} else if (url2.protocol === "ssh:") {
|
|
1798
|
-
if (url2.username && url2.username !== "git") return null;
|
|
1799
|
-
} else {
|
|
1800
|
-
return null;
|
|
1801
|
-
}
|
|
1802
|
-
return githubRepositoryPath(url2.pathname);
|
|
1803
|
-
}
|
|
1804
|
-
|
|
1805
|
-
// ../../packages/agent-targets/src/artifacts/repository-context.ts
|
|
2264
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
2265
|
+
import fs4 from "node:fs";
|
|
1806
2266
|
var MAX_ARTIFACT_CONTEXT_PREFIX_BYTES = 256 * 1024;
|
|
1807
2267
|
var MAX_ARTIFACT_CONTEXT_RECORDS = 64;
|
|
1808
2268
|
var ARTIFACT_CONTEXT_READ_CHUNK_BYTES = 32 * 1024;
|
|
@@ -1825,12 +2285,12 @@ function recordValue(value) {
|
|
|
1825
2285
|
function readJsonlContextRecords(sourcePath) {
|
|
1826
2286
|
let file2;
|
|
1827
2287
|
try {
|
|
1828
|
-
file2 =
|
|
2288
|
+
file2 = fs4.openSync(sourcePath, "r");
|
|
1829
2289
|
} catch {
|
|
1830
2290
|
return [];
|
|
1831
2291
|
}
|
|
1832
2292
|
try {
|
|
1833
|
-
const stat =
|
|
2293
|
+
const stat = fs4.fstatSync(file2);
|
|
1834
2294
|
const byteLimit = Math.min(stat.size, MAX_ARTIFACT_CONTEXT_PREFIX_BYTES);
|
|
1835
2295
|
if (byteLimit <= 0) return [];
|
|
1836
2296
|
const records = [];
|
|
@@ -1848,7 +2308,7 @@ function readJsonlContextRecords(sourcePath) {
|
|
|
1848
2308
|
const chunk = Buffer.allocUnsafe(ARTIFACT_CONTEXT_READ_CHUNK_BYTES);
|
|
1849
2309
|
while (position < byteLimit && records.length < MAX_ARTIFACT_CONTEXT_RECORDS) {
|
|
1850
2310
|
const want = Math.min(chunk.length, byteLimit - position);
|
|
1851
|
-
const bytesRead =
|
|
2311
|
+
const bytesRead = fs4.readSync(file2, chunk, 0, want, position);
|
|
1852
2312
|
if (bytesRead <= 0) break;
|
|
1853
2313
|
position += bytesRead;
|
|
1854
2314
|
const buffered = Buffer.concat([carry, chunk.subarray(0, bytesRead)]);
|
|
@@ -1871,7 +2331,7 @@ function readJsonlContextRecords(sourcePath) {
|
|
|
1871
2331
|
} catch {
|
|
1872
2332
|
return [];
|
|
1873
2333
|
} finally {
|
|
1874
|
-
|
|
2334
|
+
fs4.closeSync(file2);
|
|
1875
2335
|
}
|
|
1876
2336
|
}
|
|
1877
2337
|
function contextTimestampMs(value, fallback) {
|
|
@@ -1899,7 +2359,7 @@ function repositoryObservation(input2) {
|
|
|
1899
2359
|
branch ?? null,
|
|
1900
2360
|
commitHash ?? null
|
|
1901
2361
|
]);
|
|
1902
|
-
const id = `repo_observation:${
|
|
2362
|
+
const id = `repo_observation:${createHash2("sha256").update(identity).digest("hex")}`;
|
|
1903
2363
|
return {
|
|
1904
2364
|
id,
|
|
1905
2365
|
repository: input2.repository,
|
|
@@ -1910,14 +2370,29 @@ function repositoryObservation(input2) {
|
|
|
1910
2370
|
...commitHash ? { commitHash } : {}
|
|
1911
2371
|
};
|
|
1912
2372
|
}
|
|
2373
|
+
function localGitStateMetadata(states, source = "transcript_discovery") {
|
|
2374
|
+
const bounded = [];
|
|
2375
|
+
for (const state of states) {
|
|
2376
|
+
if (!state || Buffer.byteLength(
|
|
2377
|
+
JSON.stringify({ version: 1, source, states: [...bounded, state] }),
|
|
2378
|
+
"utf8"
|
|
2379
|
+
) > 4 * 1024)
|
|
2380
|
+
continue;
|
|
2381
|
+
bounded.push(state);
|
|
2382
|
+
}
|
|
2383
|
+
return bounded.length ? { localGitStates: { version: 1, source, states: bounded } } : {};
|
|
2384
|
+
}
|
|
1913
2385
|
|
|
1914
2386
|
// ../../packages/agent-targets/src/artifacts/targets/claude.ts
|
|
1915
2387
|
function claudeTranscriptCandidate(sourcePath, context, harnessInstallId) {
|
|
1916
2388
|
const fallbackObservedAtMs = (context.nowMs ?? Date.now)();
|
|
2389
|
+
const states = /* @__PURE__ */ new Map();
|
|
1917
2390
|
const observations = /* @__PURE__ */ new Map();
|
|
1918
2391
|
for (const record2 of readJsonlContextRecords(sourcePath)) {
|
|
1919
2392
|
if (typeof record2.cwd !== "string" || !record2.cwd.trim()) continue;
|
|
1920
2393
|
const origin = context.resolveGitHubOrigin?.(record2.cwd);
|
|
2394
|
+
const state = origin?.gitState ?? context.resolveGitState?.(record2.cwd);
|
|
2395
|
+
if (state) states.set(state.worktreeRoot, state);
|
|
1921
2396
|
if (!origin) continue;
|
|
1922
2397
|
const observation = repositoryObservation({
|
|
1923
2398
|
repository: origin.repository,
|
|
@@ -1932,7 +2407,10 @@ function claudeTranscriptCandidate(sourcePath, context, harnessInstallId) {
|
|
|
1932
2407
|
return transcriptCandidate({
|
|
1933
2408
|
targetId: "claude",
|
|
1934
2409
|
sourcePath,
|
|
1935
|
-
metadata:
|
|
2410
|
+
metadata: {
|
|
2411
|
+
...harnessInstallId ? { harnessInstallId } : {},
|
|
2412
|
+
...localGitStateMetadata([...states.values()])
|
|
2413
|
+
},
|
|
1936
2414
|
repositoryObservations: [...observations.values()]
|
|
1937
2415
|
});
|
|
1938
2416
|
}
|
|
@@ -1941,9 +2419,9 @@ var claudeArtifactDiscovery = Object.freeze({
|
|
|
1941
2419
|
discover: (context) => {
|
|
1942
2420
|
const { homeDir, env } = context;
|
|
1943
2421
|
const configDir = claudeConfigDirectory({ homeDir, env });
|
|
1944
|
-
const harnessInstallId = readClaudeMachineId(
|
|
2422
|
+
const harnessInstallId = readClaudeMachineId(path5.join(configDir, ".claude.json")) ?? readClaudeMachineId(path5.join(homeDir, ".claude.json"));
|
|
1945
2423
|
return walkFiles({
|
|
1946
|
-
root:
|
|
2424
|
+
root: path5.join(configDir, "projects"),
|
|
1947
2425
|
maxDepth: 6,
|
|
1948
2426
|
includeFile: (filePath) => filePath.endsWith(".jsonl")
|
|
1949
2427
|
}).map(
|
|
@@ -1953,10 +2431,10 @@ var claudeArtifactDiscovery = Object.freeze({
|
|
|
1953
2431
|
});
|
|
1954
2432
|
|
|
1955
2433
|
// ../../packages/agent-targets/src/artifacts/targets/codex.ts
|
|
1956
|
-
import
|
|
2434
|
+
import path6 from "node:path";
|
|
1957
2435
|
var CODEX_ROLLOUT_ID = /-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl(?:\.zst)?$/i;
|
|
1958
2436
|
function codexRolloutId(sourcePath) {
|
|
1959
|
-
return CODEX_ROLLOUT_ID.exec(
|
|
2437
|
+
return CODEX_ROLLOUT_ID.exec(path6.basename(sourcePath))?.[1]?.toLowerCase() ?? null;
|
|
1960
2438
|
}
|
|
1961
2439
|
function codexTranscriptCandidate(sourcePath, readPath, store, context, harnessInstallId) {
|
|
1962
2440
|
const rolloutId = codexRolloutId(sourcePath);
|
|
@@ -1969,9 +2447,10 @@ function codexTranscriptCandidate(sourcePath, readPath, store, context, harnessI
|
|
|
1969
2447
|
if (!payload) continue;
|
|
1970
2448
|
const sessionId = typeof payload.id === "string" && payload.id.trim() ? payload.id.trim() : null;
|
|
1971
2449
|
const cwd = typeof payload.cwd === "string" ? payload.cwd : void 0;
|
|
1972
|
-
const
|
|
1973
|
-
const nativeRepository = typeof
|
|
2450
|
+
const git = recordValue(payload.git);
|
|
2451
|
+
const nativeRepository = typeof git?.repository_url === "string" ? canonicalGitHubRepository(git.repository_url) : null;
|
|
1974
2452
|
const origin = cwd ? context.resolveGitHubOrigin?.(cwd) : null;
|
|
2453
|
+
const state = origin?.gitState ?? (cwd ? context.resolveGitState?.(cwd) : null);
|
|
1975
2454
|
const observations = [];
|
|
1976
2455
|
if (nativeRepository) {
|
|
1977
2456
|
observations.push(
|
|
@@ -1983,8 +2462,8 @@ function codexTranscriptCandidate(sourcePath, readPath, store, context, harnessI
|
|
|
1983
2462
|
fallbackObservedAtMs
|
|
1984
2463
|
),
|
|
1985
2464
|
cwd,
|
|
1986
|
-
branch:
|
|
1987
|
-
commitHash:
|
|
2465
|
+
branch: git?.branch,
|
|
2466
|
+
commitHash: git?.commit_hash
|
|
1988
2467
|
})
|
|
1989
2468
|
);
|
|
1990
2469
|
}
|
|
@@ -2006,7 +2485,11 @@ function codexTranscriptCandidate(sourcePath, readPath, store, context, harnessI
|
|
|
2006
2485
|
identityKey,
|
|
2007
2486
|
readPath,
|
|
2008
2487
|
sessionId,
|
|
2009
|
-
metadata: {
|
|
2488
|
+
metadata: {
|
|
2489
|
+
store,
|
|
2490
|
+
...identityMetadata,
|
|
2491
|
+
...localGitStateMetadata([state])
|
|
2492
|
+
},
|
|
2010
2493
|
repositoryObservations: observations
|
|
2011
2494
|
});
|
|
2012
2495
|
}
|
|
@@ -2026,15 +2509,15 @@ var codexArtifactDiscovery = Object.freeze({
|
|
|
2026
2509
|
discover: (context) => {
|
|
2027
2510
|
const { homeDir, env } = context;
|
|
2028
2511
|
const configuredHome = env.CODEX_HOME?.trim();
|
|
2029
|
-
const codexHome = configuredHome ?
|
|
2512
|
+
const codexHome = configuredHome ? path6.resolve(configuredHome) : path6.join(homeDir, ".codex");
|
|
2030
2513
|
const harnessInstallId = readHarnessInstallIdFile(
|
|
2031
|
-
|
|
2514
|
+
path6.join(codexHome, "installation_id")
|
|
2032
2515
|
);
|
|
2033
2516
|
const roots = [
|
|
2034
|
-
{ store: "sessions", root:
|
|
2517
|
+
{ store: "sessions", root: path6.join(codexHome, "sessions") },
|
|
2035
2518
|
{
|
|
2036
2519
|
store: "archived_sessions",
|
|
2037
|
-
root:
|
|
2520
|
+
root: path6.join(codexHome, "archived_sessions")
|
|
2038
2521
|
}
|
|
2039
2522
|
];
|
|
2040
2523
|
const files = roots.flatMap(
|
|
@@ -2072,27 +2555,27 @@ var codexArtifactDiscovery = Object.freeze({
|
|
|
2072
2555
|
});
|
|
2073
2556
|
|
|
2074
2557
|
// ../../packages/agent-targets/src/artifacts/targets/copilot-cli.ts
|
|
2075
|
-
import
|
|
2558
|
+
import path7 from "node:path";
|
|
2076
2559
|
var copilotCliArtifactDiscovery = Object.freeze({
|
|
2077
2560
|
targetId: "copilot-cli",
|
|
2078
2561
|
discover: ({ homeDir }) => walkFiles({
|
|
2079
|
-
root:
|
|
2562
|
+
root: path7.join(homeDir, ".copilot", "session-state"),
|
|
2080
2563
|
maxDepth: 3,
|
|
2081
|
-
includeFile: (filePath) =>
|
|
2564
|
+
includeFile: (filePath) => path7.basename(filePath) === "events.jsonl"
|
|
2082
2565
|
}).map(
|
|
2083
2566
|
(sourcePath) => transcriptCandidate({ targetId: "copilot-cli", sourcePath })
|
|
2084
2567
|
)
|
|
2085
2568
|
});
|
|
2086
2569
|
|
|
2087
2570
|
// ../../packages/agent-targets/src/artifacts/targets/cursor.ts
|
|
2088
|
-
import
|
|
2571
|
+
import path8 from "node:path";
|
|
2089
2572
|
function hasPathSegment(filePath, segment) {
|
|
2090
|
-
return normalizeFilePath(filePath).split(
|
|
2573
|
+
return normalizeFilePath(filePath).split(path8.sep).includes(segment);
|
|
2091
2574
|
}
|
|
2092
2575
|
var cursorArtifactDiscovery = Object.freeze({
|
|
2093
2576
|
targetId: "cursor",
|
|
2094
2577
|
discover: ({ homeDir }) => walkFiles({
|
|
2095
|
-
root:
|
|
2578
|
+
root: path8.join(homeDir, ".cursor", "projects"),
|
|
2096
2579
|
maxDepth: 5,
|
|
2097
2580
|
includeFile: (filePath) => filePath.endsWith(".jsonl") && hasPathSegment(filePath, "agent-transcripts")
|
|
2098
2581
|
}).map(
|
|
@@ -2101,11 +2584,11 @@ var cursorArtifactDiscovery = Object.freeze({
|
|
|
2101
2584
|
});
|
|
2102
2585
|
|
|
2103
2586
|
// ../../packages/agent-targets/src/artifacts/targets/factory-droid.ts
|
|
2104
|
-
import
|
|
2587
|
+
import path9 from "node:path";
|
|
2105
2588
|
var factoryDroidArtifactDiscovery = Object.freeze({
|
|
2106
2589
|
targetId: "factory-droid",
|
|
2107
2590
|
discover: ({ homeDir }) => walkFiles({
|
|
2108
|
-
root:
|
|
2591
|
+
root: path9.join(homeDir, ".factory", "sessions"),
|
|
2109
2592
|
maxDepth: 4,
|
|
2110
2593
|
includeFile: (filePath) => filePath.endsWith(".jsonl")
|
|
2111
2594
|
}).map(
|
|
@@ -2114,7 +2597,7 @@ var factoryDroidArtifactDiscovery = Object.freeze({
|
|
|
2114
2597
|
});
|
|
2115
2598
|
|
|
2116
2599
|
// ../../packages/agent-targets/src/artifacts/targets/gemini.ts
|
|
2117
|
-
import
|
|
2600
|
+
import path10 from "node:path";
|
|
2118
2601
|
var GEMINI_DISCOVER_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
2119
2602
|
"bin",
|
|
2120
2603
|
"checkpoints",
|
|
@@ -2129,15 +2612,15 @@ var geminiArtifactDiscovery = Object.freeze({
|
|
|
2129
2612
|
discover: ({ homeDir, env }) => {
|
|
2130
2613
|
const geminiDir = geminiConfigDirectory({ homeDir, env });
|
|
2131
2614
|
const harnessInstallId = readHarnessInstallIdFile(
|
|
2132
|
-
|
|
2615
|
+
path10.join(geminiDir, "installation_id")
|
|
2133
2616
|
);
|
|
2134
|
-
const root =
|
|
2617
|
+
const root = path10.join(geminiDir, "tmp");
|
|
2135
2618
|
const files = [];
|
|
2136
2619
|
const seen = /* @__PURE__ */ new Set();
|
|
2137
2620
|
function walk(dir, depth) {
|
|
2138
2621
|
if (depth > 4) return;
|
|
2139
2622
|
for (const entry of safeReaddir(dir)) {
|
|
2140
|
-
const entryPath =
|
|
2623
|
+
const entryPath = path10.join(dir, entry.name);
|
|
2141
2624
|
if (!entry.isDirectory()) continue;
|
|
2142
2625
|
if (GEMINI_DISCOVER_SKIP_DIRS.has(entry.name)) continue;
|
|
2143
2626
|
if (entry.name === "chats") {
|
|
@@ -2146,7 +2629,7 @@ var geminiArtifactDiscovery = Object.freeze({
|
|
|
2146
2629
|
continue;
|
|
2147
2630
|
}
|
|
2148
2631
|
const sourcePath = normalizeFilePath(
|
|
2149
|
-
|
|
2632
|
+
path10.join(entryPath, chatEntry.name)
|
|
2150
2633
|
);
|
|
2151
2634
|
if (seen.has(sourcePath)) continue;
|
|
2152
2635
|
seen.add(sourcePath);
|
|
@@ -2169,52 +2652,52 @@ var geminiArtifactDiscovery = Object.freeze({
|
|
|
2169
2652
|
});
|
|
2170
2653
|
|
|
2171
2654
|
// ../../packages/agent-targets/src/artifacts/targets/grok.ts
|
|
2172
|
-
import
|
|
2655
|
+
import path11 from "node:path";
|
|
2173
2656
|
function grokHome(homeDir, env) {
|
|
2174
2657
|
const configuredHome = env.GROK_HOME?.trim();
|
|
2175
|
-
return configuredHome ?
|
|
2658
|
+
return configuredHome ? path11.resolve(configuredHome) : path11.join(homeDir, ".grok");
|
|
2176
2659
|
}
|
|
2177
2660
|
var grokArtifactDiscovery = Object.freeze({
|
|
2178
2661
|
targetId: "grok",
|
|
2179
2662
|
discover: ({ homeDir, env }) => walkFiles({
|
|
2180
|
-
root:
|
|
2663
|
+
root: path11.join(grokHome(homeDir, env), "sessions"),
|
|
2181
2664
|
maxDepth: 5,
|
|
2182
|
-
includeFile: (filePath) =>
|
|
2665
|
+
includeFile: (filePath) => path11.basename(filePath) === "updates.jsonl"
|
|
2183
2666
|
}).map(
|
|
2184
2667
|
(sourcePath) => transcriptCandidate({ targetId: "grok", sourcePath })
|
|
2185
2668
|
)
|
|
2186
2669
|
});
|
|
2187
2670
|
|
|
2188
2671
|
// ../../packages/agent-targets/src/artifacts/targets/mini-swe-agent.ts
|
|
2189
|
-
import
|
|
2190
|
-
import
|
|
2672
|
+
import fs5 from "node:fs";
|
|
2673
|
+
import path12 from "node:path";
|
|
2191
2674
|
var SESSION_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2192
2675
|
function dataDirectory({ homeDir, env }) {
|
|
2193
2676
|
if (env.FML_DATA_DIR?.trim()) {
|
|
2194
|
-
if (!
|
|
2677
|
+
if (!path12.isAbsolute(env.FML_DATA_DIR)) {
|
|
2195
2678
|
throw new Error("FML_DATA_DIR must be an absolute path");
|
|
2196
2679
|
}
|
|
2197
|
-
return
|
|
2680
|
+
return path12.normalize(env.FML_DATA_DIR);
|
|
2198
2681
|
}
|
|
2199
2682
|
if (process.platform === "darwin") {
|
|
2200
|
-
return
|
|
2683
|
+
return path12.join(homeDir, "Library", "Application Support", "fml");
|
|
2201
2684
|
}
|
|
2202
2685
|
if (process.platform === "win32") {
|
|
2203
|
-
const roaming = env.APPDATA &&
|
|
2204
|
-
return
|
|
2686
|
+
const roaming = env.APPDATA && path12.isAbsolute(env.APPDATA) ? env.APPDATA : path12.join(homeDir, "AppData", "Roaming");
|
|
2687
|
+
return path12.join(roaming, "fml");
|
|
2205
2688
|
}
|
|
2206
|
-
const xdgDataHome = env.XDG_DATA_HOME &&
|
|
2207
|
-
return
|
|
2689
|
+
const xdgDataHome = env.XDG_DATA_HOME && path12.isAbsolute(env.XDG_DATA_HOME) ? env.XDG_DATA_HOME : path12.join(homeDir, ".local", "share");
|
|
2690
|
+
return path12.join(xdgDataHome, "fml");
|
|
2208
2691
|
}
|
|
2209
2692
|
function snapshotCandidate(sourcePath, context) {
|
|
2210
2693
|
let file2;
|
|
2211
2694
|
try {
|
|
2212
|
-
file2 =
|
|
2695
|
+
file2 = fs5.openSync(sourcePath, "r");
|
|
2213
2696
|
} catch {
|
|
2214
2697
|
return null;
|
|
2215
2698
|
}
|
|
2216
2699
|
try {
|
|
2217
|
-
const stat =
|
|
2700
|
+
const stat = fs5.fstatSync(file2);
|
|
2218
2701
|
if (!stat.isFile()) return null;
|
|
2219
2702
|
if (context.maxFileBytes !== void 0 && stat.size > context.maxFileBytes) {
|
|
2220
2703
|
return transcriptCandidate({ targetId: "mini-swe-agent", sourcePath });
|
|
@@ -2222,7 +2705,7 @@ function snapshotCandidate(sourcePath, context) {
|
|
|
2222
2705
|
const bytes = Buffer.allocUnsafe(stat.size);
|
|
2223
2706
|
let offset = 0;
|
|
2224
2707
|
while (offset < bytes.length) {
|
|
2225
|
-
const read =
|
|
2708
|
+
const read = fs5.readSync(
|
|
2226
2709
|
file2,
|
|
2227
2710
|
bytes,
|
|
2228
2711
|
offset,
|
|
@@ -2232,7 +2715,7 @@ function snapshotCandidate(sourcePath, context) {
|
|
|
2232
2715
|
if (read <= 0) return null;
|
|
2233
2716
|
offset += read;
|
|
2234
2717
|
}
|
|
2235
|
-
if (
|
|
2718
|
+
if (fs5.fstatSync(file2).size !== stat.size) return null;
|
|
2236
2719
|
const value = JSON.parse(bytes.toString("utf8"));
|
|
2237
2720
|
if (typeof value !== "object" || value === null || !("fml" in value)) {
|
|
2238
2721
|
return null;
|
|
@@ -2262,13 +2745,13 @@ function snapshotCandidate(sourcePath, context) {
|
|
|
2262
2745
|
} catch {
|
|
2263
2746
|
return null;
|
|
2264
2747
|
} finally {
|
|
2265
|
-
|
|
2748
|
+
fs5.closeSync(file2);
|
|
2266
2749
|
}
|
|
2267
2750
|
}
|
|
2268
2751
|
var miniSweAgentArtifactDiscovery = Object.freeze({
|
|
2269
2752
|
targetId: "mini-swe-agent",
|
|
2270
2753
|
discover: (context) => walkFiles({
|
|
2271
|
-
root:
|
|
2754
|
+
root: path12.join(dataDirectory(context), "mini-swe-agent", "trajectories"),
|
|
2272
2755
|
maxDepth: 0,
|
|
2273
2756
|
includeFile: (filePath) => filePath.endsWith(".traj.json")
|
|
2274
2757
|
}).flatMap((sourcePath) => {
|
|
@@ -2278,7 +2761,7 @@ var miniSweAgentArtifactDiscovery = Object.freeze({
|
|
|
2278
2761
|
});
|
|
2279
2762
|
|
|
2280
2763
|
// ../../packages/agent-targets/src/artifacts/targets/pi.ts
|
|
2281
|
-
import
|
|
2764
|
+
import path13 from "node:path";
|
|
2282
2765
|
|
|
2283
2766
|
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/external.js
|
|
2284
2767
|
var external_exports = {};
|
|
@@ -3097,10 +3580,10 @@ function mergeDefs(...defs) {
|
|
|
3097
3580
|
function cloneDef(schema) {
|
|
3098
3581
|
return mergeDefs(schema._zod.def);
|
|
3099
3582
|
}
|
|
3100
|
-
function getElementAtPath(obj,
|
|
3101
|
-
if (!
|
|
3583
|
+
function getElementAtPath(obj, path23) {
|
|
3584
|
+
if (!path23)
|
|
3102
3585
|
return obj;
|
|
3103
|
-
return
|
|
3586
|
+
return path23.reduce((acc, key) => acc?.[key], obj);
|
|
3104
3587
|
}
|
|
3105
3588
|
function promiseAllObject(promisesObj) {
|
|
3106
3589
|
const keys = Object.keys(promisesObj);
|
|
@@ -3440,11 +3923,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
3440
3923
|
}
|
|
3441
3924
|
return false;
|
|
3442
3925
|
}
|
|
3443
|
-
function prefixIssues(
|
|
3926
|
+
function prefixIssues(path23, issues) {
|
|
3444
3927
|
return issues.map((iss) => {
|
|
3445
3928
|
var _a3;
|
|
3446
3929
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
3447
|
-
iss.path.unshift(
|
|
3930
|
+
iss.path.unshift(path23);
|
|
3448
3931
|
return iss;
|
|
3449
3932
|
});
|
|
3450
3933
|
}
|
|
@@ -3906,16 +4389,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
|
|
|
3906
4389
|
}
|
|
3907
4390
|
function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
3908
4391
|
const fieldErrors = { _errors: [] };
|
|
3909
|
-
const processError = (error63,
|
|
4392
|
+
const processError = (error63, path23 = []) => {
|
|
3910
4393
|
for (const issue2 of error63.issues) {
|
|
3911
4394
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
3912
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
4395
|
+
issue2.errors.map((issues) => processError({ issues }, [...path23, ...issue2.path]));
|
|
3913
4396
|
} else if (issue2.code === "invalid_key") {
|
|
3914
|
-
processError({ issues: issue2.issues }, [...
|
|
4397
|
+
processError({ issues: issue2.issues }, [...path23, ...issue2.path]);
|
|
3915
4398
|
} else if (issue2.code === "invalid_element") {
|
|
3916
|
-
processError({ issues: issue2.issues }, [...
|
|
4399
|
+
processError({ issues: issue2.issues }, [...path23, ...issue2.path]);
|
|
3917
4400
|
} else {
|
|
3918
|
-
const fullpath = [...
|
|
4401
|
+
const fullpath = [...path23, ...issue2.path];
|
|
3919
4402
|
if (fullpath.length === 0) {
|
|
3920
4403
|
fieldErrors._errors.push(mapper(issue2));
|
|
3921
4404
|
} else {
|
|
@@ -3954,17 +4437,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
|
3954
4437
|
}
|
|
3955
4438
|
function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
3956
4439
|
const result = { errors: [] };
|
|
3957
|
-
const processError = (error63,
|
|
4440
|
+
const processError = (error63, path23 = []) => {
|
|
3958
4441
|
var _a3;
|
|
3959
4442
|
for (const issue2 of error63.issues) {
|
|
3960
4443
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
3961
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
4444
|
+
issue2.errors.map((issues) => processError({ issues }, [...path23, ...issue2.path]));
|
|
3962
4445
|
} else if (issue2.code === "invalid_key") {
|
|
3963
|
-
processError({ issues: issue2.issues }, [...
|
|
4446
|
+
processError({ issues: issue2.issues }, [...path23, ...issue2.path]);
|
|
3964
4447
|
} else if (issue2.code === "invalid_element") {
|
|
3965
|
-
processError({ issues: issue2.issues }, [...
|
|
4448
|
+
processError({ issues: issue2.issues }, [...path23, ...issue2.path]);
|
|
3966
4449
|
} else {
|
|
3967
|
-
const fullpath = [...
|
|
4450
|
+
const fullpath = [...path23, ...issue2.path];
|
|
3968
4451
|
if (fullpath.length === 0) {
|
|
3969
4452
|
result.errors.push(mapper(issue2));
|
|
3970
4453
|
continue;
|
|
@@ -4003,8 +4486,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
|
4003
4486
|
}
|
|
4004
4487
|
function toDotPath(_path) {
|
|
4005
4488
|
const segs = [];
|
|
4006
|
-
const
|
|
4007
|
-
for (const seg of
|
|
4489
|
+
const path23 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
4490
|
+
for (const seg of path23) {
|
|
4008
4491
|
if (typeof seg === "number")
|
|
4009
4492
|
segs.push(`[${seg}]`);
|
|
4010
4493
|
else if (typeof seg === "symbol")
|
|
@@ -21142,13 +21625,13 @@ function resolveRef(ref, ctx) {
|
|
|
21142
21625
|
if (!ref.startsWith("#")) {
|
|
21143
21626
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
21144
21627
|
}
|
|
21145
|
-
const
|
|
21146
|
-
if (
|
|
21628
|
+
const path23 = ref.slice(1).split("/").filter(Boolean);
|
|
21629
|
+
if (path23.length === 0) {
|
|
21147
21630
|
return ctx.rootSchema;
|
|
21148
21631
|
}
|
|
21149
21632
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
21150
|
-
if (
|
|
21151
|
-
const key =
|
|
21633
|
+
if (path23[0] === defsKey) {
|
|
21634
|
+
const key = path23[1] === void 0 ? void 0 : decodeJSONPointerSegment(path23[1]);
|
|
21152
21635
|
if (!key || !ctx.defs[key]) {
|
|
21153
21636
|
throw new Error(`Reference not found: ${ref}`);
|
|
21154
21637
|
}
|
|
@@ -22002,6 +22485,12 @@ var PI_REPOSITORY_ORIGIN_ENTRY_TYPE = "fml-repository-origin";
|
|
|
22002
22485
|
var RepositorySlugSchema = external_exports.string().regex(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u).transform((slug) => canonicalGitHubRepository(`https://github.com/${slug}`)).pipe(external_exports.string());
|
|
22003
22486
|
var PiRepositoryOriginDataSchema = external_exports.object({
|
|
22004
22487
|
version: external_exports.literal(1),
|
|
22488
|
+
gitState: external_exports.unknown().transform((value, ctx) => {
|
|
22489
|
+
const state = parseGitState(value);
|
|
22490
|
+
if (state) return state;
|
|
22491
|
+
ctx.addIssue({ code: "custom", message: "Invalid Git state snapshot" });
|
|
22492
|
+
return external_exports.NEVER;
|
|
22493
|
+
}).nullable().optional(),
|
|
22005
22494
|
repository: RepositorySlugSchema.nullable(),
|
|
22006
22495
|
cwd: external_exports.string().min(1).refine(
|
|
22007
22496
|
(cwd) => cwd.trim().length > 0 && !new RegExp("\\p{Cc}", "u").test(cwd) && Buffer.byteLength(cwd, "utf8") <= 4096
|
|
@@ -22043,12 +22532,15 @@ function piTranscriptCandidate(sourcePath) {
|
|
|
22043
22532
|
targetId: "pi",
|
|
22044
22533
|
sourcePath,
|
|
22045
22534
|
sessionId,
|
|
22535
|
+
metadata: localGitStateMetadata([snapshot?.gitState], "pi_origin"),
|
|
22046
22536
|
repositoryObservations: snapshot?.repository ? [
|
|
22047
22537
|
repositoryObservation({
|
|
22048
22538
|
repository: snapshot.repository,
|
|
22049
22539
|
source: "git_origin",
|
|
22050
22540
|
observedAtMs: snapshot.observedAtMs,
|
|
22051
|
-
cwd: snapshot.cwd
|
|
22541
|
+
cwd: snapshot.cwd,
|
|
22542
|
+
branch: snapshot.gitState?.branch,
|
|
22543
|
+
commitHash: snapshot.gitState?.commitHash
|
|
22052
22544
|
})
|
|
22053
22545
|
] : []
|
|
22054
22546
|
});
|
|
@@ -22056,9 +22548,8 @@ function piTranscriptCandidate(sourcePath) {
|
|
|
22056
22548
|
var piArtifactDiscovery = Object.freeze({
|
|
22057
22549
|
targetId: "pi",
|
|
22058
22550
|
discover: (context) => {
|
|
22059
|
-
const { homeDir } = context;
|
|
22060
22551
|
return walkFiles({
|
|
22061
|
-
root:
|
|
22552
|
+
root: path13.join(resolvePiAgentDir(context), "sessions"),
|
|
22062
22553
|
maxDepth: 8,
|
|
22063
22554
|
includeFile: (filePath) => filePath.endsWith(".jsonl"),
|
|
22064
22555
|
skipDirs: PI_SKIP_DIRS
|
|
@@ -22084,9 +22575,9 @@ var MAX_MATERIALIZED_ARTIFACT_BYTES = 100 * 1024 * 1024;
|
|
|
22084
22575
|
var DEFAULT_MATERIALIZED_CACHE_BYTES = 2 * 1024 * 1024 * 1024;
|
|
22085
22576
|
|
|
22086
22577
|
// ../../packages/agent-targets/src/install/pi/index.ts
|
|
22087
|
-
import
|
|
22088
|
-
import
|
|
22089
|
-
import { randomUUID } from "node:crypto";
|
|
22578
|
+
import fs6 from "node:fs";
|
|
22579
|
+
import path14 from "node:path";
|
|
22580
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
22090
22581
|
var FML_PI_EXTENSION_MARKER = "__FML_PI_EXTENSION_V1__";
|
|
22091
22582
|
var LEGACY_PI_EXTENSION_MARKER = "__panopticonPiExtensionTest";
|
|
22092
22583
|
var LEGACY_PI_EXTENSION_SIGNATURES = Object.freeze([
|
|
@@ -22098,22 +22589,11 @@ var LEGACY_PI_EXTENSION_SIGNATURES = Object.freeze([
|
|
|
22098
22589
|
"SessionEnd"
|
|
22099
22590
|
]);
|
|
22100
22591
|
function pathApiFor(platform) {
|
|
22101
|
-
return platform === "win32" ?
|
|
22102
|
-
}
|
|
22103
|
-
function resolvePiAgentDir(context, pathApi) {
|
|
22104
|
-
const configured = context.env.PI_CODING_AGENT_DIR?.trim();
|
|
22105
|
-
if (!configured) {
|
|
22106
|
-
return pathApi.join(context.homeDir, ".pi", "agent");
|
|
22107
|
-
}
|
|
22108
|
-
if (configured === "~") return context.homeDir;
|
|
22109
|
-
if (configured.startsWith("~/") || configured.startsWith("~\\")) {
|
|
22110
|
-
return pathApi.join(context.homeDir, configured.slice(2));
|
|
22111
|
-
}
|
|
22112
|
-
return pathApi.normalize(configured);
|
|
22592
|
+
return platform === "win32" ? path14.win32 : path14.posix;
|
|
22113
22593
|
}
|
|
22114
22594
|
function readFileIfPresent(filePath) {
|
|
22115
22595
|
try {
|
|
22116
|
-
return
|
|
22596
|
+
return fs6.readFileSync(filePath, "utf8");
|
|
22117
22597
|
} catch (error62) {
|
|
22118
22598
|
if (error62.code === "ENOENT") return null;
|
|
22119
22599
|
throw error62;
|
|
@@ -22130,30 +22610,30 @@ function removeLegacyExtension(filePath) {
|
|
|
22130
22610
|
if (!isOwnedExtension(source, true)) {
|
|
22131
22611
|
return { removed: false, preserved: true };
|
|
22132
22612
|
}
|
|
22133
|
-
|
|
22613
|
+
fs6.rmSync(filePath);
|
|
22134
22614
|
return { removed: true, preserved: false };
|
|
22135
22615
|
}
|
|
22136
22616
|
function writeExtensionAtomically(filePath, source) {
|
|
22137
|
-
|
|
22138
|
-
const temporaryPath =
|
|
22139
|
-
|
|
22140
|
-
`.${
|
|
22617
|
+
fs6.mkdirSync(path14.dirname(filePath), { recursive: true, mode: 448 });
|
|
22618
|
+
const temporaryPath = path14.join(
|
|
22619
|
+
path14.dirname(filePath),
|
|
22620
|
+
`.${path14.basename(filePath)}.${process.pid}.${randomUUID2()}.tmp`
|
|
22141
22621
|
);
|
|
22142
22622
|
try {
|
|
22143
|
-
|
|
22144
|
-
if (process.platform === "win32" &&
|
|
22145
|
-
|
|
22623
|
+
fs6.writeFileSync(temporaryPath, source, { encoding: "utf8", mode: 384 });
|
|
22624
|
+
if (process.platform === "win32" && fs6.existsSync(filePath)) {
|
|
22625
|
+
fs6.rmSync(filePath);
|
|
22146
22626
|
}
|
|
22147
|
-
|
|
22627
|
+
fs6.renameSync(temporaryPath, filePath);
|
|
22148
22628
|
} finally {
|
|
22149
|
-
|
|
22629
|
+
fs6.rmSync(temporaryPath, { force: true });
|
|
22150
22630
|
}
|
|
22151
22631
|
}
|
|
22152
22632
|
var PI_EXTENSION_INSTALLER = Object.freeze({
|
|
22153
22633
|
resolvePaths(context) {
|
|
22154
22634
|
const pathApi = pathApiFor(context.platform);
|
|
22155
22635
|
const extensionsDir = pathApi.join(
|
|
22156
|
-
resolvePiAgentDir(context
|
|
22636
|
+
resolvePiAgentDir(context),
|
|
22157
22637
|
"extensions"
|
|
22158
22638
|
);
|
|
22159
22639
|
return {
|
|
@@ -22169,7 +22649,7 @@ var PI_EXTENSION_INSTALLER = Object.freeze({
|
|
|
22169
22649
|
};
|
|
22170
22650
|
},
|
|
22171
22651
|
install(paths) {
|
|
22172
|
-
const source =
|
|
22652
|
+
const source = fs6.readFileSync(paths.sourcePath, "utf8");
|
|
22173
22653
|
if (!source.includes(FML_PI_EXTENSION_MARKER)) {
|
|
22174
22654
|
throw new Error(
|
|
22175
22655
|
`Packaged Pi extension is invalid: ${paths.sourcePath} does not contain the FML ownership marker`
|
|
@@ -22218,7 +22698,7 @@ var PI_EXTENSION_INSTALLER = Object.freeze({
|
|
|
22218
22698
|
legacyPreserved: legacy.preserved
|
|
22219
22699
|
};
|
|
22220
22700
|
}
|
|
22221
|
-
|
|
22701
|
+
fs6.rmSync(paths.destinationPath);
|
|
22222
22702
|
return {
|
|
22223
22703
|
status: "removed",
|
|
22224
22704
|
destinationPath: paths.destinationPath,
|
|
@@ -22229,11 +22709,11 @@ var PI_EXTENSION_INSTALLER = Object.freeze({
|
|
|
22229
22709
|
});
|
|
22230
22710
|
|
|
22231
22711
|
// src/hooks/context.ts
|
|
22232
|
-
import { createHash as
|
|
22233
|
-
import
|
|
22712
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
22713
|
+
import path21 from "node:path";
|
|
22234
22714
|
|
|
22235
22715
|
// ../../packages/agent-targets/src/hooks/normalize.ts
|
|
22236
|
-
import { createHash as
|
|
22716
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
22237
22717
|
|
|
22238
22718
|
// ../../packages/agent-targets/src/hooks/types.ts
|
|
22239
22719
|
var HOOK_TARGETS = Object.freeze([
|
|
@@ -22349,7 +22829,7 @@ var MINI_SWE_AGENT_HOOK_EVENTS = Object.freeze([
|
|
|
22349
22829
|
]);
|
|
22350
22830
|
|
|
22351
22831
|
// ../../packages/agent-targets/src/hooks/read-command.ts
|
|
22352
|
-
import
|
|
22832
|
+
import path15 from "node:path";
|
|
22353
22833
|
var SHELL_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
22354
22834
|
"bash",
|
|
22355
22835
|
"exec_command",
|
|
@@ -22465,12 +22945,12 @@ function splitCommandSegments(tokens) {
|
|
|
22465
22945
|
return segments;
|
|
22466
22946
|
}
|
|
22467
22947
|
function stripSimpleOptions(args, flagsWithValues = /* @__PURE__ */ new Set(), patternFlags = /* @__PURE__ */ new Set()) {
|
|
22468
|
-
const
|
|
22948
|
+
const remaining2 = [];
|
|
22469
22949
|
let consumedPatternFlag = false;
|
|
22470
22950
|
for (let index = 0; index < args.length; index += 1) {
|
|
22471
22951
|
const argument = args[index];
|
|
22472
22952
|
if (argument === "--") {
|
|
22473
|
-
|
|
22953
|
+
remaining2.push(...args.slice(index + 1));
|
|
22474
22954
|
break;
|
|
22475
22955
|
}
|
|
22476
22956
|
if (argument.startsWith("-") && argument.length > 1) {
|
|
@@ -22480,9 +22960,9 @@ function stripSimpleOptions(args, flagsWithValues = /* @__PURE__ */ new Set(), p
|
|
|
22480
22960
|
}
|
|
22481
22961
|
continue;
|
|
22482
22962
|
}
|
|
22483
|
-
|
|
22963
|
+
remaining2.push(argument);
|
|
22484
22964
|
}
|
|
22485
|
-
return { args:
|
|
22965
|
+
return { args: remaining2, consumedPatternFlag };
|
|
22486
22966
|
}
|
|
22487
22967
|
function readCommandFileArgs(args, flagsWithValues, patternFlags = flagsWithValues) {
|
|
22488
22968
|
const stripped = stripSimpleOptions(args, flagsWithValues, patternFlags);
|
|
@@ -22493,7 +22973,7 @@ function isPlausibleReadPath(argument) {
|
|
|
22493
22973
|
if (COMMAND_SEPARATORS.has(argument)) return false;
|
|
22494
22974
|
if (/[\0*?[{}`$]/.test(argument)) return false;
|
|
22495
22975
|
if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(argument)) return false;
|
|
22496
|
-
return
|
|
22976
|
+
return path15.posix.isAbsolute(argument) || path15.win32.isAbsolute(argument) || argument.startsWith("~/") || argument.startsWith("./") || argument.startsWith("../") || argument.includes("/") || /\.[A-Za-z0-9][A-Za-z0-9_-]*$/.test(argument) || /^[A-Z][A-Za-z0-9_.-]*$/.test(argument);
|
|
22497
22977
|
}
|
|
22498
22978
|
function filePathFromSegment(tokens) {
|
|
22499
22979
|
const commandTokens = [...tokens];
|
|
@@ -22564,7 +23044,7 @@ function extractReadFilePathFromCommand(command) {
|
|
|
22564
23044
|
|
|
22565
23045
|
// ../../packages/agent-targets/src/hooks/normalize.ts
|
|
22566
23046
|
var MAX_EXTRACTED_TEXT_BYTES = 64 * 1024;
|
|
22567
|
-
function
|
|
23047
|
+
function isRecord3(value) {
|
|
22568
23048
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
22569
23049
|
}
|
|
22570
23050
|
function firstString(record2, keys) {
|
|
@@ -22611,7 +23091,7 @@ function boundedExtractedText(value, field) {
|
|
|
22611
23091
|
if (value === void 0) return void 0;
|
|
22612
23092
|
const originalBytes = Buffer.byteLength(value, "utf8");
|
|
22613
23093
|
if (originalBytes <= MAX_EXTRACTED_TEXT_BYTES) return value;
|
|
22614
|
-
const digest =
|
|
23094
|
+
const digest = createHash3("sha256").update(value).digest("hex");
|
|
22615
23095
|
const marker = `
|
|
22616
23096
|
[fml:${field}:truncated originalBytes=${originalBytes} sha256=${digest}]`;
|
|
22617
23097
|
return `${utf8Prefix(
|
|
@@ -22622,7 +23102,7 @@ function boundedExtractedText(value, field) {
|
|
|
22622
23102
|
function nestedRecord(record2, keys) {
|
|
22623
23103
|
for (const key of keys) {
|
|
22624
23104
|
const value = record2[key];
|
|
22625
|
-
if (
|
|
23105
|
+
if (isRecord3(value)) return value;
|
|
22626
23106
|
}
|
|
22627
23107
|
return void 0;
|
|
22628
23108
|
}
|
|
@@ -22680,9 +23160,9 @@ function grokEventType(eventType) {
|
|
|
22680
23160
|
}
|
|
22681
23161
|
function geminiUserPrompt(payload) {
|
|
22682
23162
|
const request = payload.llm_request;
|
|
22683
|
-
if (!
|
|
23163
|
+
if (!isRecord3(request) || !Array.isArray(request.messages)) return void 0;
|
|
22684
23164
|
const userMessage = [...request.messages].reverse().find(
|
|
22685
|
-
(message) =>
|
|
23165
|
+
(message) => isRecord3(message) && message.role === "user"
|
|
22686
23166
|
);
|
|
22687
23167
|
if (!userMessage) return void 0;
|
|
22688
23168
|
if (typeof userMessage.content === "string") {
|
|
@@ -22690,7 +23170,7 @@ function geminiUserPrompt(payload) {
|
|
|
22690
23170
|
}
|
|
22691
23171
|
if (!Array.isArray(userMessage.content)) return void 0;
|
|
22692
23172
|
const text = userMessage.content.filter(
|
|
22693
|
-
(part) =>
|
|
23173
|
+
(part) => isRecord3(part) && part.type === "text" && typeof part.text === "string"
|
|
22694
23174
|
).map((part) => part.text).join("\n");
|
|
22695
23175
|
return text || void 0;
|
|
22696
23176
|
}
|
|
@@ -22789,51 +23269,15 @@ init_token_store();
|
|
|
22789
23269
|
init_dirs();
|
|
22790
23270
|
|
|
22791
23271
|
// src/repo.ts
|
|
22792
|
-
import
|
|
22793
|
-
|
|
22794
|
-
var
|
|
22795
|
-
|
|
22796
|
-
|
|
22797
|
-
|
|
22798
|
-
if (entry.expiresAtMs <= Date.now()) {
|
|
22799
|
-
REPO_CACHE.delete(cwd);
|
|
22800
|
-
return { hit: false };
|
|
22801
|
-
}
|
|
22802
|
-
REPO_CACHE.delete(cwd);
|
|
22803
|
-
REPO_CACHE.set(cwd, entry);
|
|
22804
|
-
return { hit: true, value: entry.value };
|
|
22805
|
-
}
|
|
22806
|
-
function git(cwd, args, timeoutMs) {
|
|
22807
|
-
try {
|
|
22808
|
-
return execFileSync2("git", ["-C", cwd, ...args], {
|
|
22809
|
-
encoding: "utf8",
|
|
22810
|
-
timeout: timeoutMs,
|
|
22811
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
22812
|
-
windowsHide: true
|
|
22813
|
-
}).trim() || null;
|
|
22814
|
-
} catch {
|
|
22815
|
-
return null;
|
|
22816
|
-
}
|
|
22817
|
-
}
|
|
22818
|
-
function repositorySlug(remote) {
|
|
22819
|
-
const ssh = remote.match(/^[^@]+@[^:]+:([^/]+\/[^/]+?)(?:\.git)?$/);
|
|
22820
|
-
if (ssh?.[1]) return ssh[1].toLowerCase();
|
|
22821
|
-
try {
|
|
22822
|
-
const url2 = new URL(remote);
|
|
22823
|
-
const slug = url2.pathname.replace(/^\/+|\/+$/g, "").replace(/\.git$/, "");
|
|
22824
|
-
return /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(slug) ? slug.toLowerCase() : null;
|
|
22825
|
-
} catch {
|
|
22826
|
-
return null;
|
|
22827
|
-
}
|
|
22828
|
-
}
|
|
22829
|
-
function resolveRepositorySlugFromCwd(cwd, timeoutMs = GIT_TIMEOUT_MS) {
|
|
22830
|
-
const cached2 = cachedRepo(cwd);
|
|
22831
|
-
if (cached2.hit) return cached2.value?.repo ?? null;
|
|
22832
|
-
const remote = git(cwd, ["remote", "get-url", "origin"], timeoutMs);
|
|
22833
|
-
return remote ? repositorySlug(remote) : null;
|
|
23272
|
+
import path20 from "node:path";
|
|
23273
|
+
init_dirs();
|
|
23274
|
+
var CACHE_FILE = path20.join(FML_OBSERVER_DIR, GIT_STATE_CACHE_FILENAME);
|
|
23275
|
+
var resolver = createGitStateResolver({ cacheFile: CACHE_FILE });
|
|
23276
|
+
function resolveGitStateFromCwd(cwd, timeoutMs = 1e3) {
|
|
23277
|
+
return resolver.resolve(cwd, { timeoutMs });
|
|
22834
23278
|
}
|
|
22835
|
-
function resolveRepositoryRootFromCwd(cwd, timeoutMs =
|
|
22836
|
-
return
|
|
23279
|
+
function resolveRepositoryRootFromCwd(cwd, timeoutMs = 1e3) {
|
|
23280
|
+
return resolveGitStateFromCwd(cwd, timeoutMs)?.worktreeRoot ?? null;
|
|
22837
23281
|
}
|
|
22838
23282
|
|
|
22839
23283
|
// src/hooks/sync-loop.ts
|
|
@@ -22918,14 +23362,14 @@ function sameDestination(left, right) {
|
|
|
22918
23362
|
return left.environment === right.environment && left.targetUrl === right.targetUrl && left.destinationIdentity === right.destinationIdentity;
|
|
22919
23363
|
}
|
|
22920
23364
|
function hookContextInjectionId(target, syncId) {
|
|
22921
|
-
const digest =
|
|
23365
|
+
const digest = createHash6("sha256").update(`${INJECTION_ID_VERSION}\0${target}\0${syncId}`).digest("base64url");
|
|
22922
23366
|
return `${INJECTION_ID_VERSION}_${digest}`;
|
|
22923
23367
|
}
|
|
22924
23368
|
function canonicalHookContextFilePath(filePath, cwd, repositoryRoot) {
|
|
22925
23369
|
const windows = [filePath, cwd, repositoryRoot].some(
|
|
22926
23370
|
(value) => value !== void 0 && value !== null && /^[A-Za-z]:[\\/]/.test(value)
|
|
22927
23371
|
);
|
|
22928
|
-
const pathApi = windows ?
|
|
23372
|
+
const pathApi = windows ? path21.win32 : path21.posix;
|
|
22929
23373
|
const normalized = pathApi.normalize(filePath);
|
|
22930
23374
|
const resolved = pathApi.isAbsolute(normalized) ? normalized : cwd && pathApi.isAbsolute(cwd) ? pathApi.resolve(cwd, normalized) : normalized;
|
|
22931
23375
|
if (repositoryRoot && pathApi.isAbsolute(resolved)) {
|
|
@@ -23085,16 +23529,16 @@ function wrapReceiptDetail(label, detail) {
|
|
|
23085
23529
|
const firstPrefix = `\u2502 ${label}: `;
|
|
23086
23530
|
const continuationPrefix = "\u2502 ";
|
|
23087
23531
|
const lines = [];
|
|
23088
|
-
let
|
|
23532
|
+
let remaining2 = detail;
|
|
23089
23533
|
let prefix = firstPrefix;
|
|
23090
|
-
while (
|
|
23534
|
+
while (remaining2) {
|
|
23091
23535
|
const available = RECEIPT_LINE_WIDTH - receiptDisplayWidth(prefix);
|
|
23092
|
-
if (receiptDisplayWidth(
|
|
23093
|
-
lines.push(`${prefix}${
|
|
23536
|
+
if (receiptDisplayWidth(remaining2) <= available) {
|
|
23537
|
+
lines.push(`${prefix}${remaining2}`);
|
|
23094
23538
|
break;
|
|
23095
23539
|
}
|
|
23096
|
-
const tokenEnd =
|
|
23097
|
-
const token = tokenEnd === -1 ?
|
|
23540
|
+
const tokenEnd = remaining2.search(/\s/u);
|
|
23541
|
+
const token = tokenEnd === -1 ? remaining2 : remaining2.slice(0, tokenEnd);
|
|
23098
23542
|
const inlineCodeToken = splitInlineCodeToken(token);
|
|
23099
23543
|
const inlineCodeWidth = inlineCodeToken ? receiptDisplayWidth(inlineCodeToken.inlineCode) + receiptDisplayWidth(inlineCodeToken.trailingPunctuation) : 0;
|
|
23100
23544
|
if (inlineCodeToken && inlineCodeWidth > available) {
|
|
@@ -23106,7 +23550,7 @@ function wrapReceiptDetail(label, detail) {
|
|
|
23106
23550
|
codeWidth
|
|
23107
23551
|
)}${inlineCodeToken.trailingPunctuation}`
|
|
23108
23552
|
);
|
|
23109
|
-
|
|
23553
|
+
remaining2 = tokenEnd === -1 ? "" : remaining2.slice(tokenEnd).trimStart();
|
|
23110
23554
|
} else {
|
|
23111
23555
|
lines.push(
|
|
23112
23556
|
`${prefix}${truncateInlineCodeToken(
|
|
@@ -23114,13 +23558,13 @@ function wrapReceiptDetail(label, detail) {
|
|
|
23114
23558
|
available
|
|
23115
23559
|
)}`
|
|
23116
23560
|
);
|
|
23117
|
-
|
|
23561
|
+
remaining2 = `${inlineCodeToken.trailingPunctuation}${tokenEnd === -1 ? "" : ` ${remaining2.slice(tokenEnd).trimStart()}`}`;
|
|
23118
23562
|
}
|
|
23119
23563
|
} else {
|
|
23120
|
-
const { head, tail } = splitAtDisplayWidth(
|
|
23564
|
+
const { head, tail } = splitAtDisplayWidth(remaining2, available);
|
|
23121
23565
|
if (!head) return [];
|
|
23122
23566
|
lines.push(`${prefix}${head}`);
|
|
23123
|
-
|
|
23567
|
+
remaining2 = tail;
|
|
23124
23568
|
}
|
|
23125
23569
|
prefix = continuationPrefix;
|
|
23126
23570
|
}
|
|
@@ -23254,17 +23698,17 @@ function absoluteEnvPath(value) {
|
|
|
23254
23698
|
const trimmed = value.trim();
|
|
23255
23699
|
if (trimmed === "~") return os2.homedir();
|
|
23256
23700
|
if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
|
|
23257
|
-
return
|
|
23701
|
+
return path22.join(os2.homedir(), trimmed.slice(2));
|
|
23258
23702
|
}
|
|
23259
|
-
return
|
|
23703
|
+
return path22.isAbsolute(trimmed) ? path22.normalize(trimmed) : void 0;
|
|
23260
23704
|
}
|
|
23261
23705
|
function observerDirectory() {
|
|
23262
23706
|
const fmlHome = absoluteEnvPath(process.env.FML_HOME);
|
|
23263
|
-
return absoluteEnvPath(process.env.FML_OBSERVER_DIR) ?? absoluteEnvPath(process.env.PANOPTICON_HOME) ?? (fmlHome ?
|
|
23707
|
+
return absoluteEnvPath(process.env.FML_OBSERVER_DIR) ?? absoluteEnvPath(process.env.PANOPTICON_HOME) ?? (fmlHome ? path22.join(fmlHome, "panopticon") : path22.join(os2.homedir(), ".fml", "panopticon"));
|
|
23264
23708
|
}
|
|
23265
23709
|
function readObserverEndpoint(filePath) {
|
|
23266
23710
|
try {
|
|
23267
|
-
const parsed = JSON.parse(
|
|
23711
|
+
const parsed = JSON.parse(fs10.readFileSync(filePath, "utf8"));
|
|
23268
23712
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
23269
23713
|
return null;
|
|
23270
23714
|
}
|
|
@@ -23279,15 +23723,15 @@ function readObserverEndpoint(filePath) {
|
|
|
23279
23723
|
}
|
|
23280
23724
|
function readObserverAuthToken(filePath) {
|
|
23281
23725
|
try {
|
|
23282
|
-
return
|
|
23726
|
+
return fs10.readFileSync(filePath, "utf8").trim() || null;
|
|
23283
23727
|
} catch {
|
|
23284
23728
|
return null;
|
|
23285
23729
|
}
|
|
23286
23730
|
}
|
|
23287
23731
|
async function deliver(event, repository) {
|
|
23288
23732
|
const directory = observerDirectory();
|
|
23289
|
-
const endpoint = readObserverEndpoint(
|
|
23290
|
-
const authToken = readObserverAuthToken(
|
|
23733
|
+
const endpoint = readObserverEndpoint(path22.join(directory, "endpoint.json"));
|
|
23734
|
+
const authToken = readObserverAuthToken(path22.join(directory, "auth-token"));
|
|
23291
23735
|
if (!endpoint || !authToken) return;
|
|
23292
23736
|
const host = endpoint.host === "::1" ? "[::1]" : endpoint.host;
|
|
23293
23737
|
try {
|
|
@@ -23299,7 +23743,10 @@ async function deliver(event, repository) {
|
|
|
23299
23743
|
},
|
|
23300
23744
|
body: JSON.stringify({
|
|
23301
23745
|
target: "pi",
|
|
23302
|
-
payload:
|
|
23746
|
+
payload: {
|
|
23747
|
+
...event,
|
|
23748
|
+
[GIT_STATE_PAYLOAD_KEY]: event.cwd ? resolveGitStateFromCwd(event.cwd, 250) : null
|
|
23749
|
+
},
|
|
23303
23750
|
...event.cwd ? { cwd: event.cwd } : {},
|
|
23304
23751
|
...repository ? { repository } : {}
|
|
23305
23752
|
}),
|
|
@@ -23357,16 +23804,16 @@ var RECEIPT_GRAPHEME_SEGMENTER = new Intl.Segmenter(void 0, {
|
|
|
23357
23804
|
});
|
|
23358
23805
|
function wrapReceiptLine(text, width) {
|
|
23359
23806
|
if (width < 1) return [""];
|
|
23360
|
-
let
|
|
23361
|
-
if (!
|
|
23807
|
+
let remaining2 = text.trim();
|
|
23808
|
+
if (!remaining2) return [""];
|
|
23362
23809
|
const lines = [];
|
|
23363
|
-
while (
|
|
23810
|
+
while (remaining2) {
|
|
23364
23811
|
let line = "";
|
|
23365
23812
|
let lineWidth = 0;
|
|
23366
23813
|
let end = 0;
|
|
23367
23814
|
let whitespaceEnd = 0;
|
|
23368
23815
|
for (const { segment, index } of RECEIPT_GRAPHEME_SEGMENTER.segment(
|
|
23369
|
-
|
|
23816
|
+
remaining2
|
|
23370
23817
|
)) {
|
|
23371
23818
|
const segmentWidth = receiptDisplayWidth(segment);
|
|
23372
23819
|
if (segmentWidth > width) {
|
|
@@ -23381,16 +23828,16 @@ function wrapReceiptLine(text, width) {
|
|
|
23381
23828
|
end = index + segment.length;
|
|
23382
23829
|
if (/^\s$/u.test(segment)) whitespaceEnd = end;
|
|
23383
23830
|
}
|
|
23384
|
-
if (end ===
|
|
23831
|
+
if (end === remaining2.length) {
|
|
23385
23832
|
lines.push(line.trimEnd());
|
|
23386
23833
|
break;
|
|
23387
23834
|
}
|
|
23388
23835
|
if (whitespaceEnd > 0) {
|
|
23389
|
-
lines.push(
|
|
23390
|
-
|
|
23836
|
+
lines.push(remaining2.slice(0, whitespaceEnd).trimEnd());
|
|
23837
|
+
remaining2 = remaining2.slice(whitespaceEnd).trimStart();
|
|
23391
23838
|
} else {
|
|
23392
23839
|
lines.push(line);
|
|
23393
|
-
|
|
23840
|
+
remaining2 = remaining2.slice(end);
|
|
23394
23841
|
}
|
|
23395
23842
|
}
|
|
23396
23843
|
return lines;
|
|
@@ -23430,7 +23877,7 @@ var PiReceiptComponent = class {
|
|
|
23430
23877
|
}
|
|
23431
23878
|
};
|
|
23432
23879
|
function fmlPiExtension(pi) {
|
|
23433
|
-
let sessionId =
|
|
23880
|
+
let sessionId = randomUUID5();
|
|
23434
23881
|
let initializedSessionId = null;
|
|
23435
23882
|
let cwd;
|
|
23436
23883
|
let repository = null;
|
|
@@ -23555,7 +24002,7 @@ function fmlPiExtension(pi) {
|
|
|
23555
24002
|
}
|
|
23556
24003
|
repository = null;
|
|
23557
24004
|
try {
|
|
23558
|
-
repository = cwd ?
|
|
24005
|
+
repository = cwd ? resolveGitStateFromCwd(cwd, 250)?.repository ?? null : null;
|
|
23559
24006
|
} catch {
|
|
23560
24007
|
}
|
|
23561
24008
|
if (newIdentity && evidence?.status === "absent" && (event.reason === "startup" || event.reason === "new" || event.reason === "resume") && entries.every(
|
|
@@ -23564,7 +24011,8 @@ function fmlPiExtension(pi) {
|
|
|
23564
24011
|
const origin = PiRepositoryOriginDataSchema.safeParse({
|
|
23565
24012
|
version: 1,
|
|
23566
24013
|
repository,
|
|
23567
|
-
cwd
|
|
24014
|
+
cwd,
|
|
24015
|
+
gitState: cwd ? resolveGitStateFromCwd(cwd, 250) : null
|
|
23568
24016
|
});
|
|
23569
24017
|
if (origin.success) {
|
|
23570
24018
|
capturedRepository = origin.data.repository;
|