@fml-inc/fml 0.8.13-rc.1 → 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 +42 -0
- package/dist/chunk-3RKICLMI.js +34 -0
- package/dist/{chunk-HVD2GMN3.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-U7SITLCL.js → chunk-LYKMU7QR.js} +185 -301
- package/dist/{chunk-3VN2SQTP.js → chunk-PVOZYFMP.js} +1 -1
- package/dist/cli.js +10 -9
- 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 +788 -343
- 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,16 +1612,508 @@ 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";
|
|
1620
2112
|
|
|
1621
2113
|
// ../../packages/agent-targets/src/pi-agent-dir.ts
|
|
1622
|
-
import
|
|
2114
|
+
import path2 from "node:path";
|
|
1623
2115
|
function resolvePiAgentDir(context) {
|
|
1624
|
-
const pathApi = (context.platform ?? process.platform) === "win32" ?
|
|
2116
|
+
const pathApi = (context.platform ?? process.platform) === "win32" ? path2.win32 : path2.posix;
|
|
1625
2117
|
const configured = context.env.PI_CODING_AGENT_DIR?.trim();
|
|
1626
2118
|
if (!configured) {
|
|
1627
2119
|
return pathApi.join(context.homeDir, ".pi", "agent");
|
|
@@ -1634,23 +2126,23 @@ function resolvePiAgentDir(context) {
|
|
|
1634
2126
|
}
|
|
1635
2127
|
|
|
1636
2128
|
// ../../packages/agent-targets/src/artifacts/targets/claude.ts
|
|
1637
|
-
import
|
|
2129
|
+
import path5 from "node:path";
|
|
1638
2130
|
|
|
1639
2131
|
// ../../packages/agent-targets/src/config-paths.ts
|
|
1640
|
-
import
|
|
2132
|
+
import path3 from "node:path";
|
|
1641
2133
|
function claudeConfigDirectory(context) {
|
|
1642
2134
|
const configuredDir = context.claudeConfigDir ?? context.env?.CLAUDE_CONFIG_DIR;
|
|
1643
|
-
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");
|
|
1644
2136
|
}
|
|
1645
2137
|
function geminiConfigDirectory(context) {
|
|
1646
2138
|
const configuredHome = context.env.GEMINI_CLI_HOME;
|
|
1647
|
-
const homeRoot = typeof configuredHome === "string" && configuredHome.trim().length > 0 ?
|
|
1648
|
-
return
|
|
2139
|
+
const homeRoot = typeof configuredHome === "string" && configuredHome.trim().length > 0 ? path3.resolve(configuredHome) : context.homeDir;
|
|
2140
|
+
return path3.join(homeRoot, ".gemini");
|
|
1649
2141
|
}
|
|
1650
2142
|
|
|
1651
2143
|
// ../../packages/agent-targets/src/artifacts/filesystem.ts
|
|
1652
|
-
import
|
|
1653
|
-
import
|
|
2144
|
+
import fs2 from "node:fs";
|
|
2145
|
+
import path4 from "node:path";
|
|
1654
2146
|
var DEFAULT_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
1655
2147
|
"node_modules",
|
|
1656
2148
|
"dist",
|
|
@@ -1667,11 +2159,11 @@ var DEFAULT_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
1667
2159
|
"designqc-captures"
|
|
1668
2160
|
]);
|
|
1669
2161
|
function normalizeFilePath(filePath) {
|
|
1670
|
-
return
|
|
2162
|
+
return path4.resolve(filePath);
|
|
1671
2163
|
}
|
|
1672
2164
|
function safeReaddir(dir) {
|
|
1673
2165
|
try {
|
|
1674
|
-
return
|
|
2166
|
+
return fs2.readdirSync(dir, { withFileTypes: true });
|
|
1675
2167
|
} catch {
|
|
1676
2168
|
return [];
|
|
1677
2169
|
}
|
|
@@ -1684,7 +2176,7 @@ function walkFiles(input2) {
|
|
|
1684
2176
|
function walk(dir, depth) {
|
|
1685
2177
|
if (depth > input2.maxDepth) return;
|
|
1686
2178
|
for (const entry of safeReaddir(dir)) {
|
|
1687
|
-
const entryPath =
|
|
2179
|
+
const entryPath = path4.join(dir, entry.name);
|
|
1688
2180
|
if (entry.isDirectory()) {
|
|
1689
2181
|
if (!skipDirs.has(entry.name)) walk(entryPath, depth + 1);
|
|
1690
2182
|
continue;
|
|
@@ -1720,7 +2212,7 @@ function transcriptCandidate(input2) {
|
|
|
1720
2212
|
}
|
|
1721
2213
|
|
|
1722
2214
|
// ../../packages/agent-targets/src/artifacts/harness-install-id.ts
|
|
1723
|
-
import
|
|
2215
|
+
import fs3 from "node:fs";
|
|
1724
2216
|
var MAX_HARNESS_INSTALL_ID_LENGTH = 128;
|
|
1725
2217
|
function normalizeHarnessInstallId(value) {
|
|
1726
2218
|
if (typeof value !== "string") return null;
|
|
@@ -1731,7 +2223,7 @@ function normalizeHarnessInstallId(value) {
|
|
|
1731
2223
|
}
|
|
1732
2224
|
function readHarnessInstallIdFile(filePath) {
|
|
1733
2225
|
try {
|
|
1734
|
-
return normalizeHarnessInstallId(
|
|
2226
|
+
return normalizeHarnessInstallId(fs3.readFileSync(filePath, "utf8"));
|
|
1735
2227
|
} catch {
|
|
1736
2228
|
return null;
|
|
1737
2229
|
}
|
|
@@ -1740,7 +2232,7 @@ var claudeMachineIdCache = /* @__PURE__ */ new Map();
|
|
|
1740
2232
|
function readClaudeMachineId(configFile) {
|
|
1741
2233
|
let stat;
|
|
1742
2234
|
try {
|
|
1743
|
-
stat =
|
|
2235
|
+
stat = fs3.statSync(configFile);
|
|
1744
2236
|
} catch {
|
|
1745
2237
|
claudeMachineIdCache.delete(configFile);
|
|
1746
2238
|
return null;
|
|
@@ -1751,7 +2243,7 @@ function readClaudeMachineId(configFile) {
|
|
|
1751
2243
|
}
|
|
1752
2244
|
let machineId = null;
|
|
1753
2245
|
try {
|
|
1754
|
-
const parsed = JSON.parse(
|
|
2246
|
+
const parsed = JSON.parse(fs3.readFileSync(configFile, "utf8"));
|
|
1755
2247
|
if (parsed && typeof parsed === "object") {
|
|
1756
2248
|
machineId = normalizeHarnessInstallId(
|
|
1757
2249
|
parsed.machineID
|
|
@@ -1769,55 +2261,8 @@ function readClaudeMachineId(configFile) {
|
|
|
1769
2261
|
}
|
|
1770
2262
|
|
|
1771
2263
|
// ../../packages/agent-targets/src/artifacts/repository-context.ts
|
|
1772
|
-
import { createHash } from "node:crypto";
|
|
1773
|
-
import
|
|
1774
|
-
|
|
1775
|
-
// ../../packages/shared/src/github-repository.ts
|
|
1776
|
-
var MAX_REPOSITORY_BYTES = 201;
|
|
1777
|
-
var UTF8_ENCODER = new TextEncoder();
|
|
1778
|
-
function utf8Bytes(value) {
|
|
1779
|
-
return UTF8_ENCODER.encode(value).byteLength;
|
|
1780
|
-
}
|
|
1781
|
-
function githubRepositoryPath(pathname) {
|
|
1782
|
-
const parts = pathname.replace(/^\/+|\/+$/g, "").split("/");
|
|
1783
|
-
if (parts.length !== 2) return null;
|
|
1784
|
-
const owner = parts[0];
|
|
1785
|
-
const repository = parts[1]?.replace(/\.git$/i, "");
|
|
1786
|
-
if (!owner || !repository) return null;
|
|
1787
|
-
const segmentPattern = /^[A-Za-z0-9_.-]+$/;
|
|
1788
|
-
if (!segmentPattern.test(owner) || !segmentPattern.test(repository)) {
|
|
1789
|
-
return null;
|
|
1790
|
-
}
|
|
1791
|
-
const canonical = `${owner.toLowerCase()}/${repository.toLowerCase()}`;
|
|
1792
|
-
if (canonical.startsWith("./") || canonical.startsWith("../") || repository === "." || repository === ".." || repository.toLowerCase().endsWith(".git")) {
|
|
1793
|
-
return null;
|
|
1794
|
-
}
|
|
1795
|
-
return utf8Bytes(canonical) <= MAX_REPOSITORY_BYTES ? canonical : null;
|
|
1796
|
-
}
|
|
1797
|
-
function canonicalGitHubRepository(remote) {
|
|
1798
|
-
const trimmed = remote.trim();
|
|
1799
|
-
if (!trimmed || trimmed.includes("\0")) return null;
|
|
1800
|
-
const scp = trimmed.match(/^git@github\.com:([^?#]+)$/i);
|
|
1801
|
-
if (scp?.[1]) return githubRepositoryPath(scp[1]);
|
|
1802
|
-
let url2;
|
|
1803
|
-
try {
|
|
1804
|
-
url2 = new URL(trimmed);
|
|
1805
|
-
} catch {
|
|
1806
|
-
return null;
|
|
1807
|
-
}
|
|
1808
|
-
if (url2.hostname.toLowerCase() !== "github.com") return null;
|
|
1809
|
-
if (url2.search || url2.hash || url2.password || url2.port) return null;
|
|
1810
|
-
if (url2.protocol === "https:" || url2.protocol === "http:") {
|
|
1811
|
-
if (url2.username) return null;
|
|
1812
|
-
} else if (url2.protocol === "ssh:") {
|
|
1813
|
-
if (url2.username && url2.username !== "git") return null;
|
|
1814
|
-
} else {
|
|
1815
|
-
return null;
|
|
1816
|
-
}
|
|
1817
|
-
return githubRepositoryPath(url2.pathname);
|
|
1818
|
-
}
|
|
1819
|
-
|
|
1820
|
-
// ../../packages/agent-targets/src/artifacts/repository-context.ts
|
|
2264
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
2265
|
+
import fs4 from "node:fs";
|
|
1821
2266
|
var MAX_ARTIFACT_CONTEXT_PREFIX_BYTES = 256 * 1024;
|
|
1822
2267
|
var MAX_ARTIFACT_CONTEXT_RECORDS = 64;
|
|
1823
2268
|
var ARTIFACT_CONTEXT_READ_CHUNK_BYTES = 32 * 1024;
|
|
@@ -1840,12 +2285,12 @@ function recordValue(value) {
|
|
|
1840
2285
|
function readJsonlContextRecords(sourcePath) {
|
|
1841
2286
|
let file2;
|
|
1842
2287
|
try {
|
|
1843
|
-
file2 =
|
|
2288
|
+
file2 = fs4.openSync(sourcePath, "r");
|
|
1844
2289
|
} catch {
|
|
1845
2290
|
return [];
|
|
1846
2291
|
}
|
|
1847
2292
|
try {
|
|
1848
|
-
const stat =
|
|
2293
|
+
const stat = fs4.fstatSync(file2);
|
|
1849
2294
|
const byteLimit = Math.min(stat.size, MAX_ARTIFACT_CONTEXT_PREFIX_BYTES);
|
|
1850
2295
|
if (byteLimit <= 0) return [];
|
|
1851
2296
|
const records = [];
|
|
@@ -1863,7 +2308,7 @@ function readJsonlContextRecords(sourcePath) {
|
|
|
1863
2308
|
const chunk = Buffer.allocUnsafe(ARTIFACT_CONTEXT_READ_CHUNK_BYTES);
|
|
1864
2309
|
while (position < byteLimit && records.length < MAX_ARTIFACT_CONTEXT_RECORDS) {
|
|
1865
2310
|
const want = Math.min(chunk.length, byteLimit - position);
|
|
1866
|
-
const bytesRead =
|
|
2311
|
+
const bytesRead = fs4.readSync(file2, chunk, 0, want, position);
|
|
1867
2312
|
if (bytesRead <= 0) break;
|
|
1868
2313
|
position += bytesRead;
|
|
1869
2314
|
const buffered = Buffer.concat([carry, chunk.subarray(0, bytesRead)]);
|
|
@@ -1886,7 +2331,7 @@ function readJsonlContextRecords(sourcePath) {
|
|
|
1886
2331
|
} catch {
|
|
1887
2332
|
return [];
|
|
1888
2333
|
} finally {
|
|
1889
|
-
|
|
2334
|
+
fs4.closeSync(file2);
|
|
1890
2335
|
}
|
|
1891
2336
|
}
|
|
1892
2337
|
function contextTimestampMs(value, fallback) {
|
|
@@ -1914,7 +2359,7 @@ function repositoryObservation(input2) {
|
|
|
1914
2359
|
branch ?? null,
|
|
1915
2360
|
commitHash ?? null
|
|
1916
2361
|
]);
|
|
1917
|
-
const id = `repo_observation:${
|
|
2362
|
+
const id = `repo_observation:${createHash2("sha256").update(identity).digest("hex")}`;
|
|
1918
2363
|
return {
|
|
1919
2364
|
id,
|
|
1920
2365
|
repository: input2.repository,
|
|
@@ -1925,14 +2370,29 @@ function repositoryObservation(input2) {
|
|
|
1925
2370
|
...commitHash ? { commitHash } : {}
|
|
1926
2371
|
};
|
|
1927
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
|
+
}
|
|
1928
2385
|
|
|
1929
2386
|
// ../../packages/agent-targets/src/artifacts/targets/claude.ts
|
|
1930
2387
|
function claudeTranscriptCandidate(sourcePath, context, harnessInstallId) {
|
|
1931
2388
|
const fallbackObservedAtMs = (context.nowMs ?? Date.now)();
|
|
2389
|
+
const states = /* @__PURE__ */ new Map();
|
|
1932
2390
|
const observations = /* @__PURE__ */ new Map();
|
|
1933
2391
|
for (const record2 of readJsonlContextRecords(sourcePath)) {
|
|
1934
2392
|
if (typeof record2.cwd !== "string" || !record2.cwd.trim()) continue;
|
|
1935
2393
|
const origin = context.resolveGitHubOrigin?.(record2.cwd);
|
|
2394
|
+
const state = origin?.gitState ?? context.resolveGitState?.(record2.cwd);
|
|
2395
|
+
if (state) states.set(state.worktreeRoot, state);
|
|
1936
2396
|
if (!origin) continue;
|
|
1937
2397
|
const observation = repositoryObservation({
|
|
1938
2398
|
repository: origin.repository,
|
|
@@ -1947,7 +2407,10 @@ function claudeTranscriptCandidate(sourcePath, context, harnessInstallId) {
|
|
|
1947
2407
|
return transcriptCandidate({
|
|
1948
2408
|
targetId: "claude",
|
|
1949
2409
|
sourcePath,
|
|
1950
|
-
metadata:
|
|
2410
|
+
metadata: {
|
|
2411
|
+
...harnessInstallId ? { harnessInstallId } : {},
|
|
2412
|
+
...localGitStateMetadata([...states.values()])
|
|
2413
|
+
},
|
|
1951
2414
|
repositoryObservations: [...observations.values()]
|
|
1952
2415
|
});
|
|
1953
2416
|
}
|
|
@@ -1956,9 +2419,9 @@ var claudeArtifactDiscovery = Object.freeze({
|
|
|
1956
2419
|
discover: (context) => {
|
|
1957
2420
|
const { homeDir, env } = context;
|
|
1958
2421
|
const configDir = claudeConfigDirectory({ homeDir, env });
|
|
1959
|
-
const harnessInstallId = readClaudeMachineId(
|
|
2422
|
+
const harnessInstallId = readClaudeMachineId(path5.join(configDir, ".claude.json")) ?? readClaudeMachineId(path5.join(homeDir, ".claude.json"));
|
|
1960
2423
|
return walkFiles({
|
|
1961
|
-
root:
|
|
2424
|
+
root: path5.join(configDir, "projects"),
|
|
1962
2425
|
maxDepth: 6,
|
|
1963
2426
|
includeFile: (filePath) => filePath.endsWith(".jsonl")
|
|
1964
2427
|
}).map(
|
|
@@ -1968,10 +2431,10 @@ var claudeArtifactDiscovery = Object.freeze({
|
|
|
1968
2431
|
});
|
|
1969
2432
|
|
|
1970
2433
|
// ../../packages/agent-targets/src/artifacts/targets/codex.ts
|
|
1971
|
-
import
|
|
2434
|
+
import path6 from "node:path";
|
|
1972
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;
|
|
1973
2436
|
function codexRolloutId(sourcePath) {
|
|
1974
|
-
return CODEX_ROLLOUT_ID.exec(
|
|
2437
|
+
return CODEX_ROLLOUT_ID.exec(path6.basename(sourcePath))?.[1]?.toLowerCase() ?? null;
|
|
1975
2438
|
}
|
|
1976
2439
|
function codexTranscriptCandidate(sourcePath, readPath, store, context, harnessInstallId) {
|
|
1977
2440
|
const rolloutId = codexRolloutId(sourcePath);
|
|
@@ -1984,9 +2447,10 @@ function codexTranscriptCandidate(sourcePath, readPath, store, context, harnessI
|
|
|
1984
2447
|
if (!payload) continue;
|
|
1985
2448
|
const sessionId = typeof payload.id === "string" && payload.id.trim() ? payload.id.trim() : null;
|
|
1986
2449
|
const cwd = typeof payload.cwd === "string" ? payload.cwd : void 0;
|
|
1987
|
-
const
|
|
1988
|
-
const nativeRepository = typeof
|
|
2450
|
+
const git = recordValue(payload.git);
|
|
2451
|
+
const nativeRepository = typeof git?.repository_url === "string" ? canonicalGitHubRepository(git.repository_url) : null;
|
|
1989
2452
|
const origin = cwd ? context.resolveGitHubOrigin?.(cwd) : null;
|
|
2453
|
+
const state = origin?.gitState ?? (cwd ? context.resolveGitState?.(cwd) : null);
|
|
1990
2454
|
const observations = [];
|
|
1991
2455
|
if (nativeRepository) {
|
|
1992
2456
|
observations.push(
|
|
@@ -1998,8 +2462,8 @@ function codexTranscriptCandidate(sourcePath, readPath, store, context, harnessI
|
|
|
1998
2462
|
fallbackObservedAtMs
|
|
1999
2463
|
),
|
|
2000
2464
|
cwd,
|
|
2001
|
-
branch:
|
|
2002
|
-
commitHash:
|
|
2465
|
+
branch: git?.branch,
|
|
2466
|
+
commitHash: git?.commit_hash
|
|
2003
2467
|
})
|
|
2004
2468
|
);
|
|
2005
2469
|
}
|
|
@@ -2021,7 +2485,11 @@ function codexTranscriptCandidate(sourcePath, readPath, store, context, harnessI
|
|
|
2021
2485
|
identityKey,
|
|
2022
2486
|
readPath,
|
|
2023
2487
|
sessionId,
|
|
2024
|
-
metadata: {
|
|
2488
|
+
metadata: {
|
|
2489
|
+
store,
|
|
2490
|
+
...identityMetadata,
|
|
2491
|
+
...localGitStateMetadata([state])
|
|
2492
|
+
},
|
|
2025
2493
|
repositoryObservations: observations
|
|
2026
2494
|
});
|
|
2027
2495
|
}
|
|
@@ -2041,15 +2509,15 @@ var codexArtifactDiscovery = Object.freeze({
|
|
|
2041
2509
|
discover: (context) => {
|
|
2042
2510
|
const { homeDir, env } = context;
|
|
2043
2511
|
const configuredHome = env.CODEX_HOME?.trim();
|
|
2044
|
-
const codexHome = configuredHome ?
|
|
2512
|
+
const codexHome = configuredHome ? path6.resolve(configuredHome) : path6.join(homeDir, ".codex");
|
|
2045
2513
|
const harnessInstallId = readHarnessInstallIdFile(
|
|
2046
|
-
|
|
2514
|
+
path6.join(codexHome, "installation_id")
|
|
2047
2515
|
);
|
|
2048
2516
|
const roots = [
|
|
2049
|
-
{ store: "sessions", root:
|
|
2517
|
+
{ store: "sessions", root: path6.join(codexHome, "sessions") },
|
|
2050
2518
|
{
|
|
2051
2519
|
store: "archived_sessions",
|
|
2052
|
-
root:
|
|
2520
|
+
root: path6.join(codexHome, "archived_sessions")
|
|
2053
2521
|
}
|
|
2054
2522
|
];
|
|
2055
2523
|
const files = roots.flatMap(
|
|
@@ -2087,27 +2555,27 @@ var codexArtifactDiscovery = Object.freeze({
|
|
|
2087
2555
|
});
|
|
2088
2556
|
|
|
2089
2557
|
// ../../packages/agent-targets/src/artifacts/targets/copilot-cli.ts
|
|
2090
|
-
import
|
|
2558
|
+
import path7 from "node:path";
|
|
2091
2559
|
var copilotCliArtifactDiscovery = Object.freeze({
|
|
2092
2560
|
targetId: "copilot-cli",
|
|
2093
2561
|
discover: ({ homeDir }) => walkFiles({
|
|
2094
|
-
root:
|
|
2562
|
+
root: path7.join(homeDir, ".copilot", "session-state"),
|
|
2095
2563
|
maxDepth: 3,
|
|
2096
|
-
includeFile: (filePath) =>
|
|
2564
|
+
includeFile: (filePath) => path7.basename(filePath) === "events.jsonl"
|
|
2097
2565
|
}).map(
|
|
2098
2566
|
(sourcePath) => transcriptCandidate({ targetId: "copilot-cli", sourcePath })
|
|
2099
2567
|
)
|
|
2100
2568
|
});
|
|
2101
2569
|
|
|
2102
2570
|
// ../../packages/agent-targets/src/artifacts/targets/cursor.ts
|
|
2103
|
-
import
|
|
2571
|
+
import path8 from "node:path";
|
|
2104
2572
|
function hasPathSegment(filePath, segment) {
|
|
2105
|
-
return normalizeFilePath(filePath).split(
|
|
2573
|
+
return normalizeFilePath(filePath).split(path8.sep).includes(segment);
|
|
2106
2574
|
}
|
|
2107
2575
|
var cursorArtifactDiscovery = Object.freeze({
|
|
2108
2576
|
targetId: "cursor",
|
|
2109
2577
|
discover: ({ homeDir }) => walkFiles({
|
|
2110
|
-
root:
|
|
2578
|
+
root: path8.join(homeDir, ".cursor", "projects"),
|
|
2111
2579
|
maxDepth: 5,
|
|
2112
2580
|
includeFile: (filePath) => filePath.endsWith(".jsonl") && hasPathSegment(filePath, "agent-transcripts")
|
|
2113
2581
|
}).map(
|
|
@@ -2116,11 +2584,11 @@ var cursorArtifactDiscovery = Object.freeze({
|
|
|
2116
2584
|
});
|
|
2117
2585
|
|
|
2118
2586
|
// ../../packages/agent-targets/src/artifacts/targets/factory-droid.ts
|
|
2119
|
-
import
|
|
2587
|
+
import path9 from "node:path";
|
|
2120
2588
|
var factoryDroidArtifactDiscovery = Object.freeze({
|
|
2121
2589
|
targetId: "factory-droid",
|
|
2122
2590
|
discover: ({ homeDir }) => walkFiles({
|
|
2123
|
-
root:
|
|
2591
|
+
root: path9.join(homeDir, ".factory", "sessions"),
|
|
2124
2592
|
maxDepth: 4,
|
|
2125
2593
|
includeFile: (filePath) => filePath.endsWith(".jsonl")
|
|
2126
2594
|
}).map(
|
|
@@ -2129,7 +2597,7 @@ var factoryDroidArtifactDiscovery = Object.freeze({
|
|
|
2129
2597
|
});
|
|
2130
2598
|
|
|
2131
2599
|
// ../../packages/agent-targets/src/artifacts/targets/gemini.ts
|
|
2132
|
-
import
|
|
2600
|
+
import path10 from "node:path";
|
|
2133
2601
|
var GEMINI_DISCOVER_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
2134
2602
|
"bin",
|
|
2135
2603
|
"checkpoints",
|
|
@@ -2144,15 +2612,15 @@ var geminiArtifactDiscovery = Object.freeze({
|
|
|
2144
2612
|
discover: ({ homeDir, env }) => {
|
|
2145
2613
|
const geminiDir = geminiConfigDirectory({ homeDir, env });
|
|
2146
2614
|
const harnessInstallId = readHarnessInstallIdFile(
|
|
2147
|
-
|
|
2615
|
+
path10.join(geminiDir, "installation_id")
|
|
2148
2616
|
);
|
|
2149
|
-
const root =
|
|
2617
|
+
const root = path10.join(geminiDir, "tmp");
|
|
2150
2618
|
const files = [];
|
|
2151
2619
|
const seen = /* @__PURE__ */ new Set();
|
|
2152
2620
|
function walk(dir, depth) {
|
|
2153
2621
|
if (depth > 4) return;
|
|
2154
2622
|
for (const entry of safeReaddir(dir)) {
|
|
2155
|
-
const entryPath =
|
|
2623
|
+
const entryPath = path10.join(dir, entry.name);
|
|
2156
2624
|
if (!entry.isDirectory()) continue;
|
|
2157
2625
|
if (GEMINI_DISCOVER_SKIP_DIRS.has(entry.name)) continue;
|
|
2158
2626
|
if (entry.name === "chats") {
|
|
@@ -2161,7 +2629,7 @@ var geminiArtifactDiscovery = Object.freeze({
|
|
|
2161
2629
|
continue;
|
|
2162
2630
|
}
|
|
2163
2631
|
const sourcePath = normalizeFilePath(
|
|
2164
|
-
|
|
2632
|
+
path10.join(entryPath, chatEntry.name)
|
|
2165
2633
|
);
|
|
2166
2634
|
if (seen.has(sourcePath)) continue;
|
|
2167
2635
|
seen.add(sourcePath);
|
|
@@ -2184,52 +2652,52 @@ var geminiArtifactDiscovery = Object.freeze({
|
|
|
2184
2652
|
});
|
|
2185
2653
|
|
|
2186
2654
|
// ../../packages/agent-targets/src/artifacts/targets/grok.ts
|
|
2187
|
-
import
|
|
2655
|
+
import path11 from "node:path";
|
|
2188
2656
|
function grokHome(homeDir, env) {
|
|
2189
2657
|
const configuredHome = env.GROK_HOME?.trim();
|
|
2190
|
-
return configuredHome ?
|
|
2658
|
+
return configuredHome ? path11.resolve(configuredHome) : path11.join(homeDir, ".grok");
|
|
2191
2659
|
}
|
|
2192
2660
|
var grokArtifactDiscovery = Object.freeze({
|
|
2193
2661
|
targetId: "grok",
|
|
2194
2662
|
discover: ({ homeDir, env }) => walkFiles({
|
|
2195
|
-
root:
|
|
2663
|
+
root: path11.join(grokHome(homeDir, env), "sessions"),
|
|
2196
2664
|
maxDepth: 5,
|
|
2197
|
-
includeFile: (filePath) =>
|
|
2665
|
+
includeFile: (filePath) => path11.basename(filePath) === "updates.jsonl"
|
|
2198
2666
|
}).map(
|
|
2199
2667
|
(sourcePath) => transcriptCandidate({ targetId: "grok", sourcePath })
|
|
2200
2668
|
)
|
|
2201
2669
|
});
|
|
2202
2670
|
|
|
2203
2671
|
// ../../packages/agent-targets/src/artifacts/targets/mini-swe-agent.ts
|
|
2204
|
-
import
|
|
2205
|
-
import
|
|
2672
|
+
import fs5 from "node:fs";
|
|
2673
|
+
import path12 from "node:path";
|
|
2206
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;
|
|
2207
2675
|
function dataDirectory({ homeDir, env }) {
|
|
2208
2676
|
if (env.FML_DATA_DIR?.trim()) {
|
|
2209
|
-
if (!
|
|
2677
|
+
if (!path12.isAbsolute(env.FML_DATA_DIR)) {
|
|
2210
2678
|
throw new Error("FML_DATA_DIR must be an absolute path");
|
|
2211
2679
|
}
|
|
2212
|
-
return
|
|
2680
|
+
return path12.normalize(env.FML_DATA_DIR);
|
|
2213
2681
|
}
|
|
2214
2682
|
if (process.platform === "darwin") {
|
|
2215
|
-
return
|
|
2683
|
+
return path12.join(homeDir, "Library", "Application Support", "fml");
|
|
2216
2684
|
}
|
|
2217
2685
|
if (process.platform === "win32") {
|
|
2218
|
-
const roaming = env.APPDATA &&
|
|
2219
|
-
return
|
|
2686
|
+
const roaming = env.APPDATA && path12.isAbsolute(env.APPDATA) ? env.APPDATA : path12.join(homeDir, "AppData", "Roaming");
|
|
2687
|
+
return path12.join(roaming, "fml");
|
|
2220
2688
|
}
|
|
2221
|
-
const xdgDataHome = env.XDG_DATA_HOME &&
|
|
2222
|
-
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");
|
|
2223
2691
|
}
|
|
2224
2692
|
function snapshotCandidate(sourcePath, context) {
|
|
2225
2693
|
let file2;
|
|
2226
2694
|
try {
|
|
2227
|
-
file2 =
|
|
2695
|
+
file2 = fs5.openSync(sourcePath, "r");
|
|
2228
2696
|
} catch {
|
|
2229
2697
|
return null;
|
|
2230
2698
|
}
|
|
2231
2699
|
try {
|
|
2232
|
-
const stat =
|
|
2700
|
+
const stat = fs5.fstatSync(file2);
|
|
2233
2701
|
if (!stat.isFile()) return null;
|
|
2234
2702
|
if (context.maxFileBytes !== void 0 && stat.size > context.maxFileBytes) {
|
|
2235
2703
|
return transcriptCandidate({ targetId: "mini-swe-agent", sourcePath });
|
|
@@ -2237,7 +2705,7 @@ function snapshotCandidate(sourcePath, context) {
|
|
|
2237
2705
|
const bytes = Buffer.allocUnsafe(stat.size);
|
|
2238
2706
|
let offset = 0;
|
|
2239
2707
|
while (offset < bytes.length) {
|
|
2240
|
-
const read =
|
|
2708
|
+
const read = fs5.readSync(
|
|
2241
2709
|
file2,
|
|
2242
2710
|
bytes,
|
|
2243
2711
|
offset,
|
|
@@ -2247,7 +2715,7 @@ function snapshotCandidate(sourcePath, context) {
|
|
|
2247
2715
|
if (read <= 0) return null;
|
|
2248
2716
|
offset += read;
|
|
2249
2717
|
}
|
|
2250
|
-
if (
|
|
2718
|
+
if (fs5.fstatSync(file2).size !== stat.size) return null;
|
|
2251
2719
|
const value = JSON.parse(bytes.toString("utf8"));
|
|
2252
2720
|
if (typeof value !== "object" || value === null || !("fml" in value)) {
|
|
2253
2721
|
return null;
|
|
@@ -2277,13 +2745,13 @@ function snapshotCandidate(sourcePath, context) {
|
|
|
2277
2745
|
} catch {
|
|
2278
2746
|
return null;
|
|
2279
2747
|
} finally {
|
|
2280
|
-
|
|
2748
|
+
fs5.closeSync(file2);
|
|
2281
2749
|
}
|
|
2282
2750
|
}
|
|
2283
2751
|
var miniSweAgentArtifactDiscovery = Object.freeze({
|
|
2284
2752
|
targetId: "mini-swe-agent",
|
|
2285
2753
|
discover: (context) => walkFiles({
|
|
2286
|
-
root:
|
|
2754
|
+
root: path12.join(dataDirectory(context), "mini-swe-agent", "trajectories"),
|
|
2287
2755
|
maxDepth: 0,
|
|
2288
2756
|
includeFile: (filePath) => filePath.endsWith(".traj.json")
|
|
2289
2757
|
}).flatMap((sourcePath) => {
|
|
@@ -2293,7 +2761,7 @@ var miniSweAgentArtifactDiscovery = Object.freeze({
|
|
|
2293
2761
|
});
|
|
2294
2762
|
|
|
2295
2763
|
// ../../packages/agent-targets/src/artifacts/targets/pi.ts
|
|
2296
|
-
import
|
|
2764
|
+
import path13 from "node:path";
|
|
2297
2765
|
|
|
2298
2766
|
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/external.js
|
|
2299
2767
|
var external_exports = {};
|
|
@@ -3112,10 +3580,10 @@ function mergeDefs(...defs) {
|
|
|
3112
3580
|
function cloneDef(schema) {
|
|
3113
3581
|
return mergeDefs(schema._zod.def);
|
|
3114
3582
|
}
|
|
3115
|
-
function getElementAtPath(obj,
|
|
3116
|
-
if (!
|
|
3583
|
+
function getElementAtPath(obj, path23) {
|
|
3584
|
+
if (!path23)
|
|
3117
3585
|
return obj;
|
|
3118
|
-
return
|
|
3586
|
+
return path23.reduce((acc, key) => acc?.[key], obj);
|
|
3119
3587
|
}
|
|
3120
3588
|
function promiseAllObject(promisesObj) {
|
|
3121
3589
|
const keys = Object.keys(promisesObj);
|
|
@@ -3455,11 +3923,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
3455
3923
|
}
|
|
3456
3924
|
return false;
|
|
3457
3925
|
}
|
|
3458
|
-
function prefixIssues(
|
|
3926
|
+
function prefixIssues(path23, issues) {
|
|
3459
3927
|
return issues.map((iss) => {
|
|
3460
3928
|
var _a3;
|
|
3461
3929
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
3462
|
-
iss.path.unshift(
|
|
3930
|
+
iss.path.unshift(path23);
|
|
3463
3931
|
return iss;
|
|
3464
3932
|
});
|
|
3465
3933
|
}
|
|
@@ -3921,16 +4389,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
|
|
|
3921
4389
|
}
|
|
3922
4390
|
function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
3923
4391
|
const fieldErrors = { _errors: [] };
|
|
3924
|
-
const processError = (error63,
|
|
4392
|
+
const processError = (error63, path23 = []) => {
|
|
3925
4393
|
for (const issue2 of error63.issues) {
|
|
3926
4394
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
3927
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
4395
|
+
issue2.errors.map((issues) => processError({ issues }, [...path23, ...issue2.path]));
|
|
3928
4396
|
} else if (issue2.code === "invalid_key") {
|
|
3929
|
-
processError({ issues: issue2.issues }, [...
|
|
4397
|
+
processError({ issues: issue2.issues }, [...path23, ...issue2.path]);
|
|
3930
4398
|
} else if (issue2.code === "invalid_element") {
|
|
3931
|
-
processError({ issues: issue2.issues }, [...
|
|
4399
|
+
processError({ issues: issue2.issues }, [...path23, ...issue2.path]);
|
|
3932
4400
|
} else {
|
|
3933
|
-
const fullpath = [...
|
|
4401
|
+
const fullpath = [...path23, ...issue2.path];
|
|
3934
4402
|
if (fullpath.length === 0) {
|
|
3935
4403
|
fieldErrors._errors.push(mapper(issue2));
|
|
3936
4404
|
} else {
|
|
@@ -3969,17 +4437,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
|
3969
4437
|
}
|
|
3970
4438
|
function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
3971
4439
|
const result = { errors: [] };
|
|
3972
|
-
const processError = (error63,
|
|
4440
|
+
const processError = (error63, path23 = []) => {
|
|
3973
4441
|
var _a3;
|
|
3974
4442
|
for (const issue2 of error63.issues) {
|
|
3975
4443
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
3976
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
4444
|
+
issue2.errors.map((issues) => processError({ issues }, [...path23, ...issue2.path]));
|
|
3977
4445
|
} else if (issue2.code === "invalid_key") {
|
|
3978
|
-
processError({ issues: issue2.issues }, [...
|
|
4446
|
+
processError({ issues: issue2.issues }, [...path23, ...issue2.path]);
|
|
3979
4447
|
} else if (issue2.code === "invalid_element") {
|
|
3980
|
-
processError({ issues: issue2.issues }, [...
|
|
4448
|
+
processError({ issues: issue2.issues }, [...path23, ...issue2.path]);
|
|
3981
4449
|
} else {
|
|
3982
|
-
const fullpath = [...
|
|
4450
|
+
const fullpath = [...path23, ...issue2.path];
|
|
3983
4451
|
if (fullpath.length === 0) {
|
|
3984
4452
|
result.errors.push(mapper(issue2));
|
|
3985
4453
|
continue;
|
|
@@ -4018,8 +4486,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
|
4018
4486
|
}
|
|
4019
4487
|
function toDotPath(_path) {
|
|
4020
4488
|
const segs = [];
|
|
4021
|
-
const
|
|
4022
|
-
for (const seg of
|
|
4489
|
+
const path23 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
4490
|
+
for (const seg of path23) {
|
|
4023
4491
|
if (typeof seg === "number")
|
|
4024
4492
|
segs.push(`[${seg}]`);
|
|
4025
4493
|
else if (typeof seg === "symbol")
|
|
@@ -21157,13 +21625,13 @@ function resolveRef(ref, ctx) {
|
|
|
21157
21625
|
if (!ref.startsWith("#")) {
|
|
21158
21626
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
21159
21627
|
}
|
|
21160
|
-
const
|
|
21161
|
-
if (
|
|
21628
|
+
const path23 = ref.slice(1).split("/").filter(Boolean);
|
|
21629
|
+
if (path23.length === 0) {
|
|
21162
21630
|
return ctx.rootSchema;
|
|
21163
21631
|
}
|
|
21164
21632
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
21165
|
-
if (
|
|
21166
|
-
const key =
|
|
21633
|
+
if (path23[0] === defsKey) {
|
|
21634
|
+
const key = path23[1] === void 0 ? void 0 : decodeJSONPointerSegment(path23[1]);
|
|
21167
21635
|
if (!key || !ctx.defs[key]) {
|
|
21168
21636
|
throw new Error(`Reference not found: ${ref}`);
|
|
21169
21637
|
}
|
|
@@ -22017,6 +22485,12 @@ var PI_REPOSITORY_ORIGIN_ENTRY_TYPE = "fml-repository-origin";
|
|
|
22017
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());
|
|
22018
22486
|
var PiRepositoryOriginDataSchema = external_exports.object({
|
|
22019
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(),
|
|
22020
22494
|
repository: RepositorySlugSchema.nullable(),
|
|
22021
22495
|
cwd: external_exports.string().min(1).refine(
|
|
22022
22496
|
(cwd) => cwd.trim().length > 0 && !new RegExp("\\p{Cc}", "u").test(cwd) && Buffer.byteLength(cwd, "utf8") <= 4096
|
|
@@ -22058,12 +22532,15 @@ function piTranscriptCandidate(sourcePath) {
|
|
|
22058
22532
|
targetId: "pi",
|
|
22059
22533
|
sourcePath,
|
|
22060
22534
|
sessionId,
|
|
22535
|
+
metadata: localGitStateMetadata([snapshot?.gitState], "pi_origin"),
|
|
22061
22536
|
repositoryObservations: snapshot?.repository ? [
|
|
22062
22537
|
repositoryObservation({
|
|
22063
22538
|
repository: snapshot.repository,
|
|
22064
22539
|
source: "git_origin",
|
|
22065
22540
|
observedAtMs: snapshot.observedAtMs,
|
|
22066
|
-
cwd: snapshot.cwd
|
|
22541
|
+
cwd: snapshot.cwd,
|
|
22542
|
+
branch: snapshot.gitState?.branch,
|
|
22543
|
+
commitHash: snapshot.gitState?.commitHash
|
|
22067
22544
|
})
|
|
22068
22545
|
] : []
|
|
22069
22546
|
});
|
|
@@ -22072,7 +22549,7 @@ var piArtifactDiscovery = Object.freeze({
|
|
|
22072
22549
|
targetId: "pi",
|
|
22073
22550
|
discover: (context) => {
|
|
22074
22551
|
return walkFiles({
|
|
22075
|
-
root:
|
|
22552
|
+
root: path13.join(resolvePiAgentDir(context), "sessions"),
|
|
22076
22553
|
maxDepth: 8,
|
|
22077
22554
|
includeFile: (filePath) => filePath.endsWith(".jsonl"),
|
|
22078
22555
|
skipDirs: PI_SKIP_DIRS
|
|
@@ -22098,9 +22575,9 @@ var MAX_MATERIALIZED_ARTIFACT_BYTES = 100 * 1024 * 1024;
|
|
|
22098
22575
|
var DEFAULT_MATERIALIZED_CACHE_BYTES = 2 * 1024 * 1024 * 1024;
|
|
22099
22576
|
|
|
22100
22577
|
// ../../packages/agent-targets/src/install/pi/index.ts
|
|
22101
|
-
import
|
|
22102
|
-
import
|
|
22103
|
-
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";
|
|
22104
22581
|
var FML_PI_EXTENSION_MARKER = "__FML_PI_EXTENSION_V1__";
|
|
22105
22582
|
var LEGACY_PI_EXTENSION_MARKER = "__panopticonPiExtensionTest";
|
|
22106
22583
|
var LEGACY_PI_EXTENSION_SIGNATURES = Object.freeze([
|
|
@@ -22112,11 +22589,11 @@ var LEGACY_PI_EXTENSION_SIGNATURES = Object.freeze([
|
|
|
22112
22589
|
"SessionEnd"
|
|
22113
22590
|
]);
|
|
22114
22591
|
function pathApiFor(platform) {
|
|
22115
|
-
return platform === "win32" ?
|
|
22592
|
+
return platform === "win32" ? path14.win32 : path14.posix;
|
|
22116
22593
|
}
|
|
22117
22594
|
function readFileIfPresent(filePath) {
|
|
22118
22595
|
try {
|
|
22119
|
-
return
|
|
22596
|
+
return fs6.readFileSync(filePath, "utf8");
|
|
22120
22597
|
} catch (error62) {
|
|
22121
22598
|
if (error62.code === "ENOENT") return null;
|
|
22122
22599
|
throw error62;
|
|
@@ -22133,23 +22610,23 @@ function removeLegacyExtension(filePath) {
|
|
|
22133
22610
|
if (!isOwnedExtension(source, true)) {
|
|
22134
22611
|
return { removed: false, preserved: true };
|
|
22135
22612
|
}
|
|
22136
|
-
|
|
22613
|
+
fs6.rmSync(filePath);
|
|
22137
22614
|
return { removed: true, preserved: false };
|
|
22138
22615
|
}
|
|
22139
22616
|
function writeExtensionAtomically(filePath, source) {
|
|
22140
|
-
|
|
22141
|
-
const temporaryPath =
|
|
22142
|
-
|
|
22143
|
-
`.${
|
|
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`
|
|
22144
22621
|
);
|
|
22145
22622
|
try {
|
|
22146
|
-
|
|
22147
|
-
if (process.platform === "win32" &&
|
|
22148
|
-
|
|
22623
|
+
fs6.writeFileSync(temporaryPath, source, { encoding: "utf8", mode: 384 });
|
|
22624
|
+
if (process.platform === "win32" && fs6.existsSync(filePath)) {
|
|
22625
|
+
fs6.rmSync(filePath);
|
|
22149
22626
|
}
|
|
22150
|
-
|
|
22627
|
+
fs6.renameSync(temporaryPath, filePath);
|
|
22151
22628
|
} finally {
|
|
22152
|
-
|
|
22629
|
+
fs6.rmSync(temporaryPath, { force: true });
|
|
22153
22630
|
}
|
|
22154
22631
|
}
|
|
22155
22632
|
var PI_EXTENSION_INSTALLER = Object.freeze({
|
|
@@ -22172,7 +22649,7 @@ var PI_EXTENSION_INSTALLER = Object.freeze({
|
|
|
22172
22649
|
};
|
|
22173
22650
|
},
|
|
22174
22651
|
install(paths) {
|
|
22175
|
-
const source =
|
|
22652
|
+
const source = fs6.readFileSync(paths.sourcePath, "utf8");
|
|
22176
22653
|
if (!source.includes(FML_PI_EXTENSION_MARKER)) {
|
|
22177
22654
|
throw new Error(
|
|
22178
22655
|
`Packaged Pi extension is invalid: ${paths.sourcePath} does not contain the FML ownership marker`
|
|
@@ -22221,7 +22698,7 @@ var PI_EXTENSION_INSTALLER = Object.freeze({
|
|
|
22221
22698
|
legacyPreserved: legacy.preserved
|
|
22222
22699
|
};
|
|
22223
22700
|
}
|
|
22224
|
-
|
|
22701
|
+
fs6.rmSync(paths.destinationPath);
|
|
22225
22702
|
return {
|
|
22226
22703
|
status: "removed",
|
|
22227
22704
|
destinationPath: paths.destinationPath,
|
|
@@ -22232,11 +22709,11 @@ var PI_EXTENSION_INSTALLER = Object.freeze({
|
|
|
22232
22709
|
});
|
|
22233
22710
|
|
|
22234
22711
|
// src/hooks/context.ts
|
|
22235
|
-
import { createHash as
|
|
22236
|
-
import
|
|
22712
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
22713
|
+
import path21 from "node:path";
|
|
22237
22714
|
|
|
22238
22715
|
// ../../packages/agent-targets/src/hooks/normalize.ts
|
|
22239
|
-
import { createHash as
|
|
22716
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
22240
22717
|
|
|
22241
22718
|
// ../../packages/agent-targets/src/hooks/types.ts
|
|
22242
22719
|
var HOOK_TARGETS = Object.freeze([
|
|
@@ -22352,7 +22829,7 @@ var MINI_SWE_AGENT_HOOK_EVENTS = Object.freeze([
|
|
|
22352
22829
|
]);
|
|
22353
22830
|
|
|
22354
22831
|
// ../../packages/agent-targets/src/hooks/read-command.ts
|
|
22355
|
-
import
|
|
22832
|
+
import path15 from "node:path";
|
|
22356
22833
|
var SHELL_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
22357
22834
|
"bash",
|
|
22358
22835
|
"exec_command",
|
|
@@ -22468,12 +22945,12 @@ function splitCommandSegments(tokens) {
|
|
|
22468
22945
|
return segments;
|
|
22469
22946
|
}
|
|
22470
22947
|
function stripSimpleOptions(args, flagsWithValues = /* @__PURE__ */ new Set(), patternFlags = /* @__PURE__ */ new Set()) {
|
|
22471
|
-
const
|
|
22948
|
+
const remaining2 = [];
|
|
22472
22949
|
let consumedPatternFlag = false;
|
|
22473
22950
|
for (let index = 0; index < args.length; index += 1) {
|
|
22474
22951
|
const argument = args[index];
|
|
22475
22952
|
if (argument === "--") {
|
|
22476
|
-
|
|
22953
|
+
remaining2.push(...args.slice(index + 1));
|
|
22477
22954
|
break;
|
|
22478
22955
|
}
|
|
22479
22956
|
if (argument.startsWith("-") && argument.length > 1) {
|
|
@@ -22483,9 +22960,9 @@ function stripSimpleOptions(args, flagsWithValues = /* @__PURE__ */ new Set(), p
|
|
|
22483
22960
|
}
|
|
22484
22961
|
continue;
|
|
22485
22962
|
}
|
|
22486
|
-
|
|
22963
|
+
remaining2.push(argument);
|
|
22487
22964
|
}
|
|
22488
|
-
return { args:
|
|
22965
|
+
return { args: remaining2, consumedPatternFlag };
|
|
22489
22966
|
}
|
|
22490
22967
|
function readCommandFileArgs(args, flagsWithValues, patternFlags = flagsWithValues) {
|
|
22491
22968
|
const stripped = stripSimpleOptions(args, flagsWithValues, patternFlags);
|
|
@@ -22496,7 +22973,7 @@ function isPlausibleReadPath(argument) {
|
|
|
22496
22973
|
if (COMMAND_SEPARATORS.has(argument)) return false;
|
|
22497
22974
|
if (/[\0*?[{}`$]/.test(argument)) return false;
|
|
22498
22975
|
if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(argument)) return false;
|
|
22499
|
-
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);
|
|
22500
22977
|
}
|
|
22501
22978
|
function filePathFromSegment(tokens) {
|
|
22502
22979
|
const commandTokens = [...tokens];
|
|
@@ -22567,7 +23044,7 @@ function extractReadFilePathFromCommand(command) {
|
|
|
22567
23044
|
|
|
22568
23045
|
// ../../packages/agent-targets/src/hooks/normalize.ts
|
|
22569
23046
|
var MAX_EXTRACTED_TEXT_BYTES = 64 * 1024;
|
|
22570
|
-
function
|
|
23047
|
+
function isRecord3(value) {
|
|
22571
23048
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
22572
23049
|
}
|
|
22573
23050
|
function firstString(record2, keys) {
|
|
@@ -22614,7 +23091,7 @@ function boundedExtractedText(value, field) {
|
|
|
22614
23091
|
if (value === void 0) return void 0;
|
|
22615
23092
|
const originalBytes = Buffer.byteLength(value, "utf8");
|
|
22616
23093
|
if (originalBytes <= MAX_EXTRACTED_TEXT_BYTES) return value;
|
|
22617
|
-
const digest =
|
|
23094
|
+
const digest = createHash3("sha256").update(value).digest("hex");
|
|
22618
23095
|
const marker = `
|
|
22619
23096
|
[fml:${field}:truncated originalBytes=${originalBytes} sha256=${digest}]`;
|
|
22620
23097
|
return `${utf8Prefix(
|
|
@@ -22625,7 +23102,7 @@ function boundedExtractedText(value, field) {
|
|
|
22625
23102
|
function nestedRecord(record2, keys) {
|
|
22626
23103
|
for (const key of keys) {
|
|
22627
23104
|
const value = record2[key];
|
|
22628
|
-
if (
|
|
23105
|
+
if (isRecord3(value)) return value;
|
|
22629
23106
|
}
|
|
22630
23107
|
return void 0;
|
|
22631
23108
|
}
|
|
@@ -22683,9 +23160,9 @@ function grokEventType(eventType) {
|
|
|
22683
23160
|
}
|
|
22684
23161
|
function geminiUserPrompt(payload) {
|
|
22685
23162
|
const request = payload.llm_request;
|
|
22686
|
-
if (!
|
|
23163
|
+
if (!isRecord3(request) || !Array.isArray(request.messages)) return void 0;
|
|
22687
23164
|
const userMessage = [...request.messages].reverse().find(
|
|
22688
|
-
(message) =>
|
|
23165
|
+
(message) => isRecord3(message) && message.role === "user"
|
|
22689
23166
|
);
|
|
22690
23167
|
if (!userMessage) return void 0;
|
|
22691
23168
|
if (typeof userMessage.content === "string") {
|
|
@@ -22693,7 +23170,7 @@ function geminiUserPrompt(payload) {
|
|
|
22693
23170
|
}
|
|
22694
23171
|
if (!Array.isArray(userMessage.content)) return void 0;
|
|
22695
23172
|
const text = userMessage.content.filter(
|
|
22696
|
-
(part) =>
|
|
23173
|
+
(part) => isRecord3(part) && part.type === "text" && typeof part.text === "string"
|
|
22697
23174
|
).map((part) => part.text).join("\n");
|
|
22698
23175
|
return text || void 0;
|
|
22699
23176
|
}
|
|
@@ -22792,51 +23269,15 @@ init_token_store();
|
|
|
22792
23269
|
init_dirs();
|
|
22793
23270
|
|
|
22794
23271
|
// src/repo.ts
|
|
22795
|
-
import
|
|
22796
|
-
|
|
22797
|
-
var
|
|
22798
|
-
|
|
22799
|
-
|
|
22800
|
-
|
|
22801
|
-
if (entry.expiresAtMs <= Date.now()) {
|
|
22802
|
-
REPO_CACHE.delete(cwd);
|
|
22803
|
-
return { hit: false };
|
|
22804
|
-
}
|
|
22805
|
-
REPO_CACHE.delete(cwd);
|
|
22806
|
-
REPO_CACHE.set(cwd, entry);
|
|
22807
|
-
return { hit: true, value: entry.value };
|
|
22808
|
-
}
|
|
22809
|
-
function git(cwd, args, timeoutMs) {
|
|
22810
|
-
try {
|
|
22811
|
-
return execFileSync2("git", ["-C", cwd, ...args], {
|
|
22812
|
-
encoding: "utf8",
|
|
22813
|
-
timeout: timeoutMs,
|
|
22814
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
22815
|
-
windowsHide: true
|
|
22816
|
-
}).trim() || null;
|
|
22817
|
-
} catch {
|
|
22818
|
-
return null;
|
|
22819
|
-
}
|
|
22820
|
-
}
|
|
22821
|
-
function repositorySlug(remote) {
|
|
22822
|
-
const ssh = remote.match(/^[^@]+@[^:]+:([^/]+\/[^/]+?)(?:\.git)?$/);
|
|
22823
|
-
if (ssh?.[1]) return ssh[1].toLowerCase();
|
|
22824
|
-
try {
|
|
22825
|
-
const url2 = new URL(remote);
|
|
22826
|
-
const slug = url2.pathname.replace(/^\/+|\/+$/g, "").replace(/\.git$/, "");
|
|
22827
|
-
return /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(slug) ? slug.toLowerCase() : null;
|
|
22828
|
-
} catch {
|
|
22829
|
-
return null;
|
|
22830
|
-
}
|
|
22831
|
-
}
|
|
22832
|
-
function resolveRepositorySlugFromCwd(cwd, timeoutMs = GIT_TIMEOUT_MS) {
|
|
22833
|
-
const cached2 = cachedRepo(cwd);
|
|
22834
|
-
if (cached2.hit) return cached2.value?.repo ?? null;
|
|
22835
|
-
const remote = git(cwd, ["remote", "get-url", "origin"], timeoutMs);
|
|
22836
|
-
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 });
|
|
22837
23278
|
}
|
|
22838
|
-
function resolveRepositoryRootFromCwd(cwd, timeoutMs =
|
|
22839
|
-
return
|
|
23279
|
+
function resolveRepositoryRootFromCwd(cwd, timeoutMs = 1e3) {
|
|
23280
|
+
return resolveGitStateFromCwd(cwd, timeoutMs)?.worktreeRoot ?? null;
|
|
22840
23281
|
}
|
|
22841
23282
|
|
|
22842
23283
|
// src/hooks/sync-loop.ts
|
|
@@ -22921,14 +23362,14 @@ function sameDestination(left, right) {
|
|
|
22921
23362
|
return left.environment === right.environment && left.targetUrl === right.targetUrl && left.destinationIdentity === right.destinationIdentity;
|
|
22922
23363
|
}
|
|
22923
23364
|
function hookContextInjectionId(target, syncId) {
|
|
22924
|
-
const digest =
|
|
23365
|
+
const digest = createHash6("sha256").update(`${INJECTION_ID_VERSION}\0${target}\0${syncId}`).digest("base64url");
|
|
22925
23366
|
return `${INJECTION_ID_VERSION}_${digest}`;
|
|
22926
23367
|
}
|
|
22927
23368
|
function canonicalHookContextFilePath(filePath, cwd, repositoryRoot) {
|
|
22928
23369
|
const windows = [filePath, cwd, repositoryRoot].some(
|
|
22929
23370
|
(value) => value !== void 0 && value !== null && /^[A-Za-z]:[\\/]/.test(value)
|
|
22930
23371
|
);
|
|
22931
|
-
const pathApi = windows ?
|
|
23372
|
+
const pathApi = windows ? path21.win32 : path21.posix;
|
|
22932
23373
|
const normalized = pathApi.normalize(filePath);
|
|
22933
23374
|
const resolved = pathApi.isAbsolute(normalized) ? normalized : cwd && pathApi.isAbsolute(cwd) ? pathApi.resolve(cwd, normalized) : normalized;
|
|
22934
23375
|
if (repositoryRoot && pathApi.isAbsolute(resolved)) {
|
|
@@ -23088,16 +23529,16 @@ function wrapReceiptDetail(label, detail) {
|
|
|
23088
23529
|
const firstPrefix = `\u2502 ${label}: `;
|
|
23089
23530
|
const continuationPrefix = "\u2502 ";
|
|
23090
23531
|
const lines = [];
|
|
23091
|
-
let
|
|
23532
|
+
let remaining2 = detail;
|
|
23092
23533
|
let prefix = firstPrefix;
|
|
23093
|
-
while (
|
|
23534
|
+
while (remaining2) {
|
|
23094
23535
|
const available = RECEIPT_LINE_WIDTH - receiptDisplayWidth(prefix);
|
|
23095
|
-
if (receiptDisplayWidth(
|
|
23096
|
-
lines.push(`${prefix}${
|
|
23536
|
+
if (receiptDisplayWidth(remaining2) <= available) {
|
|
23537
|
+
lines.push(`${prefix}${remaining2}`);
|
|
23097
23538
|
break;
|
|
23098
23539
|
}
|
|
23099
|
-
const tokenEnd =
|
|
23100
|
-
const token = tokenEnd === -1 ?
|
|
23540
|
+
const tokenEnd = remaining2.search(/\s/u);
|
|
23541
|
+
const token = tokenEnd === -1 ? remaining2 : remaining2.slice(0, tokenEnd);
|
|
23101
23542
|
const inlineCodeToken = splitInlineCodeToken(token);
|
|
23102
23543
|
const inlineCodeWidth = inlineCodeToken ? receiptDisplayWidth(inlineCodeToken.inlineCode) + receiptDisplayWidth(inlineCodeToken.trailingPunctuation) : 0;
|
|
23103
23544
|
if (inlineCodeToken && inlineCodeWidth > available) {
|
|
@@ -23109,7 +23550,7 @@ function wrapReceiptDetail(label, detail) {
|
|
|
23109
23550
|
codeWidth
|
|
23110
23551
|
)}${inlineCodeToken.trailingPunctuation}`
|
|
23111
23552
|
);
|
|
23112
|
-
|
|
23553
|
+
remaining2 = tokenEnd === -1 ? "" : remaining2.slice(tokenEnd).trimStart();
|
|
23113
23554
|
} else {
|
|
23114
23555
|
lines.push(
|
|
23115
23556
|
`${prefix}${truncateInlineCodeToken(
|
|
@@ -23117,13 +23558,13 @@ function wrapReceiptDetail(label, detail) {
|
|
|
23117
23558
|
available
|
|
23118
23559
|
)}`
|
|
23119
23560
|
);
|
|
23120
|
-
|
|
23561
|
+
remaining2 = `${inlineCodeToken.trailingPunctuation}${tokenEnd === -1 ? "" : ` ${remaining2.slice(tokenEnd).trimStart()}`}`;
|
|
23121
23562
|
}
|
|
23122
23563
|
} else {
|
|
23123
|
-
const { head, tail } = splitAtDisplayWidth(
|
|
23564
|
+
const { head, tail } = splitAtDisplayWidth(remaining2, available);
|
|
23124
23565
|
if (!head) return [];
|
|
23125
23566
|
lines.push(`${prefix}${head}`);
|
|
23126
|
-
|
|
23567
|
+
remaining2 = tail;
|
|
23127
23568
|
}
|
|
23128
23569
|
prefix = continuationPrefix;
|
|
23129
23570
|
}
|
|
@@ -23257,17 +23698,17 @@ function absoluteEnvPath(value) {
|
|
|
23257
23698
|
const trimmed = value.trim();
|
|
23258
23699
|
if (trimmed === "~") return os2.homedir();
|
|
23259
23700
|
if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
|
|
23260
|
-
return
|
|
23701
|
+
return path22.join(os2.homedir(), trimmed.slice(2));
|
|
23261
23702
|
}
|
|
23262
|
-
return
|
|
23703
|
+
return path22.isAbsolute(trimmed) ? path22.normalize(trimmed) : void 0;
|
|
23263
23704
|
}
|
|
23264
23705
|
function observerDirectory() {
|
|
23265
23706
|
const fmlHome = absoluteEnvPath(process.env.FML_HOME);
|
|
23266
|
-
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"));
|
|
23267
23708
|
}
|
|
23268
23709
|
function readObserverEndpoint(filePath) {
|
|
23269
23710
|
try {
|
|
23270
|
-
const parsed = JSON.parse(
|
|
23711
|
+
const parsed = JSON.parse(fs10.readFileSync(filePath, "utf8"));
|
|
23271
23712
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
23272
23713
|
return null;
|
|
23273
23714
|
}
|
|
@@ -23282,15 +23723,15 @@ function readObserverEndpoint(filePath) {
|
|
|
23282
23723
|
}
|
|
23283
23724
|
function readObserverAuthToken(filePath) {
|
|
23284
23725
|
try {
|
|
23285
|
-
return
|
|
23726
|
+
return fs10.readFileSync(filePath, "utf8").trim() || null;
|
|
23286
23727
|
} catch {
|
|
23287
23728
|
return null;
|
|
23288
23729
|
}
|
|
23289
23730
|
}
|
|
23290
23731
|
async function deliver(event, repository) {
|
|
23291
23732
|
const directory = observerDirectory();
|
|
23292
|
-
const endpoint = readObserverEndpoint(
|
|
23293
|
-
const authToken = readObserverAuthToken(
|
|
23733
|
+
const endpoint = readObserverEndpoint(path22.join(directory, "endpoint.json"));
|
|
23734
|
+
const authToken = readObserverAuthToken(path22.join(directory, "auth-token"));
|
|
23294
23735
|
if (!endpoint || !authToken) return;
|
|
23295
23736
|
const host = endpoint.host === "::1" ? "[::1]" : endpoint.host;
|
|
23296
23737
|
try {
|
|
@@ -23302,7 +23743,10 @@ async function deliver(event, repository) {
|
|
|
23302
23743
|
},
|
|
23303
23744
|
body: JSON.stringify({
|
|
23304
23745
|
target: "pi",
|
|
23305
|
-
payload:
|
|
23746
|
+
payload: {
|
|
23747
|
+
...event,
|
|
23748
|
+
[GIT_STATE_PAYLOAD_KEY]: event.cwd ? resolveGitStateFromCwd(event.cwd, 250) : null
|
|
23749
|
+
},
|
|
23306
23750
|
...event.cwd ? { cwd: event.cwd } : {},
|
|
23307
23751
|
...repository ? { repository } : {}
|
|
23308
23752
|
}),
|
|
@@ -23360,16 +23804,16 @@ var RECEIPT_GRAPHEME_SEGMENTER = new Intl.Segmenter(void 0, {
|
|
|
23360
23804
|
});
|
|
23361
23805
|
function wrapReceiptLine(text, width) {
|
|
23362
23806
|
if (width < 1) return [""];
|
|
23363
|
-
let
|
|
23364
|
-
if (!
|
|
23807
|
+
let remaining2 = text.trim();
|
|
23808
|
+
if (!remaining2) return [""];
|
|
23365
23809
|
const lines = [];
|
|
23366
|
-
while (
|
|
23810
|
+
while (remaining2) {
|
|
23367
23811
|
let line = "";
|
|
23368
23812
|
let lineWidth = 0;
|
|
23369
23813
|
let end = 0;
|
|
23370
23814
|
let whitespaceEnd = 0;
|
|
23371
23815
|
for (const { segment, index } of RECEIPT_GRAPHEME_SEGMENTER.segment(
|
|
23372
|
-
|
|
23816
|
+
remaining2
|
|
23373
23817
|
)) {
|
|
23374
23818
|
const segmentWidth = receiptDisplayWidth(segment);
|
|
23375
23819
|
if (segmentWidth > width) {
|
|
@@ -23384,16 +23828,16 @@ function wrapReceiptLine(text, width) {
|
|
|
23384
23828
|
end = index + segment.length;
|
|
23385
23829
|
if (/^\s$/u.test(segment)) whitespaceEnd = end;
|
|
23386
23830
|
}
|
|
23387
|
-
if (end ===
|
|
23831
|
+
if (end === remaining2.length) {
|
|
23388
23832
|
lines.push(line.trimEnd());
|
|
23389
23833
|
break;
|
|
23390
23834
|
}
|
|
23391
23835
|
if (whitespaceEnd > 0) {
|
|
23392
|
-
lines.push(
|
|
23393
|
-
|
|
23836
|
+
lines.push(remaining2.slice(0, whitespaceEnd).trimEnd());
|
|
23837
|
+
remaining2 = remaining2.slice(whitespaceEnd).trimStart();
|
|
23394
23838
|
} else {
|
|
23395
23839
|
lines.push(line);
|
|
23396
|
-
|
|
23840
|
+
remaining2 = remaining2.slice(end);
|
|
23397
23841
|
}
|
|
23398
23842
|
}
|
|
23399
23843
|
return lines;
|
|
@@ -23433,7 +23877,7 @@ var PiReceiptComponent = class {
|
|
|
23433
23877
|
}
|
|
23434
23878
|
};
|
|
23435
23879
|
function fmlPiExtension(pi) {
|
|
23436
|
-
let sessionId =
|
|
23880
|
+
let sessionId = randomUUID5();
|
|
23437
23881
|
let initializedSessionId = null;
|
|
23438
23882
|
let cwd;
|
|
23439
23883
|
let repository = null;
|
|
@@ -23558,7 +24002,7 @@ function fmlPiExtension(pi) {
|
|
|
23558
24002
|
}
|
|
23559
24003
|
repository = null;
|
|
23560
24004
|
try {
|
|
23561
|
-
repository = cwd ?
|
|
24005
|
+
repository = cwd ? resolveGitStateFromCwd(cwd, 250)?.repository ?? null : null;
|
|
23562
24006
|
} catch {
|
|
23563
24007
|
}
|
|
23564
24008
|
if (newIdentity && evidence?.status === "absent" && (event.reason === "startup" || event.reason === "new" || event.reason === "resume") && entries.every(
|
|
@@ -23567,7 +24011,8 @@ function fmlPiExtension(pi) {
|
|
|
23567
24011
|
const origin = PiRepositoryOriginDataSchema.safeParse({
|
|
23568
24012
|
version: 1,
|
|
23569
24013
|
repository,
|
|
23570
|
-
cwd
|
|
24014
|
+
cwd,
|
|
24015
|
+
gitState: cwd ? resolveGitStateFromCwd(cwd, 250) : null
|
|
23571
24016
|
});
|
|
23572
24017
|
if (origin.success) {
|
|
23573
24018
|
capturedRepository = origin.data.repository;
|