@ai-matrx/coding-sessions 0.1.81 → 0.1.83
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/CHANGELOG.md +12 -0
- package/dist/engine/index.cjs +257 -81
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +1 -1
- package/dist/engine/index.d.ts +1 -1
- package/dist/engine/index.js +250 -74
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/worker.js +13 -1
- package/dist/engine/worker.js.map +1 -1
- package/dist/{index-RYOb-YZT.d.cts → index-DQ0teQvw.d.cts} +18 -0
- package/dist/{index-RYOb-YZT.d.ts → index-DQ0teQvw.d.ts} +18 -0
- package/dist/index.cjs +21 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +21 -3
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +62 -8
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +18 -0
- package/dist/react/index.d.ts +18 -0
- package/dist/react/index.js +62 -8
- package/dist/react/index.js.map +1 -1
- package/dist/server/index.cjs +266 -90
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +258 -82
- package/dist/server/index.js.map +1 -1
- package/dist/ui/assets/index-C-_1qjy9.css +2 -0
- package/dist/ui/assets/index-CmFgFvLa.js +69 -0
- package/dist/ui/index.html +2 -2
- package/package.json +1 -1
- package/dist/ui/assets/index-B3Ic2ziA.js +0 -69
- package/dist/ui/assets/index-BAkpc01s.css +0 -2
package/dist/engine/index.d.cts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { c as CodingSessionsEngine, d as CodingSessionsEngineOptions, E as EFFORT_LEVELS, P as PreferenceStore, e as ProviderHomes, R as RAW_TRANSCRIPT_MAX_BYTES, f as RawBackupPolicy, g as RawBackupSink, h as RunStatus, i as buildCleanEnv, j as claudeDefaults, k as codexDefaults, l as createAiMatrxRawBackupSink, m as createCodingSessionsEngine, n as createRawBackupScheduler, o as defaultProviderHomes, p as describeActivity, q as describeProviderStatus, r as discoverLocalSessions, s as endsWithOpenToolCall, t as findDrivingProcess, u as findSessionFile, v as isAllowedEnvName, w as listProcesses, x as readSessionFile, y as searchLocalChats, z as sessionActivity, A as snippetAround } from '../index-
|
|
1
|
+
export { c as CodingSessionsEngine, d as CodingSessionsEngineOptions, E as EFFORT_LEVELS, P as PreferenceStore, e as ProviderHomes, R as RAW_TRANSCRIPT_MAX_BYTES, f as RawBackupPolicy, g as RawBackupSink, h as RunStatus, i as buildCleanEnv, j as claudeDefaults, k as codexDefaults, l as createAiMatrxRawBackupSink, m as createCodingSessionsEngine, n as createRawBackupScheduler, o as defaultProviderHomes, p as describeActivity, q as describeProviderStatus, r as discoverLocalSessions, s as endsWithOpenToolCall, t as findDrivingProcess, u as findSessionFile, v as isAllowedEnvName, w as listProcesses, x as readSessionFile, y as searchLocalChats, z as sessionActivity, A as snippetAround } from '../index-DQ0teQvw.cjs';
|
package/dist/engine/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { c as CodingSessionsEngine, d as CodingSessionsEngineOptions, E as EFFORT_LEVELS, P as PreferenceStore, e as ProviderHomes, R as RAW_TRANSCRIPT_MAX_BYTES, f as RawBackupPolicy, g as RawBackupSink, h as RunStatus, i as buildCleanEnv, j as claudeDefaults, k as codexDefaults, l as createAiMatrxRawBackupSink, m as createCodingSessionsEngine, n as createRawBackupScheduler, o as defaultProviderHomes, p as describeActivity, q as describeProviderStatus, r as discoverLocalSessions, s as endsWithOpenToolCall, t as findDrivingProcess, u as findSessionFile, v as isAllowedEnvName, w as listProcesses, x as readSessionFile, y as searchLocalChats, z as sessionActivity, A as snippetAround } from '../index-
|
|
1
|
+
export { c as CodingSessionsEngine, d as CodingSessionsEngineOptions, E as EFFORT_LEVELS, P as PreferenceStore, e as ProviderHomes, R as RAW_TRANSCRIPT_MAX_BYTES, f as RawBackupPolicy, g as RawBackupSink, h as RunStatus, i as buildCleanEnv, j as claudeDefaults, k as codexDefaults, l as createAiMatrxRawBackupSink, m as createCodingSessionsEngine, n as createRawBackupScheduler, o as defaultProviderHomes, p as describeActivity, q as describeProviderStatus, r as discoverLocalSessions, s as endsWithOpenToolCall, t as findDrivingProcess, u as findSessionFile, v as isAllowedEnvName, w as listProcesses, x as readSessionFile, y as searchLocalChats, z as sessionActivity, A as snippetAround } from '../index-DQ0teQvw.js';
|
package/dist/engine/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/engine/index.ts
|
|
2
|
-
import { execFile as
|
|
2
|
+
import { execFile as execFile3, fork } from "child_process";
|
|
3
3
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
4
|
-
import {
|
|
4
|
+
import { existsSync } from "fs";
|
|
5
5
|
import { formatFileSize } from "@ai-matrx/kit/format";
|
|
6
6
|
|
|
7
7
|
// src/engine/raw-backup.ts
|
|
@@ -100,8 +100,8 @@ function createAiMatrxRawBackupSink(options) {
|
|
|
100
100
|
|
|
101
101
|
// src/engine/index.ts
|
|
102
102
|
import fs6 from "fs/promises";
|
|
103
|
-
import
|
|
104
|
-
import
|
|
103
|
+
import os5 from "os";
|
|
104
|
+
import path7 from "path";
|
|
105
105
|
import { fileURLToPath } from "url";
|
|
106
106
|
|
|
107
107
|
// src/normalize/claude.ts
|
|
@@ -767,9 +767,177 @@ function describeProblem(provider, problem) {
|
|
|
767
767
|
};
|
|
768
768
|
}
|
|
769
769
|
|
|
770
|
-
// src/engine/
|
|
770
|
+
// src/engine/binaries.ts
|
|
771
|
+
import { execFile } from "child_process";
|
|
772
|
+
import { accessSync, constants as fsConstants, readdirSync, readFileSync, realpathSync } from "fs";
|
|
771
773
|
import os from "os";
|
|
772
774
|
import path from "path";
|
|
775
|
+
var SOURCE_LABEL = {
|
|
776
|
+
codex: { "desktop-app": "from ChatGPT app", path: "from PATH", "npm-global": "from npm", configured: "set by host" },
|
|
777
|
+
claude: { "desktop-app": "from Claude app", path: "from PATH", "npm-global": "from npm", configured: "set by host" }
|
|
778
|
+
};
|
|
779
|
+
function sourceLabel(provider, source) {
|
|
780
|
+
return SOURCE_LABEL[provider][source];
|
|
781
|
+
}
|
|
782
|
+
function parseVersion(text) {
|
|
783
|
+
const match = /(\d+)\.(\d+)\.(\d+)(?:[-.][0-9A-Za-z.]+)?/.exec(text ?? "");
|
|
784
|
+
return match ? match[0] : null;
|
|
785
|
+
}
|
|
786
|
+
function compareVersions(a, b) {
|
|
787
|
+
const [aCore = "", aPre] = a.split("-", 2);
|
|
788
|
+
const [bCore = "", bPre] = b.split("-", 2);
|
|
789
|
+
const pa = aCore.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
790
|
+
const pb = bCore.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
791
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
792
|
+
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
793
|
+
if (d !== 0) return d < 0 ? -1 : 1;
|
|
794
|
+
}
|
|
795
|
+
if (aPre && !bPre) return -1;
|
|
796
|
+
if (!aPre && bPre) return 1;
|
|
797
|
+
return 0;
|
|
798
|
+
}
|
|
799
|
+
function pickBinary(candidates) {
|
|
800
|
+
let best = null;
|
|
801
|
+
for (const c of candidates) {
|
|
802
|
+
if (!best) {
|
|
803
|
+
best = c;
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
if (c.version && (!best.version || compareVersions(c.version, best.version) > 0)) best = c;
|
|
807
|
+
}
|
|
808
|
+
return best;
|
|
809
|
+
}
|
|
810
|
+
function executable(file) {
|
|
811
|
+
try {
|
|
812
|
+
accessSync(file, fsConstants.X_OK);
|
|
813
|
+
return true;
|
|
814
|
+
} catch {
|
|
815
|
+
return false;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
function exeName(name, platform) {
|
|
819
|
+
return platform === "win32" ? [`${name}.exe`, `${name}.cmd`, name] : [name];
|
|
820
|
+
}
|
|
821
|
+
function allOnPath(name, pathValue, platform = process.platform) {
|
|
822
|
+
const found = [];
|
|
823
|
+
for (const dir of pathValue.split(path.delimiter)) {
|
|
824
|
+
if (!dir) continue;
|
|
825
|
+
for (const file of exeName(name, platform)) {
|
|
826
|
+
const candidate = path.join(dir, file);
|
|
827
|
+
if (executable(candidate)) {
|
|
828
|
+
found.push(candidate);
|
|
829
|
+
break;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
return found;
|
|
834
|
+
}
|
|
835
|
+
function desktopAppCandidates(provider, platform, home, applications) {
|
|
836
|
+
if (platform !== "darwin") return [];
|
|
837
|
+
if (provider === "codex") {
|
|
838
|
+
return applications.map(
|
|
839
|
+
(apps) => path.join(apps, "ChatGPT.app", "Contents", "Resources", "codex-cli", "bin", "codex")
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
const root = path.join(home, "Library", "Application Support", "Claude", "claude-code");
|
|
843
|
+
let versions = [];
|
|
844
|
+
try {
|
|
845
|
+
versions = readdirSync(root);
|
|
846
|
+
} catch {
|
|
847
|
+
return [];
|
|
848
|
+
}
|
|
849
|
+
return versions.map((v) => path.join(root, v, "claude.app", "Contents", "MacOS", "claude"));
|
|
850
|
+
}
|
|
851
|
+
function npmGlobalPrefix(env) {
|
|
852
|
+
const fromEnv = env.npm_config_prefix ?? env.NPM_CONFIG_PREFIX;
|
|
853
|
+
if (fromEnv) return Promise.resolve(fromEnv);
|
|
854
|
+
const npm = allOnPath("npm", env.PATH ?? "")[0];
|
|
855
|
+
if (!npm) return Promise.resolve(null);
|
|
856
|
+
return new Promise((resolve) => {
|
|
857
|
+
const child = execFile(npm, ["prefix", "-g"], { timeout: 5e3, env }, (error, stdout) => {
|
|
858
|
+
resolve(error ? null : String(stdout).trim() || null);
|
|
859
|
+
});
|
|
860
|
+
child.stdin?.end();
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
function declaredVersion(provider, source, file) {
|
|
864
|
+
if (source !== "desktop-app") return null;
|
|
865
|
+
if (provider === "codex") {
|
|
866
|
+
try {
|
|
867
|
+
const pkg = JSON.parse(readFileSync(path.join(path.dirname(path.dirname(file)), "codex-package.json"), "utf8"));
|
|
868
|
+
return typeof pkg.version === "string" ? parseVersion(pkg.version) : null;
|
|
869
|
+
} catch {
|
|
870
|
+
return null;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
return parseVersion(path.basename(path.resolve(file, "..", "..", "..", "..")));
|
|
874
|
+
}
|
|
875
|
+
function runVersion(file, env) {
|
|
876
|
+
return new Promise((resolve) => {
|
|
877
|
+
const child = execFile(file, ["--version"], { timeout: 8e3, env }, (error, stdout) => {
|
|
878
|
+
resolve(error ? null : parseVersion(String(stdout)));
|
|
879
|
+
});
|
|
880
|
+
child.stdin?.end();
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
async function discoverBinaries(provider, options) {
|
|
884
|
+
const platform = options.platform ?? process.platform;
|
|
885
|
+
const home = options.home ?? options.env.HOME ?? os.homedir();
|
|
886
|
+
const listed2 = [
|
|
887
|
+
...desktopAppCandidates(provider, platform, home, options.applications ?? ["/Applications", path.join(home, "Applications")]).filter(executable).map((p) => ({ path: p, source: "desktop-app" })),
|
|
888
|
+
...allOnPath(provider, options.env.PATH ?? "", platform).map((p) => ({ path: p, source: "path" }))
|
|
889
|
+
];
|
|
890
|
+
const prefix = await npmGlobalPrefix(options.env);
|
|
891
|
+
if (prefix) {
|
|
892
|
+
const dir = platform === "win32" ? prefix : path.join(prefix, "bin");
|
|
893
|
+
for (const file of exeName(provider, platform)) {
|
|
894
|
+
const candidate = path.join(dir, file);
|
|
895
|
+
if (executable(candidate)) {
|
|
896
|
+
listed2.push({ path: candidate, source: "npm-global" });
|
|
897
|
+
break;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
const seen = /* @__PURE__ */ new Set();
|
|
902
|
+
const unique = listed2.filter((c) => {
|
|
903
|
+
let real = c.path;
|
|
904
|
+
try {
|
|
905
|
+
real = realpathSync(c.path);
|
|
906
|
+
} catch {
|
|
907
|
+
}
|
|
908
|
+
if (seen.has(real)) return false;
|
|
909
|
+
seen.add(real);
|
|
910
|
+
return true;
|
|
911
|
+
});
|
|
912
|
+
return Promise.all(
|
|
913
|
+
unique.map(async (c) => ({
|
|
914
|
+
...c,
|
|
915
|
+
version: declaredVersion(provider, c.source, c.path) ?? await runVersion(c.path, options.env)
|
|
916
|
+
}))
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
async function resolveBinary(provider, options) {
|
|
920
|
+
const candidates = options.discover === false ? [] : await discoverBinaries(provider, options);
|
|
921
|
+
const best = pickBinary(candidates);
|
|
922
|
+
const inUse = options.forced ? {
|
|
923
|
+
path: options.forced,
|
|
924
|
+
source: "configured",
|
|
925
|
+
version: candidates.find((c) => c.path === options.forced)?.version ?? (options.discover === false ? null : await runVersion(options.forced, options.env))
|
|
926
|
+
} : best;
|
|
927
|
+
if (!inUse) return null;
|
|
928
|
+
const newer = best && best.path !== inUse.path && best.version && (!inUse.version || compareVersions(best.version, inUse.version) > 0) ? { path: best.path, version: best.version, sourceLabel: sourceLabel(provider, best.source) } : null;
|
|
929
|
+
return {
|
|
930
|
+
path: inUse.path,
|
|
931
|
+
version: inUse.version,
|
|
932
|
+
source: inUse.source,
|
|
933
|
+
sourceLabel: sourceLabel(provider, inUse.source),
|
|
934
|
+
newer
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// src/engine/clean-env.ts
|
|
939
|
+
import os2 from "os";
|
|
940
|
+
import path2 from "path";
|
|
773
941
|
var ALLOWED_NAMES = /* @__PURE__ */ new Set([
|
|
774
942
|
"HOME",
|
|
775
943
|
"USER",
|
|
@@ -791,11 +959,11 @@ var ALLOWED_NAMES = /* @__PURE__ */ new Set([
|
|
|
791
959
|
var ALLOWED_PREFIXES = ["LC_"];
|
|
792
960
|
function wellKnownBinDirs(home) {
|
|
793
961
|
return [
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
962
|
+
path2.join(home, ".local", "bin"),
|
|
963
|
+
path2.join(home, ".claude", "local"),
|
|
964
|
+
path2.join(home, ".npm-global", "bin"),
|
|
965
|
+
path2.join(home, ".bun", "bin"),
|
|
966
|
+
path2.join(home, ".volta", "bin"),
|
|
799
967
|
"/opt/homebrew/bin",
|
|
800
968
|
"/usr/local/bin",
|
|
801
969
|
"/usr/bin",
|
|
@@ -813,14 +981,14 @@ function buildCleanEnv(hostEnv, extra = {}) {
|
|
|
813
981
|
if (typeof value !== "string") continue;
|
|
814
982
|
if (isAllowedEnvName(name)) out[name] = value;
|
|
815
983
|
}
|
|
816
|
-
const home = out.HOME ??
|
|
984
|
+
const home = out.HOME ?? os2.homedir();
|
|
817
985
|
out.HOME = home;
|
|
818
|
-
const hostPath = (hostEnv.PATH ?? "").split(
|
|
986
|
+
const hostPath = (hostEnv.PATH ?? "").split(path2.delimiter).filter(Boolean);
|
|
819
987
|
const merged = [];
|
|
820
988
|
for (const dir of [...hostPath, ...wellKnownBinDirs(home)]) {
|
|
821
989
|
if (!merged.includes(dir)) merged.push(dir);
|
|
822
990
|
}
|
|
823
|
-
out.PATH = merged.join(
|
|
991
|
+
out.PATH = merged.join(path2.delimiter);
|
|
824
992
|
for (const [name, value] of Object.entries(extra)) out[name] = value;
|
|
825
993
|
return out;
|
|
826
994
|
}
|
|
@@ -828,16 +996,16 @@ function buildCleanEnv(hostEnv, extra = {}) {
|
|
|
828
996
|
// src/engine/files.ts
|
|
829
997
|
import { createReadStream } from "fs";
|
|
830
998
|
import fs2 from "fs/promises";
|
|
831
|
-
import
|
|
832
|
-
import
|
|
999
|
+
import os3 from "os";
|
|
1000
|
+
import path3 from "path";
|
|
833
1001
|
|
|
834
1002
|
// src/engine/activity.ts
|
|
835
|
-
import { execFile } from "child_process";
|
|
1003
|
+
import { execFile as execFile2 } from "child_process";
|
|
836
1004
|
var RECENT_WRITE_MS = 2 * 60 * 1e3;
|
|
837
1005
|
var OPEN_TOOL_WINDOW_MS = 15 * 60 * 1e3;
|
|
838
1006
|
function listProcesses() {
|
|
839
1007
|
return new Promise((resolve) => {
|
|
840
|
-
|
|
1008
|
+
execFile2("ps", ["-axww", "-o", "pid=,args="], { maxBuffer: 64 * 1024 * 1024, timeout: 5e3 }, (error, stdout) => {
|
|
841
1009
|
if (error) {
|
|
842
1010
|
resolve([]);
|
|
843
1011
|
return;
|
|
@@ -896,11 +1064,11 @@ function describeActivity(input) {
|
|
|
896
1064
|
}
|
|
897
1065
|
|
|
898
1066
|
// src/engine/files.ts
|
|
899
|
-
function defaultProviderHomes(home =
|
|
1067
|
+
function defaultProviderHomes(home = os3.homedir()) {
|
|
900
1068
|
return {
|
|
901
|
-
claudeProjects:
|
|
902
|
-
codexSessions:
|
|
903
|
-
codexIndex:
|
|
1069
|
+
claudeProjects: path3.join(home, ".claude", "projects"),
|
|
1070
|
+
codexSessions: path3.join(home, ".codex", "sessions"),
|
|
1071
|
+
codexIndex: path3.join(home, ".codex", "session_index.jsonl")
|
|
904
1072
|
};
|
|
905
1073
|
}
|
|
906
1074
|
var UUIDISH = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;
|
|
@@ -925,10 +1093,10 @@ async function claudeCandidates(root) {
|
|
|
925
1093
|
const out = [];
|
|
926
1094
|
for (const project of await safeReaddir(root)) {
|
|
927
1095
|
if (!project.isDirectory()) continue;
|
|
928
|
-
const dir =
|
|
1096
|
+
const dir = path3.join(root, project.name);
|
|
929
1097
|
const entries = await safeReaddir(dir);
|
|
930
1098
|
const stats = await Promise.all(
|
|
931
|
-
entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => statCandidate("claude",
|
|
1099
|
+
entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => statCandidate("claude", path3.join(dir, e.name)))
|
|
932
1100
|
);
|
|
933
1101
|
for (const s of stats) if (s) out.push(s);
|
|
934
1102
|
}
|
|
@@ -938,14 +1106,14 @@ async function codexCandidates(root) {
|
|
|
938
1106
|
const out = [];
|
|
939
1107
|
for (const year of await safeReaddir(root)) {
|
|
940
1108
|
if (!year.isDirectory()) continue;
|
|
941
|
-
for (const month of await safeReaddir(
|
|
1109
|
+
for (const month of await safeReaddir(path3.join(root, year.name))) {
|
|
942
1110
|
if (!month.isDirectory()) continue;
|
|
943
|
-
for (const day of await safeReaddir(
|
|
1111
|
+
for (const day of await safeReaddir(path3.join(root, year.name, month.name))) {
|
|
944
1112
|
if (!day.isDirectory()) continue;
|
|
945
|
-
const dir =
|
|
1113
|
+
const dir = path3.join(root, year.name, month.name, day.name);
|
|
946
1114
|
const entries = await safeReaddir(dir);
|
|
947
1115
|
const stats = await Promise.all(
|
|
948
|
-
entries.filter((e) => e.isFile() && e.name.startsWith("rollout-") && e.name.endsWith(".jsonl")).map((e) => statCandidate("codex",
|
|
1116
|
+
entries.filter((e) => e.isFile() && e.name.startsWith("rollout-") && e.name.endsWith(".jsonl")).map((e) => statCandidate("codex", path3.join(dir, e.name)))
|
|
949
1117
|
);
|
|
950
1118
|
for (const s of stats) if (s) out.push(s);
|
|
951
1119
|
}
|
|
@@ -1206,7 +1374,7 @@ async function describeSessionFile(candidate, codexNames) {
|
|
|
1206
1374
|
// src/engine/preferences.ts
|
|
1207
1375
|
import { randomUUID } from "crypto";
|
|
1208
1376
|
import fs3 from "fs/promises";
|
|
1209
|
-
import
|
|
1377
|
+
import path4 from "path";
|
|
1210
1378
|
var PreferenceStore = class {
|
|
1211
1379
|
constructor(dir) {
|
|
1212
1380
|
this.dir = dir;
|
|
@@ -1215,7 +1383,7 @@ var PreferenceStore = class {
|
|
|
1215
1383
|
data = {};
|
|
1216
1384
|
writing = Promise.resolve();
|
|
1217
1385
|
get file() {
|
|
1218
|
-
return
|
|
1386
|
+
return path4.join(this.dir, "preferences.json");
|
|
1219
1387
|
}
|
|
1220
1388
|
async load() {
|
|
1221
1389
|
try {
|
|
@@ -1419,7 +1587,7 @@ async function searchLocalChats(query, options = {}) {
|
|
|
1419
1587
|
// src/engine/store.ts
|
|
1420
1588
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
1421
1589
|
import fs4 from "fs/promises";
|
|
1422
|
-
import
|
|
1590
|
+
import path5 from "path";
|
|
1423
1591
|
var MAX_RUNS = 2e3;
|
|
1424
1592
|
var RunIndex = class {
|
|
1425
1593
|
constructor(dir) {
|
|
@@ -1429,7 +1597,7 @@ var RunIndex = class {
|
|
|
1429
1597
|
data = { version: 1, sessions: [], runs: [] };
|
|
1430
1598
|
writing = Promise.resolve();
|
|
1431
1599
|
get file() {
|
|
1432
|
-
return
|
|
1600
|
+
return path5.join(this.dir, "index.json");
|
|
1433
1601
|
}
|
|
1434
1602
|
async load() {
|
|
1435
1603
|
await fs4.mkdir(this.dir, { recursive: true });
|
|
@@ -1488,7 +1656,7 @@ var RunIndex = class {
|
|
|
1488
1656
|
/** A stable Paperclip agent/company identity for this install, so the
|
|
1489
1657
|
* driver's scratch state is reused rather than growing per run. */
|
|
1490
1658
|
async identity() {
|
|
1491
|
-
const file =
|
|
1659
|
+
const file = path5.join(this.dir, "identity.json");
|
|
1492
1660
|
try {
|
|
1493
1661
|
const parsed = JSON.parse(await fs4.readFile(file, "utf8"));
|
|
1494
1662
|
if (typeof parsed.companyId === "string" && typeof parsed.agentId === "string") {
|
|
@@ -1504,8 +1672,8 @@ var RunIndex = class {
|
|
|
1504
1672
|
|
|
1505
1673
|
// src/engine/user-defaults.ts
|
|
1506
1674
|
import fs5 from "fs/promises";
|
|
1507
|
-
import
|
|
1508
|
-
import
|
|
1675
|
+
import os4 from "os";
|
|
1676
|
+
import path6 from "path";
|
|
1509
1677
|
async function readJson(file) {
|
|
1510
1678
|
try {
|
|
1511
1679
|
const parsed = JSON.parse(await fs5.readFile(file, "utf8"));
|
|
@@ -1517,10 +1685,10 @@ async function readJson(file) {
|
|
|
1517
1685
|
function str3(value) {
|
|
1518
1686
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
1519
1687
|
}
|
|
1520
|
-
async function claudeDefaults(cwd, home =
|
|
1688
|
+
async function claudeDefaults(cwd, home = os4.homedir()) {
|
|
1521
1689
|
const files = [
|
|
1522
|
-
...cwd ? [
|
|
1523
|
-
|
|
1690
|
+
...cwd ? [path6.join(cwd, ".claude", "settings.local.json"), path6.join(cwd, ".claude", "settings.json")] : [],
|
|
1691
|
+
path6.join(home, ".claude", "settings.json")
|
|
1524
1692
|
];
|
|
1525
1693
|
let model = null;
|
|
1526
1694
|
let effort = null;
|
|
@@ -1533,10 +1701,10 @@ async function claudeDefaults(cwd, home = os3.homedir()) {
|
|
|
1533
1701
|
}
|
|
1534
1702
|
return { model, effort };
|
|
1535
1703
|
}
|
|
1536
|
-
async function codexDefaults(home =
|
|
1704
|
+
async function codexDefaults(home = os4.homedir()) {
|
|
1537
1705
|
let text;
|
|
1538
1706
|
try {
|
|
1539
|
-
text = await fs5.readFile(
|
|
1707
|
+
text = await fs5.readFile(path6.join(home, ".codex", "config.toml"), "utf8");
|
|
1540
1708
|
} catch {
|
|
1541
1709
|
return { model: null, effort: null };
|
|
1542
1710
|
}
|
|
@@ -1560,45 +1728,33 @@ var EFFORT_LEVELS = {
|
|
|
1560
1728
|
// src/engine/index.ts
|
|
1561
1729
|
var RAW_TRANSCRIPT_MAX_BYTES = 200 * 1048576;
|
|
1562
1730
|
function moduleDir() {
|
|
1563
|
-
return
|
|
1731
|
+
return path7.dirname(fileURLToPath(import.meta.url));
|
|
1564
1732
|
}
|
|
1565
1733
|
function resolveWorkerPath() {
|
|
1566
1734
|
const dir = moduleDir();
|
|
1567
1735
|
for (const candidate of [
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1736
|
+
path7.join(dir, "worker.js"),
|
|
1737
|
+
path7.join(dir, "worker.ts"),
|
|
1738
|
+
path7.join(dir, "..", "engine", "worker.js"),
|
|
1739
|
+
path7.join(dir, "..", "engine", "worker.ts")
|
|
1572
1740
|
]) {
|
|
1573
1741
|
if (existsSync(candidate)) return candidate;
|
|
1574
1742
|
}
|
|
1575
1743
|
throw new Error(`[coding-sessions] The driver worker is missing next to ${dir}.`);
|
|
1576
1744
|
}
|
|
1577
1745
|
async function installShim(dataDir) {
|
|
1578
|
-
const source =
|
|
1746
|
+
const source = path7.resolve(moduleDir(), "..", "..", "bin", "cli-shim.sh");
|
|
1579
1747
|
if (!existsSync(source)) throw new Error(`[coding-sessions] The CLI shim is missing at ${source}.`);
|
|
1580
1748
|
const body = await fs6.readFile(source, "utf8");
|
|
1581
|
-
const target =
|
|
1749
|
+
const target = path7.join(dataDir, "bin", "cli-shim.sh");
|
|
1582
1750
|
const current = await fs6.readFile(target, "utf8").catch(() => null);
|
|
1583
1751
|
if (current !== body) {
|
|
1584
|
-
await fs6.mkdir(
|
|
1752
|
+
await fs6.mkdir(path7.dirname(target), { recursive: true });
|
|
1585
1753
|
await fs6.writeFile(target, body, { mode: 493 });
|
|
1586
1754
|
}
|
|
1587
1755
|
await fs6.chmod(target, 493);
|
|
1588
1756
|
return target;
|
|
1589
1757
|
}
|
|
1590
|
-
function whichOnPath(name, pathValue) {
|
|
1591
|
-
for (const dir of pathValue.split(path6.delimiter)) {
|
|
1592
|
-
if (!dir) continue;
|
|
1593
|
-
const candidate = path6.join(dir, name);
|
|
1594
|
-
try {
|
|
1595
|
-
accessSync(candidate, fsConstants.X_OK);
|
|
1596
|
-
return candidate;
|
|
1597
|
-
} catch {
|
|
1598
|
-
}
|
|
1599
|
-
}
|
|
1600
|
-
return null;
|
|
1601
|
-
}
|
|
1602
1758
|
function nowIso() {
|
|
1603
1759
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1604
1760
|
}
|
|
@@ -1637,12 +1793,14 @@ function describeProviderStatus(provider, probe, lastProblem = null, quota = [])
|
|
|
1637
1793
|
function describeAuthStatus(provider, probe) {
|
|
1638
1794
|
const label = PROVIDER_LABEL[provider];
|
|
1639
1795
|
const installed = probe?.installed === true;
|
|
1640
|
-
const
|
|
1796
|
+
const binary = probe?.binary ?? null;
|
|
1797
|
+
const version = binary?.version ?? (typeof probe?.version === "string" ? probe.version : null);
|
|
1641
1798
|
const base = {
|
|
1642
1799
|
provider,
|
|
1643
1800
|
label,
|
|
1644
1801
|
installed,
|
|
1645
1802
|
version,
|
|
1803
|
+
binary,
|
|
1646
1804
|
loggedIn: false,
|
|
1647
1805
|
billing: "unknown",
|
|
1648
1806
|
plan: null,
|
|
@@ -1694,8 +1852,8 @@ function describeAuthStatus(provider, probe) {
|
|
|
1694
1852
|
};
|
|
1695
1853
|
}
|
|
1696
1854
|
async function createCodingSessionsEngine(options = {}) {
|
|
1697
|
-
const dataDir = options.dataDir ??
|
|
1698
|
-
const stateDir =
|
|
1855
|
+
const dataDir = options.dataDir ?? path7.join(os5.homedir(), ".matrx", "coding-sessions");
|
|
1856
|
+
const stateDir = path7.join(dataDir, "paperclip-state");
|
|
1699
1857
|
const homes = options.homes ?? defaultProviderHomes();
|
|
1700
1858
|
const index = new RunIndex(dataDir);
|
|
1701
1859
|
await index.load();
|
|
@@ -1709,7 +1867,7 @@ async function createCodingSessionsEngine(options = {}) {
|
|
|
1709
1867
|
onError: (error) => console.warn("[coding-sessions] raw transcript backup failed:", error)
|
|
1710
1868
|
}) : null;
|
|
1711
1869
|
const discoveredSeen = /* @__PURE__ */ new Map();
|
|
1712
|
-
const healthFile =
|
|
1870
|
+
const healthFile = path7.join(dataDir, "provider-health.json");
|
|
1713
1871
|
let health = {};
|
|
1714
1872
|
try {
|
|
1715
1873
|
health = JSON.parse(await fs6.readFile(healthFile, "utf8"));
|
|
@@ -1744,7 +1902,24 @@ async function createCodingSessionsEngine(options = {}) {
|
|
|
1744
1902
|
// Marks the forked process as a driver worker (see worker.ts).
|
|
1745
1903
|
MATRX_CODING_SESSIONS_WORKER: "1"
|
|
1746
1904
|
});
|
|
1747
|
-
const
|
|
1905
|
+
const binaryHome = options.hostEnv?.HOME ?? os5.homedir();
|
|
1906
|
+
const binaryCache = /* @__PURE__ */ new Map();
|
|
1907
|
+
function binaryFor(provider, fresh = false) {
|
|
1908
|
+
const cached = binaryCache.get(provider);
|
|
1909
|
+
if (cached && !fresh && Date.now() - cached.at < 5 * 6e4) return cached.value;
|
|
1910
|
+
const forced = options.commands?.[provider];
|
|
1911
|
+
const value = resolveBinary(provider, {
|
|
1912
|
+
env: workerEnv,
|
|
1913
|
+
home: binaryHome,
|
|
1914
|
+
forced,
|
|
1915
|
+
// A forced command needs no discovery to run; the status check still
|
|
1916
|
+
// looks, so it can say when a newer copy exists.
|
|
1917
|
+
discover: !forced || fresh
|
|
1918
|
+
}).catch(() => forced ? { path: forced, version: null, source: "configured", sourceLabel: "set by host", newer: null } : null);
|
|
1919
|
+
if (!forced || !fresh) binaryCache.set(provider, { at: Date.now(), value });
|
|
1920
|
+
return value;
|
|
1921
|
+
}
|
|
1922
|
+
const commandFor = async (provider) => (await binaryFor(provider))?.path ?? null;
|
|
1748
1923
|
const workerPath = resolveWorkerPath();
|
|
1749
1924
|
const shimPath = await installShim(dataDir);
|
|
1750
1925
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -1815,7 +1990,7 @@ async function createCodingSessionsEngine(options = {}) {
|
|
|
1815
1990
|
}
|
|
1816
1991
|
async function startRun(session, prompt) {
|
|
1817
1992
|
const provider = session.provider;
|
|
1818
|
-
const realCommand = commandFor(provider);
|
|
1993
|
+
const realCommand = await commandFor(provider);
|
|
1819
1994
|
const runId = randomUUID3();
|
|
1820
1995
|
const model = session.model ?? (provider === "claude" ? (await userDefaults(provider, session.cwd)).model ?? null : null);
|
|
1821
1996
|
const run = {
|
|
@@ -2047,7 +2222,7 @@ async function createCodingSessionsEngine(options = {}) {
|
|
|
2047
2222
|
}, 3e3);
|
|
2048
2223
|
}
|
|
2049
2224
|
async function pruneDriverScratch() {
|
|
2050
|
-
const runsDir =
|
|
2225
|
+
const runsDir = path7.join(
|
|
2051
2226
|
stateDir,
|
|
2052
2227
|
"instances",
|
|
2053
2228
|
"default",
|
|
@@ -2060,10 +2235,11 @@ async function createCodingSessionsEngine(options = {}) {
|
|
|
2060
2235
|
);
|
|
2061
2236
|
const entries = await fs6.readdir(runsDir).catch(() => []);
|
|
2062
2237
|
await Promise.all(
|
|
2063
|
-
entries.filter((name) => !live.has(name)).map((name) => fs6.rm(
|
|
2238
|
+
entries.filter((name) => !live.has(name)).map((name) => fs6.rm(path7.join(runsDir, name), { recursive: true, force: true }).catch(() => void 0))
|
|
2064
2239
|
);
|
|
2065
2240
|
}
|
|
2066
2241
|
async function probe() {
|
|
2242
|
+
const [claudeBinary, codexBinary] = await Promise.all([binaryFor("claude", true), binaryFor("codex", true)]);
|
|
2067
2243
|
return new Promise((resolve) => {
|
|
2068
2244
|
const child = forkWorker();
|
|
2069
2245
|
let answered = false;
|
|
@@ -2079,8 +2255,8 @@ async function createCodingSessionsEngine(options = {}) {
|
|
|
2079
2255
|
answered = true;
|
|
2080
2256
|
clearTimeout(timer);
|
|
2081
2257
|
resolve({
|
|
2082
|
-
claude: message.claude ?? {},
|
|
2083
|
-
codex: message.codex ?? {}
|
|
2258
|
+
claude: { ...message.claude ?? {}, binary: claudeBinary },
|
|
2259
|
+
codex: { ...message.codex ?? {}, binary: codexBinary }
|
|
2084
2260
|
});
|
|
2085
2261
|
}
|
|
2086
2262
|
});
|
|
@@ -2092,7 +2268,7 @@ async function createCodingSessionsEngine(options = {}) {
|
|
|
2092
2268
|
});
|
|
2093
2269
|
child.send({
|
|
2094
2270
|
kind: "probe",
|
|
2095
|
-
commands: { claude:
|
|
2271
|
+
commands: { claude: claudeBinary?.path ?? null, codex: codexBinary?.path ?? null },
|
|
2096
2272
|
quota: options.quota !== false
|
|
2097
2273
|
});
|
|
2098
2274
|
});
|
|
@@ -2118,7 +2294,7 @@ async function createCodingSessionsEngine(options = {}) {
|
|
|
2118
2294
|
function resolveFolder(raw) {
|
|
2119
2295
|
const trimmed = raw.trim();
|
|
2120
2296
|
if (!trimmed) return null;
|
|
2121
|
-
return
|
|
2297
|
+
return path7.resolve(trimmed.replace(/^~(?=$|\/)/, os5.homedir()));
|
|
2122
2298
|
}
|
|
2123
2299
|
async function checkFolder(raw) {
|
|
2124
2300
|
const target = resolveFolder(raw);
|
|
@@ -2127,7 +2303,7 @@ async function createCodingSessionsEngine(options = {}) {
|
|
|
2127
2303
|
return { path: target, exists: stat !== null, isDirectory: stat?.isDirectory() === true };
|
|
2128
2304
|
}
|
|
2129
2305
|
async function assertFolder(cwd) {
|
|
2130
|
-
const resolved =
|
|
2306
|
+
const resolved = path7.resolve(cwd.replace(/^~(?=$|\/)/, os5.homedir()));
|
|
2131
2307
|
const stat = await fs6.stat(resolved).catch(() => null);
|
|
2132
2308
|
if (!stat?.isDirectory()) {
|
|
2133
2309
|
throw new Error(`The folder ${resolved} doesn't exist on this computer.`);
|
|
@@ -2281,7 +2457,7 @@ async function createCodingSessionsEngine(options = {}) {
|
|
|
2281
2457
|
};
|
|
2282
2458
|
}
|
|
2283
2459
|
const content = await fs6.readFile(file, "utf8");
|
|
2284
|
-
return { available: true, fileName:
|
|
2460
|
+
return { available: true, fileName: path7.basename(file), bytes: stat.size, content };
|
|
2285
2461
|
},
|
|
2286
2462
|
async start(input) {
|
|
2287
2463
|
const prompt = input.prompt.trim();
|
|
@@ -2402,7 +2578,7 @@ function openFolderIn(folder, target) {
|
|
|
2402
2578
|
args = ["-a", app.app, folder];
|
|
2403
2579
|
}
|
|
2404
2580
|
return new Promise((resolve, reject) => {
|
|
2405
|
-
|
|
2581
|
+
execFile3(command, args, { timeout: 1e4 }, (error) => {
|
|
2406
2582
|
if (!error) return resolve();
|
|
2407
2583
|
const label = target === "finder" ? "the folder" : OPEN_APP[target]?.label ?? target;
|
|
2408
2584
|
reject(
|