@mnemonik/cli 7.140.1 → 7.140.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/credentials.js +41 -8
- package/dist/auth/index.js +41 -8
- package/dist/auth/installSession.js +1 -1
- package/dist/auth/status.js +1 -1
- package/dist/authorizationWords.js +1 -1
- package/dist/diagnostics.js +41 -8
- package/dist/digests.json +71 -67
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +50 -4
- package/dist/help.js.map +1 -1
- package/dist/hooksLog.d.ts +61 -0
- package/dist/hooksLog.d.ts.map +1 -0
- package/dist/hooksLog.js +350 -0
- package/dist/hooksLog.js.map +1 -0
- package/dist/humanReason.d.ts +7 -0
- package/dist/humanReason.d.ts.map +1 -1
- package/dist/humanReason.js +12 -4
- package/dist/humanReason.js.map +1 -1
- package/dist/identity/migrate.js +102 -69
- package/dist/install/hosts.js +1 -1
- package/dist/install/journal.js +41 -8
- package/dist/install/journey.d.ts.map +1 -1
- package/dist/install/journey.js +41 -11
- package/dist/install/journey.js.map +1 -1
- package/dist/install/ownership.js +41 -8
- package/dist/install/transaction.d.ts.map +1 -1
- package/dist/install/transaction.js +42 -10
- package/dist/install/transaction.js.map +1 -1
- package/dist/install/ui.js +41 -8
- package/dist/installSession.js +1 -1
- package/dist/installation.js +41 -8
- package/dist/launcher.js +41 -8
- package/dist/preflight.js +201 -164
- package/dist/project/eligibility.js +41 -8
- package/dist/project/records.js +41 -8
- package/dist/project.d.ts +35 -1
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +294 -231
- package/dist/project.js.map +1 -1
- package/dist/router.d.ts +7 -0
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +340 -182
- package/dist/router.js.map +1 -1
- package/dist/runtime/bootstrap.d.ts +22 -1
- package/dist/runtime/bootstrap.d.ts.map +1 -1
- package/dist/runtime/bootstrap.js +61 -8
- package/dist/runtime/bootstrap.js.map +1 -1
- package/dist/runtime/releaseSource.js +41 -8
- package/dist/runtime/selfUpdate.js +41 -8
- package/dist/runtime/signers.js +789 -183
- package/dist/runtime/store.d.ts +1 -1
- package/dist/runtime/store.d.ts.map +1 -1
- package/dist/runtime/store.js +54 -5
- package/dist/runtime/store.js.map +1 -1
- package/dist/runtime/updateCheck.js +41 -8
- package/dist/sbom.json +21 -21
- package/dist/scanner/control.js +41 -8
- package/dist/scanner/data.js +1 -1
- package/dist/scanner/discover.d.ts.map +1 -1
- package/dist/scanner/discover.js +211 -171
- package/dist/scanner/discover.js.map +1 -1
- package/dist/scanner/enable.d.ts.map +1 -1
- package/dist/scanner/enable.js +440 -57
- package/dist/scanner/enable.js.map +1 -1
- package/dist/scanner/picker.d.ts.map +1 -1
- package/dist/scanner/picker.js +12 -4
- package/dist/scanner/picker.js.map +1 -1
- package/dist/scanner/service.js +41 -8
- package/dist/scanner/update.js +41 -8
- package/dist/scanner-release.json +24 -24
- package/dist/status.js +41 -8
- package/dist/vendor/local-setup.js +44 -11
- package/package.json +31 -31
package/dist/runtime/signers.js
CHANGED
|
@@ -469,8 +469,8 @@ var init_runtimeSigners = __esm({
|
|
|
469
469
|
const result = promisify(execFile)(file, args);
|
|
470
470
|
if (input === void 0)
|
|
471
471
|
return result;
|
|
472
|
-
return new Promise((
|
|
473
|
-
void result.then(
|
|
472
|
+
return new Promise((resolve6, reject2) => {
|
|
473
|
+
void result.then(resolve6, reject2);
|
|
474
474
|
result.child.stdin?.on("error", reject2);
|
|
475
475
|
result.child.stdin?.end(input);
|
|
476
476
|
});
|
|
@@ -503,8 +503,8 @@ var init_runtimeSigners = __esm({
|
|
|
503
503
|
const options = hookOptions(file, args);
|
|
504
504
|
if (treeListing)
|
|
505
505
|
options.timeout = 3e4;
|
|
506
|
-
return new Promise((
|
|
507
|
-
const child = execFile(file, args, options, (error, stdout2) => error ? reject2(error) :
|
|
506
|
+
return new Promise((resolve6, reject2) => {
|
|
507
|
+
const child = execFile(file, args, options, (error, stdout2) => error ? reject2(error) : resolve6({ stdout: stdout2 }));
|
|
508
508
|
if (input !== void 0) {
|
|
509
509
|
child.stdin?.on("error", reject2);
|
|
510
510
|
child.stdin?.end(input);
|
|
@@ -593,6 +593,14 @@ function parseIdentityFile(text) {
|
|
|
593
593
|
}
|
|
594
594
|
};
|
|
595
595
|
}
|
|
596
|
+
function isConfigurationOnly(text) {
|
|
597
|
+
try {
|
|
598
|
+
const value = JSON.parse(text);
|
|
599
|
+
return !!value && typeof value === "object" && !Array.isArray(value) && !("projectId" in value) && (!("schemaVersion" in value) || value.schemaVersion === 1);
|
|
600
|
+
} catch {
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
596
604
|
async function readIdentityFile(dir, options = {}) {
|
|
597
605
|
const path = join2(dir, ".mnemonik.json");
|
|
598
606
|
try {
|
|
@@ -602,14 +610,8 @@ async function readIdentityFile(dir, options = {}) {
|
|
|
602
610
|
if (!stat2.isFile())
|
|
603
611
|
return { kind: "malformed", detail: "identity path is not a regular file" };
|
|
604
612
|
const text = await readFile2(path, "utf8");
|
|
605
|
-
if (options.selectedRoot)
|
|
606
|
-
|
|
607
|
-
const value = JSON.parse(text);
|
|
608
|
-
if (value && typeof value === "object" && !Array.isArray(value) && !("projectId" in value) && (!("schemaVersion" in value) || value.schemaVersion === 1))
|
|
609
|
-
return { kind: "absent" };
|
|
610
|
-
} catch {
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
+
if ((options.selectedRoot || options.configurationOnlyAbsent) && isConfigurationOnly(text))
|
|
614
|
+
return { kind: "absent" };
|
|
613
615
|
return parseIdentityFile(text);
|
|
614
616
|
} catch (error) {
|
|
615
617
|
if (error.code === "ENOENT")
|
|
@@ -634,7 +636,7 @@ var init_projectIdentityFile = __esm({
|
|
|
634
636
|
});
|
|
635
637
|
|
|
636
638
|
// packages/shared/dist/repositoryRoot.js
|
|
637
|
-
import { lstat as lstat2, readFile as readFile3 } from "node:fs/promises";
|
|
639
|
+
import { lstat as lstat2, readFile as readFile3, realpath } from "node:fs/promises";
|
|
638
640
|
import { execFile as execFile2 } from "node:child_process";
|
|
639
641
|
import { homedir } from "node:os";
|
|
640
642
|
import { dirname as dirname2, join as join3, resolve as resolve2 } from "node:path";
|
|
@@ -685,7 +687,7 @@ async function confirmedBoundaryAt(path) {
|
|
|
685
687
|
async function boundariesBetween(cwd, root) {
|
|
686
688
|
const boundaries = [];
|
|
687
689
|
let directory = cwd;
|
|
688
|
-
while (directory
|
|
690
|
+
while (!samePath(directory, root)) {
|
|
689
691
|
const boundary = await confirmedBoundaryAt(directory);
|
|
690
692
|
if (boundary)
|
|
691
693
|
boundaries.push(boundary);
|
|
@@ -708,37 +710,31 @@ async function containingRepository(root) {
|
|
|
708
710
|
directory = parent;
|
|
709
711
|
}
|
|
710
712
|
}
|
|
711
|
-
async function
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
const gitDir = resolve2(boundary.path, pointer);
|
|
718
|
-
try {
|
|
719
|
-
const commonDir = resolve2(gitDir, (await readFile3(join3(gitDir, "commondir"), "utf8")).trim());
|
|
720
|
-
return dirname2(commonDir);
|
|
721
|
-
} catch (error) {
|
|
722
|
-
if (error.code === "ENOENT")
|
|
723
|
-
return boundary.path;
|
|
724
|
-
throw error;
|
|
725
|
-
}
|
|
713
|
+
async function canonicalPath(path) {
|
|
714
|
+
const absolute = resolve2(path);
|
|
715
|
+
return realpath(absolute).catch(() => absolute);
|
|
716
|
+
}
|
|
717
|
+
function samePath(left, right) {
|
|
718
|
+
return process.platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right;
|
|
726
719
|
}
|
|
727
720
|
async function resolveRepositoryRoot(cwd) {
|
|
728
|
-
const absoluteCwd =
|
|
721
|
+
const absoluteCwd = await canonicalPath(cwd);
|
|
729
722
|
try {
|
|
730
723
|
const { stdout: stdout2 } = await runGit(absoluteCwd, [
|
|
731
724
|
"rev-parse",
|
|
732
725
|
"--show-toplevel",
|
|
726
|
+
"--git-dir",
|
|
733
727
|
"--git-common-dir"
|
|
734
728
|
]);
|
|
735
|
-
const [rootLine, commonLine, ...extra] = stdout2.trim().split(/\r?\n/);
|
|
736
|
-
if (!rootLine || !commonLine || extra.length) {
|
|
729
|
+
const [rootLine, gitDirLine, commonLine, ...extra] = stdout2.trim().split(/\r?\n/);
|
|
730
|
+
if (!rootLine || !gitDirLine || !commonLine || extra.length) {
|
|
737
731
|
return { kind: "git_unavailable", detail: "git rev-parse returned an unexpected response" };
|
|
738
732
|
}
|
|
739
|
-
const
|
|
740
|
-
const
|
|
741
|
-
const
|
|
733
|
+
const fromGit = (line) => canonicalPath(resolve2(absoluteCwd, line));
|
|
734
|
+
const root = await fromGit(rootLine);
|
|
735
|
+
const gitDir = await fromGit(gitDirLine);
|
|
736
|
+
const commonDir = await fromGit(commonLine);
|
|
737
|
+
const isLinkedWorktree = !samePath(gitDir, commonDir);
|
|
742
738
|
const nested = await boundariesBetween(absoluteCwd, root);
|
|
743
739
|
if (!isLinkedWorktree && await containingRepository(root)) {
|
|
744
740
|
const boundary = await boundaryAt(root);
|
|
@@ -760,14 +756,41 @@ async function resolveRepositoryRoot(cwd) {
|
|
|
760
756
|
return { kind: "git_unavailable", detail: failure.message };
|
|
761
757
|
}
|
|
762
758
|
}
|
|
759
|
+
async function linkedWorktreeCommonDir(dir) {
|
|
760
|
+
try {
|
|
761
|
+
if (!(await lstat2(join3(dir, ".git"))).isFile())
|
|
762
|
+
return void 0;
|
|
763
|
+
const pointer = /^gitdir:\s*(.+?)\s*$/im.exec(await readFile3(join3(dir, ".git"), "utf8"))?.[1];
|
|
764
|
+
if (!pointer)
|
|
765
|
+
return void 0;
|
|
766
|
+
const gitDir = resolve2(dir, pointer);
|
|
767
|
+
const common = (await readFile3(join3(gitDir, "commondir"), "utf8")).trim();
|
|
768
|
+
return common ? await canonicalPath(resolve2(gitDir, common)) : void 0;
|
|
769
|
+
} catch {
|
|
770
|
+
return void 0;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
async function mainCheckout(worktreeTop, commonDir) {
|
|
774
|
+
try {
|
|
775
|
+
const { stdout: stdout2 } = await runGit(worktreeTop, ["worktree", "list", "--porcelain"]);
|
|
776
|
+
const first = stdout2.split(/\r?\n\r?\n/)[0] ?? "";
|
|
777
|
+
const path = /^worktree (.+)$/m.exec(first)?.[1];
|
|
778
|
+
if (path) {
|
|
779
|
+
const main = await canonicalPath(resolve2(worktreeTop, path));
|
|
780
|
+
return /^bare$/m.test(first) ? dirname2(main) : main;
|
|
781
|
+
}
|
|
782
|
+
} catch {
|
|
783
|
+
}
|
|
784
|
+
return dirname2(commonDir);
|
|
785
|
+
}
|
|
763
786
|
async function readPlainIdentity(start) {
|
|
764
|
-
const home =
|
|
787
|
+
const home = await canonicalPath(homedir());
|
|
765
788
|
let directory = start;
|
|
766
789
|
while (true) {
|
|
767
|
-
const result = await readIdentityFile(directory);
|
|
790
|
+
const result = await readIdentityFile(directory, { configurationOnlyAbsent: true });
|
|
768
791
|
if (result.kind !== "absent")
|
|
769
792
|
return { root: directory, result };
|
|
770
|
-
if (directory
|
|
793
|
+
if (samePath(directory, home))
|
|
771
794
|
break;
|
|
772
795
|
const parent = dirname2(directory);
|
|
773
796
|
if (parent === directory)
|
|
@@ -776,59 +799,59 @@ async function readPlainIdentity(start) {
|
|
|
776
799
|
}
|
|
777
800
|
return { root: start, result: { kind: "absent" } };
|
|
778
801
|
}
|
|
779
|
-
function
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
const
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
if (
|
|
793
|
-
return
|
|
794
|
-
|
|
795
|
-
}
|
|
796
|
-
const ownRoot = repository.isLinkedWorktree ? dirname2(repository.commonDir) : repository.root;
|
|
797
|
-
const outer = repository.isLinkedWorktree ? void 0 : await containingRepository(repository.root);
|
|
798
|
-
const own = await readIdentityFile(ownRoot, options);
|
|
799
|
-
if (!outer || options.selectedRoot || own.kind === "ok") {
|
|
800
|
-
const base2 = withBase(repository, ownRoot, repository.nested);
|
|
801
|
-
if (own.kind === "ok")
|
|
802
|
-
return { ...base2, ...own };
|
|
803
|
-
if (own.kind === "unknown_version")
|
|
804
|
-
return { ...base2, ...own, path: ownRoot };
|
|
805
|
-
if (own.kind === "malformed")
|
|
806
|
-
return { ...base2, ...own, path: ownRoot };
|
|
807
|
-
return { ...base2, kind: "absent" };
|
|
808
|
-
}
|
|
809
|
-
const nested = repository.nested;
|
|
810
|
-
const parentRoot = await canonicalBoundaryRoot(outer);
|
|
811
|
-
const parent = await readIdentityFile(parentRoot);
|
|
812
|
-
const base = withBase(repository, parentRoot, nested);
|
|
813
|
-
if (own.kind === "unknown_version")
|
|
814
|
-
return { ...base, ...own, path: ownRoot };
|
|
815
|
-
if (own.kind === "malformed")
|
|
816
|
-
return { ...base, ...own, path: ownRoot };
|
|
817
|
-
if (parent.kind === "unknown_version")
|
|
818
|
-
return { ...base, ...parent, path: parentRoot };
|
|
819
|
-
if (parent.kind === "malformed")
|
|
820
|
-
return { ...base, ...parent, path: parentRoot };
|
|
821
|
-
if (!options.allowNestedInherit) {
|
|
822
|
-
return {
|
|
823
|
-
...base,
|
|
824
|
-
kind: "nested",
|
|
825
|
-
...parent.kind === "ok" ? { parentIdentity: parent.identity } : {}
|
|
826
|
-
};
|
|
802
|
+
async function nearestIdentity(start) {
|
|
803
|
+
const home = await canonicalPath(homedir());
|
|
804
|
+
let directory = start;
|
|
805
|
+
while (true) {
|
|
806
|
+
const commonDir = await linkedWorktreeCommonDir(directory);
|
|
807
|
+
if (commonDir)
|
|
808
|
+
return { worktreeTop: directory, commonDir };
|
|
809
|
+
const result = await readIdentityFile(directory, { configurationOnlyAbsent: true });
|
|
810
|
+
if (result.kind !== "absent")
|
|
811
|
+
return { root: directory, result };
|
|
812
|
+
if (samePath(directory, home))
|
|
813
|
+
return void 0;
|
|
814
|
+
const parent = dirname2(directory);
|
|
815
|
+
if (parent === directory)
|
|
816
|
+
return void 0;
|
|
817
|
+
directory = parent;
|
|
827
818
|
}
|
|
828
|
-
|
|
829
|
-
|
|
819
|
+
}
|
|
820
|
+
async function resolveNearestProjectIdentity(cwd) {
|
|
821
|
+
const start = await canonicalPath(cwd);
|
|
822
|
+
const resolved = await resolveRepositoryRoot(start);
|
|
823
|
+
const repository = resolved.kind === "git_unavailable" ? { kind: "plain", root: start } : resolved;
|
|
824
|
+
const nested = repository.kind === "git" ? repository.nested : [];
|
|
825
|
+
let found = await nearestIdentity(start);
|
|
826
|
+
if (found && "worktreeTop" in found) {
|
|
827
|
+
found = await readPlainIdentity(await mainCheckout(found.worktreeTop, found.commonDir));
|
|
828
|
+
}
|
|
829
|
+
if (found) {
|
|
830
|
+
return shapedIdentity(found, await projectFolderRepository(found.root, repository), nested);
|
|
831
|
+
}
|
|
832
|
+
return { ...withBase(repository, repository.root, nested), kind: "absent" };
|
|
833
|
+
}
|
|
834
|
+
function shapedIdentity(found, repository, nested) {
|
|
835
|
+
const base = withBase(repository, found.root, nested);
|
|
836
|
+
if (found.result.kind === "ok")
|
|
837
|
+
return { ...base, ...found.result };
|
|
838
|
+
if (found.result.kind === "unknown_version")
|
|
839
|
+
return { ...base, ...found.result, path: found.root };
|
|
840
|
+
if (found.result.kind === "malformed")
|
|
841
|
+
return { ...base, ...found.result, path: found.root };
|
|
830
842
|
return { ...base, kind: "absent" };
|
|
831
843
|
}
|
|
844
|
+
async function projectFolderRepository(projectRoot, known) {
|
|
845
|
+
if (known.kind === "git" && samePath(known.root, projectRoot))
|
|
846
|
+
return known;
|
|
847
|
+
const repository = await resolveRepositoryRoot(projectRoot);
|
|
848
|
+
if (repository.kind === "git" && samePath(repository.root, projectRoot))
|
|
849
|
+
return repository;
|
|
850
|
+
return { kind: "plain", root: projectRoot };
|
|
851
|
+
}
|
|
852
|
+
function withBase(repository, root, nested) {
|
|
853
|
+
return { root, repository, nested };
|
|
854
|
+
}
|
|
832
855
|
var GIT_TIMEOUT_MS, GIT_ENV_KEYS;
|
|
833
856
|
var init_repositoryRoot = __esm({
|
|
834
857
|
"packages/shared/dist/repositoryRoot.js"() {
|
|
@@ -848,11 +871,564 @@ var init_repositoryRoot = __esm({
|
|
|
848
871
|
}
|
|
849
872
|
});
|
|
850
873
|
|
|
874
|
+
// packages/shared/dist/protectedPaths.js
|
|
875
|
+
import { homedir as homedir2 } from "node:os";
|
|
876
|
+
import { posix, win32 as win322 } from "node:path";
|
|
877
|
+
function mnemonikStateDirectory(platform = process.platform, env = process.env, home = homedir2()) {
|
|
878
|
+
if (env.MNEMONIK_STATE_DIR)
|
|
879
|
+
return env.MNEMONIK_STATE_DIR;
|
|
880
|
+
if (platform === "win32")
|
|
881
|
+
return win322.join(env.LOCALAPPDATA || win322.join(home, "AppData", "Local"), "Mnemonik");
|
|
882
|
+
if (platform === "darwin")
|
|
883
|
+
return posix.join(home, "Library", "Application Support", "Mnemonik");
|
|
884
|
+
return posix.join(env.XDG_STATE_HOME || posix.join(home, ".local", "state"), "mnemonik");
|
|
885
|
+
}
|
|
886
|
+
var init_protectedPaths = __esm({
|
|
887
|
+
"packages/shared/dist/protectedPaths.js"() {
|
|
888
|
+
"use strict";
|
|
889
|
+
}
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
// packages/shared/dist/secretPatterns.js
|
|
893
|
+
function shannonEntropy(token) {
|
|
894
|
+
const counts = /* @__PURE__ */ new Map();
|
|
895
|
+
for (const ch of token)
|
|
896
|
+
counts.set(ch, (counts.get(ch) ?? 0) + 1);
|
|
897
|
+
let bits = 0;
|
|
898
|
+
for (const count of counts.values()) {
|
|
899
|
+
const p = count / token.length;
|
|
900
|
+
bits -= p * Math.log2(p);
|
|
901
|
+
}
|
|
902
|
+
return bits;
|
|
903
|
+
}
|
|
904
|
+
function maxLowercaseRun(token) {
|
|
905
|
+
let max = 0;
|
|
906
|
+
let current = 0;
|
|
907
|
+
for (const ch of token) {
|
|
908
|
+
if (ch >= "a" && ch <= "z") {
|
|
909
|
+
current += 1;
|
|
910
|
+
if (current > max)
|
|
911
|
+
max = current;
|
|
912
|
+
} else {
|
|
913
|
+
current = 0;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
return max;
|
|
917
|
+
}
|
|
918
|
+
function isHighEntropySecret(token) {
|
|
919
|
+
if (token.length < ENTROPY_MIN_LENGTH)
|
|
920
|
+
return false;
|
|
921
|
+
const stripped = token.replace(/[-_]/g, "");
|
|
922
|
+
if (stripped.length > 0 && ENTROPY_HEXISH_RE.test(stripped))
|
|
923
|
+
return false;
|
|
924
|
+
if (!/[a-z]/.test(token) || !/[A-Z]/.test(token) || !/[0-9]/.test(token))
|
|
925
|
+
return false;
|
|
926
|
+
if (ENTROPY_STRUCTURED_RE.test(token) && token.split(/[/_-]/).every((seg) => seg.length <= ENTROPY_MAX_STRUCTURED_SEGMENT)) {
|
|
927
|
+
return false;
|
|
928
|
+
}
|
|
929
|
+
if (maxLowercaseRun(token) > ENTROPY_MAX_LOWERCASE_RUN)
|
|
930
|
+
return false;
|
|
931
|
+
return shannonEntropy(token) >= ENTROPY_MIN_BITS_PER_CHAR;
|
|
932
|
+
}
|
|
933
|
+
function redactHighEntropyTokens(text) {
|
|
934
|
+
if (!text)
|
|
935
|
+
return text;
|
|
936
|
+
return text.replace(ENTROPY_CANDIDATE_RE, (match, offset) => {
|
|
937
|
+
const before = text.slice(Math.max(0, offset - 8), offset);
|
|
938
|
+
if (!ENTROPY_CONTEXT_RE.test(before))
|
|
939
|
+
return match;
|
|
940
|
+
const operatorContext = before.replace(/["'`]$/, "").trimEnd().slice(0, -1);
|
|
941
|
+
if (ENTROPY_OPERATOR_TAIL_RE.test(operatorContext))
|
|
942
|
+
return match;
|
|
943
|
+
if (ENV_READ_VALUE_RE.test(before + match))
|
|
944
|
+
return match;
|
|
945
|
+
return isHighEntropySecret(match) ? SECRET_REDACTION_PLACEHOLDER : match;
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
function scrubSecrets(text) {
|
|
949
|
+
if (!text)
|
|
950
|
+
return text;
|
|
951
|
+
let result = text;
|
|
952
|
+
for (const pattern of SECRET_PATTERNS) {
|
|
953
|
+
result = result.replace(pattern, (match) => ENV_READ_VALUE_RE.test(match) ? match : SECRET_REDACTION_PLACEHOLDER);
|
|
954
|
+
}
|
|
955
|
+
return redactHighEntropyTokens(result);
|
|
956
|
+
}
|
|
957
|
+
var SECRET_REDACTION_PLACEHOLDER, SECRET_PATTERNS, ENV_READ_VALUE_RE, ENTROPY_MIN_LENGTH, ENTROPY_MIN_BITS_PER_CHAR, ENTROPY_MAX_STRUCTURED_SEGMENT, ENTROPY_MAX_LOWERCASE_RUN, ENTROPY_CANDIDATE_RE, ENTROPY_CONTEXT_RE, ENTROPY_OPERATOR_TAIL_RE, ENTROPY_HEXISH_RE, ENTROPY_STRUCTURED_RE;
|
|
958
|
+
var init_secretPatterns = __esm({
|
|
959
|
+
"packages/shared/dist/secretPatterns.js"() {
|
|
960
|
+
"use strict";
|
|
961
|
+
SECRET_REDACTION_PLACEHOLDER = "[REDACTED]";
|
|
962
|
+
SECRET_PATTERNS = [
|
|
963
|
+
/(?:api[_-]?key|secret|token|password|credential|auth)\s*[:=]\s*(?=\S*[A-Za-z0-9])\S+/gi,
|
|
964
|
+
// Stripe-shape: (sk|pk)_(live|test)_<24+ alphanumerics>. Catches modern
|
|
965
|
+
// Stripe keys whose body is split by an environment separator that
|
|
966
|
+
// breaks the contiguous-alphanum pattern below. Required `live|test`
|
|
967
|
+
// literal prevents false-positives on snake_case identifiers like
|
|
968
|
+
// pkg_install_helper_function_xyz_abc_def.
|
|
969
|
+
/(?:sk|pk)_(?:live|test)_[a-zA-Z0-9]{24,}/g,
|
|
970
|
+
/(?:sk|pk)[-_][a-zA-Z0-9]{20,}/g,
|
|
971
|
+
/ghp_[a-zA-Z0-9]{36}/g,
|
|
972
|
+
/glpat-[a-zA-Z0-9-]{20,}/g,
|
|
973
|
+
// PEM private keys. The BLOCK pattern runs first so header->footer (the
|
|
974
|
+
// actual key material) is redacted as one unit; the header-only pattern
|
|
975
|
+
// stays as the fallback for the case a chunk boundary lands between
|
|
976
|
+
// header and footer, where the block never closes inside the chunk.
|
|
977
|
+
// `[\s\S]*?` is lazy so two adjacent keys don't merge into one match.
|
|
978
|
+
/-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g,
|
|
979
|
+
/-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g,
|
|
980
|
+
// AWS access key IDs. The 16-char body must be uppercase-alphanumeric and
|
|
981
|
+
// \b-delimited, so prose ("AKIAMIA is a place name") cannot match.
|
|
982
|
+
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
983
|
+
/\bASIA[0-9A-Z]{16}\b/g,
|
|
984
|
+
// AWS secret access key, labeled. The generic key=value pattern above does
|
|
985
|
+
// NOT cover this: its `secret` alternative must be followed immediately by
|
|
986
|
+
// `[:=]`, and here it is followed by `_access_key`.
|
|
987
|
+
/\baws_secret_access_key\s*[:=]\s*[A-Za-z0-9/+=]{40}\b/gi,
|
|
988
|
+
// JWT: three base64url segments. Length minimums keep dotted identifiers
|
|
989
|
+
// (`payload.header.signature`) out.
|
|
990
|
+
/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{5,}\b/g,
|
|
991
|
+
/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g,
|
|
992
|
+
/\bAIza[0-9A-Za-z_-]{35}\b/g,
|
|
993
|
+
/\bsk-ant-[A-Za-z0-9_-]{20,}\b/g,
|
|
994
|
+
/\b(?:npm_|npms_)[A-Za-z0-9]{36,}\b/g,
|
|
995
|
+
// scheme://userinfo@: only the credential segment is consumed, so the
|
|
996
|
+
// host and path survive for anyone reading the code. Userinfo may contain
|
|
997
|
+
// empty fields or additional `:` / `@` characters; greediness selects its
|
|
998
|
+
// final `@`, while `/`, `?`, and `#` keep paths and queries out of the match.
|
|
999
|
+
/\b[a-z][a-z0-9+.-]*:\/\/(?=[^/\s?#]*[A-Za-z0-9][^/\s?#]*@)[^/\s?#]*@/gi,
|
|
1000
|
+
// Authorization: Bearer <token>. The token class deliberately excludes
|
|
1001
|
+
// `<` and `$`, so documentation placeholders (`Bearer <your-api-key>`,
|
|
1002
|
+
// `Bearer $MNEMONIK_PROXY_TOKEN`) are left intact - redacting those would
|
|
1003
|
+
// destroy the instruction without hiding a secret.
|
|
1004
|
+
/Authorization\s*:\s*Bearer\s+(?=[._~+/-]*[A-Za-z0-9])[A-Za-z0-9._~+/-]+=*/gi,
|
|
1005
|
+
// sshpass -p <password> (also -p'<password>' and -p"<password>"): only the
|
|
1006
|
+
// password word is consumed, so the command stays readable. A shell
|
|
1007
|
+
// expansion (`$PASS`, `"$PASS"`), a `<placeholder>`, an earlier redaction
|
|
1008
|
+
// and quoted punctuation are left, and so is prose about the flag: a bare all-lower-case
|
|
1009
|
+
// word counts only when the command sshpass runs (ssh, scp, ...) follows it.
|
|
1010
|
+
// Eleven scanned chunks in production carried a root password this way
|
|
1011
|
+
// (2026-10-12); the server's memory redactor (src/core/redactCredentials.ts)
|
|
1012
|
+
// has the same rule.
|
|
1013
|
+
/(?<=\bsshpass(?:[ \t]+-[a-oq-zA-Z]\S*)*[ \t]+-p[ \t]*)(?:'(?![$<]|\[REDACTED\])(?=[^'\n]*[A-Za-z0-9])[^'\n]+'|"(?![$<]|\[REDACTED\])(?=[^"\n]*[A-Za-z0-9])[^"\n]+"|(?![$<'"`(),[{\\]|\[REDACTED\]|[a-z]+[.,:;!?)]*(?![^\s'"`])(?![ \t]+(?:ssh|scp|sftp|rsync|ssh-copy-id|ansible[\w-]*)(?![\w-])))[^\s'"`;|&]+)/g
|
|
1014
|
+
];
|
|
1015
|
+
ENV_READ_VALUE_RE = /[:=]\s*(?:await\s+)?(?:process\.env[.[]|import\.meta\.env[.[])/;
|
|
1016
|
+
ENTROPY_MIN_LENGTH = 36;
|
|
1017
|
+
ENTROPY_MIN_BITS_PER_CHAR = 4;
|
|
1018
|
+
ENTROPY_MAX_STRUCTURED_SEGMENT = 16;
|
|
1019
|
+
ENTROPY_MAX_LOWERCASE_RUN = 5;
|
|
1020
|
+
ENTROPY_CANDIDATE_RE = /[A-Za-z0-9+/_-]{35}[A-Za-z0-9+/_-]+={0,2}/g;
|
|
1021
|
+
ENTROPY_CONTEXT_RE = /(?:["'`]|[:=]\s*["'`]?)$/;
|
|
1022
|
+
ENTROPY_OPERATOR_TAIL_RE = /[=!<>+\-*/%&|^]$/;
|
|
1023
|
+
ENTROPY_HEXISH_RE = /^[0-9a-fA-F]+$/;
|
|
1024
|
+
ENTROPY_STRUCTURED_RE = /^[A-Za-z0-9]+(?:[/_-][A-Za-z0-9]+)+$/;
|
|
1025
|
+
}
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
// packages/shared/dist/hookTrace.js
|
|
1029
|
+
import { randomBytes } from "node:crypto";
|
|
1030
|
+
import { closeSync, constants, fstatSync, lstatSync as lstatSync2, mkdirSync as mkdirSync2, openSync, renameSync as renameSync2, statSync, unlinkSync as unlinkSync2, writeSync } from "node:fs";
|
|
1031
|
+
import { dirname as dirname3, join as join4 } from "node:path";
|
|
1032
|
+
function hookTraceEnabled(env = process.env) {
|
|
1033
|
+
const value = env[HOOK_TRACE_ENV]?.trim().toLowerCase();
|
|
1034
|
+
return !value || !["off", "0", "false", "no", "disabled"].includes(value);
|
|
1035
|
+
}
|
|
1036
|
+
function hookTraceDirectory(stateDir = mnemonikStateDirectory()) {
|
|
1037
|
+
return join4(stateDir, "hook-trace");
|
|
1038
|
+
}
|
|
1039
|
+
function hookTraceFile(host, stateDir) {
|
|
1040
|
+
return join4(hookTraceDirectory(stateDir), `${host}.jsonl`);
|
|
1041
|
+
}
|
|
1042
|
+
function ensurePrivateDirectory(directory) {
|
|
1043
|
+
if (preparedDirectories.has(directory))
|
|
1044
|
+
return;
|
|
1045
|
+
if (process.platform === "win32")
|
|
1046
|
+
ensureWindowsPrivateDirectorySync(directory);
|
|
1047
|
+
else {
|
|
1048
|
+
mkdirSync2(directory, { recursive: true, mode: 448 });
|
|
1049
|
+
const stat2 = lstatSync2(directory);
|
|
1050
|
+
if (!stat2.isDirectory() || stat2.isSymbolicLink() || process.getuid && stat2.uid !== process.getuid() || stat2.mode & 63)
|
|
1051
|
+
throw new Error("hook trace directory is not private");
|
|
1052
|
+
}
|
|
1053
|
+
preparedDirectories.add(directory);
|
|
1054
|
+
}
|
|
1055
|
+
function openTrace(file) {
|
|
1056
|
+
return openSync(file, constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT | (constants.O_NOFOLLOW ?? 0), 384);
|
|
1057
|
+
}
|
|
1058
|
+
function sameFile(file, fd) {
|
|
1059
|
+
try {
|
|
1060
|
+
const opened = fstatSync(fd, { bigint: true });
|
|
1061
|
+
const named = statSync(file, { bigint: true });
|
|
1062
|
+
return opened.ino === named.ino && opened.dev === named.dev;
|
|
1063
|
+
} catch {
|
|
1064
|
+
return false;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
function takeLock(lock) {
|
|
1068
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
1069
|
+
try {
|
|
1070
|
+
closeSync(openSync(lock, "wx", 384));
|
|
1071
|
+
return true;
|
|
1072
|
+
} catch {
|
|
1073
|
+
try {
|
|
1074
|
+
if (attempt > 0 || Date.now() - lstatSync2(lock).mtimeMs < STALE_LOCK_MS)
|
|
1075
|
+
return false;
|
|
1076
|
+
unlinkSync2(lock);
|
|
1077
|
+
} catch {
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
return false;
|
|
1082
|
+
}
|
|
1083
|
+
function rotate(file, fd) {
|
|
1084
|
+
const lock = `${file}.lock`;
|
|
1085
|
+
if (!takeLock(lock))
|
|
1086
|
+
return;
|
|
1087
|
+
try {
|
|
1088
|
+
if (sameFile(file, fd))
|
|
1089
|
+
renameSync2(file, `${file}.1`);
|
|
1090
|
+
} finally {
|
|
1091
|
+
try {
|
|
1092
|
+
unlinkSync2(lock);
|
|
1093
|
+
} catch {
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
function appendHookTraceLine(file, record2) {
|
|
1098
|
+
ensurePrivateDirectory(dirname3(file));
|
|
1099
|
+
const line = `${JSON.stringify(record2)}
|
|
1100
|
+
`;
|
|
1101
|
+
let fd = openTrace(file);
|
|
1102
|
+
try {
|
|
1103
|
+
const stat2 = fstatSync(fd);
|
|
1104
|
+
if (!stat2.isFile() || process.getuid && stat2.uid !== process.getuid())
|
|
1105
|
+
throw new Error("hook trace file is not ours");
|
|
1106
|
+
if (stat2.size > 0 && stat2.size + Buffer.byteLength(line) > HOOK_TRACE_MAX_BYTES) {
|
|
1107
|
+
try {
|
|
1108
|
+
rotate(file, fd);
|
|
1109
|
+
} finally {
|
|
1110
|
+
closeSync(fd);
|
|
1111
|
+
fd = -1;
|
|
1112
|
+
}
|
|
1113
|
+
fd = openTrace(file);
|
|
1114
|
+
}
|
|
1115
|
+
writeSync(fd, line);
|
|
1116
|
+
} finally {
|
|
1117
|
+
if (fd >= 0)
|
|
1118
|
+
closeSync(fd);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
function byteSize(value) {
|
|
1122
|
+
if (typeof value === "string")
|
|
1123
|
+
return Buffer.byteLength(value);
|
|
1124
|
+
try {
|
|
1125
|
+
return Buffer.byteLength(JSON.stringify(value) ?? "");
|
|
1126
|
+
} catch {
|
|
1127
|
+
return 0;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
function safe(value, pattern) {
|
|
1131
|
+
return typeof value === "string" && pattern.test(value) ? value : void 0;
|
|
1132
|
+
}
|
|
1133
|
+
function sizesOf(object, prefix = "") {
|
|
1134
|
+
const sizes = {};
|
|
1135
|
+
for (const key of Object.keys(object).slice(0, MAX_FIELDS))
|
|
1136
|
+
if (SAFE_NAME.test(key))
|
|
1137
|
+
sizes[prefix + key] = byteSize(object[key]);
|
|
1138
|
+
return sizes;
|
|
1139
|
+
}
|
|
1140
|
+
function summarizeHookOutput(text) {
|
|
1141
|
+
const output = { bytes: Buffer.byteLength(text), sizes: {} };
|
|
1142
|
+
let value;
|
|
1143
|
+
try {
|
|
1144
|
+
value = JSON.parse(text);
|
|
1145
|
+
} catch {
|
|
1146
|
+
return output;
|
|
1147
|
+
}
|
|
1148
|
+
if (!isObject(value))
|
|
1149
|
+
return output;
|
|
1150
|
+
const nested = isObject(value.hookSpecificOutput) ? value.hookSpecificOutput : void 0;
|
|
1151
|
+
const { hookSpecificOutput: _nested, ...top } = value;
|
|
1152
|
+
output.sizes = { ...sizesOf(top), ...nested ? sizesOf(nested, "hookSpecificOutput.") : {} };
|
|
1153
|
+
const permission = safe(value.permission ?? nested?.permissionDecision, SAFE_ENUM);
|
|
1154
|
+
const decision = safe(value.decision, SAFE_ENUM);
|
|
1155
|
+
if (permission)
|
|
1156
|
+
output.permission = permission;
|
|
1157
|
+
if (decision)
|
|
1158
|
+
output.decision = decision;
|
|
1159
|
+
if (typeof value.continue === "boolean")
|
|
1160
|
+
output.continue = value.continue;
|
|
1161
|
+
return output;
|
|
1162
|
+
}
|
|
1163
|
+
function routeOf(input) {
|
|
1164
|
+
try {
|
|
1165
|
+
const href = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
1166
|
+
return new URL(String(href)).pathname.split("/").map((segment) => segment.length >= 16 && /\d/.test(segment) || /[%@]/.test(segment) ? ":id" : segment).join("/").slice(0, 200);
|
|
1167
|
+
} catch {
|
|
1168
|
+
return "unknown";
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
function errorKind(error) {
|
|
1172
|
+
const failure = error;
|
|
1173
|
+
if (failure?.name === "TimeoutError")
|
|
1174
|
+
return "timeout";
|
|
1175
|
+
if (failure?.name === "AbortError")
|
|
1176
|
+
return "aborted";
|
|
1177
|
+
const code = failure?.cause?.code ?? failure?.code;
|
|
1178
|
+
if (typeof code === "string" && /^[A-Z0-9_]{2,40}$/.test(code))
|
|
1179
|
+
return code;
|
|
1180
|
+
return safe(failure?.name, /^\w{1,40}$/) ?? "error";
|
|
1181
|
+
}
|
|
1182
|
+
function scrubHookTraceMessage(error) {
|
|
1183
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
1184
|
+
return scrubSecrets(text.replace(/\s+/g, " ").trim()).slice(0, MAX_ERROR_CHARS);
|
|
1185
|
+
}
|
|
1186
|
+
function noteHookTraceProject(projectId) {
|
|
1187
|
+
active?.project(projectId);
|
|
1188
|
+
}
|
|
1189
|
+
function startHookTrace(options) {
|
|
1190
|
+
if (active)
|
|
1191
|
+
return active;
|
|
1192
|
+
let file;
|
|
1193
|
+
try {
|
|
1194
|
+
if (hookTraceEnabled(options.env))
|
|
1195
|
+
file = hookTraceFile(options.host, options.stateDir ?? mnemonikStateDirectory(process.platform, options.env ?? process.env));
|
|
1196
|
+
} catch {
|
|
1197
|
+
file = void 0;
|
|
1198
|
+
}
|
|
1199
|
+
const trace = new HookTrace(options.host, file, options.hookVersion);
|
|
1200
|
+
active = trace;
|
|
1201
|
+
if (!file)
|
|
1202
|
+
return trace;
|
|
1203
|
+
const original = globalThis.fetch;
|
|
1204
|
+
if (typeof original === "function") {
|
|
1205
|
+
globalThis.fetch = async (input, init) => {
|
|
1206
|
+
const token = trace.callStarted(input);
|
|
1207
|
+
try {
|
|
1208
|
+
const response = await original.call(globalThis, input, init);
|
|
1209
|
+
trace.settle(token, response.status);
|
|
1210
|
+
return response;
|
|
1211
|
+
} catch (error) {
|
|
1212
|
+
trace.settle(token, void 0, error);
|
|
1213
|
+
throw error;
|
|
1214
|
+
}
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
process.once("exit", (code) => trace.finish(typeof code === "number" ? code : 0));
|
|
1218
|
+
trace.open();
|
|
1219
|
+
return trace;
|
|
1220
|
+
}
|
|
1221
|
+
var HOOK_TRACE_MAX_BYTES, HOOK_TRACE_ENV, MAX_FIELDS, MAX_CALLS, MAX_ERROR_CHARS, SAFE_ID, SAFE_NAME, SAFE_ENUM, preparedDirectories, STALE_LOCK_MS, isObject, HookTrace, active;
|
|
1222
|
+
var init_hookTrace = __esm({
|
|
1223
|
+
"packages/shared/dist/hookTrace.js"() {
|
|
1224
|
+
"use strict";
|
|
1225
|
+
init_protectedPaths();
|
|
1226
|
+
init_runtimeSigners();
|
|
1227
|
+
init_secretPatterns();
|
|
1228
|
+
HOOK_TRACE_MAX_BYTES = 2 * 1024 * 1024;
|
|
1229
|
+
HOOK_TRACE_ENV = "MNEMONIK_HOOK_TRACE";
|
|
1230
|
+
MAX_FIELDS = 64;
|
|
1231
|
+
MAX_CALLS = 64;
|
|
1232
|
+
MAX_ERROR_CHARS = 80;
|
|
1233
|
+
SAFE_ID = /^[A-Za-z0-9@._+:-]{1,128}$/;
|
|
1234
|
+
SAFE_NAME = /^[A-Za-z0-9@._+:/-]{1,120}$/;
|
|
1235
|
+
SAFE_ENUM = /^[A-Za-z_]{1,24}$/;
|
|
1236
|
+
preparedDirectories = /* @__PURE__ */ new Set();
|
|
1237
|
+
STALE_LOCK_MS = 1e4;
|
|
1238
|
+
isObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
1239
|
+
HookTrace = class {
|
|
1240
|
+
host;
|
|
1241
|
+
file;
|
|
1242
|
+
hookVersion;
|
|
1243
|
+
id = randomBytes(8).toString("hex");
|
|
1244
|
+
startedAt = performance.now();
|
|
1245
|
+
opened = false;
|
|
1246
|
+
started = false;
|
|
1247
|
+
ended = false;
|
|
1248
|
+
event = null;
|
|
1249
|
+
outputSummary = null;
|
|
1250
|
+
error;
|
|
1251
|
+
errorKind;
|
|
1252
|
+
projectId;
|
|
1253
|
+
calls = [];
|
|
1254
|
+
inFlight = /* @__PURE__ */ new Set();
|
|
1255
|
+
constructor(host, file, hookVersion) {
|
|
1256
|
+
this.host = host;
|
|
1257
|
+
this.file = file;
|
|
1258
|
+
this.hookVersion = hookVersion;
|
|
1259
|
+
}
|
|
1260
|
+
get enabled() {
|
|
1261
|
+
return this.file !== void 0;
|
|
1262
|
+
}
|
|
1263
|
+
write(record2) {
|
|
1264
|
+
if (!this.file)
|
|
1265
|
+
return;
|
|
1266
|
+
try {
|
|
1267
|
+
appendHookTraceLine(this.file, record2);
|
|
1268
|
+
} catch {
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
* Write the `start` line: pid, run id and time since process start, nothing
|
|
1273
|
+
* from the editor. Runs before stdin is read, so a run killed while the
|
|
1274
|
+
* editor is still sending its event leaves this line.
|
|
1275
|
+
*/
|
|
1276
|
+
open() {
|
|
1277
|
+
if (!this.file || this.opened)
|
|
1278
|
+
return;
|
|
1279
|
+
this.opened = true;
|
|
1280
|
+
this.write({
|
|
1281
|
+
v: 1,
|
|
1282
|
+
phase: "start",
|
|
1283
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1284
|
+
host: this.host,
|
|
1285
|
+
...this.hookVersion ? { hookVersion: this.hookVersion } : {},
|
|
1286
|
+
pid: process.pid,
|
|
1287
|
+
id: this.id,
|
|
1288
|
+
processMs: Math.round(process.uptime() * 1e3)
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
/** Write the `input` line. `input` is the parsed payload, or null when none parsed. */
|
|
1292
|
+
begin(input, event) {
|
|
1293
|
+
if (!this.file || this.started)
|
|
1294
|
+
return;
|
|
1295
|
+
this.open();
|
|
1296
|
+
this.started = true;
|
|
1297
|
+
try {
|
|
1298
|
+
const payload = isObject(input) ? input : {};
|
|
1299
|
+
this.event = safe(event, SAFE_NAME) ?? safe(payload.hook_event_name, SAFE_NAME) ?? safe(payload.hookEventName, SAFE_NAME) ?? null;
|
|
1300
|
+
const roots = Array.isArray(payload.workspace_roots) ? payload.workspace_roots : [];
|
|
1301
|
+
const cwd = typeof payload.cwd === "string" && payload.cwd ? payload.cwd : typeof roots[0] === "string" ? roots[0] : process.cwd();
|
|
1302
|
+
const record2 = {
|
|
1303
|
+
v: 1,
|
|
1304
|
+
phase: "input",
|
|
1305
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1306
|
+
host: this.host,
|
|
1307
|
+
pid: process.pid,
|
|
1308
|
+
id: this.id,
|
|
1309
|
+
event: this.event,
|
|
1310
|
+
inputBytes: isObject(input) ? byteSize(input) : 0,
|
|
1311
|
+
fields: sizesOf(payload),
|
|
1312
|
+
readMs: Math.round(performance.now() - this.startedAt)
|
|
1313
|
+
};
|
|
1314
|
+
const hostVersion = safe(payload.cursor_version ?? payload.version, /^[\w.+-]{1,40}$/);
|
|
1315
|
+
if (hostVersion)
|
|
1316
|
+
record2.hostVersion = hostVersion;
|
|
1317
|
+
const ids = [
|
|
1318
|
+
["sessionId", "session_id"],
|
|
1319
|
+
["conversationId", "conversation_id"],
|
|
1320
|
+
["generationId", "generation_id"],
|
|
1321
|
+
["agentId", "agent_id"],
|
|
1322
|
+
["agentId", "subagent_id"],
|
|
1323
|
+
["turnId", "turn_id"]
|
|
1324
|
+
];
|
|
1325
|
+
for (const [name, key] of ids) {
|
|
1326
|
+
const id = safe(payload[key], SAFE_ID);
|
|
1327
|
+
if (id && record2[name] === void 0)
|
|
1328
|
+
record2[name] = id;
|
|
1329
|
+
}
|
|
1330
|
+
const tool = safe(payload.tool_name, SAFE_NAME);
|
|
1331
|
+
if (tool)
|
|
1332
|
+
record2.tool = tool;
|
|
1333
|
+
if (cwd.length <= 1024)
|
|
1334
|
+
record2.cwd = cwd;
|
|
1335
|
+
this.write(record2);
|
|
1336
|
+
} catch {
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
/** The exact text the hook wrote to stdout (or nothing). */
|
|
1340
|
+
output(text) {
|
|
1341
|
+
if (!this.file || text === null || text === void 0)
|
|
1342
|
+
return;
|
|
1343
|
+
try {
|
|
1344
|
+
this.outputSummary = summarizeHookOutput(text);
|
|
1345
|
+
} catch {
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
/** The error the hook failed open on. */
|
|
1349
|
+
fail(error) {
|
|
1350
|
+
if (!this.file)
|
|
1351
|
+
return;
|
|
1352
|
+
try {
|
|
1353
|
+
this.errorKind = errorKind(error);
|
|
1354
|
+
this.error = scrubHookTraceMessage(error);
|
|
1355
|
+
} catch {
|
|
1356
|
+
this.errorKind = "error";
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
project(projectId) {
|
|
1360
|
+
if (projectId && !this.projectId && SAFE_ID.test(projectId))
|
|
1361
|
+
this.projectId = projectId;
|
|
1362
|
+
}
|
|
1363
|
+
/** A call has gone out; {@link settle} records it, {@link finish} records it as pending. */
|
|
1364
|
+
callStarted(input) {
|
|
1365
|
+
const token = { input, startedAt: performance.now() };
|
|
1366
|
+
if (this.file)
|
|
1367
|
+
this.inFlight.add(token);
|
|
1368
|
+
return token;
|
|
1369
|
+
}
|
|
1370
|
+
settle(token, status, error) {
|
|
1371
|
+
this.inFlight.delete(token);
|
|
1372
|
+
this.call(token.input, token.startedAt, status, error);
|
|
1373
|
+
}
|
|
1374
|
+
call(input, startedAt, status, error) {
|
|
1375
|
+
if (!this.file || this.calls.length >= MAX_CALLS)
|
|
1376
|
+
return;
|
|
1377
|
+
try {
|
|
1378
|
+
this.calls.push({
|
|
1379
|
+
path: routeOf(input),
|
|
1380
|
+
...status !== void 0 ? { status } : { error: errorKind(error) },
|
|
1381
|
+
ms: Math.round(performance.now() - startedAt)
|
|
1382
|
+
});
|
|
1383
|
+
} catch {
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
/** Write the `end` line once. Runs from the process `exit` event. */
|
|
1387
|
+
finish(exitCode = 0) {
|
|
1388
|
+
if (!this.file || this.ended)
|
|
1389
|
+
return;
|
|
1390
|
+
this.open();
|
|
1391
|
+
this.ended = true;
|
|
1392
|
+
for (const { input, startedAt } of this.inFlight) {
|
|
1393
|
+
if (this.calls.length >= MAX_CALLS)
|
|
1394
|
+
break;
|
|
1395
|
+
try {
|
|
1396
|
+
this.calls.push({
|
|
1397
|
+
path: routeOf(input),
|
|
1398
|
+
pending: true,
|
|
1399
|
+
ms: Math.round(performance.now() - startedAt)
|
|
1400
|
+
});
|
|
1401
|
+
} catch {
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
this.inFlight.clear();
|
|
1405
|
+
this.write({
|
|
1406
|
+
v: 1,
|
|
1407
|
+
phase: "end",
|
|
1408
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1409
|
+
host: this.host,
|
|
1410
|
+
pid: process.pid,
|
|
1411
|
+
id: this.id,
|
|
1412
|
+
event: this.event,
|
|
1413
|
+
...this.projectId ? { projectId: this.projectId } : {},
|
|
1414
|
+
output: this.outputSummary,
|
|
1415
|
+
calls: this.calls,
|
|
1416
|
+
durationMs: Math.round(performance.now() - this.startedAt),
|
|
1417
|
+
processMs: Math.round(process.uptime() * 1e3),
|
|
1418
|
+
exitCode,
|
|
1419
|
+
...this.errorKind ? { errorKind: this.errorKind } : {},
|
|
1420
|
+
...this.error ? { error: this.error } : {}
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
});
|
|
1426
|
+
|
|
851
1427
|
// packages/shared/dist/runtimeReader.js
|
|
852
1428
|
import { createHash as createHash2 } from "node:crypto";
|
|
853
|
-
import { constants } from "node:fs";
|
|
1429
|
+
import { constants as constants2 } from "node:fs";
|
|
854
1430
|
import { lstat as lstat3, open, readdir } from "node:fs/promises";
|
|
855
|
-
import { dirname as
|
|
1431
|
+
import { dirname as dirname4, join as join5, parse, resolve as resolve3, sep as sep2 } from "node:path";
|
|
856
1432
|
function safePath(path) {
|
|
857
1433
|
if (typeof path !== "string" || !/^[a-zA-Z0-9_@.+/-]+$/.test(path) || path.split("/").some((p) => !p || p === "." || p === "..") || path.includes(":"))
|
|
858
1434
|
throw new RuntimeError("permission");
|
|
@@ -903,7 +1479,7 @@ var init_runtimeReader = __esm({
|
|
|
903
1479
|
pointerPath(artifact) {
|
|
904
1480
|
if (!["cli", "scanner", "claude-code", "codex", "cursor", "grok"].includes(artifact))
|
|
905
1481
|
throw new RuntimeError("permission");
|
|
906
|
-
return
|
|
1482
|
+
return join5(this.state, "runtimes", artifact, "current");
|
|
907
1483
|
}
|
|
908
1484
|
guardedPath(path) {
|
|
909
1485
|
const absolute = resolve3(path);
|
|
@@ -913,7 +1489,7 @@ var init_runtimeReader = __esm({
|
|
|
913
1489
|
}
|
|
914
1490
|
auditRoot(path) {
|
|
915
1491
|
const [area, artifact] = path.slice(this.state.length + 1).split(sep2);
|
|
916
|
-
return area === "runtimes" ?
|
|
1492
|
+
return area === "runtimes" ? join5(this.state, area, ...artifact ? [artifact] : []) : path;
|
|
917
1493
|
}
|
|
918
1494
|
traceAudit(event) {
|
|
919
1495
|
const level = process.env.MNEMONIK_AUDIT_TRACE;
|
|
@@ -930,7 +1506,7 @@ var init_runtimeReader = __esm({
|
|
|
930
1506
|
invalidateAudit(path) {
|
|
931
1507
|
const root = this.auditRoot(path);
|
|
932
1508
|
for (const key of this.windowsAudit.keys())
|
|
933
|
-
if (key === root || (!this.options.cacheDirectories || root.startsWith(
|
|
1509
|
+
if (key === root || (!this.options.cacheDirectories || root.startsWith(join5(this.state, "runtimes") + sep2)) && key.startsWith(root + sep2))
|
|
934
1510
|
this.windowsAudit.delete(key);
|
|
935
1511
|
}
|
|
936
1512
|
audit(paths) {
|
|
@@ -964,7 +1540,7 @@ var init_runtimeReader = __esm({
|
|
|
964
1540
|
pending.add(part);
|
|
965
1541
|
if (part === this.state)
|
|
966
1542
|
break;
|
|
967
|
-
part =
|
|
1543
|
+
part = dirname4(part);
|
|
968
1544
|
}
|
|
969
1545
|
}
|
|
970
1546
|
if (!pending.size)
|
|
@@ -974,7 +1550,7 @@ var init_runtimeReader = __esm({
|
|
|
974
1550
|
const ancestors = [...pending].filter((path) => path !== root && !path.startsWith(root + sep2));
|
|
975
1551
|
const tree = [...pending].filter((path) => !ancestors.includes(path));
|
|
976
1552
|
for (const part of [...ancestors, ...tree.length ? [root] : []]) {
|
|
977
|
-
const recursive = part === root && root.startsWith(
|
|
1553
|
+
const recursive = part === root && root.startsWith(join5(this.state, "runtimes") + sep2);
|
|
978
1554
|
this.traceAudit({ event: "audit", root: part, recursive });
|
|
979
1555
|
const requested = part === root ? tree : [part];
|
|
980
1556
|
if (this.options.cacheDirectories && !this.windowsDirectories.has(part) && this.windowsAudit.get(part)?.verdict === "ok") {
|
|
@@ -1002,7 +1578,7 @@ var init_runtimeReader = __esm({
|
|
|
1002
1578
|
const windowsPaths = [];
|
|
1003
1579
|
let part = parse(absolute).root;
|
|
1004
1580
|
for (const name of absolute.slice(part.length).split(sep2)) {
|
|
1005
|
-
part =
|
|
1581
|
+
part = join5(part, name);
|
|
1006
1582
|
const stat2 = await lstat3(part).catch((e) => {
|
|
1007
1583
|
if (allowMissing && missing(e))
|
|
1008
1584
|
return null;
|
|
@@ -1056,7 +1632,7 @@ var init_runtimeReader = __esm({
|
|
|
1056
1632
|
if (process.platform !== "win32" || !this.options.cacheDirectories)
|
|
1057
1633
|
return;
|
|
1058
1634
|
const absolute = this.guardedPath(path);
|
|
1059
|
-
const parent =
|
|
1635
|
+
const parent = dirname4(absolute);
|
|
1060
1636
|
const stat2 = await lstat3(parent);
|
|
1061
1637
|
if (!stat2.isDirectory() || stat2.isSymbolicLink() || !this.windowsStats.get(parent)?.startsWith(`${stat2.dev}:${stat2.ino}:`))
|
|
1062
1638
|
throw new RuntimeError("permission");
|
|
@@ -1066,7 +1642,7 @@ var init_runtimeReader = __esm({
|
|
|
1066
1642
|
}
|
|
1067
1643
|
async bytes(path) {
|
|
1068
1644
|
await this.inspect(path);
|
|
1069
|
-
const fd = await open(path,
|
|
1645
|
+
const fd = await open(path, constants2.O_RDONLY | (process.platform === "win32" ? 0 : constants2.O_NOFOLLOW));
|
|
1070
1646
|
try {
|
|
1071
1647
|
return await fd.readFile();
|
|
1072
1648
|
} finally {
|
|
@@ -1114,7 +1690,7 @@ var init_runtimeReader = __esm({
|
|
|
1114
1690
|
async verifyOpen(artifact, ref, directory) {
|
|
1115
1691
|
let bytes;
|
|
1116
1692
|
try {
|
|
1117
|
-
bytes = await this.bytes(
|
|
1693
|
+
bytes = await this.bytes(join5(directory, "manifest.json"));
|
|
1118
1694
|
} catch (e) {
|
|
1119
1695
|
if (missing(e))
|
|
1120
1696
|
throw new RuntimeError("manifest_missing");
|
|
@@ -1135,7 +1711,7 @@ var init_runtimeReader = __esm({
|
|
|
1135
1711
|
if (artifact === "cli" && (m.source.kind !== "npm" || !Array.isArray(m.source.packages) || !m.source.packages.some((p) => p.name === "@mnemonik/cli" && p.version === m.version)))
|
|
1136
1712
|
throw new RuntimeError("unsigned");
|
|
1137
1713
|
if (process.platform === "win32")
|
|
1138
|
-
await this.audit(Object.keys(m.files).map((name) =>
|
|
1714
|
+
await this.audit(Object.keys(m.files).map((name) => join5(directory, safePath(name))));
|
|
1139
1715
|
const actual = [];
|
|
1140
1716
|
const walk = async (dir, prefix = "") => {
|
|
1141
1717
|
await this.inspect(dir, true);
|
|
@@ -1143,7 +1719,7 @@ var init_runtimeReader = __esm({
|
|
|
1143
1719
|
const key = prefix + name;
|
|
1144
1720
|
if (!prefix && name === "manifest.json")
|
|
1145
1721
|
continue;
|
|
1146
|
-
const path =
|
|
1722
|
+
const path = join5(dir, name);
|
|
1147
1723
|
if ((await lstat3(path)).isDirectory())
|
|
1148
1724
|
await walk(path, key + "/");
|
|
1149
1725
|
else {
|
|
@@ -1159,7 +1735,7 @@ var init_runtimeReader = __esm({
|
|
|
1159
1735
|
for (const [name, expected] of Object.entries(m.files)) {
|
|
1160
1736
|
if (!expected || !/^[a-f0-9]{64}$/.test(expected.sha256) || !Number.isSafeInteger(expected.size) || expected.size < 0 || typeof expected.executable !== "boolean")
|
|
1161
1737
|
throw new RuntimeError("digest_mismatch");
|
|
1162
|
-
const path =
|
|
1738
|
+
const path = join5(directory, safePath(name));
|
|
1163
1739
|
const contents = await this.bytes(path);
|
|
1164
1740
|
if (hash(contents) !== expected.sha256 || contents.length !== expected.size)
|
|
1165
1741
|
throw new RuntimeError("digest_mismatch");
|
|
@@ -1170,11 +1746,11 @@ var init_runtimeReader = __esm({
|
|
|
1170
1746
|
if (total !== m.totalSize)
|
|
1171
1747
|
throw new RuntimeError("digest_mismatch");
|
|
1172
1748
|
if (m.signer) {
|
|
1173
|
-
const signer = m.signer.platform === "linux" ? { ...m.signer, signature:
|
|
1749
|
+
const signer = m.signer.platform === "linux" ? { ...m.signer, signature: join5(directory, safePath(m.signer.signature)) } : m.signer;
|
|
1174
1750
|
if (m.signer.platform === "linux" && !m.files[m.signer.signature])
|
|
1175
1751
|
throw new RuntimeError("unsigned");
|
|
1176
1752
|
try {
|
|
1177
|
-
const entry =
|
|
1753
|
+
const entry = join5(directory, m.entry);
|
|
1178
1754
|
if (signer.platform === "win32") {
|
|
1179
1755
|
const key = JSON.stringify([entry, m.files[m.entry]?.sha256, signer.identity]);
|
|
1180
1756
|
let verification = _RuntimeReader.windowsSignatures.get(key);
|
|
@@ -1192,18 +1768,18 @@ var init_runtimeReader = __esm({
|
|
|
1192
1768
|
throw new RuntimeError("unsigned");
|
|
1193
1769
|
}
|
|
1194
1770
|
}
|
|
1195
|
-
return { directory, manifest: m, entry:
|
|
1771
|
+
return { directory, manifest: m, entry: join5(directory, m.entry), reference: ref };
|
|
1196
1772
|
}
|
|
1197
1773
|
verifyRetainedRuntime(artifact, reference) {
|
|
1198
1774
|
versionName(reference.version);
|
|
1199
|
-
return this.verifyAt(artifact, reference,
|
|
1775
|
+
return this.verifyAt(artifact, reference, join5(dirname4(this.pointerPath(artifact)), reference.version));
|
|
1200
1776
|
}
|
|
1201
1777
|
async verifyRuntime(artifact) {
|
|
1202
1778
|
return this.operation(async () => {
|
|
1203
1779
|
const p = await this.pointer(artifact);
|
|
1204
1780
|
if (!p)
|
|
1205
1781
|
throw new RuntimeError("manifest_missing");
|
|
1206
|
-
return this.verifyOpen(artifact, p.current,
|
|
1782
|
+
return this.verifyOpen(artifact, p.current, join5(dirname4(this.pointerPath(artifact)), p.current.version));
|
|
1207
1783
|
});
|
|
1208
1784
|
}
|
|
1209
1785
|
};
|
|
@@ -1212,10 +1788,10 @@ var init_runtimeReader = __esm({
|
|
|
1212
1788
|
|
|
1213
1789
|
// packages/shared/dist/projectSetupHandoff.js
|
|
1214
1790
|
import { spawn } from "node:child_process";
|
|
1215
|
-
import { constants as
|
|
1216
|
-
import { lstat as lstat4, mkdir, open as open2, readdir as readdir2, realpath, rename, rm } from "node:fs/promises";
|
|
1791
|
+
import { constants as constants3 } from "node:fs";
|
|
1792
|
+
import { lstat as lstat4, mkdir, open as open2, readdir as readdir2, realpath as realpath2, rename, rm } from "node:fs/promises";
|
|
1217
1793
|
import { tmpdir } from "node:os";
|
|
1218
|
-
import { basename as basename2, dirname as
|
|
1794
|
+
import { basename as basename2, dirname as dirname5, join as join6, relative } from "node:path";
|
|
1219
1795
|
import { performance as performance2 } from "node:perf_hooks";
|
|
1220
1796
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1221
1797
|
function projectSetupActions(value) {
|
|
@@ -1255,7 +1831,7 @@ async function privateDirectory(path, session) {
|
|
|
1255
1831
|
}
|
|
1256
1832
|
}
|
|
1257
1833
|
async function readPrivate(path) {
|
|
1258
|
-
const file = await open2(path,
|
|
1834
|
+
const file = await open2(path, constants3.O_RDONLY | constants3.O_NOFOLLOW);
|
|
1259
1835
|
try {
|
|
1260
1836
|
const st = await file.stat();
|
|
1261
1837
|
if (!st.isFile() || st.size > 64 * 1024 || process.getuid && (st.uid !== process.getuid() || st.mode & 63))
|
|
@@ -1266,8 +1842,8 @@ async function readPrivate(path) {
|
|
|
1266
1842
|
}
|
|
1267
1843
|
}
|
|
1268
1844
|
async function writeDiagnostic(path, value) {
|
|
1269
|
-
await privateDirectory(
|
|
1270
|
-
await privateDirectory(
|
|
1845
|
+
await privateDirectory(dirname5(dirname5(path)), dirname5(path));
|
|
1846
|
+
await privateDirectory(dirname5(path), dirname5(path));
|
|
1271
1847
|
const temp = `${path}.${randomUUID2()}`;
|
|
1272
1848
|
try {
|
|
1273
1849
|
const file = await open2(temp, "wx", 384);
|
|
@@ -1307,13 +1883,13 @@ async function waitForResult(path, waitMs) {
|
|
|
1307
1883
|
return result;
|
|
1308
1884
|
if (Date.now() >= deadline)
|
|
1309
1885
|
return RUNNING;
|
|
1310
|
-
await new Promise((
|
|
1886
|
+
await new Promise((resolve6) => setTimeout(resolve6, Math.min(50, deadline - Date.now())));
|
|
1311
1887
|
}
|
|
1312
1888
|
}
|
|
1313
1889
|
async function runEnsure(options) {
|
|
1314
|
-
await privateDirectory(
|
|
1890
|
+
await privateDirectory(dirname5(options.directory), options.directory);
|
|
1315
1891
|
await privateDirectory(options.directory, options.directory);
|
|
1316
|
-
const runFile =
|
|
1892
|
+
const runFile = join6(options.directory, "project-setup-run.json");
|
|
1317
1893
|
let previous;
|
|
1318
1894
|
try {
|
|
1319
1895
|
previous = JSON.parse(await readPrivate(runFile));
|
|
@@ -1321,7 +1897,7 @@ async function runEnsure(options) {
|
|
|
1321
1897
|
previous = void 0;
|
|
1322
1898
|
}
|
|
1323
1899
|
if (previous && previous.root === options.root && typeof previous.result === "string") {
|
|
1324
|
-
const result2 =
|
|
1900
|
+
const result2 = join6(options.directory, basename2(previous.result));
|
|
1325
1901
|
const finished = await readResult(result2).catch(() => ({ status: "failed" }));
|
|
1326
1902
|
if (finished === void 0 && Date.now() - previous.startedAt < RUN_STALE_MS) {
|
|
1327
1903
|
const outcome2 = await waitForResult(result2, options.waitMs);
|
|
@@ -1334,7 +1910,7 @@ async function runEnsure(options) {
|
|
|
1334
1910
|
if (record(finished) && finished.status === "done")
|
|
1335
1911
|
return finished;
|
|
1336
1912
|
}
|
|
1337
|
-
const result =
|
|
1913
|
+
const result = join6(options.directory, `project-setup-${randomUUID2()}.result.json`);
|
|
1338
1914
|
const run = {
|
|
1339
1915
|
requestId: options.requestId,
|
|
1340
1916
|
root: options.root,
|
|
@@ -1402,28 +1978,28 @@ async function handoffProjectSetup(input) {
|
|
|
1402
1978
|
try {
|
|
1403
1979
|
if (!input.cwd)
|
|
1404
1980
|
throw new Error("safe_cwd_unavailable");
|
|
1405
|
-
const cache = JSON.parse(await readPrivate(
|
|
1981
|
+
const cache = JSON.parse(await readPrivate(join6(dirname5(input.stateFile), "bound-context.json")));
|
|
1406
1982
|
const [, family, body] = JSON.parse(cache.key);
|
|
1407
1983
|
if (!input.familyId || family !== input.familyId || body.host !== input.host || body.hostSessionId !== input.hostSessionId || cache.expiresAt <= Date.now() || !cache.canonicalRoot)
|
|
1408
1984
|
throw new Error("bound_context_unavailable");
|
|
1409
|
-
root = await
|
|
1985
|
+
root = await realpath2(cache.canonicalRoot);
|
|
1410
1986
|
const { findProjectIdentityDetailed: findProjectIdentityDetailed2 } = await Promise.resolve().then(() => (init_hookRuntime(), hookRuntime_exports));
|
|
1411
|
-
const identity = await findProjectIdentityDetailed2(await
|
|
1412
|
-
if (identity.kind === "git_unavailable" || root !== await
|
|
1987
|
+
const identity = await findProjectIdentityDetailed2(await realpath2(input.cwd));
|
|
1988
|
+
if (identity.kind === "git_unavailable" || root !== await realpath2(identity.root))
|
|
1413
1989
|
throw new Error("bound_root_changed");
|
|
1414
1990
|
if (!requestId)
|
|
1415
1991
|
throw new Error("manual_setup_required");
|
|
1416
1992
|
const runtime = await new RuntimeReader(input.stateDir).verifyRuntime("cli").catch(() => {
|
|
1417
1993
|
throw new Error("verified_cli_runtime_unavailable");
|
|
1418
1994
|
});
|
|
1419
|
-
const bin =
|
|
1995
|
+
const bin = join6(dirname5(runtime.entry), "bin.js");
|
|
1420
1996
|
if (!runtime.manifest.files[relative(runtime.directory, bin).replaceAll("\\", "/")])
|
|
1421
1997
|
throw new Error("verified_cli_bin_missing");
|
|
1422
1998
|
const result = await runEnsure({
|
|
1423
1999
|
bin,
|
|
1424
2000
|
root,
|
|
1425
2001
|
requestId,
|
|
1426
|
-
directory:
|
|
2002
|
+
directory: dirname5(input.stateFile),
|
|
1427
2003
|
waitMs: input.waitMs ?? Math.max(0, hookDeadlineMs(input.host) - performance2.now())
|
|
1428
2004
|
});
|
|
1429
2005
|
if (result === RUNNING) {
|
|
@@ -1453,7 +2029,7 @@ async function handoffProjectSetup(input) {
|
|
|
1453
2029
|
requestId,
|
|
1454
2030
|
outcome,
|
|
1455
2031
|
time: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1456
|
-
rootHash: root ? hash(root) : input.cwd ? hash(await
|
|
2032
|
+
rootHash: root ? hash(root) : input.cwd ? hash(await realpath2(input.cwd)) : null,
|
|
1457
2033
|
action,
|
|
1458
2034
|
reason
|
|
1459
2035
|
});
|
|
@@ -1463,11 +2039,11 @@ async function handoffProjectSetup(input) {
|
|
|
1463
2039
|
return action;
|
|
1464
2040
|
}
|
|
1465
2041
|
async function pendingProjectSetup(root) {
|
|
1466
|
-
const rootHash = hash(await
|
|
2042
|
+
const rootHash = hash(await realpath2(root));
|
|
1467
2043
|
const results = [];
|
|
1468
2044
|
const uid = process.getuid?.() ?? "user";
|
|
1469
2045
|
for (const host of ["credit", "cursor", "grok"]) {
|
|
1470
|
-
const directory =
|
|
2046
|
+
const directory = join6(tmpdir(), `mnemonik-${host}-${uid}`);
|
|
1471
2047
|
try {
|
|
1472
2048
|
const stat2 = await lstat4(directory);
|
|
1473
2049
|
if (stat2.isSymbolicLink() || !stat2.isDirectory())
|
|
@@ -1476,9 +2052,9 @@ async function pendingProjectSetup(root) {
|
|
|
1476
2052
|
if (!/^[a-f0-9]{64}$/.test(name))
|
|
1477
2053
|
continue;
|
|
1478
2054
|
try {
|
|
1479
|
-
if ((await lstat4(
|
|
2055
|
+
if ((await lstat4(join6(directory, name))).isSymbolicLink())
|
|
1480
2056
|
continue;
|
|
1481
|
-
const diagnostic = JSON.parse(await readPrivate(
|
|
2057
|
+
const diagnostic = JSON.parse(await readPrivate(join6(directory, name, "project-setup.json")));
|
|
1482
2058
|
if ((diagnostic.rootHash === rootHash || diagnostic.rootHash === null) && ["done", "ACTION_REQUIRED", "failed"].includes(diagnostic.outcome) && typeof diagnostic.action === "string" && diagnostic.action.length < 2e3 && Date.now() - Date.parse(diagnostic.time) < 24 * 60 * 60 * 1e3)
|
|
1483
2059
|
results.push(diagnostic);
|
|
1484
2060
|
} catch {
|
|
@@ -1530,9 +2106,9 @@ process.stdin.on('end', () => {
|
|
|
1530
2106
|
});
|
|
1531
2107
|
|
|
1532
2108
|
// packages/shared/dist/settingsIo.js
|
|
1533
|
-
import { createHash as createHash3, randomBytes } from "node:crypto";
|
|
1534
|
-
import { link, lstat as lstat5, mkdir as mkdir2, open as open3, readFile as readFile4, readdir as readdir3, realpath as
|
|
1535
|
-
import { basename as basename3, dirname as
|
|
2109
|
+
import { createHash as createHash3, randomBytes as randomBytes2 } from "node:crypto";
|
|
2110
|
+
import { link, lstat as lstat5, mkdir as mkdir2, open as open3, readFile as readFile4, readdir as readdir3, realpath as realpath3, rename as rename2, rm as rm2, rmdir, stat, unlink } from "node:fs/promises";
|
|
2111
|
+
import { basename as basename3, dirname as dirname6, join as join7, resolve as resolve4 } from "node:path";
|
|
1536
2112
|
function errorCode(error) {
|
|
1537
2113
|
return error && typeof error === "object" && "code" in error ? String(error.code) : void 0;
|
|
1538
2114
|
}
|
|
@@ -1579,7 +2155,7 @@ async function removeStaleLock(lockPath, staleMs = LOCK_STALE_MS) {
|
|
|
1579
2155
|
const [entry] = entries;
|
|
1580
2156
|
if (entries.length !== 1 || entry === void 0 || !entry.startsWith("owner-"))
|
|
1581
2157
|
return false;
|
|
1582
|
-
const tokenPath =
|
|
2158
|
+
const tokenPath = join7(lockPath, entry);
|
|
1583
2159
|
const owner = await readFile4(tokenPath, "utf8");
|
|
1584
2160
|
if (lockOwnerIsAlive(owner))
|
|
1585
2161
|
return false;
|
|
@@ -1600,13 +2176,13 @@ async function removeStaleLock(lockPath, staleMs = LOCK_STALE_MS) {
|
|
|
1600
2176
|
}
|
|
1601
2177
|
}
|
|
1602
2178
|
async function acquireLock(lockPath, options = {}) {
|
|
1603
|
-
await mkdir2(
|
|
1604
|
-
const owner = `${process.pid} ${Date.now()} ${
|
|
2179
|
+
await mkdir2(dirname6(lockPath), { recursive: true });
|
|
2180
|
+
const owner = `${process.pid} ${Date.now()} ${randomBytes2(12).toString("hex")}
|
|
1605
2181
|
`;
|
|
1606
2182
|
for (let attempt = 0; attempt < (options.retries ?? LOCK_RETRIES); attempt += 1) {
|
|
1607
2183
|
try {
|
|
1608
2184
|
await mkdir2(lockPath, { mode: 448 });
|
|
1609
|
-
const tokenPath =
|
|
2185
|
+
const tokenPath = join7(lockPath, `owner-${process.pid}-${randomBytes2(12).toString("hex")}`);
|
|
1610
2186
|
const handle = await open3(tokenPath, "wx", 384);
|
|
1611
2187
|
try {
|
|
1612
2188
|
await handle.writeFile(owner, "utf8");
|
|
@@ -1615,7 +2191,7 @@ async function acquireLock(lockPath, options = {}) {
|
|
|
1615
2191
|
await handle.close();
|
|
1616
2192
|
}
|
|
1617
2193
|
await syncDirectory(lockPath);
|
|
1618
|
-
await syncDirectory(
|
|
2194
|
+
await syncDirectory(dirname6(lockPath));
|
|
1619
2195
|
return tokenPath;
|
|
1620
2196
|
} catch (error) {
|
|
1621
2197
|
if (errorCode(error) !== "EEXIST") {
|
|
@@ -1645,7 +2221,7 @@ async function withFileLock(lockPath, action, options) {
|
|
|
1645
2221
|
try {
|
|
1646
2222
|
await unlink(tokenPath);
|
|
1647
2223
|
await rmdir(lockPath);
|
|
1648
|
-
await syncDirectory(
|
|
2224
|
+
await syncDirectory(dirname6(lockPath));
|
|
1649
2225
|
} catch {
|
|
1650
2226
|
}
|
|
1651
2227
|
}
|
|
@@ -1661,30 +2237,30 @@ var init_settingsIo = __esm({
|
|
|
1661
2237
|
});
|
|
1662
2238
|
|
|
1663
2239
|
// packages/shared/dist/hookConversation.js
|
|
1664
|
-
import { createHash as createHash4, randomBytes as
|
|
1665
|
-
import { constants as
|
|
2240
|
+
import { createHash as createHash4, randomBytes as randomBytes3 } from "node:crypto";
|
|
2241
|
+
import { constants as constants4 } from "node:fs";
|
|
1666
2242
|
import { lstat as lstat6, mkdir as mkdir3, open as open4, rename as rename3, rm as rm3, utimes } from "node:fs/promises";
|
|
1667
|
-
import { dirname as
|
|
2243
|
+
import { dirname as dirname7 } from "node:path";
|
|
1668
2244
|
function boundedHandle(value) {
|
|
1669
2245
|
return typeof value === "string" && /^cv1\.[0-9a-f-]{36}\.[A-Za-z0-9_-]{43}$/.test(value);
|
|
1670
2246
|
}
|
|
1671
2247
|
async function configureHookConversation(input) {
|
|
1672
|
-
|
|
1673
|
-
await
|
|
2248
|
+
active2 = new HookConversation(input);
|
|
2249
|
+
await active2.load();
|
|
1674
2250
|
}
|
|
1675
2251
|
function currentConversationId() {
|
|
1676
|
-
return
|
|
2252
|
+
return active2?.id;
|
|
1677
2253
|
}
|
|
1678
2254
|
function conversationMarker() {
|
|
1679
|
-
return
|
|
2255
|
+
return active2?.id ? `<system-reminder>mnemonik-conversation: ${active2.id}</system-reminder>` : void 0;
|
|
1680
2256
|
}
|
|
1681
2257
|
async function adoptHookConversation(result) {
|
|
1682
|
-
await
|
|
2258
|
+
await active2?.adopt(result);
|
|
1683
2259
|
}
|
|
1684
2260
|
function withHookConversation(url, init, post) {
|
|
1685
|
-
return
|
|
2261
|
+
return active2 ? active2.request(url, init, post) : post(init);
|
|
1686
2262
|
}
|
|
1687
|
-
var RETENTION_MS, CONVERSATION_STATE_FILE, HookConversation,
|
|
2263
|
+
var RETENTION_MS, CONVERSATION_STATE_FILE, HookConversation, active2;
|
|
1688
2264
|
var init_hookConversation = __esm({
|
|
1689
2265
|
"packages/shared/dist/hookConversation.js"() {
|
|
1690
2266
|
"use strict";
|
|
@@ -1712,10 +2288,10 @@ var init_hookConversation = __esm({
|
|
|
1712
2288
|
async load() {
|
|
1713
2289
|
this.conversationId = void 0;
|
|
1714
2290
|
try {
|
|
1715
|
-
const directory = await lstat6(
|
|
2291
|
+
const directory = await lstat6(dirname7(this.input.stateFile));
|
|
1716
2292
|
if (!directory.isDirectory() || directory.isSymbolicLink() || process.platform !== "win32" && (process.getuid && directory.uid !== process.getuid() || directory.mode & 63))
|
|
1717
2293
|
return;
|
|
1718
|
-
const file = await open4(this.input.stateFile,
|
|
2294
|
+
const file = await open4(this.input.stateFile, constants4.O_RDONLY | constants4.O_NOFOLLOW);
|
|
1719
2295
|
try {
|
|
1720
2296
|
const stat2 = await file.stat();
|
|
1721
2297
|
if (!stat2.isFile() || stat2.size > 4096 || process.platform !== "win32" && (process.getuid && stat2.uid !== process.getuid() || stat2.mode & 63))
|
|
@@ -1734,7 +2310,7 @@ var init_hookConversation = __esm({
|
|
|
1734
2310
|
}
|
|
1735
2311
|
}
|
|
1736
2312
|
async locked(run) {
|
|
1737
|
-
const directory =
|
|
2313
|
+
const directory = dirname7(this.input.stateFile);
|
|
1738
2314
|
await mkdir3(directory, { recursive: true, mode: 448 });
|
|
1739
2315
|
const stat2 = await lstat6(directory);
|
|
1740
2316
|
if (!stat2.isDirectory() || stat2.isSymbolicLink() || process.platform !== "win32" && (process.getuid && stat2.uid !== process.getuid() || stat2.mode & 63))
|
|
@@ -1747,8 +2323,8 @@ var init_hookConversation = __esm({
|
|
|
1747
2323
|
});
|
|
1748
2324
|
}
|
|
1749
2325
|
async save(id) {
|
|
1750
|
-
const temporary = `${this.input.stateFile}.${
|
|
1751
|
-
const file = await open4(temporary,
|
|
2326
|
+
const temporary = `${this.input.stateFile}.${randomBytes3(8).toString("hex")}`;
|
|
2327
|
+
const file = await open4(temporary, constants4.O_CREAT | constants4.O_EXCL | constants4.O_WRONLY | constants4.O_NOFOLLOW, 384);
|
|
1752
2328
|
try {
|
|
1753
2329
|
await file.writeFile(JSON.stringify({ v: 1, scope: this.scope, conversationId: id, usedAt: Date.now() }));
|
|
1754
2330
|
} finally {
|
|
@@ -2022,7 +2598,10 @@ var hookRuntime_exports = {};
|
|
|
2022
2598
|
__export(hookRuntime_exports, {
|
|
2023
2599
|
CONVERSATION_STATE_FILE: () => CONVERSATION_STATE_FILE,
|
|
2024
2600
|
HOOK_STDIN_MAX_BYTES: () => HOOK_STDIN_MAX_BYTES,
|
|
2601
|
+
HOOK_TRACE_ENV: () => HOOK_TRACE_ENV,
|
|
2602
|
+
HOOK_TRACE_MAX_BYTES: () => HOOK_TRACE_MAX_BYTES,
|
|
2025
2603
|
HookConversation: () => HookConversation,
|
|
2604
|
+
HookTrace: () => HookTrace,
|
|
2026
2605
|
PROJECT_ENSURE_TIMEOUT_MS: () => PROJECT_ENSURE_TIMEOUT_MS,
|
|
2027
2606
|
PROJECT_SETUP_MANUAL: () => PROJECT_SETUP_MANUAL,
|
|
2028
2607
|
PROJECT_SETUP_RUNNING: () => PROJECT_SETUP_RUNNING,
|
|
@@ -2031,6 +2610,7 @@ __export(hookRuntime_exports, {
|
|
|
2031
2610
|
RuntimeReader: () => RuntimeReader,
|
|
2032
2611
|
aclRecords: () => aclRecords,
|
|
2033
2612
|
adoptHookConversation: () => adoptHookConversation,
|
|
2613
|
+
appendHookTraceLine: () => appendHookTraceLine,
|
|
2034
2614
|
auditWindowsPermissions: () => auditWindowsPermissions,
|
|
2035
2615
|
bindHookContext: () => bindHookContext,
|
|
2036
2616
|
clearProjectIdentityCache: () => clearProjectIdentityCache,
|
|
@@ -2038,21 +2618,31 @@ __export(hookRuntime_exports, {
|
|
|
2038
2618
|
conversationMarker: () => conversationMarker,
|
|
2039
2619
|
currentConversationId: () => currentConversationId,
|
|
2040
2620
|
ensureWindowsPrivateDirectorySync: () => ensureWindowsPrivateDirectorySync,
|
|
2621
|
+
errorKind: () => errorKind,
|
|
2041
2622
|
execute: () => execute,
|
|
2042
2623
|
findProjectIdentity: () => findProjectIdentity,
|
|
2043
2624
|
findProjectIdentityDetailed: () => findProjectIdentityDetailed,
|
|
2044
2625
|
handoffProjectSetup: () => handoffProjectSetup,
|
|
2045
2626
|
hash: () => hash,
|
|
2627
|
+
hookTraceDirectory: () => hookTraceDirectory,
|
|
2628
|
+
hookTraceEnabled: () => hookTraceEnabled,
|
|
2629
|
+
hookTraceFile: () => hookTraceFile,
|
|
2046
2630
|
missing: () => missing,
|
|
2631
|
+
modelTypedCwd: () => modelTypedCwd,
|
|
2632
|
+
noteHookTraceProject: () => noteHookTraceProject,
|
|
2047
2633
|
parseHookResponse: () => parseHookResponse,
|
|
2048
2634
|
pendingProjectSetup: () => pendingProjectSetup,
|
|
2049
2635
|
postHookBoundJson: () => postHookBoundJson,
|
|
2050
2636
|
prepareWindowsAclDirectory: () => prepareWindowsAclDirectory,
|
|
2637
|
+
projectScope: () => projectScope,
|
|
2051
2638
|
projectSetupActions: () => projectSetupActions,
|
|
2052
2639
|
protectWindowsDirectory: () => protectWindowsDirectory,
|
|
2053
2640
|
readHookStdin: () => readHookStdin,
|
|
2054
2641
|
safePath: () => safePath,
|
|
2642
|
+
scrubHookTraceMessage: () => scrubHookTraceMessage,
|
|
2055
2643
|
setupResponse: () => setupResponse,
|
|
2644
|
+
startHookTrace: () => startHookTrace,
|
|
2645
|
+
summarizeHookOutput: () => summarizeHookOutput,
|
|
2056
2646
|
validHookSessionId: () => validHookSessionId,
|
|
2057
2647
|
verifyMinisign: () => verifyMinisign,
|
|
2058
2648
|
verifySigner: () => verifySigner,
|
|
@@ -2064,36 +2654,43 @@ __export(hookRuntime_exports, {
|
|
|
2064
2654
|
withHookConversation: () => withHookConversation
|
|
2065
2655
|
});
|
|
2066
2656
|
import { stderr, stdin } from "node:process";
|
|
2657
|
+
import { homedir as homedir3 } from "node:os";
|
|
2658
|
+
import { join as join8, resolve as resolve5 } from "node:path";
|
|
2659
|
+
function modelTypedCwd(hostCwd, typed) {
|
|
2660
|
+
const expanded = typed === "~" ? homedir3() : /^~[\\/]/.test(typed) ? join8(homedir3(), typed.slice(2)) : typed;
|
|
2661
|
+
return resolve5(hostCwd, expanded);
|
|
2662
|
+
}
|
|
2067
2663
|
function clearProjectIdentityCache() {
|
|
2068
2664
|
identityCache.clear();
|
|
2069
2665
|
}
|
|
2070
|
-
async function findProjectIdentity(startCwd
|
|
2666
|
+
async function findProjectIdentity(startCwd) {
|
|
2071
2667
|
if (!startCwd)
|
|
2072
2668
|
return null;
|
|
2073
|
-
const key =
|
|
2074
|
-
startCwd,
|
|
2075
|
-
options.allowNestedInherit ?? null,
|
|
2076
|
-
options.maxDepth ?? null
|
|
2077
|
-
]);
|
|
2669
|
+
const key = startCwd;
|
|
2078
2670
|
let pending = identityCache.get(key);
|
|
2079
2671
|
if (!pending) {
|
|
2080
|
-
pending = findProjectIdentityDetailed(startCwd
|
|
2672
|
+
pending = findProjectIdentityDetailed(startCwd).then((result) => result.kind === "ok" ? {
|
|
2081
2673
|
projectId: result.identity.projectId,
|
|
2082
2674
|
projectName: result.identity.projectName,
|
|
2083
2675
|
projectRoot: result.root
|
|
2084
2676
|
} : null);
|
|
2085
2677
|
identityCache.set(key, pending);
|
|
2086
2678
|
pending.catch(() => identityCache.delete(key));
|
|
2679
|
+
pending.then((found) => noteHookTraceProject(found?.projectId), () => void 0);
|
|
2087
2680
|
}
|
|
2088
2681
|
return pending;
|
|
2089
2682
|
}
|
|
2090
|
-
async function
|
|
2091
|
-
|
|
2683
|
+
async function projectScope(startCwd) {
|
|
2684
|
+
const identity = await findProjectIdentity(startCwd);
|
|
2685
|
+
return identity ? { projectId: identity.projectId, projectRoot: identity.projectRoot } : {};
|
|
2686
|
+
}
|
|
2687
|
+
async function findProjectIdentityDetailed(startCwd) {
|
|
2688
|
+
return resolveNearestProjectIdentity(startCwd);
|
|
2092
2689
|
}
|
|
2093
2690
|
function readHookStdin(options = {}) {
|
|
2094
2691
|
const stream = options.stream ?? stdin;
|
|
2095
2692
|
const maxBytes = options.maxBytes ?? HOOK_STDIN_MAX_BYTES;
|
|
2096
|
-
return new Promise((
|
|
2693
|
+
return new Promise((resolve6) => {
|
|
2097
2694
|
const chunks = [];
|
|
2098
2695
|
let bytes = 0;
|
|
2099
2696
|
let settled = false;
|
|
@@ -2109,7 +2706,7 @@ function readHookStdin(options = {}) {
|
|
|
2109
2706
|
stream.pause();
|
|
2110
2707
|
stream.destroy?.();
|
|
2111
2708
|
}
|
|
2112
|
-
|
|
2709
|
+
resolve6(value);
|
|
2113
2710
|
};
|
|
2114
2711
|
const onData = (chunk) => {
|
|
2115
2712
|
const buffer = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
@@ -2126,13 +2723,7 @@ function readHookStdin(options = {}) {
|
|
|
2126
2723
|
});
|
|
2127
2724
|
}
|
|
2128
2725
|
async function parseHookResponse(response) {
|
|
2129
|
-
|
|
2130
|
-
const result = body;
|
|
2131
|
-
if (!mismatchEmitted && result?.ok === false && result.reason === "identity_mismatch") {
|
|
2132
|
-
stderr.write("mnemonik-hook: project identity mismatch - .mnemonik.json points to one project, but this directory is registered to a different one on the server. Fix .mnemonik.json (delete it and let session_bootstrap rewrite it) or cd to the correct project root.\n");
|
|
2133
|
-
mismatchEmitted = true;
|
|
2134
|
-
}
|
|
2135
|
-
return body;
|
|
2726
|
+
return await response.json();
|
|
2136
2727
|
}
|
|
2137
2728
|
function validHookSessionId(value) {
|
|
2138
2729
|
return typeof value === "string" && /^[a-zA-Z0-9@._+:-]{1,255}$/.test(value) && !["cursor-unknown-session", "codex-unknown-session"].includes(value);
|
|
@@ -2148,21 +2739,21 @@ async function postHookBoundJson(server, path, token, body) {
|
|
|
2148
2739
|
}
|
|
2149
2740
|
async function bindHookContext(input, familyId, hmac, post, options = {}) {
|
|
2150
2741
|
const { execFile: execFile3 } = await import("node:child_process");
|
|
2151
|
-
const { constants:
|
|
2152
|
-
const { lstat: lstat7, mkdir: mkdir4, open: open5, realpath:
|
|
2153
|
-
const { dirname:
|
|
2742
|
+
const { constants: constants5 } = await import("node:fs");
|
|
2743
|
+
const { lstat: lstat7, mkdir: mkdir4, open: open5, realpath: realpath4 } = await import("node:fs/promises");
|
|
2744
|
+
const { dirname: dirname8 } = await import("node:path");
|
|
2154
2745
|
const { selectRemote: selectRemote2 } = await Promise.resolve().then(() => (init_repositoryFingerprint(), repositoryFingerprint_exports));
|
|
2155
|
-
const identity = await findProjectIdentityDetailed(await
|
|
2746
|
+
const identity = await findProjectIdentityDetailed(await realpath4(input.cwd));
|
|
2156
2747
|
if (identity.kind === "git_unavailable")
|
|
2157
2748
|
throw new Error("context unavailable");
|
|
2158
|
-
const root = await
|
|
2749
|
+
const root = await realpath4(identity.root);
|
|
2159
2750
|
const binding = await hmac(root);
|
|
2160
2751
|
const gitEnv = { LC_ALL: "C" };
|
|
2161
2752
|
for (const key2 of ["PATH", "HOME", "LANG", "SYSTEMROOT", "TEMP", "TMP", "USERPROFILE"])
|
|
2162
2753
|
if (process.env[key2] !== void 0)
|
|
2163
2754
|
gitEnv[key2] = process.env[key2];
|
|
2164
|
-
const git = (args) => new Promise((
|
|
2165
|
-
execFile3("git", args, { cwd: root, env: gitEnv, timeout: 2e3, encoding: "utf8" }, (error, stdout2) =>
|
|
2755
|
+
const git = (args) => new Promise((resolve6) => {
|
|
2756
|
+
execFile3("git", args, { cwd: root, env: gitEnv, timeout: 2e3, encoding: "utf8" }, (error, stdout2) => resolve6(error ? [] : stdout2.trim().split(/\r?\n/).filter(Boolean)));
|
|
2166
2757
|
});
|
|
2167
2758
|
const remotes = identity.repository.kind === "git" ? await git(["remote"]) : [];
|
|
2168
2759
|
const selection = selectRemote2(await Promise.all(remotes.map(async (name) => ({
|
|
@@ -2187,8 +2778,8 @@ async function bindHookContext(input, familyId, hmac, post, options = {}) {
|
|
|
2187
2778
|
...identity.kind === "ok" ? { projectId: identity.identity.projectId } : {}
|
|
2188
2779
|
};
|
|
2189
2780
|
const key = JSON.stringify([input.server, familyId, body]);
|
|
2190
|
-
const directory =
|
|
2191
|
-
for (const path of [
|
|
2781
|
+
const directory = dirname8(input.stateFile);
|
|
2782
|
+
for (const path of [dirname8(directory), directory]) {
|
|
2192
2783
|
const created = await mkdir4(path, { recursive: true, mode: 448 });
|
|
2193
2784
|
const stat2 = await lstat7(path);
|
|
2194
2785
|
if (!stat2.isDirectory() || stat2.isSymbolicLink() || (options.platform ?? process.platform) !== "win32" && (process.getuid && stat2.uid !== process.getuid() || stat2.mode & 63))
|
|
@@ -2197,7 +2788,7 @@ async function bindHookContext(input, familyId, hmac, post, options = {}) {
|
|
|
2197
2788
|
await protectWindowsDirectory(path, created !== void 0, options.run, directory);
|
|
2198
2789
|
}
|
|
2199
2790
|
}
|
|
2200
|
-
const file = await open5(input.stateFile,
|
|
2791
|
+
const file = await open5(input.stateFile, constants5.O_CREAT | constants5.O_RDWR | constants5.O_NOFOLLOW, 384);
|
|
2201
2792
|
try {
|
|
2202
2793
|
let cache;
|
|
2203
2794
|
try {
|
|
@@ -2227,20 +2818,21 @@ async function bindHookContext(input, familyId, hmac, post, options = {}) {
|
|
|
2227
2818
|
await file.close();
|
|
2228
2819
|
}
|
|
2229
2820
|
}
|
|
2230
|
-
var identityCache, HOOK_STDIN_MAX_BYTES
|
|
2821
|
+
var identityCache, HOOK_STDIN_MAX_BYTES;
|
|
2231
2822
|
var init_hookRuntime = __esm({
|
|
2232
2823
|
"packages/shared/dist/hookRuntime.js"() {
|
|
2233
2824
|
"use strict";
|
|
2234
2825
|
init_runtimeSigners();
|
|
2235
2826
|
init_hookTimeouts();
|
|
2236
2827
|
init_repositoryRoot();
|
|
2828
|
+
init_hookTrace();
|
|
2237
2829
|
init_runtimeReader();
|
|
2238
2830
|
init_runtimeSigners();
|
|
2239
2831
|
init_projectSetupHandoff();
|
|
2240
2832
|
init_hookConversation();
|
|
2833
|
+
init_hookTrace();
|
|
2241
2834
|
identityCache = /* @__PURE__ */ new Map();
|
|
2242
2835
|
HOOK_STDIN_MAX_BYTES = 1048576;
|
|
2243
|
-
mismatchEmitted = false;
|
|
2244
2836
|
}
|
|
2245
2837
|
});
|
|
2246
2838
|
|
|
@@ -2249,7 +2841,10 @@ init_hookRuntime();
|
|
|
2249
2841
|
export {
|
|
2250
2842
|
CONVERSATION_STATE_FILE,
|
|
2251
2843
|
HOOK_STDIN_MAX_BYTES,
|
|
2844
|
+
HOOK_TRACE_ENV,
|
|
2845
|
+
HOOK_TRACE_MAX_BYTES,
|
|
2252
2846
|
HookConversation,
|
|
2847
|
+
HookTrace,
|
|
2253
2848
|
PROJECT_ENSURE_TIMEOUT_MS,
|
|
2254
2849
|
PROJECT_SETUP_MANUAL,
|
|
2255
2850
|
PROJECT_SETUP_RUNNING,
|
|
@@ -2258,6 +2853,7 @@ export {
|
|
|
2258
2853
|
RuntimeReader,
|
|
2259
2854
|
aclRecords,
|
|
2260
2855
|
adoptHookConversation,
|
|
2856
|
+
appendHookTraceLine,
|
|
2261
2857
|
auditWindowsPermissions,
|
|
2262
2858
|
bindHookContext,
|
|
2263
2859
|
clearProjectIdentityCache,
|
|
@@ -2265,21 +2861,31 @@ export {
|
|
|
2265
2861
|
conversationMarker,
|
|
2266
2862
|
currentConversationId,
|
|
2267
2863
|
ensureWindowsPrivateDirectorySync,
|
|
2864
|
+
errorKind,
|
|
2268
2865
|
execute,
|
|
2269
2866
|
findProjectIdentity,
|
|
2270
2867
|
findProjectIdentityDetailed,
|
|
2271
2868
|
handoffProjectSetup,
|
|
2272
2869
|
hash,
|
|
2870
|
+
hookTraceDirectory,
|
|
2871
|
+
hookTraceEnabled,
|
|
2872
|
+
hookTraceFile,
|
|
2273
2873
|
missing,
|
|
2874
|
+
modelTypedCwd,
|
|
2875
|
+
noteHookTraceProject,
|
|
2274
2876
|
parseHookResponse,
|
|
2275
2877
|
pendingProjectSetup,
|
|
2276
2878
|
postHookBoundJson,
|
|
2277
2879
|
prepareWindowsAclDirectory,
|
|
2880
|
+
projectScope,
|
|
2278
2881
|
projectSetupActions,
|
|
2279
2882
|
protectWindowsDirectory,
|
|
2280
2883
|
readHookStdin,
|
|
2281
2884
|
safePath,
|
|
2885
|
+
scrubHookTraceMessage,
|
|
2282
2886
|
setupResponse,
|
|
2887
|
+
startHookTrace,
|
|
2888
|
+
summarizeHookOutput,
|
|
2283
2889
|
validHookSessionId,
|
|
2284
2890
|
verifyMinisign,
|
|
2285
2891
|
verifySigner,
|