archctx 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/archctx.mjs +1245 -187
- package/package.json +2 -4
package/bin/archctx.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
// @bun
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -667,7 +667,7 @@ function productVersionManifest() {
|
|
|
667
667
|
}
|
|
668
668
|
};
|
|
669
669
|
}
|
|
670
|
-
var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.1.
|
|
670
|
+
var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.1.2", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.3.10", ARCHCONTEXT_NODE_RANGE = ">=24 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v1", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-06-20.fg1";
|
|
671
671
|
// packages/contracts/src/index.ts
|
|
672
672
|
var init_src = __esm(() => {
|
|
673
673
|
init_control_plane_routes();
|
|
@@ -1288,7 +1288,7 @@ __export(exports_src, {
|
|
|
1288
1288
|
DevicePrivateKeyStore: () => DevicePrivateKeyStore
|
|
1289
1289
|
});
|
|
1290
1290
|
import {
|
|
1291
|
-
createHash as
|
|
1291
|
+
createHash as createHash7,
|
|
1292
1292
|
createPrivateKey,
|
|
1293
1293
|
generateKeyPairSync,
|
|
1294
1294
|
randomBytes as randomBytes3,
|
|
@@ -1296,7 +1296,7 @@ import {
|
|
|
1296
1296
|
} from "node:crypto";
|
|
1297
1297
|
function createPkceAuthorizationRequest(input) {
|
|
1298
1298
|
const codeVerifier = input.verifier ?? randomBytes3(32).toString("base64url");
|
|
1299
|
-
const codeChallenge =
|
|
1299
|
+
const codeChallenge = createHash7("sha256").update(codeVerifier).digest("base64url");
|
|
1300
1300
|
const url = new URL("/oauth/authorize", input.issuer);
|
|
1301
1301
|
url.searchParams.set("response_type", "code");
|
|
1302
1302
|
url.searchParams.set("client_id", input.clientId);
|
|
@@ -4262,12 +4262,12 @@ __export(exports_src3, {
|
|
|
4262
4262
|
assertLocalAttestationV1: () => assertLocalAttestationV1,
|
|
4263
4263
|
assertAttestationV2: () => assertAttestationV22
|
|
4264
4264
|
});
|
|
4265
|
-
import { createHash as
|
|
4265
|
+
import { createHash as createHash8, sign as sign3, verify as verify2 } from "node:crypto";
|
|
4266
4266
|
function publicKeyFingerprint2(publicKey) {
|
|
4267
4267
|
if (publicKey.type !== "public")
|
|
4268
4268
|
throw new Error("public-key-required");
|
|
4269
4269
|
const der = publicKey.export({ format: "der", type: "spki" });
|
|
4270
|
-
return `sha256:${
|
|
4270
|
+
return `sha256:${createHash8("sha256").update(der).digest("hex")}`;
|
|
4271
4271
|
}
|
|
4272
4272
|
function createReviewChallenge(input) {
|
|
4273
4273
|
const nonce = digestJson({ repo: input.repository, headSha: input.headSha, expiresAt: input.expiresAt });
|
|
@@ -4708,7 +4708,7 @@ function deviceIntegritySignals2(input) {
|
|
|
4708
4708
|
function signAttestation(input) {
|
|
4709
4709
|
const unsigned = unsignedPayload2({
|
|
4710
4710
|
schemaVersion: "archcontext.attestation/v1",
|
|
4711
|
-
attestationId: `att_${
|
|
4711
|
+
attestationId: `att_${createHash8("sha256").update(input.challenge.challengeId + input.reviewDigest + input.trustLevel).digest("hex").slice(0, 16)}`,
|
|
4712
4712
|
challengeId: input.challenge.challengeId,
|
|
4713
4713
|
repository: input.challenge.repository,
|
|
4714
4714
|
headSha: input.challenge.headSha,
|
|
@@ -5109,7 +5109,7 @@ var init_src7 = __esm(() => {
|
|
|
5109
5109
|
|
|
5110
5110
|
// packages/surfaces/cli/src/main.ts
|
|
5111
5111
|
import { spawn } from "child_process";
|
|
5112
|
-
import { accessSync, chmodSync as
|
|
5112
|
+
import { accessSync, chmodSync as chmodSync5, closeSync as closeSync7, constants, existsSync as existsSync11, mkdirSync as mkdirSync9, openSync as openSync7, readFileSync as readFileSync11, rmSync as rmSync9, statSync as statSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
5113
5113
|
import { dirname as dirname9, join as join8, resolve as resolve14 } from "path";
|
|
5114
5114
|
import { fileURLToPath } from "url";
|
|
5115
5115
|
|
|
@@ -5134,6 +5134,18 @@ var DEFAULT_IGNORES = new Set([
|
|
|
5134
5134
|
".archcontext/.local",
|
|
5135
5135
|
".DS_Store"
|
|
5136
5136
|
]);
|
|
5137
|
+
function repositoryFingerprint(root) {
|
|
5138
|
+
const normalized = canonicalRepositoryRoot(root);
|
|
5139
|
+
return `repo.${createHash2("sha256").update(normalized).digest("hex").slice(0, 16)}`;
|
|
5140
|
+
}
|
|
5141
|
+
function canonicalRepositoryRoot(root) {
|
|
5142
|
+
const resolved = resolve(root);
|
|
5143
|
+
try {
|
|
5144
|
+
return realpathSync.native(resolved);
|
|
5145
|
+
} catch {
|
|
5146
|
+
return resolved;
|
|
5147
|
+
}
|
|
5148
|
+
}
|
|
5137
5149
|
function computeWorktreeDigest(root, options = {}) {
|
|
5138
5150
|
const ignore = new Set([...DEFAULT_IGNORES, ...options.ignore ?? []]);
|
|
5139
5151
|
const files = listRepoFiles(root, ignore);
|
|
@@ -5198,11 +5210,11 @@ var DEFAULT_IGNORES2 = new Set([
|
|
|
5198
5210
|
".archcontext/.local",
|
|
5199
5211
|
".DS_Store"
|
|
5200
5212
|
]);
|
|
5201
|
-
function
|
|
5202
|
-
const normalized =
|
|
5213
|
+
function repositoryFingerprint2(root) {
|
|
5214
|
+
const normalized = canonicalRepositoryRoot2(root);
|
|
5203
5215
|
return `repo.${createHash3("sha256").update(normalized).digest("hex").slice(0, 16)}`;
|
|
5204
5216
|
}
|
|
5205
|
-
function
|
|
5217
|
+
function canonicalRepositoryRoot2(root) {
|
|
5206
5218
|
const resolved = resolve2(root);
|
|
5207
5219
|
try {
|
|
5208
5220
|
return realpathSync2.native(resolved);
|
|
@@ -5341,9 +5353,9 @@ function computeReviewWorktreeDigest(input) {
|
|
|
5341
5353
|
});
|
|
5342
5354
|
}
|
|
5343
5355
|
function bindRepository(root, headSha) {
|
|
5344
|
-
const canonicalRoot =
|
|
5356
|
+
const canonicalRoot = canonicalRepositoryRoot2(root);
|
|
5345
5357
|
return {
|
|
5346
|
-
repositoryId:
|
|
5358
|
+
repositoryId: repositoryFingerprint2(canonicalRoot),
|
|
5347
5359
|
root: canonicalRoot,
|
|
5348
5360
|
headSha,
|
|
5349
5361
|
worktreeDigest: computeWorktreeDigest2(canonicalRoot)
|
|
@@ -6524,18 +6536,621 @@ function listFiles(root) {
|
|
|
6524
6536
|
}
|
|
6525
6537
|
|
|
6526
6538
|
// packages/local-runtime/local-store-sqlite/src/index.ts
|
|
6527
|
-
import {
|
|
6539
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
6540
|
+
import { createHash as createHash5, randomUUID } from "node:crypto";
|
|
6541
|
+
import { chmodSync, closeSync as closeSync3, existsSync as existsSync3, fsyncSync as fsyncSync2, lstatSync as lstatSync2, mkdirSync as mkdirSync2, openSync as openSync3, readFileSync as readFileSync5, realpathSync as realpathSync4, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
6542
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
6543
|
+
import { homedir } from "node:os";
|
|
6544
|
+
import { dirname as dirname2, isAbsolute as isAbsolute3, join as join2, relative as relative4, resolve as resolve6 } from "node:path";
|
|
6545
|
+
var runtimeRequire = createRequire2(import.meta.url);
|
|
6546
|
+
var SQLITE_SIDECAR_SUFFIXES = ["", "-wal", "-shm"];
|
|
6547
|
+
var LEGACY_MIGRATION_MARKER_FILE = "runtime.sqlite.migration.json";
|
|
6548
|
+
var LEGACY_MIGRATION_LOCK_FILE = "runtime.sqlite.migration.lock";
|
|
6549
|
+
var REQUIRED_LOCAL_STORE_TABLES = [
|
|
6550
|
+
"schema_migrations",
|
|
6551
|
+
"repository_sessions",
|
|
6552
|
+
"snapshots",
|
|
6553
|
+
"task_states",
|
|
6554
|
+
"observed_evidence",
|
|
6555
|
+
"review_results",
|
|
6556
|
+
"landscapes",
|
|
6557
|
+
"cross_repo_edges",
|
|
6558
|
+
"changeset_journal"
|
|
6559
|
+
];
|
|
6560
|
+
var SQLITE_PRAGMAS = [
|
|
6561
|
+
"PRAGMA journal_mode = WAL",
|
|
6562
|
+
"PRAGMA foreign_keys = ON",
|
|
6563
|
+
"PRAGMA busy_timeout = 5000"
|
|
6564
|
+
];
|
|
6565
|
+
var LOCAL_SQLITE_MIGRATIONS = [
|
|
6566
|
+
{
|
|
6567
|
+
id: "0001_runtime_state",
|
|
6568
|
+
statements: [
|
|
6569
|
+
`CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
6570
|
+
id TEXT PRIMARY KEY,
|
|
6571
|
+
applied_at TEXT NOT NULL
|
|
6572
|
+
)`,
|
|
6573
|
+
`CREATE TABLE IF NOT EXISTS repository_sessions (
|
|
6574
|
+
repository_id TEXT PRIMARY KEY,
|
|
6575
|
+
root TEXT NOT NULL,
|
|
6576
|
+
head_sha TEXT NOT NULL,
|
|
6577
|
+
worktree_digest TEXT NOT NULL,
|
|
6578
|
+
updated_at TEXT NOT NULL
|
|
6579
|
+
)`,
|
|
6580
|
+
`CREATE TABLE IF NOT EXISTS snapshots (
|
|
6581
|
+
id TEXT PRIMARY KEY,
|
|
6582
|
+
repository_id TEXT NOT NULL,
|
|
6583
|
+
head_sha TEXT NOT NULL,
|
|
6584
|
+
worktree_digest TEXT NOT NULL,
|
|
6585
|
+
state TEXT NOT NULL,
|
|
6586
|
+
created_at TEXT NOT NULL,
|
|
6587
|
+
committed_at TEXT
|
|
6588
|
+
)`,
|
|
6589
|
+
`CREATE TABLE IF NOT EXISTS task_states (
|
|
6590
|
+
task_session_id TEXT PRIMARY KEY,
|
|
6591
|
+
payload_json TEXT NOT NULL,
|
|
6592
|
+
updated_at TEXT NOT NULL
|
|
6593
|
+
)`,
|
|
6594
|
+
`CREATE TABLE IF NOT EXISTS observed_evidence (
|
|
6595
|
+
id TEXT PRIMARY KEY,
|
|
6596
|
+
repository_id TEXT NOT NULL,
|
|
6597
|
+
head_sha TEXT NOT NULL,
|
|
6598
|
+
selector_json TEXT NOT NULL,
|
|
6599
|
+
summary TEXT NOT NULL,
|
|
6600
|
+
confidence TEXT NOT NULL,
|
|
6601
|
+
created_at TEXT NOT NULL
|
|
6602
|
+
)`,
|
|
6603
|
+
`CREATE TABLE IF NOT EXISTS review_results (
|
|
6604
|
+
review_id TEXT PRIMARY KEY,
|
|
6605
|
+
task_session_id TEXT NOT NULL,
|
|
6606
|
+
payload_json TEXT NOT NULL,
|
|
6607
|
+
created_at TEXT NOT NULL
|
|
6608
|
+
)`
|
|
6609
|
+
]
|
|
6610
|
+
},
|
|
6611
|
+
{
|
|
6612
|
+
id: "0002_indexes",
|
|
6613
|
+
statements: [
|
|
6614
|
+
"CREATE INDEX IF NOT EXISTS idx_snapshots_repository ON snapshots(repository_id, head_sha)",
|
|
6615
|
+
"CREATE INDEX IF NOT EXISTS idx_evidence_repository ON observed_evidence(repository_id, head_sha)",
|
|
6616
|
+
"CREATE INDEX IF NOT EXISTS idx_reviews_task ON review_results(task_session_id)"
|
|
6617
|
+
]
|
|
6618
|
+
},
|
|
6619
|
+
{
|
|
6620
|
+
id: "0003_landscape_state",
|
|
6621
|
+
statements: [
|
|
6622
|
+
`CREATE TABLE IF NOT EXISTS landscapes (
|
|
6623
|
+
id TEXT PRIMARY KEY,
|
|
6624
|
+
digest TEXT NOT NULL,
|
|
6625
|
+
metadata_json TEXT NOT NULL,
|
|
6626
|
+
updated_at TEXT NOT NULL
|
|
6627
|
+
)`,
|
|
6628
|
+
`CREATE TABLE IF NOT EXISTS cross_repo_edges (
|
|
6629
|
+
id TEXT PRIMARY KEY,
|
|
6630
|
+
landscape_id TEXT NOT NULL,
|
|
6631
|
+
from_repository_id TEXT NOT NULL,
|
|
6632
|
+
from_node_id TEXT NOT NULL,
|
|
6633
|
+
to_repository_id TEXT NOT NULL,
|
|
6634
|
+
to_node_id TEXT NOT NULL,
|
|
6635
|
+
via_kind TEXT NOT NULL,
|
|
6636
|
+
via_id TEXT NOT NULL,
|
|
6637
|
+
metadata_json TEXT NOT NULL,
|
|
6638
|
+
updated_at TEXT NOT NULL
|
|
6639
|
+
)`,
|
|
6640
|
+
"CREATE INDEX IF NOT EXISTS idx_cross_repo_edges_from ON cross_repo_edges(from_repository_id, from_node_id)",
|
|
6641
|
+
"CREATE INDEX IF NOT EXISTS idx_cross_repo_edges_to ON cross_repo_edges(to_repository_id, to_node_id)"
|
|
6642
|
+
]
|
|
6643
|
+
},
|
|
6644
|
+
{
|
|
6645
|
+
id: "0004_changeset_journal",
|
|
6646
|
+
statements: [
|
|
6647
|
+
`CREATE TABLE IF NOT EXISTS changeset_journal (
|
|
6648
|
+
journal_id TEXT PRIMARY KEY,
|
|
6649
|
+
changeset_id TEXT NOT NULL,
|
|
6650
|
+
root TEXT NOT NULL,
|
|
6651
|
+
status TEXT NOT NULL,
|
|
6652
|
+
metadata_json TEXT NOT NULL,
|
|
6653
|
+
files_json TEXT NOT NULL,
|
|
6654
|
+
created_at TEXT NOT NULL,
|
|
6655
|
+
updated_at TEXT NOT NULL,
|
|
6656
|
+
completed_at TEXT
|
|
6657
|
+
)`,
|
|
6658
|
+
"CREATE INDEX IF NOT EXISTS idx_changeset_journal_status ON changeset_journal(status)"
|
|
6659
|
+
]
|
|
6660
|
+
}
|
|
6661
|
+
];
|
|
6662
|
+
var ARCHCONTEXT_STATE_DIR_ENV = "ARCHCONTEXT_STATE_DIR";
|
|
6663
|
+
var ARCHCONTEXT_LOCAL_STORE_PATH_ENV = "ARCHCONTEXT_LOCAL_STORE_PATH";
|
|
6664
|
+
function defaultArchContextStateRoot(env = process.env, platform = process.platform, home = homedir()) {
|
|
6665
|
+
const override = env[ARCHCONTEXT_STATE_DIR_ENV];
|
|
6666
|
+
if (override)
|
|
6667
|
+
return { path: resolve6(override), source: "environment" };
|
|
6668
|
+
if (platform === "darwin")
|
|
6669
|
+
return { path: join2(home, "Library", "Application Support", "ArchContext"), source: "os-user-data" };
|
|
6670
|
+
if (platform === "win32")
|
|
6671
|
+
return { path: join2(env.LOCALAPPDATA ?? join2(home, "AppData", "Local"), "ArchContext"), source: "os-user-data" };
|
|
6672
|
+
return { path: join2(env.XDG_DATA_HOME ?? join2(home, ".local", "share"), "archcontext"), source: "os-user-data" };
|
|
6673
|
+
}
|
|
6674
|
+
function runtimeStatePaths(root = process.cwd(), env = process.env) {
|
|
6675
|
+
const repositoryRoot = readGitPath(root, ["rev-parse", "--show-toplevel"]) ?? root;
|
|
6676
|
+
const canonicalRepositoryRoot3 = canonicalPath(repositoryRoot);
|
|
6677
|
+
const gitCommonDir = readGitPath(canonicalRepositoryRoot3, ["rev-parse", "--git-common-dir"]);
|
|
6678
|
+
const repositoryAnchor = canonicalPath(gitCommonDir ? resolveMaybeRelative(canonicalRepositoryRoot3, gitCommonDir) : canonicalRepositoryRoot3);
|
|
6679
|
+
const workspaceAnchor = canonicalRepositoryRoot3;
|
|
6680
|
+
const storageRepositoryId = stableStorageId("repo", repositoryAnchor);
|
|
6681
|
+
const storageWorkspaceId = stableStorageId("ws", workspaceAnchor);
|
|
6682
|
+
const stateRoot = defaultArchContextStateRoot(env);
|
|
6683
|
+
const repositoryStateDir = join2(stateRoot.path, "repositories", storageRepositoryId);
|
|
6684
|
+
const workspaceStateDir = join2(repositoryStateDir, "worktrees", storageWorkspaceId);
|
|
6685
|
+
const legacyControlDir = resolve6(canonicalRepositoryRoot3, ".archcontext", ".local");
|
|
6686
|
+
return {
|
|
6687
|
+
schemaVersion: "archcontext.runtime-state-paths/v1",
|
|
6688
|
+
stateRoot: stateRoot.path,
|
|
6689
|
+
source: stateRoot.source,
|
|
6690
|
+
repositoryRoot: canonicalRepositoryRoot3,
|
|
6691
|
+
repositoryAnchor,
|
|
6692
|
+
workspaceAnchor,
|
|
6693
|
+
storageRepositoryId,
|
|
6694
|
+
storageWorkspaceId,
|
|
6695
|
+
repositoryId: storageRepositoryId,
|
|
6696
|
+
workspaceId: storageWorkspaceId,
|
|
6697
|
+
repositoryStateDir,
|
|
6698
|
+
workspaceStateDir,
|
|
6699
|
+
sharedCacheDir: join2(repositoryStateDir, "shared", "cache"),
|
|
6700
|
+
localStorePath: env[ARCHCONTEXT_LOCAL_STORE_PATH_ENV] ?? join2(workspaceStateDir, "runtime.sqlite"),
|
|
6701
|
+
daemonConnectionPath: join2(workspaceStateDir, "archctxd.json"),
|
|
6702
|
+
daemonLockPath: join2(workspaceStateDir, "archctxd.lock"),
|
|
6703
|
+
daemonLogPath: join2(workspaceStateDir, "archctxd.log"),
|
|
6704
|
+
developerReviewRunStateDir: join2(workspaceStateDir, "developer-review-runs"),
|
|
6705
|
+
legacyControlDir,
|
|
6706
|
+
legacyLocalStorePath: join2(legacyControlDir, "runtime.sqlite")
|
|
6707
|
+
};
|
|
6708
|
+
}
|
|
6528
6709
|
function defaultLocalStorePath(root = process.cwd()) {
|
|
6529
|
-
return
|
|
6710
|
+
return runtimeStatePaths(root).localStorePath;
|
|
6711
|
+
}
|
|
6712
|
+
function inspectLegacyLocalStoreMigration(root = process.cwd(), env = process.env) {
|
|
6713
|
+
const paths = runtimeStatePaths(root, env);
|
|
6714
|
+
if (env[ARCHCONTEXT_LOCAL_STORE_PATH_ENV]) {
|
|
6715
|
+
return legacyMigrationResult(false, "explicit-local-store-override", paths, [], {
|
|
6716
|
+
status: "explicit-local-store-override"
|
|
6717
|
+
});
|
|
6718
|
+
}
|
|
6719
|
+
const legacyExists = existsSync3(paths.legacyLocalStorePath);
|
|
6720
|
+
const targetExists = existsSync3(paths.localStorePath);
|
|
6721
|
+
if (targetExists) {
|
|
6722
|
+
const target = safeCurrentLocalStoreCheck(paths.localStorePath);
|
|
6723
|
+
return legacyMigrationResult(false, "target-exists", paths, [], {
|
|
6724
|
+
status: target.ok ? "target-current" : "target-incomplete",
|
|
6725
|
+
integrityCheck: target.ok ? { target: target.result } : { target: "failed", error: target.error }
|
|
6726
|
+
});
|
|
6727
|
+
}
|
|
6728
|
+
if (!legacyExists) {
|
|
6729
|
+
return legacyMigrationResult(false, "legacy-missing", paths, [], {
|
|
6730
|
+
status: "legacy-missing"
|
|
6731
|
+
});
|
|
6732
|
+
}
|
|
6733
|
+
const source = safeTrustedLegacyLocalStoreSourceCheck(paths);
|
|
6734
|
+
if (!source.ok) {
|
|
6735
|
+
return legacyMigrationResult(false, undefined, paths, [], {
|
|
6736
|
+
status: "legacy-invalid",
|
|
6737
|
+
integrityCheck: { legacy: "failed", error: source.error }
|
|
6738
|
+
});
|
|
6739
|
+
}
|
|
6740
|
+
const legacy = safeSqliteIntegrityCheck(paths.legacyLocalStorePath);
|
|
6741
|
+
return legacyMigrationResult(false, undefined, paths, [], {
|
|
6742
|
+
status: legacy.ok ? "pending" : "legacy-invalid",
|
|
6743
|
+
integrityCheck: legacy.ok ? { legacy: legacy.result } : { legacy: "failed", error: legacy.error }
|
|
6744
|
+
});
|
|
6745
|
+
}
|
|
6746
|
+
function migrateLegacyLocalStoreIfNeeded(root = process.cwd(), env = process.env) {
|
|
6747
|
+
const paths = runtimeStatePaths(root, env);
|
|
6748
|
+
if (env[ARCHCONTEXT_LOCAL_STORE_PATH_ENV]) {
|
|
6749
|
+
return legacyMigrationResult(false, "explicit-local-store-override", paths, [], {
|
|
6750
|
+
status: "explicit-local-store-override"
|
|
6751
|
+
});
|
|
6752
|
+
}
|
|
6753
|
+
ensurePrivateDir(dirname2(paths.localStorePath));
|
|
6754
|
+
const legacyExists = existsSync3(paths.legacyLocalStorePath);
|
|
6755
|
+
const integrityCheck = {};
|
|
6756
|
+
const quarantinedFiles = [];
|
|
6757
|
+
const targetExists = existsSync3(paths.localStorePath);
|
|
6758
|
+
if (targetExists) {
|
|
6759
|
+
try {
|
|
6760
|
+
integrityCheck.target = assertCurrentLocalStore(paths.localStorePath);
|
|
6761
|
+
return legacyMigrationResult(false, "target-exists", paths, [], {
|
|
6762
|
+
status: "target-current",
|
|
6763
|
+
integrityCheck
|
|
6764
|
+
});
|
|
6765
|
+
} catch (error) {
|
|
6766
|
+
integrityCheck.target = "failed";
|
|
6767
|
+
integrityCheck.error = error instanceof Error ? error.message : String(error);
|
|
6768
|
+
if (!legacyExists) {
|
|
6769
|
+
throw new Error(`ArchContext runtime state target is not a valid SQLite database and no legacy store is available: ${paths.localStorePath}`);
|
|
6770
|
+
}
|
|
6771
|
+
}
|
|
6772
|
+
}
|
|
6773
|
+
if (!legacyExists) {
|
|
6774
|
+
return legacyMigrationResult(false, "legacy-missing", paths, [], {
|
|
6775
|
+
status: "legacy-missing",
|
|
6776
|
+
integrityCheck
|
|
6777
|
+
});
|
|
6778
|
+
}
|
|
6779
|
+
assertTrustedLegacyLocalStoreSource(paths);
|
|
6780
|
+
const lock = acquireLegacyMigrationLock(paths);
|
|
6781
|
+
const stagingDir = join2(paths.workspaceStateDir, `.runtime.sqlite.migration-${process.pid}-${randomUUID()}`);
|
|
6782
|
+
const stagingPath = join2(stagingDir, "runtime.sqlite");
|
|
6783
|
+
try {
|
|
6784
|
+
if (targetExists)
|
|
6785
|
+
quarantinedFiles.push(...quarantineExistingLocalStore(paths));
|
|
6786
|
+
ensurePrivateDir(stagingDir);
|
|
6787
|
+
integrityCheck.legacy = vacuumLegacySqliteInto(paths.legacyLocalStorePath, stagingPath);
|
|
6788
|
+
makePrivateFile(stagingPath);
|
|
6789
|
+
migrateSqliteDatabaseSync(stagingPath);
|
|
6790
|
+
compactSqliteDatabase(stagingPath);
|
|
6791
|
+
integrityCheck.staging = assertCurrentLocalStore(stagingPath);
|
|
6792
|
+
publishStagedLocalStore(stagingPath, paths.localStorePath);
|
|
6793
|
+
integrityCheck.target = assertCurrentLocalStore(paths.localStorePath);
|
|
6794
|
+
const markerPath = writeLegacyMigrationMarker(paths, integrityCheck, quarantinedFiles);
|
|
6795
|
+
return legacyMigrationResult(true, undefined, paths, [paths.localStorePath], {
|
|
6796
|
+
status: quarantinedFiles.length > 0 ? "target-quarantined-and-migrated" : "migrated",
|
|
6797
|
+
integrityCheck,
|
|
6798
|
+
markerPath,
|
|
6799
|
+
quarantinedFiles
|
|
6800
|
+
});
|
|
6801
|
+
} catch (error) {
|
|
6802
|
+
throw new Error(`ArchContext legacy SQLite migration failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
6803
|
+
} finally {
|
|
6804
|
+
rmSync2(stagingDir, { recursive: true, force: true });
|
|
6805
|
+
releaseLegacyMigrationLock(lock);
|
|
6806
|
+
}
|
|
6807
|
+
}
|
|
6808
|
+
function openSqliteDatabaseSync(databasePath) {
|
|
6809
|
+
if (databasePath !== ":memory:")
|
|
6810
|
+
ensurePrivateDir(dirname2(databasePath));
|
|
6811
|
+
try {
|
|
6812
|
+
const nodeSqlite = runtimeRequire("node:sqlite");
|
|
6813
|
+
const db2 = new nodeSqlite.DatabaseSync(databasePath);
|
|
6814
|
+
return {
|
|
6815
|
+
exec: (sql) => db2.exec(sql),
|
|
6816
|
+
prepare: (sql) => db2.prepare(sql),
|
|
6817
|
+
close: () => db2.close()
|
|
6818
|
+
};
|
|
6819
|
+
} catch (error) {
|
|
6820
|
+
if (error.code !== "ERR_UNKNOWN_BUILTIN_MODULE" && error.code !== "MODULE_NOT_FOUND") {
|
|
6821
|
+
throw error;
|
|
6822
|
+
}
|
|
6823
|
+
}
|
|
6824
|
+
const bunSqlite = runtimeRequire("bun:sqlite");
|
|
6825
|
+
const db = new bunSqlite.Database(databasePath);
|
|
6826
|
+
return {
|
|
6827
|
+
exec: (sql) => db.exec(sql),
|
|
6828
|
+
prepare: (sql) => db.query(sql),
|
|
6829
|
+
close: () => db.close()
|
|
6830
|
+
};
|
|
6831
|
+
}
|
|
6832
|
+
function legacyMigrationResult(migrated, skippedReason, paths, copiedFiles, details) {
|
|
6833
|
+
return {
|
|
6834
|
+
schemaVersion: "archcontext.legacy-local-store-migration/v1",
|
|
6835
|
+
status: details.status,
|
|
6836
|
+
migrated,
|
|
6837
|
+
skippedReason,
|
|
6838
|
+
legacyLocalStorePath: paths.legacyLocalStorePath,
|
|
6839
|
+
targetLocalStorePath: paths.localStorePath,
|
|
6840
|
+
markerPath: details.markerPath ?? legacyMigrationMarkerPath(paths),
|
|
6841
|
+
lockPath: legacyMigrationLockPath(paths),
|
|
6842
|
+
integrityCheck: details.integrityCheck ?? {},
|
|
6843
|
+
copiedFiles,
|
|
6844
|
+
quarantinedFiles: details.quarantinedFiles ?? []
|
|
6845
|
+
};
|
|
6846
|
+
}
|
|
6847
|
+
function safeSqliteIntegrityCheck(path) {
|
|
6848
|
+
try {
|
|
6849
|
+
return { ok: true, result: assertSqliteIntegrity(path) };
|
|
6850
|
+
} catch (error) {
|
|
6851
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
6852
|
+
}
|
|
6853
|
+
}
|
|
6854
|
+
function safeCurrentLocalStoreCheck(path) {
|
|
6855
|
+
try {
|
|
6856
|
+
return { ok: true, result: assertCurrentLocalStore(path) };
|
|
6857
|
+
} catch (error) {
|
|
6858
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
6859
|
+
}
|
|
6860
|
+
}
|
|
6861
|
+
function safeTrustedLegacyLocalStoreSourceCheck(paths) {
|
|
6862
|
+
try {
|
|
6863
|
+
assertTrustedLegacyLocalStoreSource(paths);
|
|
6864
|
+
return { ok: true };
|
|
6865
|
+
} catch (error) {
|
|
6866
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
6867
|
+
}
|
|
6868
|
+
}
|
|
6869
|
+
function assertSqliteIntegrity(path) {
|
|
6870
|
+
const db = openSqliteDatabaseSync(path);
|
|
6871
|
+
try {
|
|
6872
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
6873
|
+
const row = db.prepare("PRAGMA integrity_check").get();
|
|
6874
|
+
const result = firstSqliteColumn(row);
|
|
6875
|
+
if (result !== "ok")
|
|
6876
|
+
throw new Error(`SQLite integrity_check failed for ${path}: ${result}`);
|
|
6877
|
+
return result;
|
|
6878
|
+
} finally {
|
|
6879
|
+
db.close();
|
|
6880
|
+
}
|
|
6881
|
+
}
|
|
6882
|
+
function assertCurrentLocalStore(path) {
|
|
6883
|
+
const db = openSqliteDatabaseSync(path);
|
|
6884
|
+
try {
|
|
6885
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
6886
|
+
const integrity = sqliteIntegrityCheckOpenDatabase(db, path);
|
|
6887
|
+
assertCurrentLocalStoreSchema(db, path);
|
|
6888
|
+
return integrity;
|
|
6889
|
+
} finally {
|
|
6890
|
+
db.close();
|
|
6891
|
+
}
|
|
6892
|
+
}
|
|
6893
|
+
function assertCurrentLocalStoreSchema(db, path) {
|
|
6894
|
+
const tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((row) => String(row.name)));
|
|
6895
|
+
const missingTables = REQUIRED_LOCAL_STORE_TABLES.filter((table) => !tables.has(table));
|
|
6896
|
+
if (missingTables.length > 0) {
|
|
6897
|
+
throw new Error(`SQLite local store schema incomplete for ${path}: missing tables ${missingTables.join(", ")}`);
|
|
6898
|
+
}
|
|
6899
|
+
const migrations = new Set(db.prepare("SELECT id FROM schema_migrations").all().map((row) => String(row.id)));
|
|
6900
|
+
const missingMigrations = LOCAL_SQLITE_MIGRATIONS.map((migration) => migration.id).filter((id) => !migrations.has(id));
|
|
6901
|
+
if (missingMigrations.length > 0) {
|
|
6902
|
+
throw new Error(`SQLite local store schema incomplete for ${path}: missing migrations ${missingMigrations.join(", ")}`);
|
|
6903
|
+
}
|
|
6904
|
+
}
|
|
6905
|
+
function assertTrustedLegacyLocalStoreSource(paths) {
|
|
6906
|
+
const stat = lstatSync2(paths.legacyLocalStorePath);
|
|
6907
|
+
if (stat.isSymbolicLink()) {
|
|
6908
|
+
throw new Error(`Legacy SQLite source must not be a symbolic link: ${paths.legacyLocalStorePath}`);
|
|
6909
|
+
}
|
|
6910
|
+
if (!stat.isFile()) {
|
|
6911
|
+
throw new Error(`Legacy SQLite source must be a regular file: ${paths.legacyLocalStorePath}`);
|
|
6912
|
+
}
|
|
6913
|
+
const sourceRealPath = realpathSync4.native(paths.legacyLocalStorePath);
|
|
6914
|
+
if (!isPathInsideOrSame(sourceRealPath, paths.repositoryRoot)) {
|
|
6915
|
+
throw new Error(`Legacy SQLite source must stay inside the repository root: ${paths.legacyLocalStorePath}`);
|
|
6916
|
+
}
|
|
6917
|
+
}
|
|
6918
|
+
function vacuumLegacySqliteInto(sourcePath, targetPath) {
|
|
6919
|
+
const db = openSqliteDatabaseSync(sourcePath);
|
|
6920
|
+
try {
|
|
6921
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
6922
|
+
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
6923
|
+
const integrity = sqliteIntegrityCheckOpenDatabase(db, sourcePath);
|
|
6924
|
+
db.exec(`VACUUM INTO ${sqliteStringLiteral(targetPath)}`);
|
|
6925
|
+
return integrity;
|
|
6926
|
+
} finally {
|
|
6927
|
+
db.close();
|
|
6928
|
+
}
|
|
6929
|
+
}
|
|
6930
|
+
function migrateSqliteDatabaseSync(databasePath) {
|
|
6931
|
+
const db = openSqliteDatabaseSync(databasePath);
|
|
6932
|
+
try {
|
|
6933
|
+
for (const pragma of SQLITE_PRAGMAS)
|
|
6934
|
+
db.exec(pragma);
|
|
6935
|
+
for (const migration of LOCAL_SQLITE_MIGRATIONS) {
|
|
6936
|
+
for (const statement of migration.statements)
|
|
6937
|
+
db.exec(statement);
|
|
6938
|
+
db.prepare("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run(migration.id, nowIso());
|
|
6939
|
+
}
|
|
6940
|
+
} finally {
|
|
6941
|
+
db.close();
|
|
6942
|
+
}
|
|
6943
|
+
}
|
|
6944
|
+
function compactSqliteDatabase(databasePath) {
|
|
6945
|
+
const db = openSqliteDatabaseSync(databasePath);
|
|
6946
|
+
try {
|
|
6947
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
6948
|
+
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
6949
|
+
db.exec("PRAGMA journal_mode = DELETE");
|
|
6950
|
+
} finally {
|
|
6951
|
+
db.close();
|
|
6952
|
+
}
|
|
6953
|
+
for (const suffix of ["-wal", "-shm"])
|
|
6954
|
+
rmSync2(`${databasePath}${suffix}`, { force: true });
|
|
6955
|
+
}
|
|
6956
|
+
function sqliteIntegrityCheckOpenDatabase(db, path) {
|
|
6957
|
+
const row = db.prepare("PRAGMA integrity_check").get();
|
|
6958
|
+
const result = firstSqliteColumn(row);
|
|
6959
|
+
if (result !== "ok")
|
|
6960
|
+
throw new Error(`SQLite integrity_check failed for ${path}: ${result}`);
|
|
6961
|
+
return result;
|
|
6962
|
+
}
|
|
6963
|
+
function firstSqliteColumn(row) {
|
|
6964
|
+
const value = row ? Object.values(row)[0] : undefined;
|
|
6965
|
+
return typeof value === "string" ? value : String(value ?? "");
|
|
6966
|
+
}
|
|
6967
|
+
function sqliteStringLiteral(value) {
|
|
6968
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
6969
|
+
}
|
|
6970
|
+
function publishStagedLocalStore(stagingPath, targetPath) {
|
|
6971
|
+
for (const suffix of SQLITE_SIDECAR_SUFFIXES) {
|
|
6972
|
+
const path = `${targetPath}${suffix}`;
|
|
6973
|
+
if (existsSync3(path))
|
|
6974
|
+
throw new Error(`Cannot publish migrated SQLite over existing target file: ${path}`);
|
|
6975
|
+
}
|
|
6976
|
+
renameSync2(stagingPath, targetPath);
|
|
6977
|
+
makePrivateFile(targetPath);
|
|
6978
|
+
fsyncDirectory2(dirname2(targetPath));
|
|
6979
|
+
}
|
|
6980
|
+
function writeLegacyMigrationMarker(paths, integrityCheck, quarantinedFiles) {
|
|
6981
|
+
const markerPath = legacyMigrationMarkerPath(paths);
|
|
6982
|
+
writePrivateJson(markerPath, {
|
|
6983
|
+
schemaVersion: "archcontext.legacy-local-store-migration-marker/v1",
|
|
6984
|
+
migratedAt: nowIso(),
|
|
6985
|
+
legacyLocalStorePath: paths.legacyLocalStorePath,
|
|
6986
|
+
targetLocalStorePath: paths.localStorePath,
|
|
6987
|
+
integrityCheck,
|
|
6988
|
+
quarantinedFiles
|
|
6989
|
+
});
|
|
6990
|
+
return markerPath;
|
|
6991
|
+
}
|
|
6992
|
+
function acquireLegacyMigrationLock(paths) {
|
|
6993
|
+
const lockPath = legacyMigrationLockPath(paths);
|
|
6994
|
+
ensurePrivateDir(dirname2(lockPath));
|
|
6995
|
+
try {
|
|
6996
|
+
const fd = openSync3(lockPath, "wx", 384);
|
|
6997
|
+
writeFileSync2(fd, JSON.stringify({
|
|
6998
|
+
schemaVersion: "archcontext.legacy-local-store-migration-lock/v1",
|
|
6999
|
+
pid: process.pid,
|
|
7000
|
+
root: paths.repositoryRoot,
|
|
7001
|
+
targetLocalStorePath: paths.localStorePath,
|
|
7002
|
+
startedAt: nowIso()
|
|
7003
|
+
}, null, 2), "utf8");
|
|
7004
|
+
fsyncSync2(fd);
|
|
7005
|
+
return { fd, path: lockPath };
|
|
7006
|
+
} catch (error) {
|
|
7007
|
+
const code = error.code;
|
|
7008
|
+
if (code === "EEXIST" && isStaleMigrationLock(lockPath)) {
|
|
7009
|
+
rmSync2(lockPath, { force: true });
|
|
7010
|
+
return acquireLegacyMigrationLock(paths);
|
|
7011
|
+
}
|
|
7012
|
+
if (code === "EEXIST")
|
|
7013
|
+
throw new Error(`Legacy SQLite migration already in progress; lock=${lockPath}`);
|
|
7014
|
+
throw error;
|
|
7015
|
+
}
|
|
7016
|
+
}
|
|
7017
|
+
function releaseLegacyMigrationLock(lock) {
|
|
7018
|
+
closeSync3(lock.fd);
|
|
7019
|
+
rmSync2(lock.path, { force: true });
|
|
7020
|
+
}
|
|
7021
|
+
function isStaleMigrationLock(lockPath) {
|
|
7022
|
+
try {
|
|
7023
|
+
const parsed = JSON.parse(readFileSync5(lockPath, "utf8"));
|
|
7024
|
+
if (typeof parsed.pid !== "number" || parsed.pid <= 0)
|
|
7025
|
+
return true;
|
|
7026
|
+
return !isProcessAlive(parsed.pid);
|
|
7027
|
+
} catch {
|
|
7028
|
+
return true;
|
|
7029
|
+
}
|
|
7030
|
+
}
|
|
7031
|
+
function isProcessAlive(pid) {
|
|
7032
|
+
try {
|
|
7033
|
+
process.kill(pid, 0);
|
|
7034
|
+
return true;
|
|
7035
|
+
} catch (error) {
|
|
7036
|
+
return error.code !== "ESRCH";
|
|
7037
|
+
}
|
|
7038
|
+
}
|
|
7039
|
+
function quarantineExistingLocalStore(paths) {
|
|
7040
|
+
const quarantineDir = join2(paths.workspaceStateDir, "quarantine", `runtime.sqlite-${Date.now()}-${randomUUID()}`);
|
|
7041
|
+
ensurePrivateDir(quarantineDir);
|
|
7042
|
+
const quarantinedFiles = [];
|
|
7043
|
+
for (const suffix of SQLITE_SIDECAR_SUFFIXES) {
|
|
7044
|
+
const source = `${paths.localStorePath}${suffix}`;
|
|
7045
|
+
if (!existsSync3(source))
|
|
7046
|
+
continue;
|
|
7047
|
+
const target = join2(quarantineDir, `runtime.sqlite${suffix}`);
|
|
7048
|
+
renameSync2(source, target);
|
|
7049
|
+
quarantinedFiles.push(target);
|
|
7050
|
+
}
|
|
7051
|
+
const markerPath = legacyMigrationMarkerPath(paths);
|
|
7052
|
+
if (existsSync3(markerPath)) {
|
|
7053
|
+
const target = join2(quarantineDir, LEGACY_MIGRATION_MARKER_FILE);
|
|
7054
|
+
renameSync2(markerPath, target);
|
|
7055
|
+
quarantinedFiles.push(target);
|
|
7056
|
+
}
|
|
7057
|
+
fsyncDirectory2(quarantineDir);
|
|
7058
|
+
fsyncDirectory2(dirname2(paths.localStorePath));
|
|
7059
|
+
return quarantinedFiles;
|
|
7060
|
+
}
|
|
7061
|
+
function legacyMigrationMarkerPath(paths) {
|
|
7062
|
+
return join2(paths.workspaceStateDir, LEGACY_MIGRATION_MARKER_FILE);
|
|
7063
|
+
}
|
|
7064
|
+
function legacyMigrationLockPath(paths) {
|
|
7065
|
+
return join2(paths.workspaceStateDir, LEGACY_MIGRATION_LOCK_FILE);
|
|
7066
|
+
}
|
|
7067
|
+
function writePrivateJson(path, value) {
|
|
7068
|
+
ensurePrivateDir(dirname2(path));
|
|
7069
|
+
const fd = openSync3(path, "w", 384);
|
|
7070
|
+
try {
|
|
7071
|
+
writeFileSync2(fd, JSON.stringify(value, null, 2), "utf8");
|
|
7072
|
+
fsyncSync2(fd);
|
|
7073
|
+
} finally {
|
|
7074
|
+
closeSync3(fd);
|
|
7075
|
+
}
|
|
7076
|
+
makePrivateFile(path);
|
|
7077
|
+
fsyncDirectory2(dirname2(path));
|
|
7078
|
+
}
|
|
7079
|
+
function ensurePrivateDir(path) {
|
|
7080
|
+
mkdirSync2(path, { recursive: true, mode: 448 });
|
|
7081
|
+
if (process.platform !== "win32") {
|
|
7082
|
+
try {
|
|
7083
|
+
chmodSync(path, 448);
|
|
7084
|
+
} catch {}
|
|
7085
|
+
}
|
|
7086
|
+
}
|
|
7087
|
+
function makePrivateFile(path) {
|
|
7088
|
+
if (process.platform !== "win32") {
|
|
7089
|
+
try {
|
|
7090
|
+
chmodSync(path, 384);
|
|
7091
|
+
} catch {}
|
|
7092
|
+
}
|
|
7093
|
+
}
|
|
7094
|
+
function readGitPath(root, args) {
|
|
7095
|
+
try {
|
|
7096
|
+
const value = execFileSync2("git", args, {
|
|
7097
|
+
cwd: root,
|
|
7098
|
+
encoding: "utf8",
|
|
7099
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
7100
|
+
}).trim();
|
|
7101
|
+
return value.length > 0 ? value : undefined;
|
|
7102
|
+
} catch {
|
|
7103
|
+
return;
|
|
7104
|
+
}
|
|
7105
|
+
}
|
|
7106
|
+
function resolveMaybeRelative(base, path) {
|
|
7107
|
+
return isAbsolute3(path) ? resolve6(path) : resolve6(base, path);
|
|
7108
|
+
}
|
|
7109
|
+
function canonicalPath(path) {
|
|
7110
|
+
const resolved = resolve6(path);
|
|
7111
|
+
try {
|
|
7112
|
+
return realpathSync4.native(resolved);
|
|
7113
|
+
} catch {
|
|
7114
|
+
return resolved;
|
|
7115
|
+
}
|
|
7116
|
+
}
|
|
7117
|
+
function isPathInsideOrSame(path, parent) {
|
|
7118
|
+
const child = resolve6(path);
|
|
7119
|
+
const base = resolve6(parent);
|
|
7120
|
+
const fromBase = relative4(base, child);
|
|
7121
|
+
return fromBase === "" || !!fromBase && !fromBase.startsWith("..") && !isAbsolute3(fromBase);
|
|
7122
|
+
}
|
|
7123
|
+
function stableStorageId(prefix, value) {
|
|
7124
|
+
return `${prefix}.${createHash5("sha256").update(value).digest("hex").slice(0, 16)}`;
|
|
7125
|
+
}
|
|
7126
|
+
function nowIso() {
|
|
7127
|
+
return new Date().toISOString();
|
|
7128
|
+
}
|
|
7129
|
+
function fsyncDirectory2(path) {
|
|
7130
|
+
try {
|
|
7131
|
+
const fd = openSync3(path, "r");
|
|
7132
|
+
try {
|
|
7133
|
+
fsyncSync2(fd);
|
|
7134
|
+
} finally {
|
|
7135
|
+
closeSync3(fd);
|
|
7136
|
+
}
|
|
7137
|
+
} catch (error) {
|
|
7138
|
+
if (!isIgnorableDirectoryFsyncError2(error))
|
|
7139
|
+
throw error;
|
|
7140
|
+
}
|
|
7141
|
+
}
|
|
7142
|
+
function isIgnorableDirectoryFsyncError2(error) {
|
|
7143
|
+
const code = error.code;
|
|
7144
|
+
return code === "EINVAL" || code === "EISDIR" || process.platform === "win32" && code === "EPERM";
|
|
6530
7145
|
}
|
|
6531
7146
|
|
|
6532
7147
|
// packages/local-runtime/git-adapter/src/index.ts
|
|
6533
|
-
import { execFileSync as
|
|
6534
|
-
import { existsSync as
|
|
7148
|
+
import { execFileSync as execFileSync3, spawnSync } from "node:child_process";
|
|
7149
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, mkdtempSync, rmSync as rmSync3 } from "node:fs";
|
|
6535
7150
|
import { dirname as dirname3, join as join3, resolve as resolve7 } from "node:path";
|
|
6536
7151
|
function findRepositoryRoot(start) {
|
|
6537
7152
|
try {
|
|
6538
|
-
return resolve7(
|
|
7153
|
+
return resolve7(execFileSync3("git", ["rev-parse", "--show-toplevel"], {
|
|
6539
7154
|
cwd: start,
|
|
6540
7155
|
encoding: "utf8",
|
|
6541
7156
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -6543,7 +7158,7 @@ function findRepositoryRoot(start) {
|
|
|
6543
7158
|
} catch {
|
|
6544
7159
|
let cursor = resolve7(start);
|
|
6545
7160
|
while (cursor !== "/") {
|
|
6546
|
-
if (
|
|
7161
|
+
if (existsSync4(resolve7(cursor, ".git")))
|
|
6547
7162
|
return cursor;
|
|
6548
7163
|
cursor = resolve7(cursor, "..");
|
|
6549
7164
|
}
|
|
@@ -6552,7 +7167,7 @@ function findRepositoryRoot(start) {
|
|
|
6552
7167
|
}
|
|
6553
7168
|
function readHeadSha(root) {
|
|
6554
7169
|
try {
|
|
6555
|
-
return
|
|
7170
|
+
return execFileSync3("git", ["rev-parse", "HEAD"], {
|
|
6556
7171
|
cwd: root,
|
|
6557
7172
|
encoding: "utf8",
|
|
6558
7173
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -6564,7 +7179,7 @@ function readHeadSha(root) {
|
|
|
6564
7179
|
|
|
6565
7180
|
// packages/local-runtime/runtime-daemon/src/index.ts
|
|
6566
7181
|
import { randomBytes } from "node:crypto";
|
|
6567
|
-
import { chmodSync, closeSync as
|
|
7182
|
+
import { chmodSync as chmodSync3, closeSync as closeSync5, existsSync as existsSync8, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync3, openSync as openSync5, readdirSync as readdirSync5, readFileSync as readFileSync8, rmSync as rmSync7, statSync as statSync4, writeFileSync as writeFileSync5 } from "node:fs";
|
|
6568
7183
|
import { createServer } from "node:http";
|
|
6569
7184
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
6570
7185
|
import { dirname as dirname7, join as join6, resolve as resolve11 } from "node:path";
|
|
@@ -6599,13 +7214,13 @@ async function prepareTask(input) {
|
|
|
6599
7214
|
init_src();
|
|
6600
7215
|
|
|
6601
7216
|
// packages/local-runtime/git-adapter/src/index.ts
|
|
6602
|
-
import { execFileSync as
|
|
6603
|
-
import { existsSync as
|
|
7217
|
+
import { execFileSync as execFileSync4, spawnSync as spawnSync2 } from "node:child_process";
|
|
7218
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, mkdtempSync as mkdtempSync2, rmSync as rmSync4 } from "node:fs";
|
|
6604
7219
|
import { dirname as dirname4, join as join4, resolve as resolve8 } from "node:path";
|
|
6605
7220
|
import { tmpdir } from "node:os";
|
|
6606
7221
|
function findRepositoryRoot2(start) {
|
|
6607
7222
|
try {
|
|
6608
|
-
return resolve8(
|
|
7223
|
+
return resolve8(execFileSync4("git", ["rev-parse", "--show-toplevel"], {
|
|
6609
7224
|
cwd: start,
|
|
6610
7225
|
encoding: "utf8",
|
|
6611
7226
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -6613,7 +7228,7 @@ function findRepositoryRoot2(start) {
|
|
|
6613
7228
|
} catch {
|
|
6614
7229
|
let cursor = resolve8(start);
|
|
6615
7230
|
while (cursor !== "/") {
|
|
6616
|
-
if (
|
|
7231
|
+
if (existsSync5(resolve8(cursor, ".git")))
|
|
6617
7232
|
return cursor;
|
|
6618
7233
|
cursor = resolve8(cursor, "..");
|
|
6619
7234
|
}
|
|
@@ -6622,7 +7237,7 @@ function findRepositoryRoot2(start) {
|
|
|
6622
7237
|
}
|
|
6623
7238
|
function readHeadSha2(root) {
|
|
6624
7239
|
try {
|
|
6625
|
-
return
|
|
7240
|
+
return execFileSync4("git", ["rev-parse", "HEAD"], {
|
|
6626
7241
|
cwd: root,
|
|
6627
7242
|
encoding: "utf8",
|
|
6628
7243
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -6682,7 +7297,7 @@ function prepareDetachedReviewWorktree(input) {
|
|
|
6682
7297
|
};
|
|
6683
7298
|
}
|
|
6684
7299
|
const parent = input.tempRoot ? resolve8(input.tempRoot) : tmpdir();
|
|
6685
|
-
|
|
7300
|
+
mkdirSync4(parent, { recursive: true });
|
|
6686
7301
|
const temporaryRoot = mkdtempSync2(join4(parent, "archctx-review-worktree-"));
|
|
6687
7302
|
const worktreeRoot = join4(temporaryRoot, "worktree");
|
|
6688
7303
|
try {
|
|
@@ -6714,7 +7329,7 @@ function prepareDetachedReviewWorktree(input) {
|
|
|
6714
7329
|
}
|
|
6715
7330
|
};
|
|
6716
7331
|
} catch (error) {
|
|
6717
|
-
|
|
7332
|
+
rmSync4(temporaryRoot, { recursive: true, force: true });
|
|
6718
7333
|
if (isGitWorktreeError(error)) {
|
|
6719
7334
|
return {
|
|
6720
7335
|
schemaVersion: "archcontext.detached-review-worktree-verification/v1",
|
|
@@ -6731,9 +7346,9 @@ function removeDetachedReviewWorktree(worktree) {
|
|
|
6731
7346
|
try {
|
|
6732
7347
|
runGit(worktree.sourceRoot, ["worktree", "remove", "--force", worktree.worktreeRoot]);
|
|
6733
7348
|
} catch {
|
|
6734
|
-
|
|
7349
|
+
rmSync4(worktree.worktreeRoot, { recursive: true, force: true });
|
|
6735
7350
|
} finally {
|
|
6736
|
-
|
|
7351
|
+
rmSync4(worktree.temporaryRoot || dirname4(worktree.worktreeRoot), { recursive: true, force: true });
|
|
6737
7352
|
}
|
|
6738
7353
|
}
|
|
6739
7354
|
function readCommitTreeOid(root, headSha) {
|
|
@@ -6769,7 +7384,7 @@ function readDetachedWorktreeObserved(worktreeRoot) {
|
|
|
6769
7384
|
}
|
|
6770
7385
|
}
|
|
6771
7386
|
function runGit(root, args) {
|
|
6772
|
-
return
|
|
7387
|
+
return execFileSync4("git", args, {
|
|
6773
7388
|
cwd: root,
|
|
6774
7389
|
encoding: "utf8",
|
|
6775
7390
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -6787,16 +7402,34 @@ function isGitWorktreeError(error) {
|
|
|
6787
7402
|
}
|
|
6788
7403
|
|
|
6789
7404
|
// packages/local-runtime/local-store-sqlite/src/index.ts
|
|
6790
|
-
import {
|
|
6791
|
-
import {
|
|
7405
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
7406
|
+
import { createHash as createHash6, randomUUID as randomUUID2 } from "node:crypto";
|
|
7407
|
+
import { chmodSync as chmodSync2, closeSync as closeSync4, existsSync as existsSync6, fsyncSync as fsyncSync3, lstatSync as lstatSync3, mkdirSync as mkdirSync5, openSync as openSync4, readFileSync as readFileSync6, realpathSync as realpathSync5, renameSync as renameSync3, rmSync as rmSync5, writeFileSync as writeFileSync3 } from "node:fs";
|
|
6792
7408
|
import { readdir, readFile } from "node:fs/promises";
|
|
6793
|
-
import {
|
|
6794
|
-
|
|
7409
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
7410
|
+
import { homedir as homedir2 } from "node:os";
|
|
7411
|
+
import { dirname as dirname5, isAbsolute as isAbsolute4, join as join5, relative as relative5, resolve as resolve9 } from "node:path";
|
|
7412
|
+
var runtimeRequire2 = createRequire3(import.meta.url);
|
|
7413
|
+
var SQLITE_SIDECAR_SUFFIXES2 = ["", "-wal", "-shm"];
|
|
7414
|
+
var LEGACY_MIGRATION_MARKER_FILE2 = "runtime.sqlite.migration.json";
|
|
7415
|
+
var LEGACY_MIGRATION_LOCK_FILE2 = "runtime.sqlite.migration.lock";
|
|
7416
|
+
var REQUIRED_LOCAL_STORE_TABLES2 = [
|
|
7417
|
+
"schema_migrations",
|
|
7418
|
+
"repository_sessions",
|
|
7419
|
+
"snapshots",
|
|
7420
|
+
"task_states",
|
|
7421
|
+
"observed_evidence",
|
|
7422
|
+
"review_results",
|
|
7423
|
+
"landscapes",
|
|
7424
|
+
"cross_repo_edges",
|
|
7425
|
+
"changeset_journal"
|
|
7426
|
+
];
|
|
7427
|
+
var SQLITE_PRAGMAS2 = [
|
|
6795
7428
|
"PRAGMA journal_mode = WAL",
|
|
6796
7429
|
"PRAGMA foreign_keys = ON",
|
|
6797
7430
|
"PRAGMA busy_timeout = 5000"
|
|
6798
7431
|
];
|
|
6799
|
-
var
|
|
7432
|
+
var LOCAL_SQLITE_MIGRATIONS2 = [
|
|
6800
7433
|
{
|
|
6801
7434
|
id: "0001_runtime_state",
|
|
6802
7435
|
statements: [
|
|
@@ -6893,8 +7526,117 @@ var LOCAL_SQLITE_MIGRATIONS = [
|
|
|
6893
7526
|
]
|
|
6894
7527
|
}
|
|
6895
7528
|
];
|
|
7529
|
+
var ARCHCONTEXT_STATE_DIR_ENV2 = "ARCHCONTEXT_STATE_DIR";
|
|
7530
|
+
var ARCHCONTEXT_LOCAL_STORE_PATH_ENV2 = "ARCHCONTEXT_LOCAL_STORE_PATH";
|
|
7531
|
+
function defaultArchContextStateRoot2(env = process.env, platform = process.platform, home = homedir2()) {
|
|
7532
|
+
const override = env[ARCHCONTEXT_STATE_DIR_ENV2];
|
|
7533
|
+
if (override)
|
|
7534
|
+
return { path: resolve9(override), source: "environment" };
|
|
7535
|
+
if (platform === "darwin")
|
|
7536
|
+
return { path: join5(home, "Library", "Application Support", "ArchContext"), source: "os-user-data" };
|
|
7537
|
+
if (platform === "win32")
|
|
7538
|
+
return { path: join5(env.LOCALAPPDATA ?? join5(home, "AppData", "Local"), "ArchContext"), source: "os-user-data" };
|
|
7539
|
+
return { path: join5(env.XDG_DATA_HOME ?? join5(home, ".local", "share"), "archcontext"), source: "os-user-data" };
|
|
7540
|
+
}
|
|
7541
|
+
function runtimeStatePaths2(root = process.cwd(), env = process.env) {
|
|
7542
|
+
const repositoryRoot = readGitPath2(root, ["rev-parse", "--show-toplevel"]) ?? root;
|
|
7543
|
+
const canonicalRepositoryRoot3 = canonicalPath2(repositoryRoot);
|
|
7544
|
+
const gitCommonDir = readGitPath2(canonicalRepositoryRoot3, ["rev-parse", "--git-common-dir"]);
|
|
7545
|
+
const repositoryAnchor = canonicalPath2(gitCommonDir ? resolveMaybeRelative2(canonicalRepositoryRoot3, gitCommonDir) : canonicalRepositoryRoot3);
|
|
7546
|
+
const workspaceAnchor = canonicalRepositoryRoot3;
|
|
7547
|
+
const storageRepositoryId = stableStorageId2("repo", repositoryAnchor);
|
|
7548
|
+
const storageWorkspaceId = stableStorageId2("ws", workspaceAnchor);
|
|
7549
|
+
const stateRoot = defaultArchContextStateRoot2(env);
|
|
7550
|
+
const repositoryStateDir = join5(stateRoot.path, "repositories", storageRepositoryId);
|
|
7551
|
+
const workspaceStateDir = join5(repositoryStateDir, "worktrees", storageWorkspaceId);
|
|
7552
|
+
const legacyControlDir = resolve9(canonicalRepositoryRoot3, ".archcontext", ".local");
|
|
7553
|
+
return {
|
|
7554
|
+
schemaVersion: "archcontext.runtime-state-paths/v1",
|
|
7555
|
+
stateRoot: stateRoot.path,
|
|
7556
|
+
source: stateRoot.source,
|
|
7557
|
+
repositoryRoot: canonicalRepositoryRoot3,
|
|
7558
|
+
repositoryAnchor,
|
|
7559
|
+
workspaceAnchor,
|
|
7560
|
+
storageRepositoryId,
|
|
7561
|
+
storageWorkspaceId,
|
|
7562
|
+
repositoryId: storageRepositoryId,
|
|
7563
|
+
workspaceId: storageWorkspaceId,
|
|
7564
|
+
repositoryStateDir,
|
|
7565
|
+
workspaceStateDir,
|
|
7566
|
+
sharedCacheDir: join5(repositoryStateDir, "shared", "cache"),
|
|
7567
|
+
localStorePath: env[ARCHCONTEXT_LOCAL_STORE_PATH_ENV2] ?? join5(workspaceStateDir, "runtime.sqlite"),
|
|
7568
|
+
daemonConnectionPath: join5(workspaceStateDir, "archctxd.json"),
|
|
7569
|
+
daemonLockPath: join5(workspaceStateDir, "archctxd.lock"),
|
|
7570
|
+
daemonLogPath: join5(workspaceStateDir, "archctxd.log"),
|
|
7571
|
+
developerReviewRunStateDir: join5(workspaceStateDir, "developer-review-runs"),
|
|
7572
|
+
legacyControlDir,
|
|
7573
|
+
legacyLocalStorePath: join5(legacyControlDir, "runtime.sqlite")
|
|
7574
|
+
};
|
|
7575
|
+
}
|
|
6896
7576
|
function defaultLocalStorePath2(root = process.cwd()) {
|
|
6897
|
-
return
|
|
7577
|
+
return runtimeStatePaths2(root).localStorePath;
|
|
7578
|
+
}
|
|
7579
|
+
function migrateLegacyLocalStoreIfNeeded2(root = process.cwd(), env = process.env) {
|
|
7580
|
+
const paths = runtimeStatePaths2(root, env);
|
|
7581
|
+
if (env[ARCHCONTEXT_LOCAL_STORE_PATH_ENV2]) {
|
|
7582
|
+
return legacyMigrationResult2(false, "explicit-local-store-override", paths, [], {
|
|
7583
|
+
status: "explicit-local-store-override"
|
|
7584
|
+
});
|
|
7585
|
+
}
|
|
7586
|
+
ensurePrivateDir2(dirname5(paths.localStorePath));
|
|
7587
|
+
const legacyExists = existsSync6(paths.legacyLocalStorePath);
|
|
7588
|
+
const integrityCheck = {};
|
|
7589
|
+
const quarantinedFiles = [];
|
|
7590
|
+
const targetExists = existsSync6(paths.localStorePath);
|
|
7591
|
+
if (targetExists) {
|
|
7592
|
+
try {
|
|
7593
|
+
integrityCheck.target = assertCurrentLocalStore2(paths.localStorePath);
|
|
7594
|
+
return legacyMigrationResult2(false, "target-exists", paths, [], {
|
|
7595
|
+
status: "target-current",
|
|
7596
|
+
integrityCheck
|
|
7597
|
+
});
|
|
7598
|
+
} catch (error) {
|
|
7599
|
+
integrityCheck.target = "failed";
|
|
7600
|
+
integrityCheck.error = error instanceof Error ? error.message : String(error);
|
|
7601
|
+
if (!legacyExists) {
|
|
7602
|
+
throw new Error(`ArchContext runtime state target is not a valid SQLite database and no legacy store is available: ${paths.localStorePath}`);
|
|
7603
|
+
}
|
|
7604
|
+
}
|
|
7605
|
+
}
|
|
7606
|
+
if (!legacyExists) {
|
|
7607
|
+
return legacyMigrationResult2(false, "legacy-missing", paths, [], {
|
|
7608
|
+
status: "legacy-missing",
|
|
7609
|
+
integrityCheck
|
|
7610
|
+
});
|
|
7611
|
+
}
|
|
7612
|
+
assertTrustedLegacyLocalStoreSource2(paths);
|
|
7613
|
+
const lock = acquireLegacyMigrationLock2(paths);
|
|
7614
|
+
const stagingDir = join5(paths.workspaceStateDir, `.runtime.sqlite.migration-${process.pid}-${randomUUID2()}`);
|
|
7615
|
+
const stagingPath = join5(stagingDir, "runtime.sqlite");
|
|
7616
|
+
try {
|
|
7617
|
+
if (targetExists)
|
|
7618
|
+
quarantinedFiles.push(...quarantineExistingLocalStore2(paths));
|
|
7619
|
+
ensurePrivateDir2(stagingDir);
|
|
7620
|
+
integrityCheck.legacy = vacuumLegacySqliteInto2(paths.legacyLocalStorePath, stagingPath);
|
|
7621
|
+
makePrivateFile2(stagingPath);
|
|
7622
|
+
migrateSqliteDatabaseSync2(stagingPath);
|
|
7623
|
+
compactSqliteDatabase2(stagingPath);
|
|
7624
|
+
integrityCheck.staging = assertCurrentLocalStore2(stagingPath);
|
|
7625
|
+
publishStagedLocalStore2(stagingPath, paths.localStorePath);
|
|
7626
|
+
integrityCheck.target = assertCurrentLocalStore2(paths.localStorePath);
|
|
7627
|
+
const markerPath = writeLegacyMigrationMarker2(paths, integrityCheck, quarantinedFiles);
|
|
7628
|
+
return legacyMigrationResult2(true, undefined, paths, [paths.localStorePath], {
|
|
7629
|
+
status: quarantinedFiles.length > 0 ? "target-quarantined-and-migrated" : "migrated",
|
|
7630
|
+
integrityCheck,
|
|
7631
|
+
markerPath,
|
|
7632
|
+
quarantinedFiles
|
|
7633
|
+
});
|
|
7634
|
+
} catch (error) {
|
|
7635
|
+
throw new Error(`ArchContext legacy SQLite migration failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
7636
|
+
} finally {
|
|
7637
|
+
rmSync5(stagingDir, { recursive: true, force: true });
|
|
7638
|
+
releaseLegacyMigrationLock2(lock);
|
|
7639
|
+
}
|
|
6898
7640
|
}
|
|
6899
7641
|
|
|
6900
7642
|
class SqliteLocalStore {
|
|
@@ -6905,20 +7647,20 @@ class SqliteLocalStore {
|
|
|
6905
7647
|
}
|
|
6906
7648
|
async migrate() {
|
|
6907
7649
|
const db = await this.database();
|
|
6908
|
-
for (const pragma of
|
|
7650
|
+
for (const pragma of SQLITE_PRAGMAS2)
|
|
6909
7651
|
db.exec(pragma);
|
|
6910
|
-
for (const migration of
|
|
7652
|
+
for (const migration of LOCAL_SQLITE_MIGRATIONS2) {
|
|
6911
7653
|
for (const statement of migration.statements)
|
|
6912
7654
|
db.exec(statement);
|
|
6913
|
-
db.prepare("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run(migration.id,
|
|
7655
|
+
db.prepare("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run(migration.id, nowIso2());
|
|
6914
7656
|
}
|
|
6915
7657
|
}
|
|
6916
7658
|
async beginSnapshot(snapshot) {
|
|
6917
7659
|
const db = await this.database();
|
|
6918
|
-
const snapshotId = `snapshot_${
|
|
7660
|
+
const snapshotId = `snapshot_${randomUUID2()}`;
|
|
6919
7661
|
db.prepare(`INSERT INTO snapshots
|
|
6920
7662
|
(id, repository_id, head_sha, worktree_digest, state, created_at)
|
|
6921
|
-
VALUES (?, ?, ?, ?, ?, ?)`).run(snapshotId, snapshot.repositoryId, snapshot.headSha, snapshot.worktreeDigest, "pending",
|
|
7663
|
+
VALUES (?, ?, ?, ?, ?, ?)`).run(snapshotId, snapshot.repositoryId, snapshot.headSha, snapshot.worktreeDigest, "pending", nowIso2());
|
|
6922
7664
|
return snapshotId;
|
|
6923
7665
|
}
|
|
6924
7666
|
async commitSnapshot(snapshotId) {
|
|
@@ -6926,7 +7668,7 @@ class SqliteLocalStore {
|
|
|
6926
7668
|
const existing = db.prepare("SELECT id FROM snapshots WHERE id = ?").get(snapshotId);
|
|
6927
7669
|
if (!existing)
|
|
6928
7670
|
throw new Error(`Snapshot not found: ${snapshotId}`);
|
|
6929
|
-
db.prepare("UPDATE snapshots SET state = ?, committed_at = ? WHERE id = ?").run("committed",
|
|
7671
|
+
db.prepare("UPDATE snapshots SET state = ?, committed_at = ? WHERE id = ?").run("committed", nowIso2(), snapshotId);
|
|
6930
7672
|
}
|
|
6931
7673
|
recoverPendingSnapshots() {
|
|
6932
7674
|
const db = this.requireOpenDatabase();
|
|
@@ -6954,10 +7696,10 @@ class SqliteLocalStore {
|
|
|
6954
7696
|
}
|
|
6955
7697
|
async beginChangeSet(root, draft) {
|
|
6956
7698
|
const db = await this.database();
|
|
6957
|
-
const journalId = `changeset_${
|
|
7699
|
+
const journalId = `changeset_${randomUUID2()}`;
|
|
6958
7700
|
db.prepare(`INSERT INTO changeset_journal
|
|
6959
7701
|
(journal_id, changeset_id, root, status, metadata_json, files_json, created_at, updated_at)
|
|
6960
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(journalId, draft.id, root, "pending", stableJson(changeSetMetadata(draft)), "[]",
|
|
7702
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(journalId, draft.id, root, "pending", stableJson(changeSetMetadata(draft)), "[]", nowIso2(), nowIso2());
|
|
6961
7703
|
return journalId;
|
|
6962
7704
|
}
|
|
6963
7705
|
async recordChangeSetFile(journalId, file) {
|
|
@@ -6967,14 +7709,14 @@ class SqliteLocalStore {
|
|
|
6967
7709
|
throw new Error(`ChangeSet journal not found: ${journalId}`);
|
|
6968
7710
|
const files = JSON.parse(String(row.files_json));
|
|
6969
7711
|
files.push(file);
|
|
6970
|
-
db.prepare("UPDATE changeset_journal SET files_json = ?, updated_at = ? WHERE journal_id = ?").run(stableJson(files),
|
|
7712
|
+
db.prepare("UPDATE changeset_journal SET files_json = ?, updated_at = ? WHERE journal_id = ?").run(stableJson(files), nowIso2(), journalId);
|
|
6971
7713
|
}
|
|
6972
7714
|
async commitChangeSet(journalId) {
|
|
6973
7715
|
const db = await this.database();
|
|
6974
7716
|
const existing = db.prepare("SELECT journal_id FROM changeset_journal WHERE journal_id = ?").get(journalId);
|
|
6975
7717
|
if (!existing)
|
|
6976
7718
|
throw new Error(`ChangeSet journal not found: ${journalId}`);
|
|
6977
|
-
db.prepare("UPDATE changeset_journal SET status = ?, updated_at = ?, completed_at = ? WHERE journal_id = ?").run("committed",
|
|
7719
|
+
db.prepare("UPDATE changeset_journal SET status = ?, updated_at = ?, completed_at = ? WHERE journal_id = ?").run("committed", nowIso2(), nowIso2(), journalId);
|
|
6978
7720
|
}
|
|
6979
7721
|
async abortChangeSet(journalId, reason) {
|
|
6980
7722
|
const db = await this.database();
|
|
@@ -6982,7 +7724,7 @@ class SqliteLocalStore {
|
|
|
6982
7724
|
if (!row)
|
|
6983
7725
|
throw new Error(`ChangeSet journal not found: ${journalId}`);
|
|
6984
7726
|
const metadata = JSON.parse(String(row.metadata_json));
|
|
6985
|
-
db.prepare("UPDATE changeset_journal SET status = ?, metadata_json = ?, updated_at = ?, completed_at = ? WHERE journal_id = ?").run("aborted", stableJson({ ...metadata, abortReason: reason }),
|
|
7727
|
+
db.prepare("UPDATE changeset_journal SET status = ?, metadata_json = ?, updated_at = ?, completed_at = ? WHERE journal_id = ?").run("aborted", stableJson({ ...metadata, abortReason: reason }), nowIso2(), nowIso2(), journalId);
|
|
6986
7728
|
}
|
|
6987
7729
|
recoverPendingChangeSets() {
|
|
6988
7730
|
const db = this.requireOpenDatabase();
|
|
@@ -6994,7 +7736,7 @@ class SqliteLocalStore {
|
|
|
6994
7736
|
for (const row of rows) {
|
|
6995
7737
|
const files = JSON.parse(String(row.files_json));
|
|
6996
7738
|
recoverJournalFiles(String(row.root), files);
|
|
6997
|
-
db.prepare("UPDATE changeset_journal SET status = ?, updated_at = ?, completed_at = ? WHERE journal_id = ?").run("recovered",
|
|
7739
|
+
db.prepare("UPDATE changeset_journal SET status = ?, updated_at = ?, completed_at = ? WHERE journal_id = ?").run("recovered", nowIso2(), nowIso2(), String(row.journal_id));
|
|
6998
7740
|
}
|
|
6999
7741
|
return rows.length;
|
|
7000
7742
|
}
|
|
@@ -7002,7 +7744,7 @@ class SqliteLocalStore {
|
|
|
7002
7744
|
const db = await this.database();
|
|
7003
7745
|
db.prepare(`INSERT OR REPLACE INTO task_states
|
|
7004
7746
|
(task_session_id, payload_json, updated_at)
|
|
7005
|
-
VALUES (?, ?, ?)`).run(taskSessionId, stableJson(state),
|
|
7747
|
+
VALUES (?, ?, ?)`).run(taskSessionId, stableJson(state), nowIso2());
|
|
7006
7748
|
}
|
|
7007
7749
|
async readTaskState(taskSessionId) {
|
|
7008
7750
|
const db = await this.database();
|
|
@@ -7013,13 +7755,13 @@ class SqliteLocalStore {
|
|
|
7013
7755
|
const db = await this.database();
|
|
7014
7756
|
db.prepare(`INSERT OR REPLACE INTO review_results
|
|
7015
7757
|
(review_id, task_session_id, payload_json, created_at)
|
|
7016
|
-
VALUES (?, ?, ?, ?)`).run(reviewId, reviewTaskSessionId(result) ?? reviewId, stableJson(result),
|
|
7758
|
+
VALUES (?, ?, ?, ?)`).run(reviewId, reviewTaskSessionId(result) ?? reviewId, stableJson(result), nowIso2());
|
|
7017
7759
|
}
|
|
7018
7760
|
async saveLandscape(landscape) {
|
|
7019
7761
|
const db = await this.database();
|
|
7020
7762
|
db.prepare(`INSERT OR REPLACE INTO landscapes
|
|
7021
7763
|
(id, digest, metadata_json, updated_at)
|
|
7022
|
-
VALUES (?, ?, ?, ?)`).run(landscape.id, landscapeDigest(landscape), stableJson(landscape),
|
|
7764
|
+
VALUES (?, ?, ?, ?)`).run(landscape.id, landscapeDigest(landscape), stableJson(landscape), nowIso2());
|
|
7023
7765
|
}
|
|
7024
7766
|
async readLandscape(landscapeId) {
|
|
7025
7767
|
const db = await this.database();
|
|
@@ -7030,7 +7772,7 @@ class SqliteLocalStore {
|
|
|
7030
7772
|
const db = await this.database();
|
|
7031
7773
|
db.prepare(`INSERT OR REPLACE INTO cross_repo_edges
|
|
7032
7774
|
(id, landscape_id, from_repository_id, from_node_id, to_repository_id, to_node_id, via_kind, via_id, metadata_json, updated_at)
|
|
7033
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(relation.id, "unscoped", relation.source.repositoryId, relation.source.nodeId, relation.target.repositoryId, relation.target.nodeId, relation.via.kind, relation.via.id, stableJson(relation),
|
|
7775
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(relation.id, "unscoped", relation.source.repositoryId, relation.source.nodeId, relation.target.repositoryId, relation.target.nodeId, relation.via.kind, relation.via.id, stableJson(relation), nowIso2());
|
|
7034
7776
|
}
|
|
7035
7777
|
async listCrossRepoRelations(landscape) {
|
|
7036
7778
|
const db = await this.database();
|
|
@@ -7090,7 +7832,7 @@ async function rebuildDerivedLandscapeState(store, input) {
|
|
|
7090
7832
|
}
|
|
7091
7833
|
async function openSqliteDatabase(databasePath) {
|
|
7092
7834
|
if (databasePath !== ":memory:")
|
|
7093
|
-
|
|
7835
|
+
ensurePrivateDir2(dirname5(databasePath));
|
|
7094
7836
|
try {
|
|
7095
7837
|
const nodeSqlite = await import("node:sqlite");
|
|
7096
7838
|
const db = new nodeSqlite.DatabaseSync(databasePath);
|
|
@@ -7109,10 +7851,293 @@ async function openSqliteDatabase(databasePath) {
|
|
|
7109
7851
|
};
|
|
7110
7852
|
}
|
|
7111
7853
|
}
|
|
7854
|
+
function openSqliteDatabaseSync2(databasePath) {
|
|
7855
|
+
if (databasePath !== ":memory:")
|
|
7856
|
+
ensurePrivateDir2(dirname5(databasePath));
|
|
7857
|
+
try {
|
|
7858
|
+
const nodeSqlite = runtimeRequire2("node:sqlite");
|
|
7859
|
+
const db2 = new nodeSqlite.DatabaseSync(databasePath);
|
|
7860
|
+
return {
|
|
7861
|
+
exec: (sql) => db2.exec(sql),
|
|
7862
|
+
prepare: (sql) => db2.prepare(sql),
|
|
7863
|
+
close: () => db2.close()
|
|
7864
|
+
};
|
|
7865
|
+
} catch (error) {
|
|
7866
|
+
if (error.code !== "ERR_UNKNOWN_BUILTIN_MODULE" && error.code !== "MODULE_NOT_FOUND") {
|
|
7867
|
+
throw error;
|
|
7868
|
+
}
|
|
7869
|
+
}
|
|
7870
|
+
const bunSqlite = runtimeRequire2("bun:sqlite");
|
|
7871
|
+
const db = new bunSqlite.Database(databasePath);
|
|
7872
|
+
return {
|
|
7873
|
+
exec: (sql) => db.exec(sql),
|
|
7874
|
+
prepare: (sql) => db.query(sql),
|
|
7875
|
+
close: () => db.close()
|
|
7876
|
+
};
|
|
7877
|
+
}
|
|
7878
|
+
function legacyMigrationResult2(migrated, skippedReason, paths, copiedFiles, details) {
|
|
7879
|
+
return {
|
|
7880
|
+
schemaVersion: "archcontext.legacy-local-store-migration/v1",
|
|
7881
|
+
status: details.status,
|
|
7882
|
+
migrated,
|
|
7883
|
+
skippedReason,
|
|
7884
|
+
legacyLocalStorePath: paths.legacyLocalStorePath,
|
|
7885
|
+
targetLocalStorePath: paths.localStorePath,
|
|
7886
|
+
markerPath: details.markerPath ?? legacyMigrationMarkerPath2(paths),
|
|
7887
|
+
lockPath: legacyMigrationLockPath2(paths),
|
|
7888
|
+
integrityCheck: details.integrityCheck ?? {},
|
|
7889
|
+
copiedFiles,
|
|
7890
|
+
quarantinedFiles: details.quarantinedFiles ?? []
|
|
7891
|
+
};
|
|
7892
|
+
}
|
|
7893
|
+
function assertCurrentLocalStore2(path) {
|
|
7894
|
+
const db = openSqliteDatabaseSync2(path);
|
|
7895
|
+
try {
|
|
7896
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
7897
|
+
const integrity = sqliteIntegrityCheckOpenDatabase2(db, path);
|
|
7898
|
+
assertCurrentLocalStoreSchema2(db, path);
|
|
7899
|
+
return integrity;
|
|
7900
|
+
} finally {
|
|
7901
|
+
db.close();
|
|
7902
|
+
}
|
|
7903
|
+
}
|
|
7904
|
+
function assertCurrentLocalStoreSchema2(db, path) {
|
|
7905
|
+
const tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((row) => String(row.name)));
|
|
7906
|
+
const missingTables = REQUIRED_LOCAL_STORE_TABLES2.filter((table) => !tables.has(table));
|
|
7907
|
+
if (missingTables.length > 0) {
|
|
7908
|
+
throw new Error(`SQLite local store schema incomplete for ${path}: missing tables ${missingTables.join(", ")}`);
|
|
7909
|
+
}
|
|
7910
|
+
const migrations = new Set(db.prepare("SELECT id FROM schema_migrations").all().map((row) => String(row.id)));
|
|
7911
|
+
const missingMigrations = LOCAL_SQLITE_MIGRATIONS2.map((migration) => migration.id).filter((id) => !migrations.has(id));
|
|
7912
|
+
if (missingMigrations.length > 0) {
|
|
7913
|
+
throw new Error(`SQLite local store schema incomplete for ${path}: missing migrations ${missingMigrations.join(", ")}`);
|
|
7914
|
+
}
|
|
7915
|
+
}
|
|
7916
|
+
function assertTrustedLegacyLocalStoreSource2(paths) {
|
|
7917
|
+
const stat = lstatSync3(paths.legacyLocalStorePath);
|
|
7918
|
+
if (stat.isSymbolicLink()) {
|
|
7919
|
+
throw new Error(`Legacy SQLite source must not be a symbolic link: ${paths.legacyLocalStorePath}`);
|
|
7920
|
+
}
|
|
7921
|
+
if (!stat.isFile()) {
|
|
7922
|
+
throw new Error(`Legacy SQLite source must be a regular file: ${paths.legacyLocalStorePath}`);
|
|
7923
|
+
}
|
|
7924
|
+
const sourceRealPath = realpathSync5.native(paths.legacyLocalStorePath);
|
|
7925
|
+
if (!isPathInsideOrSame2(sourceRealPath, paths.repositoryRoot)) {
|
|
7926
|
+
throw new Error(`Legacy SQLite source must stay inside the repository root: ${paths.legacyLocalStorePath}`);
|
|
7927
|
+
}
|
|
7928
|
+
}
|
|
7929
|
+
function vacuumLegacySqliteInto2(sourcePath, targetPath) {
|
|
7930
|
+
const db = openSqliteDatabaseSync2(sourcePath);
|
|
7931
|
+
try {
|
|
7932
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
7933
|
+
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
7934
|
+
const integrity = sqliteIntegrityCheckOpenDatabase2(db, sourcePath);
|
|
7935
|
+
db.exec(`VACUUM INTO ${sqliteStringLiteral2(targetPath)}`);
|
|
7936
|
+
return integrity;
|
|
7937
|
+
} finally {
|
|
7938
|
+
db.close();
|
|
7939
|
+
}
|
|
7940
|
+
}
|
|
7941
|
+
function migrateSqliteDatabaseSync2(databasePath) {
|
|
7942
|
+
const db = openSqliteDatabaseSync2(databasePath);
|
|
7943
|
+
try {
|
|
7944
|
+
for (const pragma of SQLITE_PRAGMAS2)
|
|
7945
|
+
db.exec(pragma);
|
|
7946
|
+
for (const migration of LOCAL_SQLITE_MIGRATIONS2) {
|
|
7947
|
+
for (const statement of migration.statements)
|
|
7948
|
+
db.exec(statement);
|
|
7949
|
+
db.prepare("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run(migration.id, nowIso2());
|
|
7950
|
+
}
|
|
7951
|
+
} finally {
|
|
7952
|
+
db.close();
|
|
7953
|
+
}
|
|
7954
|
+
}
|
|
7955
|
+
function compactSqliteDatabase2(databasePath) {
|
|
7956
|
+
const db = openSqliteDatabaseSync2(databasePath);
|
|
7957
|
+
try {
|
|
7958
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
7959
|
+
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
7960
|
+
db.exec("PRAGMA journal_mode = DELETE");
|
|
7961
|
+
} finally {
|
|
7962
|
+
db.close();
|
|
7963
|
+
}
|
|
7964
|
+
for (const suffix of ["-wal", "-shm"])
|
|
7965
|
+
rmSync5(`${databasePath}${suffix}`, { force: true });
|
|
7966
|
+
}
|
|
7967
|
+
function sqliteIntegrityCheckOpenDatabase2(db, path) {
|
|
7968
|
+
const row = db.prepare("PRAGMA integrity_check").get();
|
|
7969
|
+
const result = firstSqliteColumn2(row);
|
|
7970
|
+
if (result !== "ok")
|
|
7971
|
+
throw new Error(`SQLite integrity_check failed for ${path}: ${result}`);
|
|
7972
|
+
return result;
|
|
7973
|
+
}
|
|
7974
|
+
function firstSqliteColumn2(row) {
|
|
7975
|
+
const value = row ? Object.values(row)[0] : undefined;
|
|
7976
|
+
return typeof value === "string" ? value : String(value ?? "");
|
|
7977
|
+
}
|
|
7978
|
+
function sqliteStringLiteral2(value) {
|
|
7979
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
7980
|
+
}
|
|
7981
|
+
function publishStagedLocalStore2(stagingPath, targetPath) {
|
|
7982
|
+
for (const suffix of SQLITE_SIDECAR_SUFFIXES2) {
|
|
7983
|
+
const path = `${targetPath}${suffix}`;
|
|
7984
|
+
if (existsSync6(path))
|
|
7985
|
+
throw new Error(`Cannot publish migrated SQLite over existing target file: ${path}`);
|
|
7986
|
+
}
|
|
7987
|
+
renameSync3(stagingPath, targetPath);
|
|
7988
|
+
makePrivateFile2(targetPath);
|
|
7989
|
+
fsyncDirectory3(dirname5(targetPath));
|
|
7990
|
+
}
|
|
7991
|
+
function writeLegacyMigrationMarker2(paths, integrityCheck, quarantinedFiles) {
|
|
7992
|
+
const markerPath = legacyMigrationMarkerPath2(paths);
|
|
7993
|
+
writePrivateJson2(markerPath, {
|
|
7994
|
+
schemaVersion: "archcontext.legacy-local-store-migration-marker/v1",
|
|
7995
|
+
migratedAt: nowIso2(),
|
|
7996
|
+
legacyLocalStorePath: paths.legacyLocalStorePath,
|
|
7997
|
+
targetLocalStorePath: paths.localStorePath,
|
|
7998
|
+
integrityCheck,
|
|
7999
|
+
quarantinedFiles
|
|
8000
|
+
});
|
|
8001
|
+
return markerPath;
|
|
8002
|
+
}
|
|
8003
|
+
function acquireLegacyMigrationLock2(paths) {
|
|
8004
|
+
const lockPath = legacyMigrationLockPath2(paths);
|
|
8005
|
+
ensurePrivateDir2(dirname5(lockPath));
|
|
8006
|
+
try {
|
|
8007
|
+
const fd = openSync4(lockPath, "wx", 384);
|
|
8008
|
+
writeFileSync3(fd, JSON.stringify({
|
|
8009
|
+
schemaVersion: "archcontext.legacy-local-store-migration-lock/v1",
|
|
8010
|
+
pid: process.pid,
|
|
8011
|
+
root: paths.repositoryRoot,
|
|
8012
|
+
targetLocalStorePath: paths.localStorePath,
|
|
8013
|
+
startedAt: nowIso2()
|
|
8014
|
+
}, null, 2), "utf8");
|
|
8015
|
+
fsyncSync3(fd);
|
|
8016
|
+
return { fd, path: lockPath };
|
|
8017
|
+
} catch (error) {
|
|
8018
|
+
const code = error.code;
|
|
8019
|
+
if (code === "EEXIST" && isStaleMigrationLock2(lockPath)) {
|
|
8020
|
+
rmSync5(lockPath, { force: true });
|
|
8021
|
+
return acquireLegacyMigrationLock2(paths);
|
|
8022
|
+
}
|
|
8023
|
+
if (code === "EEXIST")
|
|
8024
|
+
throw new Error(`Legacy SQLite migration already in progress; lock=${lockPath}`);
|
|
8025
|
+
throw error;
|
|
8026
|
+
}
|
|
8027
|
+
}
|
|
8028
|
+
function releaseLegacyMigrationLock2(lock) {
|
|
8029
|
+
closeSync4(lock.fd);
|
|
8030
|
+
rmSync5(lock.path, { force: true });
|
|
8031
|
+
}
|
|
8032
|
+
function isStaleMigrationLock2(lockPath) {
|
|
8033
|
+
try {
|
|
8034
|
+
const parsed = JSON.parse(readFileSync6(lockPath, "utf8"));
|
|
8035
|
+
if (typeof parsed.pid !== "number" || parsed.pid <= 0)
|
|
8036
|
+
return true;
|
|
8037
|
+
return !isProcessAlive2(parsed.pid);
|
|
8038
|
+
} catch {
|
|
8039
|
+
return true;
|
|
8040
|
+
}
|
|
8041
|
+
}
|
|
8042
|
+
function isProcessAlive2(pid) {
|
|
8043
|
+
try {
|
|
8044
|
+
process.kill(pid, 0);
|
|
8045
|
+
return true;
|
|
8046
|
+
} catch (error) {
|
|
8047
|
+
return error.code !== "ESRCH";
|
|
8048
|
+
}
|
|
8049
|
+
}
|
|
8050
|
+
function quarantineExistingLocalStore2(paths) {
|
|
8051
|
+
const quarantineDir = join5(paths.workspaceStateDir, "quarantine", `runtime.sqlite-${Date.now()}-${randomUUID2()}`);
|
|
8052
|
+
ensurePrivateDir2(quarantineDir);
|
|
8053
|
+
const quarantinedFiles = [];
|
|
8054
|
+
for (const suffix of SQLITE_SIDECAR_SUFFIXES2) {
|
|
8055
|
+
const source = `${paths.localStorePath}${suffix}`;
|
|
8056
|
+
if (!existsSync6(source))
|
|
8057
|
+
continue;
|
|
8058
|
+
const target = join5(quarantineDir, `runtime.sqlite${suffix}`);
|
|
8059
|
+
renameSync3(source, target);
|
|
8060
|
+
quarantinedFiles.push(target);
|
|
8061
|
+
}
|
|
8062
|
+
const markerPath = legacyMigrationMarkerPath2(paths);
|
|
8063
|
+
if (existsSync6(markerPath)) {
|
|
8064
|
+
const target = join5(quarantineDir, LEGACY_MIGRATION_MARKER_FILE2);
|
|
8065
|
+
renameSync3(markerPath, target);
|
|
8066
|
+
quarantinedFiles.push(target);
|
|
8067
|
+
}
|
|
8068
|
+
fsyncDirectory3(quarantineDir);
|
|
8069
|
+
fsyncDirectory3(dirname5(paths.localStorePath));
|
|
8070
|
+
return quarantinedFiles;
|
|
8071
|
+
}
|
|
8072
|
+
function legacyMigrationMarkerPath2(paths) {
|
|
8073
|
+
return join5(paths.workspaceStateDir, LEGACY_MIGRATION_MARKER_FILE2);
|
|
8074
|
+
}
|
|
8075
|
+
function legacyMigrationLockPath2(paths) {
|
|
8076
|
+
return join5(paths.workspaceStateDir, LEGACY_MIGRATION_LOCK_FILE2);
|
|
8077
|
+
}
|
|
8078
|
+
function writePrivateJson2(path, value) {
|
|
8079
|
+
ensurePrivateDir2(dirname5(path));
|
|
8080
|
+
const fd = openSync4(path, "w", 384);
|
|
8081
|
+
try {
|
|
8082
|
+
writeFileSync3(fd, JSON.stringify(value, null, 2), "utf8");
|
|
8083
|
+
fsyncSync3(fd);
|
|
8084
|
+
} finally {
|
|
8085
|
+
closeSync4(fd);
|
|
8086
|
+
}
|
|
8087
|
+
makePrivateFile2(path);
|
|
8088
|
+
fsyncDirectory3(dirname5(path));
|
|
8089
|
+
}
|
|
8090
|
+
function ensurePrivateDir2(path) {
|
|
8091
|
+
mkdirSync5(path, { recursive: true, mode: 448 });
|
|
8092
|
+
if (process.platform !== "win32") {
|
|
8093
|
+
try {
|
|
8094
|
+
chmodSync2(path, 448);
|
|
8095
|
+
} catch {}
|
|
8096
|
+
}
|
|
8097
|
+
}
|
|
8098
|
+
function makePrivateFile2(path) {
|
|
8099
|
+
if (process.platform !== "win32") {
|
|
8100
|
+
try {
|
|
8101
|
+
chmodSync2(path, 384);
|
|
8102
|
+
} catch {}
|
|
8103
|
+
}
|
|
8104
|
+
}
|
|
8105
|
+
function readGitPath2(root, args) {
|
|
8106
|
+
try {
|
|
8107
|
+
const value = execFileSync5("git", args, {
|
|
8108
|
+
cwd: root,
|
|
8109
|
+
encoding: "utf8",
|
|
8110
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
8111
|
+
}).trim();
|
|
8112
|
+
return value.length > 0 ? value : undefined;
|
|
8113
|
+
} catch {
|
|
8114
|
+
return;
|
|
8115
|
+
}
|
|
8116
|
+
}
|
|
8117
|
+
function resolveMaybeRelative2(base, path) {
|
|
8118
|
+
return isAbsolute4(path) ? resolve9(path) : resolve9(base, path);
|
|
8119
|
+
}
|
|
8120
|
+
function canonicalPath2(path) {
|
|
8121
|
+
const resolved = resolve9(path);
|
|
8122
|
+
try {
|
|
8123
|
+
return realpathSync5.native(resolved);
|
|
8124
|
+
} catch {
|
|
8125
|
+
return resolved;
|
|
8126
|
+
}
|
|
8127
|
+
}
|
|
8128
|
+
function isPathInsideOrSame2(path, parent) {
|
|
8129
|
+
const child = resolve9(path);
|
|
8130
|
+
const base = resolve9(parent);
|
|
8131
|
+
const fromBase = relative5(base, child);
|
|
8132
|
+
return fromBase === "" || !!fromBase && !fromBase.startsWith("..") && !isAbsolute4(fromBase);
|
|
8133
|
+
}
|
|
8134
|
+
function stableStorageId2(prefix, value) {
|
|
8135
|
+
return `${prefix}.${createHash6("sha256").update(value).digest("hex").slice(0, 16)}`;
|
|
8136
|
+
}
|
|
7112
8137
|
function stableJson(value) {
|
|
7113
8138
|
return JSON.stringify(value);
|
|
7114
8139
|
}
|
|
7115
|
-
function
|
|
8140
|
+
function nowIso2() {
|
|
7116
8141
|
return new Date().toISOString();
|
|
7117
8142
|
}
|
|
7118
8143
|
function changeSetMetadata(draft) {
|
|
@@ -7137,36 +8162,36 @@ function recoverJournalFiles(root, files) {
|
|
|
7137
8162
|
for (const file of [...files].reverse()) {
|
|
7138
8163
|
const absolute = resolve9(root, file.path);
|
|
7139
8164
|
if (file.tempPath)
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
if (file.existed && file.backupPath &&
|
|
7143
|
-
|
|
8165
|
+
rmSync5(file.tempPath, { recursive: true, force: true });
|
|
8166
|
+
rmSync5(absolute, { recursive: true, force: true });
|
|
8167
|
+
if (file.existed && file.backupPath && existsSync6(file.backupPath)) {
|
|
8168
|
+
renameSync3(file.backupPath, absolute);
|
|
7144
8169
|
}
|
|
7145
|
-
|
|
8170
|
+
fsyncDirectory3(dirname5(absolute));
|
|
7146
8171
|
}
|
|
7147
8172
|
}
|
|
7148
8173
|
function cleanupCommittedJournalFiles(files) {
|
|
7149
8174
|
for (const file of files) {
|
|
7150
8175
|
if (file.tempPath)
|
|
7151
|
-
|
|
8176
|
+
rmSync5(file.tempPath, { recursive: true, force: true });
|
|
7152
8177
|
if (file.backupPath)
|
|
7153
|
-
|
|
8178
|
+
rmSync5(file.backupPath, { recursive: true, force: true });
|
|
7154
8179
|
}
|
|
7155
8180
|
}
|
|
7156
|
-
function
|
|
8181
|
+
function fsyncDirectory3(path) {
|
|
7157
8182
|
try {
|
|
7158
|
-
const fd =
|
|
8183
|
+
const fd = openSync4(path, "r");
|
|
7159
8184
|
try {
|
|
7160
|
-
|
|
8185
|
+
fsyncSync3(fd);
|
|
7161
8186
|
} finally {
|
|
7162
|
-
|
|
8187
|
+
closeSync4(fd);
|
|
7163
8188
|
}
|
|
7164
8189
|
} catch (error) {
|
|
7165
|
-
if (!
|
|
8190
|
+
if (!isIgnorableDirectoryFsyncError3(error))
|
|
7166
8191
|
throw error;
|
|
7167
8192
|
}
|
|
7168
8193
|
}
|
|
7169
|
-
function
|
|
8194
|
+
function isIgnorableDirectoryFsyncError3(error) {
|
|
7170
8195
|
const code = error.code;
|
|
7171
8196
|
return code === "EINVAL" || code === "EISDIR" || process.platform === "win32" && code === "EPERM";
|
|
7172
8197
|
}
|
|
@@ -7197,7 +8222,7 @@ async function readRelationFiles(root, relationsDir) {
|
|
|
7197
8222
|
|
|
7198
8223
|
// packages/local-runtime/model-store-yaml/src/index.ts
|
|
7199
8224
|
init_src();
|
|
7200
|
-
import { existsSync as
|
|
8225
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync6, readdirSync as readdirSync4, readFileSync as readFileSync7, rmSync as rmSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
7201
8226
|
import { dirname as dirname6, resolve as resolve10 } from "node:path";
|
|
7202
8227
|
function createDefaultManifest(productId, productName) {
|
|
7203
8228
|
return {
|
|
@@ -7251,7 +8276,7 @@ function initializeArchContextModel(root, productName = "ArchContext Project") {
|
|
|
7251
8276
|
rebuildGeneratedProjection(root);
|
|
7252
8277
|
}
|
|
7253
8278
|
function rebuildGeneratedProjection(root) {
|
|
7254
|
-
|
|
8279
|
+
rmSync6(resolve10(root, ".archcontext/generated"), { recursive: true, force: true });
|
|
7255
8280
|
writeFile(root, ".archcontext/generated/ARCHITECTURE.md", [
|
|
7256
8281
|
"<!-- Generated by ArchContext. Do not edit by hand. -->",
|
|
7257
8282
|
"",
|
|
@@ -7265,7 +8290,7 @@ function rebuildGeneratedProjection(root) {
|
|
|
7265
8290
|
|
|
7266
8291
|
class YamlModelStore {
|
|
7267
8292
|
async loadManifest(workspace) {
|
|
7268
|
-
return
|
|
8293
|
+
return readFileSync7(resolve10(workspace.root, ".archcontext/manifest.yaml"), "utf8");
|
|
7269
8294
|
}
|
|
7270
8295
|
async loadModel(workspace) {
|
|
7271
8296
|
return listModelFiles(workspace.root);
|
|
@@ -7274,7 +8299,7 @@ class YamlModelStore {
|
|
|
7274
8299
|
const errors = [];
|
|
7275
8300
|
for (const required of [".archcontext/manifest.yaml", ".archcontext/product.yaml"]) {
|
|
7276
8301
|
try {
|
|
7277
|
-
|
|
8302
|
+
readFileSync7(resolve10(workspace.root, required), "utf8");
|
|
7278
8303
|
} catch {
|
|
7279
8304
|
errors.push(`missing ${required}`);
|
|
7280
8305
|
}
|
|
@@ -7301,7 +8326,7 @@ function listModelFiles(root) {
|
|
|
7301
8326
|
".archcontext/generated"
|
|
7302
8327
|
]);
|
|
7303
8328
|
return paths.map((path) => {
|
|
7304
|
-
const body =
|
|
8329
|
+
const body = readFileSync7(resolve10(root, path), "utf8");
|
|
7305
8330
|
return {
|
|
7306
8331
|
path,
|
|
7307
8332
|
body,
|
|
@@ -7314,7 +8339,7 @@ function collectArchContextFiles(root, entries) {
|
|
|
7314
8339
|
const files = [];
|
|
7315
8340
|
for (const entry of entries) {
|
|
7316
8341
|
const absolute = resolve10(root, entry);
|
|
7317
|
-
if (!
|
|
8342
|
+
if (!existsSync7(absolute))
|
|
7318
8343
|
continue;
|
|
7319
8344
|
const stat = readdirOrFile(absolute);
|
|
7320
8345
|
if (stat === "file") {
|
|
@@ -7359,8 +8384,8 @@ function writeYaml(root, path, value) {
|
|
|
7359
8384
|
}
|
|
7360
8385
|
function writeFile(root, path, body) {
|
|
7361
8386
|
const absolute = resolve10(root, path);
|
|
7362
|
-
|
|
7363
|
-
|
|
8387
|
+
mkdirSync6(dirname6(absolute), { recursive: true });
|
|
8388
|
+
writeFileSync4(absolute, body.endsWith(`
|
|
7364
8389
|
`) ? body : `${body}
|
|
7365
8390
|
`, "utf8");
|
|
7366
8391
|
}
|
|
@@ -7568,9 +8593,9 @@ class ArchctxDaemon {
|
|
|
7568
8593
|
tempRoot: input.tempRoot,
|
|
7569
8594
|
stateDir: input.stateDir
|
|
7570
8595
|
});
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
8596
|
+
mkdirSync7(paths.stateDir, { recursive: true });
|
|
8597
|
+
mkdirSync7(paths.runRoot, { recursive: true });
|
|
8598
|
+
mkdirSync7(paths.worktreeTempRoot, { recursive: true });
|
|
7574
8599
|
const preparing = {
|
|
7575
8600
|
schemaVersion: "archcontext.developer-review-run/v1",
|
|
7576
8601
|
runId: paths.runId,
|
|
@@ -7589,13 +8614,13 @@ class ArchctxDaemon {
|
|
|
7589
8614
|
cleanup: "remove-run-root"
|
|
7590
8615
|
}
|
|
7591
8616
|
};
|
|
7592
|
-
if (
|
|
7593
|
-
|
|
8617
|
+
if (existsSync8(paths.lockPath) || existsSync8(paths.manifestPath)) {
|
|
8618
|
+
rmSync7(paths.runRoot, { recursive: true, force: true });
|
|
7594
8619
|
throw new Error(`developer-review-run-already-active: ${input.challenge.challengeId}`);
|
|
7595
8620
|
}
|
|
7596
8621
|
let lockAcquired = false;
|
|
7597
8622
|
try {
|
|
7598
|
-
|
|
8623
|
+
writePrivateJson3(paths.lockPath, {
|
|
7599
8624
|
schemaVersion: "archcontext.developer-review-run-lock/v1",
|
|
7600
8625
|
runId: paths.runId,
|
|
7601
8626
|
challengeId: input.challenge.challengeId,
|
|
@@ -7625,7 +8650,7 @@ class ArchctxDaemon {
|
|
|
7625
8650
|
if (lockAcquired) {
|
|
7626
8651
|
this.cleanupDeveloperReviewRun(preparing);
|
|
7627
8652
|
} else {
|
|
7628
|
-
|
|
8653
|
+
rmSync7(paths.runRoot, { recursive: true, force: true });
|
|
7629
8654
|
}
|
|
7630
8655
|
throw error;
|
|
7631
8656
|
}
|
|
@@ -7646,7 +8671,7 @@ class ArchctxDaemon {
|
|
|
7646
8671
|
const errors = [];
|
|
7647
8672
|
if (run.worktree) {
|
|
7648
8673
|
try {
|
|
7649
|
-
const hadWorktree =
|
|
8674
|
+
const hadWorktree = existsSync8(run.worktree.worktreeRoot);
|
|
7650
8675
|
removeDetachedReviewWorktree(run.worktree);
|
|
7651
8676
|
if (hadWorktree)
|
|
7652
8677
|
removed.push("worktree");
|
|
@@ -7660,8 +8685,8 @@ class ArchctxDaemon {
|
|
|
7660
8685
|
["lock", run.lockPath]
|
|
7661
8686
|
]) {
|
|
7662
8687
|
try {
|
|
7663
|
-
const existed =
|
|
7664
|
-
|
|
8688
|
+
const existed = existsSync8(path);
|
|
8689
|
+
rmSync7(path, { recursive: true, force: true });
|
|
7665
8690
|
if (existed)
|
|
7666
8691
|
removed.push(kind);
|
|
7667
8692
|
} catch (error) {
|
|
@@ -7689,7 +8714,7 @@ class ArchctxDaemon {
|
|
|
7689
8714
|
removedLocks: [],
|
|
7690
8715
|
skippedActive: []
|
|
7691
8716
|
};
|
|
7692
|
-
if (!
|
|
8717
|
+
if (!existsSync8(stateDir))
|
|
7693
8718
|
return recovery;
|
|
7694
8719
|
for (const entry of readdirSync5(stateDir).sort()) {
|
|
7695
8720
|
if (!entry.endsWith(".json"))
|
|
@@ -7697,7 +8722,7 @@ class ArchctxDaemon {
|
|
|
7697
8722
|
const manifestPath = join6(stateDir, entry);
|
|
7698
8723
|
const manifest = readDeveloperReviewRunManifest(manifestPath);
|
|
7699
8724
|
if (!manifest) {
|
|
7700
|
-
|
|
8725
|
+
rmSync7(manifestPath, { force: true });
|
|
7701
8726
|
continue;
|
|
7702
8727
|
}
|
|
7703
8728
|
if (!input.force && isDeveloperReviewPidAlive(manifest.pid)) {
|
|
@@ -7717,7 +8742,7 @@ class ArchctxDaemon {
|
|
|
7717
8742
|
recovery.skippedActive.push(runId);
|
|
7718
8743
|
continue;
|
|
7719
8744
|
}
|
|
7720
|
-
|
|
8745
|
+
rmSync7(lockPath, { force: true });
|
|
7721
8746
|
recovery.removedLocks.push(lockPath);
|
|
7722
8747
|
}
|
|
7723
8748
|
return recovery;
|
|
@@ -8010,7 +9035,7 @@ class ArchctxDaemon {
|
|
|
8010
9035
|
const status = this.status();
|
|
8011
9036
|
if (!root)
|
|
8012
9037
|
return okEnvelope("status", status);
|
|
8013
|
-
const repositoryId =
|
|
9038
|
+
const repositoryId = repositoryFingerprint2(root);
|
|
8014
9039
|
const session = this.sessions.get(repositoryId);
|
|
8015
9040
|
return okEnvelope("status", {
|
|
8016
9041
|
...status,
|
|
@@ -8057,9 +9082,9 @@ class ArchctxDaemon {
|
|
|
8057
9082
|
}
|
|
8058
9083
|
async restoreRepositorySessions() {
|
|
8059
9084
|
for (const record of await this.localStore.listRepositorySessions()) {
|
|
8060
|
-
if (!record.root || !
|
|
9085
|
+
if (!record.root || !existsSync8(record.root))
|
|
8061
9086
|
continue;
|
|
8062
|
-
if (
|
|
9087
|
+
if (repositoryFingerprint2(record.root) !== record.repositoryId)
|
|
8063
9088
|
continue;
|
|
8064
9089
|
this.sessions.set(record.repositoryId, {
|
|
8065
9090
|
workspace: {
|
|
@@ -8345,8 +9370,8 @@ class ArchctxRuntimeRpcServer {
|
|
|
8345
9370
|
const root = this.options.root ?? process.cwd();
|
|
8346
9371
|
const connectionPath = this.options.connectionPath ?? defaultDaemonConnectionPath(root);
|
|
8347
9372
|
const lockPath = this.options.lockPath ?? defaultDaemonLockPath(root);
|
|
8348
|
-
|
|
8349
|
-
|
|
9373
|
+
mkdirSync7(dirname7(connectionPath), { recursive: true });
|
|
9374
|
+
mkdirSync7(dirname7(lockPath), { recursive: true });
|
|
8350
9375
|
this.lockFd = acquireDaemonLock(lockPath, root);
|
|
8351
9376
|
const token = this.options.token ?? randomBytes(18).toString("base64url");
|
|
8352
9377
|
const server = createServer((request, response) => {
|
|
@@ -8369,8 +9394,8 @@ class ArchctxRuntimeRpcServer {
|
|
|
8369
9394
|
connectionPath,
|
|
8370
9395
|
startedAt: (this.options.clock ?? (() => new Date().toISOString()))()
|
|
8371
9396
|
};
|
|
8372
|
-
|
|
8373
|
-
|
|
9397
|
+
writeFileSync5(connectionPath, JSON.stringify(this.connection, null, 2), { mode: 384 });
|
|
9398
|
+
chmodSync3(connectionPath, 384);
|
|
8374
9399
|
return this.connection;
|
|
8375
9400
|
}
|
|
8376
9401
|
async stop() {
|
|
@@ -8385,12 +9410,12 @@ class ArchctxRuntimeRpcServer {
|
|
|
8385
9410
|
}
|
|
8386
9411
|
await this.daemon.stop();
|
|
8387
9412
|
if (connection)
|
|
8388
|
-
|
|
9413
|
+
rmSync7(connection.connectionPath, { force: true });
|
|
8389
9414
|
if (this.lockFd !== undefined)
|
|
8390
|
-
|
|
9415
|
+
closeSync5(this.lockFd);
|
|
8391
9416
|
this.lockFd = undefined;
|
|
8392
9417
|
if (connection)
|
|
8393
|
-
|
|
9418
|
+
rmSync7(connection.lockPath, { force: true });
|
|
8394
9419
|
this.options.onStop?.();
|
|
8395
9420
|
}
|
|
8396
9421
|
async handleRequest(request, response) {
|
|
@@ -8513,24 +9538,21 @@ class ArchctxRuntimeRpcServer {
|
|
|
8513
9538
|
}
|
|
8514
9539
|
}
|
|
8515
9540
|
}
|
|
8516
|
-
function defaultDaemonControlDir(root = process.cwd()) {
|
|
8517
|
-
return join6(root, ".archcontext", ".local");
|
|
8518
|
-
}
|
|
8519
9541
|
function defaultDeveloperReviewRunStateDir(root = process.cwd()) {
|
|
8520
|
-
return
|
|
9542
|
+
return runtimeStatePaths2(root).developerReviewRunStateDir;
|
|
8521
9543
|
}
|
|
8522
9544
|
function defaultDaemonConnectionPath(root = process.cwd()) {
|
|
8523
|
-
return
|
|
9545
|
+
return runtimeStatePaths2(root).daemonConnectionPath;
|
|
8524
9546
|
}
|
|
8525
9547
|
function defaultDaemonLockPath(root = process.cwd()) {
|
|
8526
|
-
return
|
|
9548
|
+
return runtimeStatePaths2(root).daemonLockPath;
|
|
8527
9549
|
}
|
|
8528
9550
|
function readRuntimeRpcConnectionFile(root = process.cwd()) {
|
|
8529
9551
|
const path = defaultDaemonConnectionPath(root);
|
|
8530
9552
|
try {
|
|
8531
9553
|
if (!isPrivateControlFile(path))
|
|
8532
9554
|
return;
|
|
8533
|
-
const parsed = JSON.parse(
|
|
9555
|
+
const parsed = JSON.parse(readFileSync8(path, "utf8"));
|
|
8534
9556
|
if (!parsed || typeof parsed !== "object")
|
|
8535
9557
|
return;
|
|
8536
9558
|
return {
|
|
@@ -8562,7 +9584,7 @@ function runtimeRpcCompatibilityIssue(root = process.cwd()) {
|
|
|
8562
9584
|
connectionPath: connection.connectionPath ?? defaultDaemonConnectionPath(root),
|
|
8563
9585
|
lockPath: connection.lockPath ?? defaultDaemonLockPath(root),
|
|
8564
9586
|
pid,
|
|
8565
|
-
pidAlive: pid !== undefined ?
|
|
9587
|
+
pidAlive: pid !== undefined ? isProcessAlive3(pid) : false,
|
|
8566
9588
|
upgradeCommand: "archctx daemon upgrade"
|
|
8567
9589
|
};
|
|
8568
9590
|
}
|
|
@@ -8571,7 +9593,7 @@ function readRuntimeRpcConnection(root = process.cwd()) {
|
|
|
8571
9593
|
try {
|
|
8572
9594
|
if (!isPrivateControlFile(path))
|
|
8573
9595
|
return;
|
|
8574
|
-
const parsed = JSON.parse(
|
|
9596
|
+
const parsed = JSON.parse(readFileSync8(path, "utf8"));
|
|
8575
9597
|
return isValidRuntimeRpcConnection(parsed) ? parsed : undefined;
|
|
8576
9598
|
} catch {
|
|
8577
9599
|
return;
|
|
@@ -8587,11 +9609,11 @@ function recoverStaleDaemonControlFiles(root = process.cwd(), options = {}) {
|
|
|
8587
9609
|
const removed = [];
|
|
8588
9610
|
const connectionReason = staleConnectionFileReason(connectionPath, options.removeUnhealthyConnection ?? false);
|
|
8589
9611
|
if (connectionReason) {
|
|
8590
|
-
|
|
9612
|
+
rmSync7(connectionPath, { force: true });
|
|
8591
9613
|
removed.push(connectionReason);
|
|
8592
9614
|
}
|
|
8593
|
-
if (
|
|
8594
|
-
|
|
9615
|
+
if (existsSync8(lockPath) && isStaleLock(lockPath)) {
|
|
9616
|
+
rmSync7(lockPath, { force: true });
|
|
8595
9617
|
removed.push("stale-lock-file");
|
|
8596
9618
|
}
|
|
8597
9619
|
return { connectionPath, lockPath, removed };
|
|
@@ -8687,7 +9709,7 @@ function createDeveloperReviewRunPaths(input) {
|
|
|
8687
9709
|
const runId = `${safeChallengeId}-${randomBytes(6).toString("hex")}`;
|
|
8688
9710
|
const stateDir = input.stateDir ? resolve11(input.stateDir) : defaultDeveloperReviewRunStateDir(input.sourceRoot);
|
|
8689
9711
|
const tempParent = input.tempRoot ? resolve11(input.tempRoot) : tmpdir2();
|
|
8690
|
-
|
|
9712
|
+
mkdirSync7(tempParent, { recursive: true });
|
|
8691
9713
|
const runRoot = mkdtempSync3(join6(tempParent, `archctx-developer-review-${safeChallengeId.slice(0, 32)}-`));
|
|
8692
9714
|
return {
|
|
8693
9715
|
runId,
|
|
@@ -8703,12 +9725,12 @@ function safeControlFileSegment(value) {
|
|
|
8703
9725
|
return sanitized.length > 0 ? sanitized : "developer-review";
|
|
8704
9726
|
}
|
|
8705
9727
|
function writeDeveloperReviewRunManifest(manifest) {
|
|
8706
|
-
|
|
9728
|
+
writePrivateJson3(manifest.manifestPath, manifest);
|
|
8707
9729
|
}
|
|
8708
|
-
function
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
|
|
9730
|
+
function writePrivateJson3(path, value, flag = "w") {
|
|
9731
|
+
mkdirSync7(dirname7(path), { recursive: true });
|
|
9732
|
+
writeFileSync5(path, JSON.stringify(value, null, 2), { mode: 384, flag });
|
|
9733
|
+
chmodSync3(path, 384);
|
|
8712
9734
|
}
|
|
8713
9735
|
function readDeveloperReviewRunManifest(path) {
|
|
8714
9736
|
const parsed = readJsonObject(path);
|
|
@@ -8730,7 +9752,7 @@ function readDeveloperReviewRunManifest(path) {
|
|
|
8730
9752
|
}
|
|
8731
9753
|
function readJsonObject(path) {
|
|
8732
9754
|
try {
|
|
8733
|
-
const parsed = JSON.parse(
|
|
9755
|
+
const parsed = JSON.parse(readFileSync8(path, "utf8"));
|
|
8734
9756
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : undefined;
|
|
8735
9757
|
} catch {
|
|
8736
9758
|
return;
|
|
@@ -8739,7 +9761,7 @@ function readJsonObject(path) {
|
|
|
8739
9761
|
function isDeveloperReviewPidAlive(pid) {
|
|
8740
9762
|
if (!pid || pid <= 0)
|
|
8741
9763
|
return false;
|
|
8742
|
-
return
|
|
9764
|
+
return isProcessAlive3(pid);
|
|
8743
9765
|
}
|
|
8744
9766
|
function cleanupErrorMessage(kind, error) {
|
|
8745
9767
|
return `${kind}: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -8750,6 +9772,8 @@ async function createStartedDaemon(deps = {}) {
|
|
|
8750
9772
|
return daemon;
|
|
8751
9773
|
}
|
|
8752
9774
|
function createProductionDaemon(options = {}) {
|
|
9775
|
+
if (!options.localStorePath)
|
|
9776
|
+
migrateLegacyLocalStoreIfNeeded2(options.root);
|
|
8753
9777
|
const deps = {
|
|
8754
9778
|
localStorePath: options.localStorePath ?? defaultLocalStorePath2(options.root),
|
|
8755
9779
|
maxRepoSessions: options.maxRepoSessions
|
|
@@ -8796,15 +9820,15 @@ function blockedProductionInjections(deps) {
|
|
|
8796
9820
|
}
|
|
8797
9821
|
function acquireDaemonLock(lockPath, root) {
|
|
8798
9822
|
try {
|
|
8799
|
-
const fd =
|
|
8800
|
-
|
|
9823
|
+
const fd = openSync5(lockPath, "wx", 384);
|
|
9824
|
+
writeFileSync5(fd, JSON.stringify({ pid: process.pid, root, startedAt: new Date().toISOString() }, null, 2), "utf8");
|
|
8801
9825
|
return fd;
|
|
8802
9826
|
} catch (error) {
|
|
8803
9827
|
const code = error.code;
|
|
8804
9828
|
if (code !== "EEXIST")
|
|
8805
9829
|
throw error;
|
|
8806
9830
|
if (isStaleLock(lockPath)) {
|
|
8807
|
-
|
|
9831
|
+
rmSync7(lockPath, { force: true });
|
|
8808
9832
|
return acquireDaemonLock(lockPath, root);
|
|
8809
9833
|
}
|
|
8810
9834
|
throw new Error(`archctxd already running for ${root}; lock=${lockPath}`);
|
|
@@ -8814,15 +9838,15 @@ function isValidRuntimeRpcConnection(value) {
|
|
|
8814
9838
|
return value.schemaVersion === RUNTIME_RPC_VERSION && value.protocol === "http-loopback" && value.version === 1 && typeof value.url === "string" && value.url.startsWith("http://127.0.0.1:") && typeof value.token === "string" && value.token.length > 0 && typeof value.pid === "number" && typeof value.connectionPath === "string" && typeof value.lockPath === "string";
|
|
8815
9839
|
}
|
|
8816
9840
|
function staleConnectionFileReason(path, removeUnhealthyConnection) {
|
|
8817
|
-
if (!
|
|
9841
|
+
if (!existsSync8(path))
|
|
8818
9842
|
return;
|
|
8819
9843
|
try {
|
|
8820
9844
|
if (!isPrivateControlFile(path))
|
|
8821
9845
|
return "insecure-connection-file";
|
|
8822
|
-
const parsed = JSON.parse(
|
|
9846
|
+
const parsed = JSON.parse(readFileSync8(path, "utf8"));
|
|
8823
9847
|
if (!isValidRuntimeRpcConnection(parsed))
|
|
8824
9848
|
return "invalid-connection-file";
|
|
8825
|
-
if (!
|
|
9849
|
+
if (!isProcessAlive3(parsed.pid))
|
|
8826
9850
|
return "dead-connection-pid";
|
|
8827
9851
|
return removeUnhealthyConnection ? "unhealthy-connection-file" : undefined;
|
|
8828
9852
|
} catch {
|
|
@@ -8837,15 +9861,15 @@ function isPrivateControlFile(path) {
|
|
|
8837
9861
|
}
|
|
8838
9862
|
function isStaleLock(lockPath) {
|
|
8839
9863
|
try {
|
|
8840
|
-
const lock = JSON.parse(
|
|
9864
|
+
const lock = JSON.parse(readFileSync8(lockPath, "utf8"));
|
|
8841
9865
|
if (typeof lock.pid !== "number" || lock.pid <= 0)
|
|
8842
9866
|
return true;
|
|
8843
|
-
return !
|
|
9867
|
+
return !isProcessAlive3(lock.pid);
|
|
8844
9868
|
} catch {
|
|
8845
9869
|
return true;
|
|
8846
9870
|
}
|
|
8847
9871
|
}
|
|
8848
|
-
function
|
|
9872
|
+
function isProcessAlive3(pid) {
|
|
8849
9873
|
try {
|
|
8850
9874
|
process.kill(pid, 0);
|
|
8851
9875
|
return true;
|
|
@@ -9031,7 +10055,7 @@ init_src();
|
|
|
9031
10055
|
|
|
9032
10056
|
// packages/local-runtime/runtime-daemon/src/index.ts
|
|
9033
10057
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
9034
|
-
import { chmodSync as
|
|
10058
|
+
import { chmodSync as chmodSync4, closeSync as closeSync6, existsSync as existsSync9, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync4, openSync as openSync6, readdirSync as readdirSync6, readFileSync as readFileSync9, rmSync as rmSync8, statSync as statSync5, writeFileSync as writeFileSync6 } from "node:fs";
|
|
9035
10059
|
import { createServer as createServer2 } from "node:http";
|
|
9036
10060
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
9037
10061
|
import { dirname as dirname8, join as join7, resolve as resolve12 } from "node:path";
|
|
@@ -9238,9 +10262,9 @@ class ArchctxDaemon2 {
|
|
|
9238
10262
|
tempRoot: input.tempRoot,
|
|
9239
10263
|
stateDir: input.stateDir
|
|
9240
10264
|
});
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
10265
|
+
mkdirSync8(paths.stateDir, { recursive: true });
|
|
10266
|
+
mkdirSync8(paths.runRoot, { recursive: true });
|
|
10267
|
+
mkdirSync8(paths.worktreeTempRoot, { recursive: true });
|
|
9244
10268
|
const preparing = {
|
|
9245
10269
|
schemaVersion: "archcontext.developer-review-run/v1",
|
|
9246
10270
|
runId: paths.runId,
|
|
@@ -9259,13 +10283,13 @@ class ArchctxDaemon2 {
|
|
|
9259
10283
|
cleanup: "remove-run-root"
|
|
9260
10284
|
}
|
|
9261
10285
|
};
|
|
9262
|
-
if (
|
|
9263
|
-
|
|
10286
|
+
if (existsSync9(paths.lockPath) || existsSync9(paths.manifestPath)) {
|
|
10287
|
+
rmSync8(paths.runRoot, { recursive: true, force: true });
|
|
9264
10288
|
throw new Error(`developer-review-run-already-active: ${input.challenge.challengeId}`);
|
|
9265
10289
|
}
|
|
9266
10290
|
let lockAcquired = false;
|
|
9267
10291
|
try {
|
|
9268
|
-
|
|
10292
|
+
writePrivateJson4(paths.lockPath, {
|
|
9269
10293
|
schemaVersion: "archcontext.developer-review-run-lock/v1",
|
|
9270
10294
|
runId: paths.runId,
|
|
9271
10295
|
challengeId: input.challenge.challengeId,
|
|
@@ -9295,7 +10319,7 @@ class ArchctxDaemon2 {
|
|
|
9295
10319
|
if (lockAcquired) {
|
|
9296
10320
|
this.cleanupDeveloperReviewRun(preparing);
|
|
9297
10321
|
} else {
|
|
9298
|
-
|
|
10322
|
+
rmSync8(paths.runRoot, { recursive: true, force: true });
|
|
9299
10323
|
}
|
|
9300
10324
|
throw error;
|
|
9301
10325
|
}
|
|
@@ -9316,7 +10340,7 @@ class ArchctxDaemon2 {
|
|
|
9316
10340
|
const errors = [];
|
|
9317
10341
|
if (run.worktree) {
|
|
9318
10342
|
try {
|
|
9319
|
-
const hadWorktree =
|
|
10343
|
+
const hadWorktree = existsSync9(run.worktree.worktreeRoot);
|
|
9320
10344
|
removeDetachedReviewWorktree(run.worktree);
|
|
9321
10345
|
if (hadWorktree)
|
|
9322
10346
|
removed.push("worktree");
|
|
@@ -9330,8 +10354,8 @@ class ArchctxDaemon2 {
|
|
|
9330
10354
|
["lock", run.lockPath]
|
|
9331
10355
|
]) {
|
|
9332
10356
|
try {
|
|
9333
|
-
const existed =
|
|
9334
|
-
|
|
10357
|
+
const existed = existsSync9(path);
|
|
10358
|
+
rmSync8(path, { recursive: true, force: true });
|
|
9335
10359
|
if (existed)
|
|
9336
10360
|
removed.push(kind);
|
|
9337
10361
|
} catch (error) {
|
|
@@ -9359,7 +10383,7 @@ class ArchctxDaemon2 {
|
|
|
9359
10383
|
removedLocks: [],
|
|
9360
10384
|
skippedActive: []
|
|
9361
10385
|
};
|
|
9362
|
-
if (!
|
|
10386
|
+
if (!existsSync9(stateDir))
|
|
9363
10387
|
return recovery;
|
|
9364
10388
|
for (const entry of readdirSync6(stateDir).sort()) {
|
|
9365
10389
|
if (!entry.endsWith(".json"))
|
|
@@ -9367,7 +10391,7 @@ class ArchctxDaemon2 {
|
|
|
9367
10391
|
const manifestPath = join7(stateDir, entry);
|
|
9368
10392
|
const manifest = readDeveloperReviewRunManifest2(manifestPath);
|
|
9369
10393
|
if (!manifest) {
|
|
9370
|
-
|
|
10394
|
+
rmSync8(manifestPath, { force: true });
|
|
9371
10395
|
continue;
|
|
9372
10396
|
}
|
|
9373
10397
|
if (!input.force && isDeveloperReviewPidAlive2(manifest.pid)) {
|
|
@@ -9387,7 +10411,7 @@ class ArchctxDaemon2 {
|
|
|
9387
10411
|
recovery.skippedActive.push(runId);
|
|
9388
10412
|
continue;
|
|
9389
10413
|
}
|
|
9390
|
-
|
|
10414
|
+
rmSync8(lockPath, { force: true });
|
|
9391
10415
|
recovery.removedLocks.push(lockPath);
|
|
9392
10416
|
}
|
|
9393
10417
|
return recovery;
|
|
@@ -9680,7 +10704,7 @@ class ArchctxDaemon2 {
|
|
|
9680
10704
|
const status = this.status();
|
|
9681
10705
|
if (!root)
|
|
9682
10706
|
return okEnvelope("status", status);
|
|
9683
|
-
const repositoryId =
|
|
10707
|
+
const repositoryId = repositoryFingerprint2(root);
|
|
9684
10708
|
const session = this.sessions.get(repositoryId);
|
|
9685
10709
|
return okEnvelope("status", {
|
|
9686
10710
|
...status,
|
|
@@ -9727,9 +10751,9 @@ class ArchctxDaemon2 {
|
|
|
9727
10751
|
}
|
|
9728
10752
|
async restoreRepositorySessions() {
|
|
9729
10753
|
for (const record of await this.localStore.listRepositorySessions()) {
|
|
9730
|
-
if (!record.root || !
|
|
10754
|
+
if (!record.root || !existsSync9(record.root))
|
|
9731
10755
|
continue;
|
|
9732
|
-
if (
|
|
10756
|
+
if (repositoryFingerprint2(record.root) !== record.repositoryId)
|
|
9733
10757
|
continue;
|
|
9734
10758
|
this.sessions.set(record.repositoryId, {
|
|
9735
10759
|
workspace: {
|
|
@@ -9996,14 +11020,11 @@ class RuntimeRpcClient2 {
|
|
|
9996
11020
|
return await response.json();
|
|
9997
11021
|
}
|
|
9998
11022
|
}
|
|
9999
|
-
function defaultDaemonControlDir2(root = process.cwd()) {
|
|
10000
|
-
return join7(root, ".archcontext", ".local");
|
|
10001
|
-
}
|
|
10002
11023
|
function defaultDeveloperReviewRunStateDir2(root = process.cwd()) {
|
|
10003
|
-
return
|
|
11024
|
+
return runtimeStatePaths2(root).developerReviewRunStateDir;
|
|
10004
11025
|
}
|
|
10005
11026
|
function defaultDaemonConnectionPath2(root = process.cwd()) {
|
|
10006
|
-
return
|
|
11027
|
+
return runtimeStatePaths2(root).daemonConnectionPath;
|
|
10007
11028
|
}
|
|
10008
11029
|
function unwrapRpcData2(result) {
|
|
10009
11030
|
if (!result.ok)
|
|
@@ -10015,7 +11036,7 @@ function readRuntimeRpcConnection2(root = process.cwd()) {
|
|
|
10015
11036
|
try {
|
|
10016
11037
|
if (!isPrivateControlFile2(path))
|
|
10017
11038
|
return;
|
|
10018
|
-
const parsed = JSON.parse(
|
|
11039
|
+
const parsed = JSON.parse(readFileSync9(path, "utf8"));
|
|
10019
11040
|
return isValidRuntimeRpcConnection2(parsed) ? parsed : undefined;
|
|
10020
11041
|
} catch {
|
|
10021
11042
|
return;
|
|
@@ -10116,7 +11137,7 @@ function createDeveloperReviewRunPaths2(input) {
|
|
|
10116
11137
|
const runId = `${safeChallengeId}-${randomBytes2(6).toString("hex")}`;
|
|
10117
11138
|
const stateDir = input.stateDir ? resolve12(input.stateDir) : defaultDeveloperReviewRunStateDir2(input.sourceRoot);
|
|
10118
11139
|
const tempParent = input.tempRoot ? resolve12(input.tempRoot) : tmpdir3();
|
|
10119
|
-
|
|
11140
|
+
mkdirSync8(tempParent, { recursive: true });
|
|
10120
11141
|
const runRoot = mkdtempSync4(join7(tempParent, `archctx-developer-review-${safeChallengeId.slice(0, 32)}-`));
|
|
10121
11142
|
return {
|
|
10122
11143
|
runId,
|
|
@@ -10132,12 +11153,12 @@ function safeControlFileSegment2(value) {
|
|
|
10132
11153
|
return sanitized.length > 0 ? sanitized : "developer-review";
|
|
10133
11154
|
}
|
|
10134
11155
|
function writeDeveloperReviewRunManifest2(manifest) {
|
|
10135
|
-
|
|
11156
|
+
writePrivateJson4(manifest.manifestPath, manifest);
|
|
10136
11157
|
}
|
|
10137
|
-
function
|
|
10138
|
-
|
|
10139
|
-
|
|
10140
|
-
|
|
11158
|
+
function writePrivateJson4(path, value, flag = "w") {
|
|
11159
|
+
mkdirSync8(dirname8(path), { recursive: true });
|
|
11160
|
+
writeFileSync6(path, JSON.stringify(value, null, 2), { mode: 384, flag });
|
|
11161
|
+
chmodSync4(path, 384);
|
|
10141
11162
|
}
|
|
10142
11163
|
function readDeveloperReviewRunManifest2(path) {
|
|
10143
11164
|
const parsed = readJsonObject2(path);
|
|
@@ -10159,7 +11180,7 @@ function readDeveloperReviewRunManifest2(path) {
|
|
|
10159
11180
|
}
|
|
10160
11181
|
function readJsonObject2(path) {
|
|
10161
11182
|
try {
|
|
10162
|
-
const parsed = JSON.parse(
|
|
11183
|
+
const parsed = JSON.parse(readFileSync9(path, "utf8"));
|
|
10163
11184
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : undefined;
|
|
10164
11185
|
} catch {
|
|
10165
11186
|
return;
|
|
@@ -10168,7 +11189,7 @@ function readJsonObject2(path) {
|
|
|
10168
11189
|
function isDeveloperReviewPidAlive2(pid) {
|
|
10169
11190
|
if (!pid || pid <= 0)
|
|
10170
11191
|
return false;
|
|
10171
|
-
return
|
|
11192
|
+
return isProcessAlive4(pid);
|
|
10172
11193
|
}
|
|
10173
11194
|
function cleanupErrorMessage2(kind, error) {
|
|
10174
11195
|
return `${kind}: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -10214,7 +11235,7 @@ function isPrivateControlFile2(path) {
|
|
|
10214
11235
|
const mode = statSync5(path).mode & 511;
|
|
10215
11236
|
return (mode & 63) === 0;
|
|
10216
11237
|
}
|
|
10217
|
-
function
|
|
11238
|
+
function isProcessAlive4(pid) {
|
|
10218
11239
|
try {
|
|
10219
11240
|
process.kill(pid, 0);
|
|
10220
11241
|
return true;
|
|
@@ -10401,7 +11422,7 @@ async function runStdioMcpLoop(input, output, log = (line) => process.stderr.wri
|
|
|
10401
11422
|
|
|
10402
11423
|
// packages/surfaces/renderer/src/index.ts
|
|
10403
11424
|
init_src();
|
|
10404
|
-
import { existsSync as
|
|
11425
|
+
import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync10 } from "node:fs";
|
|
10405
11426
|
import { resolve as resolve13 } from "node:path";
|
|
10406
11427
|
function normalizeNativeModel2(model) {
|
|
10407
11428
|
return {
|
|
@@ -10437,9 +11458,9 @@ function mermaidId(id) {
|
|
|
10437
11458
|
return stableId(id).replace(/-/g, "_").replace(/\./g, "_");
|
|
10438
11459
|
}
|
|
10439
11460
|
function readYamlObjects(dir) {
|
|
10440
|
-
if (!
|
|
11461
|
+
if (!existsSync10(dir))
|
|
10441
11462
|
return [];
|
|
10442
|
-
return readdirSync7(dir).filter((file) => /\.ya?ml$/.test(file)).sort().map((file) => parseFlatYaml(
|
|
11463
|
+
return readdirSync7(dir).filter((file) => /\.ya?ml$/.test(file)).sort().map((file) => parseFlatYaml(readFileSync10(resolve13(dir, file), "utf8")));
|
|
10443
11464
|
}
|
|
10444
11465
|
function parseFlatYaml(body) {
|
|
10445
11466
|
const out = {};
|
|
@@ -10464,7 +11485,7 @@ function escapeMermaid(value) {
|
|
|
10464
11485
|
// packages/surfaces/cli/src/main.ts
|
|
10465
11486
|
var [, , command, ...args] = process.argv;
|
|
10466
11487
|
var CLI_ENTRY = fileURLToPath(import.meta.url);
|
|
10467
|
-
var DAEMON_START_TIMEOUT_MS =
|
|
11488
|
+
var DAEMON_START_TIMEOUT_MS = 15000;
|
|
10468
11489
|
|
|
10469
11490
|
class RuntimeVersionUnsupportedError extends Error {
|
|
10470
11491
|
issue;
|
|
@@ -10679,6 +11700,8 @@ async function runCliUnchecked(command2 = "help", args2 = [], cwd, deps = {}) {
|
|
|
10679
11700
|
};
|
|
10680
11701
|
case "doctor":
|
|
10681
11702
|
return { schemaVersion: "archcontext.envelope/v1", ok: true, requestId: "doctor", data: await doctorReport(cwd) };
|
|
11703
|
+
case "paths":
|
|
11704
|
+
return { schemaVersion: "archcontext.envelope/v1", ok: true, requestId: "paths", data: runtimePathsReport(cwd) };
|
|
10682
11705
|
case "privacy-audit":
|
|
10683
11706
|
return {
|
|
10684
11707
|
schemaVersion: "archcontext.envelope/v1",
|
|
@@ -10724,8 +11747,8 @@ async function runCliUnchecked(command2 = "help", args2 = [], cwd, deps = {}) {
|
|
|
10724
11747
|
ok: true,
|
|
10725
11748
|
requestId: "help",
|
|
10726
11749
|
data: {
|
|
10727
|
-
commands: ["init", "sync", "validate", "context", "status", "daemon", "repo", "landscape", "explore", "prepare", "checkpoint", "plan", "apply", "review", "complete", "github", "config", "mcp", "install", "uninstall", "doctor", "privacy-audit", "export", "import", "tunnel"],
|
|
10728
|
-
examples: ["archctx init --name MyApp", "archctx github connect", "archctx github status", "archctx daemon start", "archctx explore start --foreground", "archctx export likec4", "archctx import structurizr --content '<json>'", "archctx tunnel"]
|
|
11750
|
+
commands: ["init", "sync", "validate", "context", "status", "daemon", "repo", "landscape", "explore", "prepare", "checkpoint", "plan", "apply", "review", "complete", "github", "config", "mcp", "install", "uninstall", "doctor", "paths", "privacy-audit", "export", "import", "tunnel"],
|
|
11751
|
+
examples: ["archctx init --name MyApp", "archctx paths", "archctx github connect", "archctx github status", "archctx daemon start", "archctx explore start --foreground", "archctx export likec4", "archctx import structurizr --content '<json>'", "archctx tunnel"]
|
|
10729
11752
|
}
|
|
10730
11753
|
};
|
|
10731
11754
|
}
|
|
@@ -10811,7 +11834,7 @@ async function runGithubCommand(args2, cwd, deps) {
|
|
|
10811
11834
|
tokenStore.clear(record.codeVerifierRef);
|
|
10812
11835
|
tokenStore.clear(record.refreshTokenRef);
|
|
10813
11836
|
keyStore.removeDevicePrivateKey(record.deviceKey.keyRef);
|
|
10814
|
-
|
|
11837
|
+
rmSync9(connectionPath, { force: true });
|
|
10815
11838
|
return okEnvelope("github.disconnect", {
|
|
10816
11839
|
disconnected: true,
|
|
10817
11840
|
connected: false,
|
|
@@ -11094,20 +12117,20 @@ function defaultGithubDeveloperReviewStatePath(cwd, pullRequestNumber) {
|
|
|
11094
12117
|
}
|
|
11095
12118
|
async function writeGithubDeveloperReviewState(cwd, state) {
|
|
11096
12119
|
const path = defaultGithubDeveloperReviewStatePath(cwd, state.challenge.pullRequestNumber);
|
|
11097
|
-
|
|
12120
|
+
mkdirSync9(dirname9(path), { recursive: true });
|
|
11098
12121
|
const serialized = `${JSON.stringify(state, null, 2)}
|
|
11099
12122
|
`;
|
|
11100
12123
|
assertNoCliSecretMaterial(serialized);
|
|
11101
|
-
|
|
12124
|
+
writeFileSync7(path, serialized, { mode: 384 });
|
|
11102
12125
|
if (process.platform !== "win32")
|
|
11103
|
-
|
|
12126
|
+
chmodSync5(path, 384);
|
|
11104
12127
|
return { state, path };
|
|
11105
12128
|
}
|
|
11106
12129
|
function readGithubDeveloperReviewState(path) {
|
|
11107
|
-
if (!
|
|
12130
|
+
if (!existsSync11(path))
|
|
11108
12131
|
return;
|
|
11109
12132
|
try {
|
|
11110
|
-
const parsed = JSON.parse(
|
|
12133
|
+
const parsed = JSON.parse(readFileSync11(path, "utf8"));
|
|
11111
12134
|
if (parsed.schemaVersion !== "archcontext.github-developer-review-state/v1")
|
|
11112
12135
|
return;
|
|
11113
12136
|
if (!parsed.challenge || typeof parsed.challenge.pullRequestNumber !== "number")
|
|
@@ -11156,10 +12179,10 @@ function defaultGithubConnectionPath(cwd) {
|
|
|
11156
12179
|
return join8(dirname9(defaultDaemonConnectionPath(cwd)), "github-connection.json");
|
|
11157
12180
|
}
|
|
11158
12181
|
function readGithubConnection(path) {
|
|
11159
|
-
if (!
|
|
12182
|
+
if (!existsSync11(path))
|
|
11160
12183
|
return;
|
|
11161
12184
|
try {
|
|
11162
|
-
const parsed = JSON.parse(
|
|
12185
|
+
const parsed = JSON.parse(readFileSync11(path, "utf8"));
|
|
11163
12186
|
if (parsed.schemaVersion !== "archcontext.github-connection/v1" || parsed.status !== "connected")
|
|
11164
12187
|
return;
|
|
11165
12188
|
return parsed;
|
|
@@ -11168,13 +12191,13 @@ function readGithubConnection(path) {
|
|
|
11168
12191
|
}
|
|
11169
12192
|
}
|
|
11170
12193
|
function writeGithubConnection(path, record) {
|
|
11171
|
-
|
|
12194
|
+
mkdirSync9(dirname9(path), { recursive: true });
|
|
11172
12195
|
const serialized = `${JSON.stringify(record, null, 2)}
|
|
11173
12196
|
`;
|
|
11174
12197
|
assertNoCliSecretMaterial(serialized);
|
|
11175
|
-
|
|
12198
|
+
writeFileSync7(path, serialized, { mode: 384 });
|
|
11176
12199
|
if (process.platform !== "win32")
|
|
11177
|
-
|
|
12200
|
+
chmodSync5(path, 384);
|
|
11178
12201
|
}
|
|
11179
12202
|
function sanitizeGithubConnection(record, connectionPath) {
|
|
11180
12203
|
return {
|
|
@@ -11203,7 +12226,7 @@ async function readReviewChallengeV2Arg(args2, cwd, commandName = "github verify
|
|
|
11203
12226
|
if (!inline && !challengePath)
|
|
11204
12227
|
return { ok: false, message: `${commandName} requires --challenge-json or --challenge-path` };
|
|
11205
12228
|
try {
|
|
11206
|
-
const raw = inline ??
|
|
12229
|
+
const raw = inline ?? readFileSync11(resolve14(cwd, challengePath), "utf8");
|
|
11207
12230
|
const parsed = JSON.parse(raw);
|
|
11208
12231
|
const attestation = await Promise.resolve().then(() => (init_src7(), exports_src3));
|
|
11209
12232
|
const assertReviewChallengeV23 = attestation.assertReviewChallengeV2;
|
|
@@ -11286,6 +12309,7 @@ function agentHostRemoveConfig(host) {
|
|
|
11286
12309
|
}
|
|
11287
12310
|
async function doctorReport(cwd) {
|
|
11288
12311
|
const product = productVersionManifest();
|
|
12312
|
+
const paths = runtimePathsReport(cwd);
|
|
11289
12313
|
const daemon = await doctorDaemon(cwd);
|
|
11290
12314
|
const git = doctorGit(cwd);
|
|
11291
12315
|
const sqlite = doctorSqlite(cwd);
|
|
@@ -11303,6 +12327,7 @@ async function doctorReport(cwd) {
|
|
|
11303
12327
|
},
|
|
11304
12328
|
daemon,
|
|
11305
12329
|
sqlite,
|
|
12330
|
+
paths,
|
|
11306
12331
|
codeGraph: product.runtime.codeGraph,
|
|
11307
12332
|
git,
|
|
11308
12333
|
permissions,
|
|
@@ -11350,23 +12375,46 @@ function doctorGit(cwd) {
|
|
|
11350
12375
|
}
|
|
11351
12376
|
}
|
|
11352
12377
|
function doctorSqlite(cwd) {
|
|
11353
|
-
const
|
|
12378
|
+
const paths = runtimeStatePaths(cwd);
|
|
12379
|
+
const path = paths.localStorePath;
|
|
12380
|
+
const legacyLocalStore = inspectLegacyLocalStoreMigration(cwd);
|
|
11354
12381
|
return {
|
|
11355
12382
|
path,
|
|
11356
|
-
exists:
|
|
11357
|
-
migrations: productVersionManifest().runtime.sqliteMigrations
|
|
12383
|
+
exists: existsSync11(path),
|
|
12384
|
+
migrations: productVersionManifest().runtime.sqliteMigrations,
|
|
12385
|
+
legacyPath: paths.legacyLocalStorePath,
|
|
12386
|
+
legacyExists: existsSync11(paths.legacyLocalStorePath),
|
|
12387
|
+
legacyLocalStore
|
|
11358
12388
|
};
|
|
11359
12389
|
}
|
|
11360
12390
|
function doctorPermissions(cwd) {
|
|
12391
|
+
const paths = runtimeStatePaths(cwd);
|
|
11361
12392
|
const controlDir = dirname9(defaultDaemonConnectionPath(cwd));
|
|
11362
12393
|
return {
|
|
11363
12394
|
workspace: pathAccess(cwd),
|
|
12395
|
+
stateRoot: pathAccess(paths.stateRoot),
|
|
12396
|
+
runtimeStateDir: pathAccess(paths.workspaceStateDir),
|
|
11364
12397
|
controlDir: pathAccess(controlDir),
|
|
11365
12398
|
sqlite: pathAccess(defaultLocalStorePath(cwd))
|
|
11366
12399
|
};
|
|
11367
12400
|
}
|
|
12401
|
+
function runtimePathsReport(cwd) {
|
|
12402
|
+
const paths = runtimeStatePaths(cwd);
|
|
12403
|
+
return {
|
|
12404
|
+
...paths,
|
|
12405
|
+
legacyLocalStore: inspectLegacyLocalStoreMigration(cwd),
|
|
12406
|
+
runtimeRepositoryId: repositoryFingerprint(paths.repositoryRoot),
|
|
12407
|
+
repositoryTruthDir: join8(paths.repositoryRoot, ".archcontext"),
|
|
12408
|
+
codeGraphIndexDir: join8(paths.repositoryRoot, ".codegraph"),
|
|
12409
|
+
npmGlobalInstallState: "forbidden",
|
|
12410
|
+
overrides: {
|
|
12411
|
+
stateRootEnv: "ARCHCONTEXT_STATE_DIR",
|
|
12412
|
+
localStorePathEnv: "ARCHCONTEXT_LOCAL_STORE_PATH"
|
|
12413
|
+
}
|
|
12414
|
+
};
|
|
12415
|
+
}
|
|
11368
12416
|
function pathAccess(path) {
|
|
11369
|
-
const exists =
|
|
12417
|
+
const exists = existsSync11(path);
|
|
11370
12418
|
return {
|
|
11371
12419
|
path,
|
|
11372
12420
|
exists,
|
|
@@ -11442,6 +12490,8 @@ async function createCliRuntime(cwd, deps) {
|
|
|
11442
12490
|
githubReviewSubmissionPort: _githubReviewSubmissionPort,
|
|
11443
12491
|
...runtimeDeps
|
|
11444
12492
|
} = deps;
|
|
12493
|
+
if (!runtimeDeps.localStorePath)
|
|
12494
|
+
migrateLegacyLocalStoreIfNeeded(cwd);
|
|
11445
12495
|
const daemon = await createStartedDaemon({
|
|
11446
12496
|
localStorePath: defaultLocalStorePath(cwd),
|
|
11447
12497
|
...runtimeDeps,
|
|
@@ -11544,8 +12594,8 @@ async function startBackgroundDaemon(args2, cwd) {
|
|
|
11544
12594
|
const connectionPath = defaultDaemonConnectionPath(cwd);
|
|
11545
12595
|
const controlDir = dirname9(connectionPath);
|
|
11546
12596
|
const logPath = join8(controlDir, "archctxd.log");
|
|
11547
|
-
|
|
11548
|
-
const logFd =
|
|
12597
|
+
mkdirSync9(controlDir, { recursive: true });
|
|
12598
|
+
const logFd = openSync7(logPath, "a", 384);
|
|
11549
12599
|
try {
|
|
11550
12600
|
const child = spawn(process.execPath, [
|
|
11551
12601
|
CLI_ENTRY,
|
|
@@ -11563,7 +12613,7 @@ async function startBackgroundDaemon(args2, cwd) {
|
|
|
11563
12613
|
child.unref();
|
|
11564
12614
|
const ready = await waitForDaemonReady(cwd, Number(readFlag(args2, "--timeout-ms") ?? DAEMON_START_TIMEOUT_MS));
|
|
11565
12615
|
if (!ready) {
|
|
11566
|
-
return errorEnvelope("daemon.start", "AC_RUNTIME_UNAVAILABLE", `archctxd did not become ready; log=${logPath}`);
|
|
12616
|
+
return errorEnvelope("daemon.start", "AC_RUNTIME_UNAVAILABLE", `archctxd did not become ready; log=${logPath}; logTail=${readFileTail(logPath)}`);
|
|
11567
12617
|
}
|
|
11568
12618
|
return okEnvelope("daemon.start", {
|
|
11569
12619
|
running: true,
|
|
@@ -11575,7 +12625,7 @@ async function startBackgroundDaemon(args2, cwd) {
|
|
|
11575
12625
|
...recoveryData(recovery)
|
|
11576
12626
|
});
|
|
11577
12627
|
} finally {
|
|
11578
|
-
|
|
12628
|
+
closeSync7(logFd);
|
|
11579
12629
|
}
|
|
11580
12630
|
}
|
|
11581
12631
|
async function upgradeDaemon(args2, cwd) {
|
|
@@ -11712,8 +12762,16 @@ async function waitForDaemonReady(cwd, timeoutMs) {
|
|
|
11712
12762
|
function sleep(ms) {
|
|
11713
12763
|
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
11714
12764
|
}
|
|
12765
|
+
function readFileTail(path, maxBytes = 4096) {
|
|
12766
|
+
try {
|
|
12767
|
+
const content = readFileSync11(path, "utf8");
|
|
12768
|
+
return content.slice(Math.max(0, content.length - maxBytes)).replace(/\s+/g, " ").trim();
|
|
12769
|
+
} catch {
|
|
12770
|
+
return "<unavailable>";
|
|
12771
|
+
}
|
|
12772
|
+
}
|
|
11715
12773
|
async function runForegroundDaemon(cwd, args2) {
|
|
11716
|
-
const daemon = await createStartedProductionDaemon({ root: cwd
|
|
12774
|
+
const daemon = await createStartedProductionDaemon({ root: cwd });
|
|
11717
12775
|
let resolveStopped;
|
|
11718
12776
|
const stopped = new Promise((resolve15) => {
|
|
11719
12777
|
resolveStopped = resolve15;
|