archctx 0.1.0 → 0.1.1
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 +1123 -186
- package/package.json +1 -1
package/bin/archctx.mjs
CHANGED
|
@@ -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.1", 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,543 @@ 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, 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, 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 SQLITE_PRAGMAS = [
|
|
6550
|
+
"PRAGMA journal_mode = WAL",
|
|
6551
|
+
"PRAGMA foreign_keys = ON",
|
|
6552
|
+
"PRAGMA busy_timeout = 5000"
|
|
6553
|
+
];
|
|
6554
|
+
var LOCAL_SQLITE_MIGRATIONS = [
|
|
6555
|
+
{
|
|
6556
|
+
id: "0001_runtime_state",
|
|
6557
|
+
statements: [
|
|
6558
|
+
`CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
6559
|
+
id TEXT PRIMARY KEY,
|
|
6560
|
+
applied_at TEXT NOT NULL
|
|
6561
|
+
)`,
|
|
6562
|
+
`CREATE TABLE IF NOT EXISTS repository_sessions (
|
|
6563
|
+
repository_id TEXT PRIMARY KEY,
|
|
6564
|
+
root TEXT NOT NULL,
|
|
6565
|
+
head_sha TEXT NOT NULL,
|
|
6566
|
+
worktree_digest TEXT NOT NULL,
|
|
6567
|
+
updated_at TEXT NOT NULL
|
|
6568
|
+
)`,
|
|
6569
|
+
`CREATE TABLE IF NOT EXISTS snapshots (
|
|
6570
|
+
id TEXT PRIMARY KEY,
|
|
6571
|
+
repository_id TEXT NOT NULL,
|
|
6572
|
+
head_sha TEXT NOT NULL,
|
|
6573
|
+
worktree_digest TEXT NOT NULL,
|
|
6574
|
+
state TEXT NOT NULL,
|
|
6575
|
+
created_at TEXT NOT NULL,
|
|
6576
|
+
committed_at TEXT
|
|
6577
|
+
)`,
|
|
6578
|
+
`CREATE TABLE IF NOT EXISTS task_states (
|
|
6579
|
+
task_session_id TEXT PRIMARY KEY,
|
|
6580
|
+
payload_json TEXT NOT NULL,
|
|
6581
|
+
updated_at TEXT NOT NULL
|
|
6582
|
+
)`,
|
|
6583
|
+
`CREATE TABLE IF NOT EXISTS observed_evidence (
|
|
6584
|
+
id TEXT PRIMARY KEY,
|
|
6585
|
+
repository_id TEXT NOT NULL,
|
|
6586
|
+
head_sha TEXT NOT NULL,
|
|
6587
|
+
selector_json TEXT NOT NULL,
|
|
6588
|
+
summary TEXT NOT NULL,
|
|
6589
|
+
confidence TEXT NOT NULL,
|
|
6590
|
+
created_at TEXT NOT NULL
|
|
6591
|
+
)`,
|
|
6592
|
+
`CREATE TABLE IF NOT EXISTS review_results (
|
|
6593
|
+
review_id TEXT PRIMARY KEY,
|
|
6594
|
+
task_session_id TEXT NOT NULL,
|
|
6595
|
+
payload_json TEXT NOT NULL,
|
|
6596
|
+
created_at TEXT NOT NULL
|
|
6597
|
+
)`
|
|
6598
|
+
]
|
|
6599
|
+
},
|
|
6600
|
+
{
|
|
6601
|
+
id: "0002_indexes",
|
|
6602
|
+
statements: [
|
|
6603
|
+
"CREATE INDEX IF NOT EXISTS idx_snapshots_repository ON snapshots(repository_id, head_sha)",
|
|
6604
|
+
"CREATE INDEX IF NOT EXISTS idx_evidence_repository ON observed_evidence(repository_id, head_sha)",
|
|
6605
|
+
"CREATE INDEX IF NOT EXISTS idx_reviews_task ON review_results(task_session_id)"
|
|
6606
|
+
]
|
|
6607
|
+
},
|
|
6608
|
+
{
|
|
6609
|
+
id: "0003_landscape_state",
|
|
6610
|
+
statements: [
|
|
6611
|
+
`CREATE TABLE IF NOT EXISTS landscapes (
|
|
6612
|
+
id TEXT PRIMARY KEY,
|
|
6613
|
+
digest TEXT NOT NULL,
|
|
6614
|
+
metadata_json TEXT NOT NULL,
|
|
6615
|
+
updated_at TEXT NOT NULL
|
|
6616
|
+
)`,
|
|
6617
|
+
`CREATE TABLE IF NOT EXISTS cross_repo_edges (
|
|
6618
|
+
id TEXT PRIMARY KEY,
|
|
6619
|
+
landscape_id TEXT NOT NULL,
|
|
6620
|
+
from_repository_id TEXT NOT NULL,
|
|
6621
|
+
from_node_id TEXT NOT NULL,
|
|
6622
|
+
to_repository_id TEXT NOT NULL,
|
|
6623
|
+
to_node_id TEXT NOT NULL,
|
|
6624
|
+
via_kind TEXT NOT NULL,
|
|
6625
|
+
via_id TEXT NOT NULL,
|
|
6626
|
+
metadata_json TEXT NOT NULL,
|
|
6627
|
+
updated_at TEXT NOT NULL
|
|
6628
|
+
)`,
|
|
6629
|
+
"CREATE INDEX IF NOT EXISTS idx_cross_repo_edges_from ON cross_repo_edges(from_repository_id, from_node_id)",
|
|
6630
|
+
"CREATE INDEX IF NOT EXISTS idx_cross_repo_edges_to ON cross_repo_edges(to_repository_id, to_node_id)"
|
|
6631
|
+
]
|
|
6632
|
+
},
|
|
6633
|
+
{
|
|
6634
|
+
id: "0004_changeset_journal",
|
|
6635
|
+
statements: [
|
|
6636
|
+
`CREATE TABLE IF NOT EXISTS changeset_journal (
|
|
6637
|
+
journal_id TEXT PRIMARY KEY,
|
|
6638
|
+
changeset_id TEXT NOT NULL,
|
|
6639
|
+
root TEXT NOT NULL,
|
|
6640
|
+
status TEXT NOT NULL,
|
|
6641
|
+
metadata_json TEXT NOT NULL,
|
|
6642
|
+
files_json TEXT NOT NULL,
|
|
6643
|
+
created_at TEXT NOT NULL,
|
|
6644
|
+
updated_at TEXT NOT NULL,
|
|
6645
|
+
completed_at TEXT
|
|
6646
|
+
)`,
|
|
6647
|
+
"CREATE INDEX IF NOT EXISTS idx_changeset_journal_status ON changeset_journal(status)"
|
|
6648
|
+
]
|
|
6649
|
+
}
|
|
6650
|
+
];
|
|
6651
|
+
var ARCHCONTEXT_STATE_DIR_ENV = "ARCHCONTEXT_STATE_DIR";
|
|
6652
|
+
var ARCHCONTEXT_LOCAL_STORE_PATH_ENV = "ARCHCONTEXT_LOCAL_STORE_PATH";
|
|
6653
|
+
function defaultArchContextStateRoot(env = process.env, platform = process.platform, home = homedir()) {
|
|
6654
|
+
const override = env[ARCHCONTEXT_STATE_DIR_ENV];
|
|
6655
|
+
if (override)
|
|
6656
|
+
return { path: resolve6(override), source: "environment" };
|
|
6657
|
+
if (platform === "darwin")
|
|
6658
|
+
return { path: join2(home, "Library", "Application Support", "ArchContext"), source: "os-user-data" };
|
|
6659
|
+
if (platform === "win32")
|
|
6660
|
+
return { path: join2(env.LOCALAPPDATA ?? join2(home, "AppData", "Local"), "ArchContext"), source: "os-user-data" };
|
|
6661
|
+
return { path: join2(env.XDG_DATA_HOME ?? join2(home, ".local", "share"), "archcontext"), source: "os-user-data" };
|
|
6662
|
+
}
|
|
6663
|
+
function runtimeStatePaths(root = process.cwd(), env = process.env) {
|
|
6664
|
+
const repositoryRoot = readGitPath(root, ["rev-parse", "--show-toplevel"]) ?? root;
|
|
6665
|
+
const canonicalRepositoryRoot3 = canonicalPath(repositoryRoot);
|
|
6666
|
+
const gitCommonDir = readGitPath(canonicalRepositoryRoot3, ["rev-parse", "--git-common-dir"]);
|
|
6667
|
+
const repositoryAnchor = canonicalPath(gitCommonDir ? resolveMaybeRelative(canonicalRepositoryRoot3, gitCommonDir) : canonicalRepositoryRoot3);
|
|
6668
|
+
const workspaceAnchor = canonicalRepositoryRoot3;
|
|
6669
|
+
const storageRepositoryId = stableStorageId("repo", repositoryAnchor);
|
|
6670
|
+
const storageWorkspaceId = stableStorageId("ws", workspaceAnchor);
|
|
6671
|
+
const stateRoot = defaultArchContextStateRoot(env);
|
|
6672
|
+
const repositoryStateDir = join2(stateRoot.path, "repositories", storageRepositoryId);
|
|
6673
|
+
const workspaceStateDir = join2(repositoryStateDir, "worktrees", storageWorkspaceId);
|
|
6674
|
+
const legacyControlDir = resolve6(canonicalRepositoryRoot3, ".archcontext", ".local");
|
|
6675
|
+
return {
|
|
6676
|
+
schemaVersion: "archcontext.runtime-state-paths/v1",
|
|
6677
|
+
stateRoot: stateRoot.path,
|
|
6678
|
+
source: stateRoot.source,
|
|
6679
|
+
repositoryRoot: canonicalRepositoryRoot3,
|
|
6680
|
+
repositoryAnchor,
|
|
6681
|
+
workspaceAnchor,
|
|
6682
|
+
storageRepositoryId,
|
|
6683
|
+
storageWorkspaceId,
|
|
6684
|
+
repositoryId: storageRepositoryId,
|
|
6685
|
+
workspaceId: storageWorkspaceId,
|
|
6686
|
+
repositoryStateDir,
|
|
6687
|
+
workspaceStateDir,
|
|
6688
|
+
sharedCacheDir: join2(repositoryStateDir, "shared", "cache"),
|
|
6689
|
+
localStorePath: env[ARCHCONTEXT_LOCAL_STORE_PATH_ENV] ?? join2(workspaceStateDir, "runtime.sqlite"),
|
|
6690
|
+
daemonConnectionPath: join2(workspaceStateDir, "archctxd.json"),
|
|
6691
|
+
daemonLockPath: join2(workspaceStateDir, "archctxd.lock"),
|
|
6692
|
+
daemonLogPath: join2(workspaceStateDir, "archctxd.log"),
|
|
6693
|
+
developerReviewRunStateDir: join2(workspaceStateDir, "developer-review-runs"),
|
|
6694
|
+
legacyControlDir,
|
|
6695
|
+
legacyLocalStorePath: join2(legacyControlDir, "runtime.sqlite")
|
|
6696
|
+
};
|
|
6697
|
+
}
|
|
6528
6698
|
function defaultLocalStorePath(root = process.cwd()) {
|
|
6529
|
-
return
|
|
6699
|
+
return runtimeStatePaths(root).localStorePath;
|
|
6700
|
+
}
|
|
6701
|
+
function inspectLegacyLocalStoreMigration(root = process.cwd(), env = process.env) {
|
|
6702
|
+
const paths = runtimeStatePaths(root, env);
|
|
6703
|
+
if (env[ARCHCONTEXT_LOCAL_STORE_PATH_ENV]) {
|
|
6704
|
+
return legacyMigrationResult(false, "explicit-local-store-override", paths, [], {
|
|
6705
|
+
status: "explicit-local-store-override"
|
|
6706
|
+
});
|
|
6707
|
+
}
|
|
6708
|
+
const legacyExists = existsSync3(paths.legacyLocalStorePath);
|
|
6709
|
+
const targetExists = existsSync3(paths.localStorePath);
|
|
6710
|
+
if (targetExists) {
|
|
6711
|
+
const target = safeSqliteIntegrityCheck(paths.localStorePath);
|
|
6712
|
+
return legacyMigrationResult(false, "target-exists", paths, [], {
|
|
6713
|
+
status: target.ok ? "target-current" : "target-incomplete",
|
|
6714
|
+
integrityCheck: target.ok ? { target: target.result } : { target: "failed", error: target.error }
|
|
6715
|
+
});
|
|
6716
|
+
}
|
|
6717
|
+
if (!legacyExists) {
|
|
6718
|
+
return legacyMigrationResult(false, "legacy-missing", paths, [], {
|
|
6719
|
+
status: "legacy-missing"
|
|
6720
|
+
});
|
|
6721
|
+
}
|
|
6722
|
+
const legacy = safeSqliteIntegrityCheck(paths.legacyLocalStorePath);
|
|
6723
|
+
return legacyMigrationResult(false, undefined, paths, [], {
|
|
6724
|
+
status: legacy.ok ? "pending" : "legacy-invalid",
|
|
6725
|
+
integrityCheck: legacy.ok ? { legacy: legacy.result } : { legacy: "failed", error: legacy.error }
|
|
6726
|
+
});
|
|
6727
|
+
}
|
|
6728
|
+
function migrateLegacyLocalStoreIfNeeded(root = process.cwd(), env = process.env) {
|
|
6729
|
+
const paths = runtimeStatePaths(root, env);
|
|
6730
|
+
if (env[ARCHCONTEXT_LOCAL_STORE_PATH_ENV]) {
|
|
6731
|
+
return legacyMigrationResult(false, "explicit-local-store-override", paths, [], {
|
|
6732
|
+
status: "explicit-local-store-override"
|
|
6733
|
+
});
|
|
6734
|
+
}
|
|
6735
|
+
ensurePrivateDir(dirname2(paths.localStorePath));
|
|
6736
|
+
const legacyExists = existsSync3(paths.legacyLocalStorePath);
|
|
6737
|
+
const integrityCheck = {};
|
|
6738
|
+
const quarantinedFiles = [];
|
|
6739
|
+
if (existsSync3(paths.localStorePath)) {
|
|
6740
|
+
try {
|
|
6741
|
+
integrityCheck.target = assertSqliteIntegrity(paths.localStorePath);
|
|
6742
|
+
return legacyMigrationResult(false, "target-exists", paths, [], {
|
|
6743
|
+
status: "target-current",
|
|
6744
|
+
integrityCheck
|
|
6745
|
+
});
|
|
6746
|
+
} catch (error) {
|
|
6747
|
+
integrityCheck.target = "failed";
|
|
6748
|
+
integrityCheck.error = error instanceof Error ? error.message : String(error);
|
|
6749
|
+
if (!legacyExists) {
|
|
6750
|
+
throw new Error(`ArchContext runtime state target is not a valid SQLite database and no legacy store is available: ${paths.localStorePath}`);
|
|
6751
|
+
}
|
|
6752
|
+
quarantinedFiles.push(...quarantineExistingLocalStore(paths));
|
|
6753
|
+
}
|
|
6754
|
+
}
|
|
6755
|
+
if (!legacyExists) {
|
|
6756
|
+
return legacyMigrationResult(false, "legacy-missing", paths, [], {
|
|
6757
|
+
status: "legacy-missing",
|
|
6758
|
+
integrityCheck
|
|
6759
|
+
});
|
|
6760
|
+
}
|
|
6761
|
+
const lock = acquireLegacyMigrationLock(paths);
|
|
6762
|
+
const stagingDir = join2(paths.workspaceStateDir, `.runtime.sqlite.migration-${process.pid}-${randomUUID()}`);
|
|
6763
|
+
const stagingPath = join2(stagingDir, "runtime.sqlite");
|
|
6764
|
+
try {
|
|
6765
|
+
ensurePrivateDir(stagingDir);
|
|
6766
|
+
integrityCheck.legacy = vacuumLegacySqliteInto(paths.legacyLocalStorePath, stagingPath);
|
|
6767
|
+
makePrivateFile(stagingPath);
|
|
6768
|
+
migrateSqliteDatabaseSync(stagingPath);
|
|
6769
|
+
compactSqliteDatabase(stagingPath);
|
|
6770
|
+
integrityCheck.staging = assertSqliteIntegrity(stagingPath);
|
|
6771
|
+
publishStagedLocalStore(stagingPath, paths.localStorePath);
|
|
6772
|
+
integrityCheck.target = assertSqliteIntegrity(paths.localStorePath);
|
|
6773
|
+
const markerPath = writeLegacyMigrationMarker(paths, integrityCheck, quarantinedFiles);
|
|
6774
|
+
return legacyMigrationResult(true, undefined, paths, [paths.localStorePath], {
|
|
6775
|
+
status: quarantinedFiles.length > 0 ? "target-quarantined-and-migrated" : "migrated",
|
|
6776
|
+
integrityCheck,
|
|
6777
|
+
markerPath,
|
|
6778
|
+
quarantinedFiles
|
|
6779
|
+
});
|
|
6780
|
+
} catch (error) {
|
|
6781
|
+
throw new Error(`ArchContext legacy SQLite migration failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
6782
|
+
} finally {
|
|
6783
|
+
rmSync2(stagingDir, { recursive: true, force: true });
|
|
6784
|
+
releaseLegacyMigrationLock(lock);
|
|
6785
|
+
}
|
|
6786
|
+
}
|
|
6787
|
+
function openSqliteDatabaseSync(databasePath) {
|
|
6788
|
+
if (databasePath !== ":memory:")
|
|
6789
|
+
ensurePrivateDir(dirname2(databasePath));
|
|
6790
|
+
try {
|
|
6791
|
+
const nodeSqlite = runtimeRequire("node:sqlite");
|
|
6792
|
+
const db2 = new nodeSqlite.DatabaseSync(databasePath);
|
|
6793
|
+
return {
|
|
6794
|
+
exec: (sql) => db2.exec(sql),
|
|
6795
|
+
prepare: (sql) => db2.prepare(sql),
|
|
6796
|
+
close: () => db2.close()
|
|
6797
|
+
};
|
|
6798
|
+
} catch (error) {
|
|
6799
|
+
if (error.code !== "ERR_UNKNOWN_BUILTIN_MODULE" && error.code !== "MODULE_NOT_FOUND") {
|
|
6800
|
+
throw error;
|
|
6801
|
+
}
|
|
6802
|
+
}
|
|
6803
|
+
const bunSqlite = runtimeRequire("bun:sqlite");
|
|
6804
|
+
const db = new bunSqlite.Database(databasePath);
|
|
6805
|
+
return {
|
|
6806
|
+
exec: (sql) => db.exec(sql),
|
|
6807
|
+
prepare: (sql) => db.query(sql),
|
|
6808
|
+
close: () => db.close()
|
|
6809
|
+
};
|
|
6810
|
+
}
|
|
6811
|
+
function legacyMigrationResult(migrated, skippedReason, paths, copiedFiles, details) {
|
|
6812
|
+
return {
|
|
6813
|
+
schemaVersion: "archcontext.legacy-local-store-migration/v1",
|
|
6814
|
+
status: details.status,
|
|
6815
|
+
migrated,
|
|
6816
|
+
skippedReason,
|
|
6817
|
+
legacyLocalStorePath: paths.legacyLocalStorePath,
|
|
6818
|
+
targetLocalStorePath: paths.localStorePath,
|
|
6819
|
+
markerPath: details.markerPath ?? legacyMigrationMarkerPath(paths),
|
|
6820
|
+
lockPath: legacyMigrationLockPath(paths),
|
|
6821
|
+
integrityCheck: details.integrityCheck ?? {},
|
|
6822
|
+
copiedFiles,
|
|
6823
|
+
quarantinedFiles: details.quarantinedFiles ?? []
|
|
6824
|
+
};
|
|
6825
|
+
}
|
|
6826
|
+
function safeSqliteIntegrityCheck(path) {
|
|
6827
|
+
try {
|
|
6828
|
+
return { ok: true, result: assertSqliteIntegrity(path) };
|
|
6829
|
+
} catch (error) {
|
|
6830
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
6831
|
+
}
|
|
6832
|
+
}
|
|
6833
|
+
function assertSqliteIntegrity(path) {
|
|
6834
|
+
const db = openSqliteDatabaseSync(path);
|
|
6835
|
+
try {
|
|
6836
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
6837
|
+
const row = db.prepare("PRAGMA integrity_check").get();
|
|
6838
|
+
const result = firstSqliteColumn(row);
|
|
6839
|
+
if (result !== "ok")
|
|
6840
|
+
throw new Error(`SQLite integrity_check failed for ${path}: ${result}`);
|
|
6841
|
+
return result;
|
|
6842
|
+
} finally {
|
|
6843
|
+
db.close();
|
|
6844
|
+
}
|
|
6845
|
+
}
|
|
6846
|
+
function vacuumLegacySqliteInto(sourcePath, targetPath) {
|
|
6847
|
+
const db = openSqliteDatabaseSync(sourcePath);
|
|
6848
|
+
try {
|
|
6849
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
6850
|
+
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
6851
|
+
const integrity = sqliteIntegrityCheckOpenDatabase(db, sourcePath);
|
|
6852
|
+
db.exec(`VACUUM INTO ${sqliteStringLiteral(targetPath)}`);
|
|
6853
|
+
return integrity;
|
|
6854
|
+
} finally {
|
|
6855
|
+
db.close();
|
|
6856
|
+
}
|
|
6857
|
+
}
|
|
6858
|
+
function migrateSqliteDatabaseSync(databasePath) {
|
|
6859
|
+
const db = openSqliteDatabaseSync(databasePath);
|
|
6860
|
+
try {
|
|
6861
|
+
for (const pragma of SQLITE_PRAGMAS)
|
|
6862
|
+
db.exec(pragma);
|
|
6863
|
+
for (const migration of LOCAL_SQLITE_MIGRATIONS) {
|
|
6864
|
+
for (const statement of migration.statements)
|
|
6865
|
+
db.exec(statement);
|
|
6866
|
+
db.prepare("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run(migration.id, nowIso());
|
|
6867
|
+
}
|
|
6868
|
+
} finally {
|
|
6869
|
+
db.close();
|
|
6870
|
+
}
|
|
6871
|
+
}
|
|
6872
|
+
function compactSqliteDatabase(databasePath) {
|
|
6873
|
+
const db = openSqliteDatabaseSync(databasePath);
|
|
6874
|
+
try {
|
|
6875
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
6876
|
+
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
6877
|
+
db.exec("PRAGMA journal_mode = DELETE");
|
|
6878
|
+
} finally {
|
|
6879
|
+
db.close();
|
|
6880
|
+
}
|
|
6881
|
+
for (const suffix of ["-wal", "-shm"])
|
|
6882
|
+
rmSync2(`${databasePath}${suffix}`, { force: true });
|
|
6883
|
+
}
|
|
6884
|
+
function sqliteIntegrityCheckOpenDatabase(db, path) {
|
|
6885
|
+
const row = db.prepare("PRAGMA integrity_check").get();
|
|
6886
|
+
const result = firstSqliteColumn(row);
|
|
6887
|
+
if (result !== "ok")
|
|
6888
|
+
throw new Error(`SQLite integrity_check failed for ${path}: ${result}`);
|
|
6889
|
+
return result;
|
|
6890
|
+
}
|
|
6891
|
+
function firstSqliteColumn(row) {
|
|
6892
|
+
const value = row ? Object.values(row)[0] : undefined;
|
|
6893
|
+
return typeof value === "string" ? value : String(value ?? "");
|
|
6894
|
+
}
|
|
6895
|
+
function sqliteStringLiteral(value) {
|
|
6896
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
6897
|
+
}
|
|
6898
|
+
function publishStagedLocalStore(stagingPath, targetPath) {
|
|
6899
|
+
for (const suffix of SQLITE_SIDECAR_SUFFIXES) {
|
|
6900
|
+
const path = `${targetPath}${suffix}`;
|
|
6901
|
+
if (existsSync3(path))
|
|
6902
|
+
throw new Error(`Cannot publish migrated SQLite over existing target file: ${path}`);
|
|
6903
|
+
}
|
|
6904
|
+
renameSync2(stagingPath, targetPath);
|
|
6905
|
+
makePrivateFile(targetPath);
|
|
6906
|
+
fsyncDirectory2(dirname2(targetPath));
|
|
6907
|
+
}
|
|
6908
|
+
function writeLegacyMigrationMarker(paths, integrityCheck, quarantinedFiles) {
|
|
6909
|
+
const markerPath = legacyMigrationMarkerPath(paths);
|
|
6910
|
+
writePrivateJson(markerPath, {
|
|
6911
|
+
schemaVersion: "archcontext.legacy-local-store-migration-marker/v1",
|
|
6912
|
+
migratedAt: nowIso(),
|
|
6913
|
+
legacyLocalStorePath: paths.legacyLocalStorePath,
|
|
6914
|
+
targetLocalStorePath: paths.localStorePath,
|
|
6915
|
+
integrityCheck,
|
|
6916
|
+
quarantinedFiles
|
|
6917
|
+
});
|
|
6918
|
+
return markerPath;
|
|
6919
|
+
}
|
|
6920
|
+
function acquireLegacyMigrationLock(paths) {
|
|
6921
|
+
const lockPath = legacyMigrationLockPath(paths);
|
|
6922
|
+
ensurePrivateDir(dirname2(lockPath));
|
|
6923
|
+
try {
|
|
6924
|
+
const fd = openSync3(lockPath, "wx", 384);
|
|
6925
|
+
writeFileSync2(fd, JSON.stringify({
|
|
6926
|
+
schemaVersion: "archcontext.legacy-local-store-migration-lock/v1",
|
|
6927
|
+
pid: process.pid,
|
|
6928
|
+
root: paths.repositoryRoot,
|
|
6929
|
+
targetLocalStorePath: paths.localStorePath,
|
|
6930
|
+
startedAt: nowIso()
|
|
6931
|
+
}, null, 2), "utf8");
|
|
6932
|
+
fsyncSync2(fd);
|
|
6933
|
+
return { fd, path: lockPath };
|
|
6934
|
+
} catch (error) {
|
|
6935
|
+
const code = error.code;
|
|
6936
|
+
if (code === "EEXIST" && isStaleMigrationLock(lockPath)) {
|
|
6937
|
+
rmSync2(lockPath, { force: true });
|
|
6938
|
+
return acquireLegacyMigrationLock(paths);
|
|
6939
|
+
}
|
|
6940
|
+
if (code === "EEXIST")
|
|
6941
|
+
throw new Error(`Legacy SQLite migration already in progress; lock=${lockPath}`);
|
|
6942
|
+
throw error;
|
|
6943
|
+
}
|
|
6944
|
+
}
|
|
6945
|
+
function releaseLegacyMigrationLock(lock) {
|
|
6946
|
+
closeSync3(lock.fd);
|
|
6947
|
+
rmSync2(lock.path, { force: true });
|
|
6948
|
+
}
|
|
6949
|
+
function isStaleMigrationLock(lockPath) {
|
|
6950
|
+
try {
|
|
6951
|
+
const parsed = JSON.parse(readFileSync5(lockPath, "utf8"));
|
|
6952
|
+
if (typeof parsed.pid !== "number" || parsed.pid <= 0)
|
|
6953
|
+
return true;
|
|
6954
|
+
return !isProcessAlive(parsed.pid);
|
|
6955
|
+
} catch {
|
|
6956
|
+
return true;
|
|
6957
|
+
}
|
|
6958
|
+
}
|
|
6959
|
+
function isProcessAlive(pid) {
|
|
6960
|
+
try {
|
|
6961
|
+
process.kill(pid, 0);
|
|
6962
|
+
return true;
|
|
6963
|
+
} catch (error) {
|
|
6964
|
+
return error.code !== "ESRCH";
|
|
6965
|
+
}
|
|
6966
|
+
}
|
|
6967
|
+
function quarantineExistingLocalStore(paths) {
|
|
6968
|
+
const quarantineDir = join2(paths.workspaceStateDir, "quarantine", `runtime.sqlite-${Date.now()}-${randomUUID()}`);
|
|
6969
|
+
ensurePrivateDir(quarantineDir);
|
|
6970
|
+
const quarantinedFiles = [];
|
|
6971
|
+
for (const suffix of SQLITE_SIDECAR_SUFFIXES) {
|
|
6972
|
+
const source = `${paths.localStorePath}${suffix}`;
|
|
6973
|
+
if (!existsSync3(source))
|
|
6974
|
+
continue;
|
|
6975
|
+
const target = join2(quarantineDir, `runtime.sqlite${suffix}`);
|
|
6976
|
+
renameSync2(source, target);
|
|
6977
|
+
quarantinedFiles.push(target);
|
|
6978
|
+
}
|
|
6979
|
+
const markerPath = legacyMigrationMarkerPath(paths);
|
|
6980
|
+
if (existsSync3(markerPath)) {
|
|
6981
|
+
const target = join2(quarantineDir, LEGACY_MIGRATION_MARKER_FILE);
|
|
6982
|
+
renameSync2(markerPath, target);
|
|
6983
|
+
quarantinedFiles.push(target);
|
|
6984
|
+
}
|
|
6985
|
+
fsyncDirectory2(quarantineDir);
|
|
6986
|
+
fsyncDirectory2(dirname2(paths.localStorePath));
|
|
6987
|
+
return quarantinedFiles;
|
|
6988
|
+
}
|
|
6989
|
+
function legacyMigrationMarkerPath(paths) {
|
|
6990
|
+
return join2(paths.workspaceStateDir, LEGACY_MIGRATION_MARKER_FILE);
|
|
6991
|
+
}
|
|
6992
|
+
function legacyMigrationLockPath(paths) {
|
|
6993
|
+
return join2(paths.workspaceStateDir, LEGACY_MIGRATION_LOCK_FILE);
|
|
6994
|
+
}
|
|
6995
|
+
function writePrivateJson(path, value) {
|
|
6996
|
+
ensurePrivateDir(dirname2(path));
|
|
6997
|
+
const fd = openSync3(path, "w", 384);
|
|
6998
|
+
try {
|
|
6999
|
+
writeFileSync2(fd, JSON.stringify(value, null, 2), "utf8");
|
|
7000
|
+
fsyncSync2(fd);
|
|
7001
|
+
} finally {
|
|
7002
|
+
closeSync3(fd);
|
|
7003
|
+
}
|
|
7004
|
+
makePrivateFile(path);
|
|
7005
|
+
fsyncDirectory2(dirname2(path));
|
|
7006
|
+
}
|
|
7007
|
+
function ensurePrivateDir(path) {
|
|
7008
|
+
mkdirSync2(path, { recursive: true, mode: 448 });
|
|
7009
|
+
if (process.platform !== "win32") {
|
|
7010
|
+
try {
|
|
7011
|
+
chmodSync(path, 448);
|
|
7012
|
+
} catch {}
|
|
7013
|
+
}
|
|
7014
|
+
}
|
|
7015
|
+
function makePrivateFile(path) {
|
|
7016
|
+
if (process.platform !== "win32") {
|
|
7017
|
+
try {
|
|
7018
|
+
chmodSync(path, 384);
|
|
7019
|
+
} catch {}
|
|
7020
|
+
}
|
|
7021
|
+
}
|
|
7022
|
+
function readGitPath(root, args) {
|
|
7023
|
+
try {
|
|
7024
|
+
const value = execFileSync2("git", args, {
|
|
7025
|
+
cwd: root,
|
|
7026
|
+
encoding: "utf8",
|
|
7027
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
7028
|
+
}).trim();
|
|
7029
|
+
return value.length > 0 ? value : undefined;
|
|
7030
|
+
} catch {
|
|
7031
|
+
return;
|
|
7032
|
+
}
|
|
7033
|
+
}
|
|
7034
|
+
function resolveMaybeRelative(base, path) {
|
|
7035
|
+
return isAbsolute3(path) ? resolve6(path) : resolve6(base, path);
|
|
7036
|
+
}
|
|
7037
|
+
function canonicalPath(path) {
|
|
7038
|
+
const resolved = resolve6(path);
|
|
7039
|
+
try {
|
|
7040
|
+
return realpathSync4.native(resolved);
|
|
7041
|
+
} catch {
|
|
7042
|
+
return resolved;
|
|
7043
|
+
}
|
|
7044
|
+
}
|
|
7045
|
+
function stableStorageId(prefix, value) {
|
|
7046
|
+
return `${prefix}.${createHash5("sha256").update(value).digest("hex").slice(0, 16)}`;
|
|
7047
|
+
}
|
|
7048
|
+
function nowIso() {
|
|
7049
|
+
return new Date().toISOString();
|
|
7050
|
+
}
|
|
7051
|
+
function fsyncDirectory2(path) {
|
|
7052
|
+
try {
|
|
7053
|
+
const fd = openSync3(path, "r");
|
|
7054
|
+
try {
|
|
7055
|
+
fsyncSync2(fd);
|
|
7056
|
+
} finally {
|
|
7057
|
+
closeSync3(fd);
|
|
7058
|
+
}
|
|
7059
|
+
} catch (error) {
|
|
7060
|
+
if (!isIgnorableDirectoryFsyncError2(error))
|
|
7061
|
+
throw error;
|
|
7062
|
+
}
|
|
7063
|
+
}
|
|
7064
|
+
function isIgnorableDirectoryFsyncError2(error) {
|
|
7065
|
+
const code = error.code;
|
|
7066
|
+
return code === "EINVAL" || code === "EISDIR" || process.platform === "win32" && code === "EPERM";
|
|
6530
7067
|
}
|
|
6531
7068
|
|
|
6532
7069
|
// packages/local-runtime/git-adapter/src/index.ts
|
|
6533
|
-
import { execFileSync as
|
|
6534
|
-
import { existsSync as
|
|
7070
|
+
import { execFileSync as execFileSync3, spawnSync } from "node:child_process";
|
|
7071
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, mkdtempSync, rmSync as rmSync3 } from "node:fs";
|
|
6535
7072
|
import { dirname as dirname3, join as join3, resolve as resolve7 } from "node:path";
|
|
6536
7073
|
function findRepositoryRoot(start) {
|
|
6537
7074
|
try {
|
|
6538
|
-
return resolve7(
|
|
7075
|
+
return resolve7(execFileSync3("git", ["rev-parse", "--show-toplevel"], {
|
|
6539
7076
|
cwd: start,
|
|
6540
7077
|
encoding: "utf8",
|
|
6541
7078
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -6543,7 +7080,7 @@ function findRepositoryRoot(start) {
|
|
|
6543
7080
|
} catch {
|
|
6544
7081
|
let cursor = resolve7(start);
|
|
6545
7082
|
while (cursor !== "/") {
|
|
6546
|
-
if (
|
|
7083
|
+
if (existsSync4(resolve7(cursor, ".git")))
|
|
6547
7084
|
return cursor;
|
|
6548
7085
|
cursor = resolve7(cursor, "..");
|
|
6549
7086
|
}
|
|
@@ -6552,7 +7089,7 @@ function findRepositoryRoot(start) {
|
|
|
6552
7089
|
}
|
|
6553
7090
|
function readHeadSha(root) {
|
|
6554
7091
|
try {
|
|
6555
|
-
return
|
|
7092
|
+
return execFileSync3("git", ["rev-parse", "HEAD"], {
|
|
6556
7093
|
cwd: root,
|
|
6557
7094
|
encoding: "utf8",
|
|
6558
7095
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -6564,7 +7101,7 @@ function readHeadSha(root) {
|
|
|
6564
7101
|
|
|
6565
7102
|
// packages/local-runtime/runtime-daemon/src/index.ts
|
|
6566
7103
|
import { randomBytes } from "node:crypto";
|
|
6567
|
-
import { chmodSync, closeSync as
|
|
7104
|
+
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
7105
|
import { createServer } from "node:http";
|
|
6569
7106
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
6570
7107
|
import { dirname as dirname7, join as join6, resolve as resolve11 } from "node:path";
|
|
@@ -6599,13 +7136,13 @@ async function prepareTask(input) {
|
|
|
6599
7136
|
init_src();
|
|
6600
7137
|
|
|
6601
7138
|
// packages/local-runtime/git-adapter/src/index.ts
|
|
6602
|
-
import { execFileSync as
|
|
6603
|
-
import { existsSync as
|
|
7139
|
+
import { execFileSync as execFileSync4, spawnSync as spawnSync2 } from "node:child_process";
|
|
7140
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, mkdtempSync as mkdtempSync2, rmSync as rmSync4 } from "node:fs";
|
|
6604
7141
|
import { dirname as dirname4, join as join4, resolve as resolve8 } from "node:path";
|
|
6605
7142
|
import { tmpdir } from "node:os";
|
|
6606
7143
|
function findRepositoryRoot2(start) {
|
|
6607
7144
|
try {
|
|
6608
|
-
return resolve8(
|
|
7145
|
+
return resolve8(execFileSync4("git", ["rev-parse", "--show-toplevel"], {
|
|
6609
7146
|
cwd: start,
|
|
6610
7147
|
encoding: "utf8",
|
|
6611
7148
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -6613,7 +7150,7 @@ function findRepositoryRoot2(start) {
|
|
|
6613
7150
|
} catch {
|
|
6614
7151
|
let cursor = resolve8(start);
|
|
6615
7152
|
while (cursor !== "/") {
|
|
6616
|
-
if (
|
|
7153
|
+
if (existsSync5(resolve8(cursor, ".git")))
|
|
6617
7154
|
return cursor;
|
|
6618
7155
|
cursor = resolve8(cursor, "..");
|
|
6619
7156
|
}
|
|
@@ -6622,7 +7159,7 @@ function findRepositoryRoot2(start) {
|
|
|
6622
7159
|
}
|
|
6623
7160
|
function readHeadSha2(root) {
|
|
6624
7161
|
try {
|
|
6625
|
-
return
|
|
7162
|
+
return execFileSync4("git", ["rev-parse", "HEAD"], {
|
|
6626
7163
|
cwd: root,
|
|
6627
7164
|
encoding: "utf8",
|
|
6628
7165
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -6682,7 +7219,7 @@ function prepareDetachedReviewWorktree(input) {
|
|
|
6682
7219
|
};
|
|
6683
7220
|
}
|
|
6684
7221
|
const parent = input.tempRoot ? resolve8(input.tempRoot) : tmpdir();
|
|
6685
|
-
|
|
7222
|
+
mkdirSync4(parent, { recursive: true });
|
|
6686
7223
|
const temporaryRoot = mkdtempSync2(join4(parent, "archctx-review-worktree-"));
|
|
6687
7224
|
const worktreeRoot = join4(temporaryRoot, "worktree");
|
|
6688
7225
|
try {
|
|
@@ -6714,7 +7251,7 @@ function prepareDetachedReviewWorktree(input) {
|
|
|
6714
7251
|
}
|
|
6715
7252
|
};
|
|
6716
7253
|
} catch (error) {
|
|
6717
|
-
|
|
7254
|
+
rmSync4(temporaryRoot, { recursive: true, force: true });
|
|
6718
7255
|
if (isGitWorktreeError(error)) {
|
|
6719
7256
|
return {
|
|
6720
7257
|
schemaVersion: "archcontext.detached-review-worktree-verification/v1",
|
|
@@ -6731,9 +7268,9 @@ function removeDetachedReviewWorktree(worktree) {
|
|
|
6731
7268
|
try {
|
|
6732
7269
|
runGit(worktree.sourceRoot, ["worktree", "remove", "--force", worktree.worktreeRoot]);
|
|
6733
7270
|
} catch {
|
|
6734
|
-
|
|
7271
|
+
rmSync4(worktree.worktreeRoot, { recursive: true, force: true });
|
|
6735
7272
|
} finally {
|
|
6736
|
-
|
|
7273
|
+
rmSync4(worktree.temporaryRoot || dirname4(worktree.worktreeRoot), { recursive: true, force: true });
|
|
6737
7274
|
}
|
|
6738
7275
|
}
|
|
6739
7276
|
function readCommitTreeOid(root, headSha) {
|
|
@@ -6769,7 +7306,7 @@ function readDetachedWorktreeObserved(worktreeRoot) {
|
|
|
6769
7306
|
}
|
|
6770
7307
|
}
|
|
6771
7308
|
function runGit(root, args) {
|
|
6772
|
-
return
|
|
7309
|
+
return execFileSync4("git", args, {
|
|
6773
7310
|
cwd: root,
|
|
6774
7311
|
encoding: "utf8",
|
|
6775
7312
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -6787,16 +7324,23 @@ function isGitWorktreeError(error) {
|
|
|
6787
7324
|
}
|
|
6788
7325
|
|
|
6789
7326
|
// packages/local-runtime/local-store-sqlite/src/index.ts
|
|
6790
|
-
import {
|
|
6791
|
-
import {
|
|
7327
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
7328
|
+
import { createHash as createHash6, randomUUID as randomUUID2 } from "node:crypto";
|
|
7329
|
+
import { chmodSync as chmodSync2, closeSync as closeSync4, existsSync as existsSync6, fsyncSync as fsyncSync3, 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
7330
|
import { readdir, readFile } from "node:fs/promises";
|
|
6793
|
-
import {
|
|
6794
|
-
|
|
7331
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
7332
|
+
import { homedir as homedir2 } from "node:os";
|
|
7333
|
+
import { dirname as dirname5, isAbsolute as isAbsolute4, join as join5, resolve as resolve9 } from "node:path";
|
|
7334
|
+
var runtimeRequire2 = createRequire3(import.meta.url);
|
|
7335
|
+
var SQLITE_SIDECAR_SUFFIXES2 = ["", "-wal", "-shm"];
|
|
7336
|
+
var LEGACY_MIGRATION_MARKER_FILE2 = "runtime.sqlite.migration.json";
|
|
7337
|
+
var LEGACY_MIGRATION_LOCK_FILE2 = "runtime.sqlite.migration.lock";
|
|
7338
|
+
var SQLITE_PRAGMAS2 = [
|
|
6795
7339
|
"PRAGMA journal_mode = WAL",
|
|
6796
7340
|
"PRAGMA foreign_keys = ON",
|
|
6797
7341
|
"PRAGMA busy_timeout = 5000"
|
|
6798
7342
|
];
|
|
6799
|
-
var
|
|
7343
|
+
var LOCAL_SQLITE_MIGRATIONS2 = [
|
|
6800
7344
|
{
|
|
6801
7345
|
id: "0001_runtime_state",
|
|
6802
7346
|
statements: [
|
|
@@ -6893,8 +7437,114 @@ var LOCAL_SQLITE_MIGRATIONS = [
|
|
|
6893
7437
|
]
|
|
6894
7438
|
}
|
|
6895
7439
|
];
|
|
7440
|
+
var ARCHCONTEXT_STATE_DIR_ENV2 = "ARCHCONTEXT_STATE_DIR";
|
|
7441
|
+
var ARCHCONTEXT_LOCAL_STORE_PATH_ENV2 = "ARCHCONTEXT_LOCAL_STORE_PATH";
|
|
7442
|
+
function defaultArchContextStateRoot2(env = process.env, platform = process.platform, home = homedir2()) {
|
|
7443
|
+
const override = env[ARCHCONTEXT_STATE_DIR_ENV2];
|
|
7444
|
+
if (override)
|
|
7445
|
+
return { path: resolve9(override), source: "environment" };
|
|
7446
|
+
if (platform === "darwin")
|
|
7447
|
+
return { path: join5(home, "Library", "Application Support", "ArchContext"), source: "os-user-data" };
|
|
7448
|
+
if (platform === "win32")
|
|
7449
|
+
return { path: join5(env.LOCALAPPDATA ?? join5(home, "AppData", "Local"), "ArchContext"), source: "os-user-data" };
|
|
7450
|
+
return { path: join5(env.XDG_DATA_HOME ?? join5(home, ".local", "share"), "archcontext"), source: "os-user-data" };
|
|
7451
|
+
}
|
|
7452
|
+
function runtimeStatePaths2(root = process.cwd(), env = process.env) {
|
|
7453
|
+
const repositoryRoot = readGitPath2(root, ["rev-parse", "--show-toplevel"]) ?? root;
|
|
7454
|
+
const canonicalRepositoryRoot3 = canonicalPath2(repositoryRoot);
|
|
7455
|
+
const gitCommonDir = readGitPath2(canonicalRepositoryRoot3, ["rev-parse", "--git-common-dir"]);
|
|
7456
|
+
const repositoryAnchor = canonicalPath2(gitCommonDir ? resolveMaybeRelative2(canonicalRepositoryRoot3, gitCommonDir) : canonicalRepositoryRoot3);
|
|
7457
|
+
const workspaceAnchor = canonicalRepositoryRoot3;
|
|
7458
|
+
const storageRepositoryId = stableStorageId2("repo", repositoryAnchor);
|
|
7459
|
+
const storageWorkspaceId = stableStorageId2("ws", workspaceAnchor);
|
|
7460
|
+
const stateRoot = defaultArchContextStateRoot2(env);
|
|
7461
|
+
const repositoryStateDir = join5(stateRoot.path, "repositories", storageRepositoryId);
|
|
7462
|
+
const workspaceStateDir = join5(repositoryStateDir, "worktrees", storageWorkspaceId);
|
|
7463
|
+
const legacyControlDir = resolve9(canonicalRepositoryRoot3, ".archcontext", ".local");
|
|
7464
|
+
return {
|
|
7465
|
+
schemaVersion: "archcontext.runtime-state-paths/v1",
|
|
7466
|
+
stateRoot: stateRoot.path,
|
|
7467
|
+
source: stateRoot.source,
|
|
7468
|
+
repositoryRoot: canonicalRepositoryRoot3,
|
|
7469
|
+
repositoryAnchor,
|
|
7470
|
+
workspaceAnchor,
|
|
7471
|
+
storageRepositoryId,
|
|
7472
|
+
storageWorkspaceId,
|
|
7473
|
+
repositoryId: storageRepositoryId,
|
|
7474
|
+
workspaceId: storageWorkspaceId,
|
|
7475
|
+
repositoryStateDir,
|
|
7476
|
+
workspaceStateDir,
|
|
7477
|
+
sharedCacheDir: join5(repositoryStateDir, "shared", "cache"),
|
|
7478
|
+
localStorePath: env[ARCHCONTEXT_LOCAL_STORE_PATH_ENV2] ?? join5(workspaceStateDir, "runtime.sqlite"),
|
|
7479
|
+
daemonConnectionPath: join5(workspaceStateDir, "archctxd.json"),
|
|
7480
|
+
daemonLockPath: join5(workspaceStateDir, "archctxd.lock"),
|
|
7481
|
+
daemonLogPath: join5(workspaceStateDir, "archctxd.log"),
|
|
7482
|
+
developerReviewRunStateDir: join5(workspaceStateDir, "developer-review-runs"),
|
|
7483
|
+
legacyControlDir,
|
|
7484
|
+
legacyLocalStorePath: join5(legacyControlDir, "runtime.sqlite")
|
|
7485
|
+
};
|
|
7486
|
+
}
|
|
6896
7487
|
function defaultLocalStorePath2(root = process.cwd()) {
|
|
6897
|
-
return
|
|
7488
|
+
return runtimeStatePaths2(root).localStorePath;
|
|
7489
|
+
}
|
|
7490
|
+
function migrateLegacyLocalStoreIfNeeded2(root = process.cwd(), env = process.env) {
|
|
7491
|
+
const paths = runtimeStatePaths2(root, env);
|
|
7492
|
+
if (env[ARCHCONTEXT_LOCAL_STORE_PATH_ENV2]) {
|
|
7493
|
+
return legacyMigrationResult2(false, "explicit-local-store-override", paths, [], {
|
|
7494
|
+
status: "explicit-local-store-override"
|
|
7495
|
+
});
|
|
7496
|
+
}
|
|
7497
|
+
ensurePrivateDir2(dirname5(paths.localStorePath));
|
|
7498
|
+
const legacyExists = existsSync6(paths.legacyLocalStorePath);
|
|
7499
|
+
const integrityCheck = {};
|
|
7500
|
+
const quarantinedFiles = [];
|
|
7501
|
+
if (existsSync6(paths.localStorePath)) {
|
|
7502
|
+
try {
|
|
7503
|
+
integrityCheck.target = assertSqliteIntegrity2(paths.localStorePath);
|
|
7504
|
+
return legacyMigrationResult2(false, "target-exists", paths, [], {
|
|
7505
|
+
status: "target-current",
|
|
7506
|
+
integrityCheck
|
|
7507
|
+
});
|
|
7508
|
+
} catch (error) {
|
|
7509
|
+
integrityCheck.target = "failed";
|
|
7510
|
+
integrityCheck.error = error instanceof Error ? error.message : String(error);
|
|
7511
|
+
if (!legacyExists) {
|
|
7512
|
+
throw new Error(`ArchContext runtime state target is not a valid SQLite database and no legacy store is available: ${paths.localStorePath}`);
|
|
7513
|
+
}
|
|
7514
|
+
quarantinedFiles.push(...quarantineExistingLocalStore2(paths));
|
|
7515
|
+
}
|
|
7516
|
+
}
|
|
7517
|
+
if (!legacyExists) {
|
|
7518
|
+
return legacyMigrationResult2(false, "legacy-missing", paths, [], {
|
|
7519
|
+
status: "legacy-missing",
|
|
7520
|
+
integrityCheck
|
|
7521
|
+
});
|
|
7522
|
+
}
|
|
7523
|
+
const lock = acquireLegacyMigrationLock2(paths);
|
|
7524
|
+
const stagingDir = join5(paths.workspaceStateDir, `.runtime.sqlite.migration-${process.pid}-${randomUUID2()}`);
|
|
7525
|
+
const stagingPath = join5(stagingDir, "runtime.sqlite");
|
|
7526
|
+
try {
|
|
7527
|
+
ensurePrivateDir2(stagingDir);
|
|
7528
|
+
integrityCheck.legacy = vacuumLegacySqliteInto2(paths.legacyLocalStorePath, stagingPath);
|
|
7529
|
+
makePrivateFile2(stagingPath);
|
|
7530
|
+
migrateSqliteDatabaseSync2(stagingPath);
|
|
7531
|
+
compactSqliteDatabase2(stagingPath);
|
|
7532
|
+
integrityCheck.staging = assertSqliteIntegrity2(stagingPath);
|
|
7533
|
+
publishStagedLocalStore2(stagingPath, paths.localStorePath);
|
|
7534
|
+
integrityCheck.target = assertSqliteIntegrity2(paths.localStorePath);
|
|
7535
|
+
const markerPath = writeLegacyMigrationMarker2(paths, integrityCheck, quarantinedFiles);
|
|
7536
|
+
return legacyMigrationResult2(true, undefined, paths, [paths.localStorePath], {
|
|
7537
|
+
status: quarantinedFiles.length > 0 ? "target-quarantined-and-migrated" : "migrated",
|
|
7538
|
+
integrityCheck,
|
|
7539
|
+
markerPath,
|
|
7540
|
+
quarantinedFiles
|
|
7541
|
+
});
|
|
7542
|
+
} catch (error) {
|
|
7543
|
+
throw new Error(`ArchContext legacy SQLite migration failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
7544
|
+
} finally {
|
|
7545
|
+
rmSync5(stagingDir, { recursive: true, force: true });
|
|
7546
|
+
releaseLegacyMigrationLock2(lock);
|
|
7547
|
+
}
|
|
6898
7548
|
}
|
|
6899
7549
|
|
|
6900
7550
|
class SqliteLocalStore {
|
|
@@ -6905,20 +7555,20 @@ class SqliteLocalStore {
|
|
|
6905
7555
|
}
|
|
6906
7556
|
async migrate() {
|
|
6907
7557
|
const db = await this.database();
|
|
6908
|
-
for (const pragma of
|
|
7558
|
+
for (const pragma of SQLITE_PRAGMAS2)
|
|
6909
7559
|
db.exec(pragma);
|
|
6910
|
-
for (const migration of
|
|
7560
|
+
for (const migration of LOCAL_SQLITE_MIGRATIONS2) {
|
|
6911
7561
|
for (const statement of migration.statements)
|
|
6912
7562
|
db.exec(statement);
|
|
6913
|
-
db.prepare("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run(migration.id,
|
|
7563
|
+
db.prepare("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run(migration.id, nowIso2());
|
|
6914
7564
|
}
|
|
6915
7565
|
}
|
|
6916
7566
|
async beginSnapshot(snapshot) {
|
|
6917
7567
|
const db = await this.database();
|
|
6918
|
-
const snapshotId = `snapshot_${
|
|
7568
|
+
const snapshotId = `snapshot_${randomUUID2()}`;
|
|
6919
7569
|
db.prepare(`INSERT INTO snapshots
|
|
6920
7570
|
(id, repository_id, head_sha, worktree_digest, state, created_at)
|
|
6921
|
-
VALUES (?, ?, ?, ?, ?, ?)`).run(snapshotId, snapshot.repositoryId, snapshot.headSha, snapshot.worktreeDigest, "pending",
|
|
7571
|
+
VALUES (?, ?, ?, ?, ?, ?)`).run(snapshotId, snapshot.repositoryId, snapshot.headSha, snapshot.worktreeDigest, "pending", nowIso2());
|
|
6922
7572
|
return snapshotId;
|
|
6923
7573
|
}
|
|
6924
7574
|
async commitSnapshot(snapshotId) {
|
|
@@ -6926,7 +7576,7 @@ class SqliteLocalStore {
|
|
|
6926
7576
|
const existing = db.prepare("SELECT id FROM snapshots WHERE id = ?").get(snapshotId);
|
|
6927
7577
|
if (!existing)
|
|
6928
7578
|
throw new Error(`Snapshot not found: ${snapshotId}`);
|
|
6929
|
-
db.prepare("UPDATE snapshots SET state = ?, committed_at = ? WHERE id = ?").run("committed",
|
|
7579
|
+
db.prepare("UPDATE snapshots SET state = ?, committed_at = ? WHERE id = ?").run("committed", nowIso2(), snapshotId);
|
|
6930
7580
|
}
|
|
6931
7581
|
recoverPendingSnapshots() {
|
|
6932
7582
|
const db = this.requireOpenDatabase();
|
|
@@ -6954,10 +7604,10 @@ class SqliteLocalStore {
|
|
|
6954
7604
|
}
|
|
6955
7605
|
async beginChangeSet(root, draft) {
|
|
6956
7606
|
const db = await this.database();
|
|
6957
|
-
const journalId = `changeset_${
|
|
7607
|
+
const journalId = `changeset_${randomUUID2()}`;
|
|
6958
7608
|
db.prepare(`INSERT INTO changeset_journal
|
|
6959
7609
|
(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)), "[]",
|
|
7610
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(journalId, draft.id, root, "pending", stableJson(changeSetMetadata(draft)), "[]", nowIso2(), nowIso2());
|
|
6961
7611
|
return journalId;
|
|
6962
7612
|
}
|
|
6963
7613
|
async recordChangeSetFile(journalId, file) {
|
|
@@ -6967,14 +7617,14 @@ class SqliteLocalStore {
|
|
|
6967
7617
|
throw new Error(`ChangeSet journal not found: ${journalId}`);
|
|
6968
7618
|
const files = JSON.parse(String(row.files_json));
|
|
6969
7619
|
files.push(file);
|
|
6970
|
-
db.prepare("UPDATE changeset_journal SET files_json = ?, updated_at = ? WHERE journal_id = ?").run(stableJson(files),
|
|
7620
|
+
db.prepare("UPDATE changeset_journal SET files_json = ?, updated_at = ? WHERE journal_id = ?").run(stableJson(files), nowIso2(), journalId);
|
|
6971
7621
|
}
|
|
6972
7622
|
async commitChangeSet(journalId) {
|
|
6973
7623
|
const db = await this.database();
|
|
6974
7624
|
const existing = db.prepare("SELECT journal_id FROM changeset_journal WHERE journal_id = ?").get(journalId);
|
|
6975
7625
|
if (!existing)
|
|
6976
7626
|
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",
|
|
7627
|
+
db.prepare("UPDATE changeset_journal SET status = ?, updated_at = ?, completed_at = ? WHERE journal_id = ?").run("committed", nowIso2(), nowIso2(), journalId);
|
|
6978
7628
|
}
|
|
6979
7629
|
async abortChangeSet(journalId, reason) {
|
|
6980
7630
|
const db = await this.database();
|
|
@@ -6982,7 +7632,7 @@ class SqliteLocalStore {
|
|
|
6982
7632
|
if (!row)
|
|
6983
7633
|
throw new Error(`ChangeSet journal not found: ${journalId}`);
|
|
6984
7634
|
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 }),
|
|
7635
|
+
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
7636
|
}
|
|
6987
7637
|
recoverPendingChangeSets() {
|
|
6988
7638
|
const db = this.requireOpenDatabase();
|
|
@@ -6994,7 +7644,7 @@ class SqliteLocalStore {
|
|
|
6994
7644
|
for (const row of rows) {
|
|
6995
7645
|
const files = JSON.parse(String(row.files_json));
|
|
6996
7646
|
recoverJournalFiles(String(row.root), files);
|
|
6997
|
-
db.prepare("UPDATE changeset_journal SET status = ?, updated_at = ?, completed_at = ? WHERE journal_id = ?").run("recovered",
|
|
7647
|
+
db.prepare("UPDATE changeset_journal SET status = ?, updated_at = ?, completed_at = ? WHERE journal_id = ?").run("recovered", nowIso2(), nowIso2(), String(row.journal_id));
|
|
6998
7648
|
}
|
|
6999
7649
|
return rows.length;
|
|
7000
7650
|
}
|
|
@@ -7002,7 +7652,7 @@ class SqliteLocalStore {
|
|
|
7002
7652
|
const db = await this.database();
|
|
7003
7653
|
db.prepare(`INSERT OR REPLACE INTO task_states
|
|
7004
7654
|
(task_session_id, payload_json, updated_at)
|
|
7005
|
-
VALUES (?, ?, ?)`).run(taskSessionId, stableJson(state),
|
|
7655
|
+
VALUES (?, ?, ?)`).run(taskSessionId, stableJson(state), nowIso2());
|
|
7006
7656
|
}
|
|
7007
7657
|
async readTaskState(taskSessionId) {
|
|
7008
7658
|
const db = await this.database();
|
|
@@ -7013,13 +7663,13 @@ class SqliteLocalStore {
|
|
|
7013
7663
|
const db = await this.database();
|
|
7014
7664
|
db.prepare(`INSERT OR REPLACE INTO review_results
|
|
7015
7665
|
(review_id, task_session_id, payload_json, created_at)
|
|
7016
|
-
VALUES (?, ?, ?, ?)`).run(reviewId, reviewTaskSessionId(result) ?? reviewId, stableJson(result),
|
|
7666
|
+
VALUES (?, ?, ?, ?)`).run(reviewId, reviewTaskSessionId(result) ?? reviewId, stableJson(result), nowIso2());
|
|
7017
7667
|
}
|
|
7018
7668
|
async saveLandscape(landscape) {
|
|
7019
7669
|
const db = await this.database();
|
|
7020
7670
|
db.prepare(`INSERT OR REPLACE INTO landscapes
|
|
7021
7671
|
(id, digest, metadata_json, updated_at)
|
|
7022
|
-
VALUES (?, ?, ?, ?)`).run(landscape.id, landscapeDigest(landscape), stableJson(landscape),
|
|
7672
|
+
VALUES (?, ?, ?, ?)`).run(landscape.id, landscapeDigest(landscape), stableJson(landscape), nowIso2());
|
|
7023
7673
|
}
|
|
7024
7674
|
async readLandscape(landscapeId) {
|
|
7025
7675
|
const db = await this.database();
|
|
@@ -7030,7 +7680,7 @@ class SqliteLocalStore {
|
|
|
7030
7680
|
const db = await this.database();
|
|
7031
7681
|
db.prepare(`INSERT OR REPLACE INTO cross_repo_edges
|
|
7032
7682
|
(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),
|
|
7683
|
+
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
7684
|
}
|
|
7035
7685
|
async listCrossRepoRelations(landscape) {
|
|
7036
7686
|
const db = await this.database();
|
|
@@ -7090,7 +7740,7 @@ async function rebuildDerivedLandscapeState(store, input) {
|
|
|
7090
7740
|
}
|
|
7091
7741
|
async function openSqliteDatabase(databasePath) {
|
|
7092
7742
|
if (databasePath !== ":memory:")
|
|
7093
|
-
|
|
7743
|
+
ensurePrivateDir2(dirname5(databasePath));
|
|
7094
7744
|
try {
|
|
7095
7745
|
const nodeSqlite = await import("node:sqlite");
|
|
7096
7746
|
const db = new nodeSqlite.DatabaseSync(databasePath);
|
|
@@ -7109,10 +7759,264 @@ async function openSqliteDatabase(databasePath) {
|
|
|
7109
7759
|
};
|
|
7110
7760
|
}
|
|
7111
7761
|
}
|
|
7762
|
+
function openSqliteDatabaseSync2(databasePath) {
|
|
7763
|
+
if (databasePath !== ":memory:")
|
|
7764
|
+
ensurePrivateDir2(dirname5(databasePath));
|
|
7765
|
+
try {
|
|
7766
|
+
const nodeSqlite = runtimeRequire2("node:sqlite");
|
|
7767
|
+
const db2 = new nodeSqlite.DatabaseSync(databasePath);
|
|
7768
|
+
return {
|
|
7769
|
+
exec: (sql) => db2.exec(sql),
|
|
7770
|
+
prepare: (sql) => db2.prepare(sql),
|
|
7771
|
+
close: () => db2.close()
|
|
7772
|
+
};
|
|
7773
|
+
} catch (error) {
|
|
7774
|
+
if (error.code !== "ERR_UNKNOWN_BUILTIN_MODULE" && error.code !== "MODULE_NOT_FOUND") {
|
|
7775
|
+
throw error;
|
|
7776
|
+
}
|
|
7777
|
+
}
|
|
7778
|
+
const bunSqlite = runtimeRequire2("bun:sqlite");
|
|
7779
|
+
const db = new bunSqlite.Database(databasePath);
|
|
7780
|
+
return {
|
|
7781
|
+
exec: (sql) => db.exec(sql),
|
|
7782
|
+
prepare: (sql) => db.query(sql),
|
|
7783
|
+
close: () => db.close()
|
|
7784
|
+
};
|
|
7785
|
+
}
|
|
7786
|
+
function legacyMigrationResult2(migrated, skippedReason, paths, copiedFiles, details) {
|
|
7787
|
+
return {
|
|
7788
|
+
schemaVersion: "archcontext.legacy-local-store-migration/v1",
|
|
7789
|
+
status: details.status,
|
|
7790
|
+
migrated,
|
|
7791
|
+
skippedReason,
|
|
7792
|
+
legacyLocalStorePath: paths.legacyLocalStorePath,
|
|
7793
|
+
targetLocalStorePath: paths.localStorePath,
|
|
7794
|
+
markerPath: details.markerPath ?? legacyMigrationMarkerPath2(paths),
|
|
7795
|
+
lockPath: legacyMigrationLockPath2(paths),
|
|
7796
|
+
integrityCheck: details.integrityCheck ?? {},
|
|
7797
|
+
copiedFiles,
|
|
7798
|
+
quarantinedFiles: details.quarantinedFiles ?? []
|
|
7799
|
+
};
|
|
7800
|
+
}
|
|
7801
|
+
function assertSqliteIntegrity2(path) {
|
|
7802
|
+
const db = openSqliteDatabaseSync2(path);
|
|
7803
|
+
try {
|
|
7804
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
7805
|
+
const row = db.prepare("PRAGMA integrity_check").get();
|
|
7806
|
+
const result = firstSqliteColumn2(row);
|
|
7807
|
+
if (result !== "ok")
|
|
7808
|
+
throw new Error(`SQLite integrity_check failed for ${path}: ${result}`);
|
|
7809
|
+
return result;
|
|
7810
|
+
} finally {
|
|
7811
|
+
db.close();
|
|
7812
|
+
}
|
|
7813
|
+
}
|
|
7814
|
+
function vacuumLegacySqliteInto2(sourcePath, targetPath) {
|
|
7815
|
+
const db = openSqliteDatabaseSync2(sourcePath);
|
|
7816
|
+
try {
|
|
7817
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
7818
|
+
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
7819
|
+
const integrity = sqliteIntegrityCheckOpenDatabase2(db, sourcePath);
|
|
7820
|
+
db.exec(`VACUUM INTO ${sqliteStringLiteral2(targetPath)}`);
|
|
7821
|
+
return integrity;
|
|
7822
|
+
} finally {
|
|
7823
|
+
db.close();
|
|
7824
|
+
}
|
|
7825
|
+
}
|
|
7826
|
+
function migrateSqliteDatabaseSync2(databasePath) {
|
|
7827
|
+
const db = openSqliteDatabaseSync2(databasePath);
|
|
7828
|
+
try {
|
|
7829
|
+
for (const pragma of SQLITE_PRAGMAS2)
|
|
7830
|
+
db.exec(pragma);
|
|
7831
|
+
for (const migration of LOCAL_SQLITE_MIGRATIONS2) {
|
|
7832
|
+
for (const statement of migration.statements)
|
|
7833
|
+
db.exec(statement);
|
|
7834
|
+
db.prepare("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run(migration.id, nowIso2());
|
|
7835
|
+
}
|
|
7836
|
+
} finally {
|
|
7837
|
+
db.close();
|
|
7838
|
+
}
|
|
7839
|
+
}
|
|
7840
|
+
function compactSqliteDatabase2(databasePath) {
|
|
7841
|
+
const db = openSqliteDatabaseSync2(databasePath);
|
|
7842
|
+
try {
|
|
7843
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
7844
|
+
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
7845
|
+
db.exec("PRAGMA journal_mode = DELETE");
|
|
7846
|
+
} finally {
|
|
7847
|
+
db.close();
|
|
7848
|
+
}
|
|
7849
|
+
for (const suffix of ["-wal", "-shm"])
|
|
7850
|
+
rmSync5(`${databasePath}${suffix}`, { force: true });
|
|
7851
|
+
}
|
|
7852
|
+
function sqliteIntegrityCheckOpenDatabase2(db, path) {
|
|
7853
|
+
const row = db.prepare("PRAGMA integrity_check").get();
|
|
7854
|
+
const result = firstSqliteColumn2(row);
|
|
7855
|
+
if (result !== "ok")
|
|
7856
|
+
throw new Error(`SQLite integrity_check failed for ${path}: ${result}`);
|
|
7857
|
+
return result;
|
|
7858
|
+
}
|
|
7859
|
+
function firstSqliteColumn2(row) {
|
|
7860
|
+
const value = row ? Object.values(row)[0] : undefined;
|
|
7861
|
+
return typeof value === "string" ? value : String(value ?? "");
|
|
7862
|
+
}
|
|
7863
|
+
function sqliteStringLiteral2(value) {
|
|
7864
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
7865
|
+
}
|
|
7866
|
+
function publishStagedLocalStore2(stagingPath, targetPath) {
|
|
7867
|
+
for (const suffix of SQLITE_SIDECAR_SUFFIXES2) {
|
|
7868
|
+
const path = `${targetPath}${suffix}`;
|
|
7869
|
+
if (existsSync6(path))
|
|
7870
|
+
throw new Error(`Cannot publish migrated SQLite over existing target file: ${path}`);
|
|
7871
|
+
}
|
|
7872
|
+
renameSync3(stagingPath, targetPath);
|
|
7873
|
+
makePrivateFile2(targetPath);
|
|
7874
|
+
fsyncDirectory3(dirname5(targetPath));
|
|
7875
|
+
}
|
|
7876
|
+
function writeLegacyMigrationMarker2(paths, integrityCheck, quarantinedFiles) {
|
|
7877
|
+
const markerPath = legacyMigrationMarkerPath2(paths);
|
|
7878
|
+
writePrivateJson2(markerPath, {
|
|
7879
|
+
schemaVersion: "archcontext.legacy-local-store-migration-marker/v1",
|
|
7880
|
+
migratedAt: nowIso2(),
|
|
7881
|
+
legacyLocalStorePath: paths.legacyLocalStorePath,
|
|
7882
|
+
targetLocalStorePath: paths.localStorePath,
|
|
7883
|
+
integrityCheck,
|
|
7884
|
+
quarantinedFiles
|
|
7885
|
+
});
|
|
7886
|
+
return markerPath;
|
|
7887
|
+
}
|
|
7888
|
+
function acquireLegacyMigrationLock2(paths) {
|
|
7889
|
+
const lockPath = legacyMigrationLockPath2(paths);
|
|
7890
|
+
ensurePrivateDir2(dirname5(lockPath));
|
|
7891
|
+
try {
|
|
7892
|
+
const fd = openSync4(lockPath, "wx", 384);
|
|
7893
|
+
writeFileSync3(fd, JSON.stringify({
|
|
7894
|
+
schemaVersion: "archcontext.legacy-local-store-migration-lock/v1",
|
|
7895
|
+
pid: process.pid,
|
|
7896
|
+
root: paths.repositoryRoot,
|
|
7897
|
+
targetLocalStorePath: paths.localStorePath,
|
|
7898
|
+
startedAt: nowIso2()
|
|
7899
|
+
}, null, 2), "utf8");
|
|
7900
|
+
fsyncSync3(fd);
|
|
7901
|
+
return { fd, path: lockPath };
|
|
7902
|
+
} catch (error) {
|
|
7903
|
+
const code = error.code;
|
|
7904
|
+
if (code === "EEXIST" && isStaleMigrationLock2(lockPath)) {
|
|
7905
|
+
rmSync5(lockPath, { force: true });
|
|
7906
|
+
return acquireLegacyMigrationLock2(paths);
|
|
7907
|
+
}
|
|
7908
|
+
if (code === "EEXIST")
|
|
7909
|
+
throw new Error(`Legacy SQLite migration already in progress; lock=${lockPath}`);
|
|
7910
|
+
throw error;
|
|
7911
|
+
}
|
|
7912
|
+
}
|
|
7913
|
+
function releaseLegacyMigrationLock2(lock) {
|
|
7914
|
+
closeSync4(lock.fd);
|
|
7915
|
+
rmSync5(lock.path, { force: true });
|
|
7916
|
+
}
|
|
7917
|
+
function isStaleMigrationLock2(lockPath) {
|
|
7918
|
+
try {
|
|
7919
|
+
const parsed = JSON.parse(readFileSync6(lockPath, "utf8"));
|
|
7920
|
+
if (typeof parsed.pid !== "number" || parsed.pid <= 0)
|
|
7921
|
+
return true;
|
|
7922
|
+
return !isProcessAlive2(parsed.pid);
|
|
7923
|
+
} catch {
|
|
7924
|
+
return true;
|
|
7925
|
+
}
|
|
7926
|
+
}
|
|
7927
|
+
function isProcessAlive2(pid) {
|
|
7928
|
+
try {
|
|
7929
|
+
process.kill(pid, 0);
|
|
7930
|
+
return true;
|
|
7931
|
+
} catch (error) {
|
|
7932
|
+
return error.code !== "ESRCH";
|
|
7933
|
+
}
|
|
7934
|
+
}
|
|
7935
|
+
function quarantineExistingLocalStore2(paths) {
|
|
7936
|
+
const quarantineDir = join5(paths.workspaceStateDir, "quarantine", `runtime.sqlite-${Date.now()}-${randomUUID2()}`);
|
|
7937
|
+
ensurePrivateDir2(quarantineDir);
|
|
7938
|
+
const quarantinedFiles = [];
|
|
7939
|
+
for (const suffix of SQLITE_SIDECAR_SUFFIXES2) {
|
|
7940
|
+
const source = `${paths.localStorePath}${suffix}`;
|
|
7941
|
+
if (!existsSync6(source))
|
|
7942
|
+
continue;
|
|
7943
|
+
const target = join5(quarantineDir, `runtime.sqlite${suffix}`);
|
|
7944
|
+
renameSync3(source, target);
|
|
7945
|
+
quarantinedFiles.push(target);
|
|
7946
|
+
}
|
|
7947
|
+
const markerPath = legacyMigrationMarkerPath2(paths);
|
|
7948
|
+
if (existsSync6(markerPath)) {
|
|
7949
|
+
const target = join5(quarantineDir, LEGACY_MIGRATION_MARKER_FILE2);
|
|
7950
|
+
renameSync3(markerPath, target);
|
|
7951
|
+
quarantinedFiles.push(target);
|
|
7952
|
+
}
|
|
7953
|
+
fsyncDirectory3(quarantineDir);
|
|
7954
|
+
fsyncDirectory3(dirname5(paths.localStorePath));
|
|
7955
|
+
return quarantinedFiles;
|
|
7956
|
+
}
|
|
7957
|
+
function legacyMigrationMarkerPath2(paths) {
|
|
7958
|
+
return join5(paths.workspaceStateDir, LEGACY_MIGRATION_MARKER_FILE2);
|
|
7959
|
+
}
|
|
7960
|
+
function legacyMigrationLockPath2(paths) {
|
|
7961
|
+
return join5(paths.workspaceStateDir, LEGACY_MIGRATION_LOCK_FILE2);
|
|
7962
|
+
}
|
|
7963
|
+
function writePrivateJson2(path, value) {
|
|
7964
|
+
ensurePrivateDir2(dirname5(path));
|
|
7965
|
+
const fd = openSync4(path, "w", 384);
|
|
7966
|
+
try {
|
|
7967
|
+
writeFileSync3(fd, JSON.stringify(value, null, 2), "utf8");
|
|
7968
|
+
fsyncSync3(fd);
|
|
7969
|
+
} finally {
|
|
7970
|
+
closeSync4(fd);
|
|
7971
|
+
}
|
|
7972
|
+
makePrivateFile2(path);
|
|
7973
|
+
fsyncDirectory3(dirname5(path));
|
|
7974
|
+
}
|
|
7975
|
+
function ensurePrivateDir2(path) {
|
|
7976
|
+
mkdirSync5(path, { recursive: true, mode: 448 });
|
|
7977
|
+
if (process.platform !== "win32") {
|
|
7978
|
+
try {
|
|
7979
|
+
chmodSync2(path, 448);
|
|
7980
|
+
} catch {}
|
|
7981
|
+
}
|
|
7982
|
+
}
|
|
7983
|
+
function makePrivateFile2(path) {
|
|
7984
|
+
if (process.platform !== "win32") {
|
|
7985
|
+
try {
|
|
7986
|
+
chmodSync2(path, 384);
|
|
7987
|
+
} catch {}
|
|
7988
|
+
}
|
|
7989
|
+
}
|
|
7990
|
+
function readGitPath2(root, args) {
|
|
7991
|
+
try {
|
|
7992
|
+
const value = execFileSync5("git", args, {
|
|
7993
|
+
cwd: root,
|
|
7994
|
+
encoding: "utf8",
|
|
7995
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
7996
|
+
}).trim();
|
|
7997
|
+
return value.length > 0 ? value : undefined;
|
|
7998
|
+
} catch {
|
|
7999
|
+
return;
|
|
8000
|
+
}
|
|
8001
|
+
}
|
|
8002
|
+
function resolveMaybeRelative2(base, path) {
|
|
8003
|
+
return isAbsolute4(path) ? resolve9(path) : resolve9(base, path);
|
|
8004
|
+
}
|
|
8005
|
+
function canonicalPath2(path) {
|
|
8006
|
+
const resolved = resolve9(path);
|
|
8007
|
+
try {
|
|
8008
|
+
return realpathSync5.native(resolved);
|
|
8009
|
+
} catch {
|
|
8010
|
+
return resolved;
|
|
8011
|
+
}
|
|
8012
|
+
}
|
|
8013
|
+
function stableStorageId2(prefix, value) {
|
|
8014
|
+
return `${prefix}.${createHash6("sha256").update(value).digest("hex").slice(0, 16)}`;
|
|
8015
|
+
}
|
|
7112
8016
|
function stableJson(value) {
|
|
7113
8017
|
return JSON.stringify(value);
|
|
7114
8018
|
}
|
|
7115
|
-
function
|
|
8019
|
+
function nowIso2() {
|
|
7116
8020
|
return new Date().toISOString();
|
|
7117
8021
|
}
|
|
7118
8022
|
function changeSetMetadata(draft) {
|
|
@@ -7137,36 +8041,36 @@ function recoverJournalFiles(root, files) {
|
|
|
7137
8041
|
for (const file of [...files].reverse()) {
|
|
7138
8042
|
const absolute = resolve9(root, file.path);
|
|
7139
8043
|
if (file.tempPath)
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
if (file.existed && file.backupPath &&
|
|
7143
|
-
|
|
8044
|
+
rmSync5(file.tempPath, { recursive: true, force: true });
|
|
8045
|
+
rmSync5(absolute, { recursive: true, force: true });
|
|
8046
|
+
if (file.existed && file.backupPath && existsSync6(file.backupPath)) {
|
|
8047
|
+
renameSync3(file.backupPath, absolute);
|
|
7144
8048
|
}
|
|
7145
|
-
|
|
8049
|
+
fsyncDirectory3(dirname5(absolute));
|
|
7146
8050
|
}
|
|
7147
8051
|
}
|
|
7148
8052
|
function cleanupCommittedJournalFiles(files) {
|
|
7149
8053
|
for (const file of files) {
|
|
7150
8054
|
if (file.tempPath)
|
|
7151
|
-
|
|
8055
|
+
rmSync5(file.tempPath, { recursive: true, force: true });
|
|
7152
8056
|
if (file.backupPath)
|
|
7153
|
-
|
|
8057
|
+
rmSync5(file.backupPath, { recursive: true, force: true });
|
|
7154
8058
|
}
|
|
7155
8059
|
}
|
|
7156
|
-
function
|
|
8060
|
+
function fsyncDirectory3(path) {
|
|
7157
8061
|
try {
|
|
7158
|
-
const fd =
|
|
8062
|
+
const fd = openSync4(path, "r");
|
|
7159
8063
|
try {
|
|
7160
|
-
|
|
8064
|
+
fsyncSync3(fd);
|
|
7161
8065
|
} finally {
|
|
7162
|
-
|
|
8066
|
+
closeSync4(fd);
|
|
7163
8067
|
}
|
|
7164
8068
|
} catch (error) {
|
|
7165
|
-
if (!
|
|
8069
|
+
if (!isIgnorableDirectoryFsyncError3(error))
|
|
7166
8070
|
throw error;
|
|
7167
8071
|
}
|
|
7168
8072
|
}
|
|
7169
|
-
function
|
|
8073
|
+
function isIgnorableDirectoryFsyncError3(error) {
|
|
7170
8074
|
const code = error.code;
|
|
7171
8075
|
return code === "EINVAL" || code === "EISDIR" || process.platform === "win32" && code === "EPERM";
|
|
7172
8076
|
}
|
|
@@ -7197,7 +8101,7 @@ async function readRelationFiles(root, relationsDir) {
|
|
|
7197
8101
|
|
|
7198
8102
|
// packages/local-runtime/model-store-yaml/src/index.ts
|
|
7199
8103
|
init_src();
|
|
7200
|
-
import { existsSync as
|
|
8104
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync6, readdirSync as readdirSync4, readFileSync as readFileSync7, rmSync as rmSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
7201
8105
|
import { dirname as dirname6, resolve as resolve10 } from "node:path";
|
|
7202
8106
|
function createDefaultManifest(productId, productName) {
|
|
7203
8107
|
return {
|
|
@@ -7251,7 +8155,7 @@ function initializeArchContextModel(root, productName = "ArchContext Project") {
|
|
|
7251
8155
|
rebuildGeneratedProjection(root);
|
|
7252
8156
|
}
|
|
7253
8157
|
function rebuildGeneratedProjection(root) {
|
|
7254
|
-
|
|
8158
|
+
rmSync6(resolve10(root, ".archcontext/generated"), { recursive: true, force: true });
|
|
7255
8159
|
writeFile(root, ".archcontext/generated/ARCHITECTURE.md", [
|
|
7256
8160
|
"<!-- Generated by ArchContext. Do not edit by hand. -->",
|
|
7257
8161
|
"",
|
|
@@ -7265,7 +8169,7 @@ function rebuildGeneratedProjection(root) {
|
|
|
7265
8169
|
|
|
7266
8170
|
class YamlModelStore {
|
|
7267
8171
|
async loadManifest(workspace) {
|
|
7268
|
-
return
|
|
8172
|
+
return readFileSync7(resolve10(workspace.root, ".archcontext/manifest.yaml"), "utf8");
|
|
7269
8173
|
}
|
|
7270
8174
|
async loadModel(workspace) {
|
|
7271
8175
|
return listModelFiles(workspace.root);
|
|
@@ -7274,7 +8178,7 @@ class YamlModelStore {
|
|
|
7274
8178
|
const errors = [];
|
|
7275
8179
|
for (const required of [".archcontext/manifest.yaml", ".archcontext/product.yaml"]) {
|
|
7276
8180
|
try {
|
|
7277
|
-
|
|
8181
|
+
readFileSync7(resolve10(workspace.root, required), "utf8");
|
|
7278
8182
|
} catch {
|
|
7279
8183
|
errors.push(`missing ${required}`);
|
|
7280
8184
|
}
|
|
@@ -7301,7 +8205,7 @@ function listModelFiles(root) {
|
|
|
7301
8205
|
".archcontext/generated"
|
|
7302
8206
|
]);
|
|
7303
8207
|
return paths.map((path) => {
|
|
7304
|
-
const body =
|
|
8208
|
+
const body = readFileSync7(resolve10(root, path), "utf8");
|
|
7305
8209
|
return {
|
|
7306
8210
|
path,
|
|
7307
8211
|
body,
|
|
@@ -7314,7 +8218,7 @@ function collectArchContextFiles(root, entries) {
|
|
|
7314
8218
|
const files = [];
|
|
7315
8219
|
for (const entry of entries) {
|
|
7316
8220
|
const absolute = resolve10(root, entry);
|
|
7317
|
-
if (!
|
|
8221
|
+
if (!existsSync7(absolute))
|
|
7318
8222
|
continue;
|
|
7319
8223
|
const stat = readdirOrFile(absolute);
|
|
7320
8224
|
if (stat === "file") {
|
|
@@ -7359,8 +8263,8 @@ function writeYaml(root, path, value) {
|
|
|
7359
8263
|
}
|
|
7360
8264
|
function writeFile(root, path, body) {
|
|
7361
8265
|
const absolute = resolve10(root, path);
|
|
7362
|
-
|
|
7363
|
-
|
|
8266
|
+
mkdirSync6(dirname6(absolute), { recursive: true });
|
|
8267
|
+
writeFileSync4(absolute, body.endsWith(`
|
|
7364
8268
|
`) ? body : `${body}
|
|
7365
8269
|
`, "utf8");
|
|
7366
8270
|
}
|
|
@@ -7568,9 +8472,9 @@ class ArchctxDaemon {
|
|
|
7568
8472
|
tempRoot: input.tempRoot,
|
|
7569
8473
|
stateDir: input.stateDir
|
|
7570
8474
|
});
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
8475
|
+
mkdirSync7(paths.stateDir, { recursive: true });
|
|
8476
|
+
mkdirSync7(paths.runRoot, { recursive: true });
|
|
8477
|
+
mkdirSync7(paths.worktreeTempRoot, { recursive: true });
|
|
7574
8478
|
const preparing = {
|
|
7575
8479
|
schemaVersion: "archcontext.developer-review-run/v1",
|
|
7576
8480
|
runId: paths.runId,
|
|
@@ -7589,13 +8493,13 @@ class ArchctxDaemon {
|
|
|
7589
8493
|
cleanup: "remove-run-root"
|
|
7590
8494
|
}
|
|
7591
8495
|
};
|
|
7592
|
-
if (
|
|
7593
|
-
|
|
8496
|
+
if (existsSync8(paths.lockPath) || existsSync8(paths.manifestPath)) {
|
|
8497
|
+
rmSync7(paths.runRoot, { recursive: true, force: true });
|
|
7594
8498
|
throw new Error(`developer-review-run-already-active: ${input.challenge.challengeId}`);
|
|
7595
8499
|
}
|
|
7596
8500
|
let lockAcquired = false;
|
|
7597
8501
|
try {
|
|
7598
|
-
|
|
8502
|
+
writePrivateJson3(paths.lockPath, {
|
|
7599
8503
|
schemaVersion: "archcontext.developer-review-run-lock/v1",
|
|
7600
8504
|
runId: paths.runId,
|
|
7601
8505
|
challengeId: input.challenge.challengeId,
|
|
@@ -7625,7 +8529,7 @@ class ArchctxDaemon {
|
|
|
7625
8529
|
if (lockAcquired) {
|
|
7626
8530
|
this.cleanupDeveloperReviewRun(preparing);
|
|
7627
8531
|
} else {
|
|
7628
|
-
|
|
8532
|
+
rmSync7(paths.runRoot, { recursive: true, force: true });
|
|
7629
8533
|
}
|
|
7630
8534
|
throw error;
|
|
7631
8535
|
}
|
|
@@ -7646,7 +8550,7 @@ class ArchctxDaemon {
|
|
|
7646
8550
|
const errors = [];
|
|
7647
8551
|
if (run.worktree) {
|
|
7648
8552
|
try {
|
|
7649
|
-
const hadWorktree =
|
|
8553
|
+
const hadWorktree = existsSync8(run.worktree.worktreeRoot);
|
|
7650
8554
|
removeDetachedReviewWorktree(run.worktree);
|
|
7651
8555
|
if (hadWorktree)
|
|
7652
8556
|
removed.push("worktree");
|
|
@@ -7660,8 +8564,8 @@ class ArchctxDaemon {
|
|
|
7660
8564
|
["lock", run.lockPath]
|
|
7661
8565
|
]) {
|
|
7662
8566
|
try {
|
|
7663
|
-
const existed =
|
|
7664
|
-
|
|
8567
|
+
const existed = existsSync8(path);
|
|
8568
|
+
rmSync7(path, { recursive: true, force: true });
|
|
7665
8569
|
if (existed)
|
|
7666
8570
|
removed.push(kind);
|
|
7667
8571
|
} catch (error) {
|
|
@@ -7689,7 +8593,7 @@ class ArchctxDaemon {
|
|
|
7689
8593
|
removedLocks: [],
|
|
7690
8594
|
skippedActive: []
|
|
7691
8595
|
};
|
|
7692
|
-
if (!
|
|
8596
|
+
if (!existsSync8(stateDir))
|
|
7693
8597
|
return recovery;
|
|
7694
8598
|
for (const entry of readdirSync5(stateDir).sort()) {
|
|
7695
8599
|
if (!entry.endsWith(".json"))
|
|
@@ -7697,7 +8601,7 @@ class ArchctxDaemon {
|
|
|
7697
8601
|
const manifestPath = join6(stateDir, entry);
|
|
7698
8602
|
const manifest = readDeveloperReviewRunManifest(manifestPath);
|
|
7699
8603
|
if (!manifest) {
|
|
7700
|
-
|
|
8604
|
+
rmSync7(manifestPath, { force: true });
|
|
7701
8605
|
continue;
|
|
7702
8606
|
}
|
|
7703
8607
|
if (!input.force && isDeveloperReviewPidAlive(manifest.pid)) {
|
|
@@ -7717,7 +8621,7 @@ class ArchctxDaemon {
|
|
|
7717
8621
|
recovery.skippedActive.push(runId);
|
|
7718
8622
|
continue;
|
|
7719
8623
|
}
|
|
7720
|
-
|
|
8624
|
+
rmSync7(lockPath, { force: true });
|
|
7721
8625
|
recovery.removedLocks.push(lockPath);
|
|
7722
8626
|
}
|
|
7723
8627
|
return recovery;
|
|
@@ -8010,7 +8914,7 @@ class ArchctxDaemon {
|
|
|
8010
8914
|
const status = this.status();
|
|
8011
8915
|
if (!root)
|
|
8012
8916
|
return okEnvelope("status", status);
|
|
8013
|
-
const repositoryId =
|
|
8917
|
+
const repositoryId = repositoryFingerprint2(root);
|
|
8014
8918
|
const session = this.sessions.get(repositoryId);
|
|
8015
8919
|
return okEnvelope("status", {
|
|
8016
8920
|
...status,
|
|
@@ -8057,9 +8961,9 @@ class ArchctxDaemon {
|
|
|
8057
8961
|
}
|
|
8058
8962
|
async restoreRepositorySessions() {
|
|
8059
8963
|
for (const record of await this.localStore.listRepositorySessions()) {
|
|
8060
|
-
if (!record.root || !
|
|
8964
|
+
if (!record.root || !existsSync8(record.root))
|
|
8061
8965
|
continue;
|
|
8062
|
-
if (
|
|
8966
|
+
if (repositoryFingerprint2(record.root) !== record.repositoryId)
|
|
8063
8967
|
continue;
|
|
8064
8968
|
this.sessions.set(record.repositoryId, {
|
|
8065
8969
|
workspace: {
|
|
@@ -8345,8 +9249,8 @@ class ArchctxRuntimeRpcServer {
|
|
|
8345
9249
|
const root = this.options.root ?? process.cwd();
|
|
8346
9250
|
const connectionPath = this.options.connectionPath ?? defaultDaemonConnectionPath(root);
|
|
8347
9251
|
const lockPath = this.options.lockPath ?? defaultDaemonLockPath(root);
|
|
8348
|
-
|
|
8349
|
-
|
|
9252
|
+
mkdirSync7(dirname7(connectionPath), { recursive: true });
|
|
9253
|
+
mkdirSync7(dirname7(lockPath), { recursive: true });
|
|
8350
9254
|
this.lockFd = acquireDaemonLock(lockPath, root);
|
|
8351
9255
|
const token = this.options.token ?? randomBytes(18).toString("base64url");
|
|
8352
9256
|
const server = createServer((request, response) => {
|
|
@@ -8369,8 +9273,8 @@ class ArchctxRuntimeRpcServer {
|
|
|
8369
9273
|
connectionPath,
|
|
8370
9274
|
startedAt: (this.options.clock ?? (() => new Date().toISOString()))()
|
|
8371
9275
|
};
|
|
8372
|
-
|
|
8373
|
-
|
|
9276
|
+
writeFileSync5(connectionPath, JSON.stringify(this.connection, null, 2), { mode: 384 });
|
|
9277
|
+
chmodSync3(connectionPath, 384);
|
|
8374
9278
|
return this.connection;
|
|
8375
9279
|
}
|
|
8376
9280
|
async stop() {
|
|
@@ -8385,12 +9289,12 @@ class ArchctxRuntimeRpcServer {
|
|
|
8385
9289
|
}
|
|
8386
9290
|
await this.daemon.stop();
|
|
8387
9291
|
if (connection)
|
|
8388
|
-
|
|
9292
|
+
rmSync7(connection.connectionPath, { force: true });
|
|
8389
9293
|
if (this.lockFd !== undefined)
|
|
8390
|
-
|
|
9294
|
+
closeSync5(this.lockFd);
|
|
8391
9295
|
this.lockFd = undefined;
|
|
8392
9296
|
if (connection)
|
|
8393
|
-
|
|
9297
|
+
rmSync7(connection.lockPath, { force: true });
|
|
8394
9298
|
this.options.onStop?.();
|
|
8395
9299
|
}
|
|
8396
9300
|
async handleRequest(request, response) {
|
|
@@ -8513,24 +9417,21 @@ class ArchctxRuntimeRpcServer {
|
|
|
8513
9417
|
}
|
|
8514
9418
|
}
|
|
8515
9419
|
}
|
|
8516
|
-
function defaultDaemonControlDir(root = process.cwd()) {
|
|
8517
|
-
return join6(root, ".archcontext", ".local");
|
|
8518
|
-
}
|
|
8519
9420
|
function defaultDeveloperReviewRunStateDir(root = process.cwd()) {
|
|
8520
|
-
return
|
|
9421
|
+
return runtimeStatePaths2(root).developerReviewRunStateDir;
|
|
8521
9422
|
}
|
|
8522
9423
|
function defaultDaemonConnectionPath(root = process.cwd()) {
|
|
8523
|
-
return
|
|
9424
|
+
return runtimeStatePaths2(root).daemonConnectionPath;
|
|
8524
9425
|
}
|
|
8525
9426
|
function defaultDaemonLockPath(root = process.cwd()) {
|
|
8526
|
-
return
|
|
9427
|
+
return runtimeStatePaths2(root).daemonLockPath;
|
|
8527
9428
|
}
|
|
8528
9429
|
function readRuntimeRpcConnectionFile(root = process.cwd()) {
|
|
8529
9430
|
const path = defaultDaemonConnectionPath(root);
|
|
8530
9431
|
try {
|
|
8531
9432
|
if (!isPrivateControlFile(path))
|
|
8532
9433
|
return;
|
|
8533
|
-
const parsed = JSON.parse(
|
|
9434
|
+
const parsed = JSON.parse(readFileSync8(path, "utf8"));
|
|
8534
9435
|
if (!parsed || typeof parsed !== "object")
|
|
8535
9436
|
return;
|
|
8536
9437
|
return {
|
|
@@ -8562,7 +9463,7 @@ function runtimeRpcCompatibilityIssue(root = process.cwd()) {
|
|
|
8562
9463
|
connectionPath: connection.connectionPath ?? defaultDaemonConnectionPath(root),
|
|
8563
9464
|
lockPath: connection.lockPath ?? defaultDaemonLockPath(root),
|
|
8564
9465
|
pid,
|
|
8565
|
-
pidAlive: pid !== undefined ?
|
|
9466
|
+
pidAlive: pid !== undefined ? isProcessAlive3(pid) : false,
|
|
8566
9467
|
upgradeCommand: "archctx daemon upgrade"
|
|
8567
9468
|
};
|
|
8568
9469
|
}
|
|
@@ -8571,7 +9472,7 @@ function readRuntimeRpcConnection(root = process.cwd()) {
|
|
|
8571
9472
|
try {
|
|
8572
9473
|
if (!isPrivateControlFile(path))
|
|
8573
9474
|
return;
|
|
8574
|
-
const parsed = JSON.parse(
|
|
9475
|
+
const parsed = JSON.parse(readFileSync8(path, "utf8"));
|
|
8575
9476
|
return isValidRuntimeRpcConnection(parsed) ? parsed : undefined;
|
|
8576
9477
|
} catch {
|
|
8577
9478
|
return;
|
|
@@ -8587,11 +9488,11 @@ function recoverStaleDaemonControlFiles(root = process.cwd(), options = {}) {
|
|
|
8587
9488
|
const removed = [];
|
|
8588
9489
|
const connectionReason = staleConnectionFileReason(connectionPath, options.removeUnhealthyConnection ?? false);
|
|
8589
9490
|
if (connectionReason) {
|
|
8590
|
-
|
|
9491
|
+
rmSync7(connectionPath, { force: true });
|
|
8591
9492
|
removed.push(connectionReason);
|
|
8592
9493
|
}
|
|
8593
|
-
if (
|
|
8594
|
-
|
|
9494
|
+
if (existsSync8(lockPath) && isStaleLock(lockPath)) {
|
|
9495
|
+
rmSync7(lockPath, { force: true });
|
|
8595
9496
|
removed.push("stale-lock-file");
|
|
8596
9497
|
}
|
|
8597
9498
|
return { connectionPath, lockPath, removed };
|
|
@@ -8687,7 +9588,7 @@ function createDeveloperReviewRunPaths(input) {
|
|
|
8687
9588
|
const runId = `${safeChallengeId}-${randomBytes(6).toString("hex")}`;
|
|
8688
9589
|
const stateDir = input.stateDir ? resolve11(input.stateDir) : defaultDeveloperReviewRunStateDir(input.sourceRoot);
|
|
8689
9590
|
const tempParent = input.tempRoot ? resolve11(input.tempRoot) : tmpdir2();
|
|
8690
|
-
|
|
9591
|
+
mkdirSync7(tempParent, { recursive: true });
|
|
8691
9592
|
const runRoot = mkdtempSync3(join6(tempParent, `archctx-developer-review-${safeChallengeId.slice(0, 32)}-`));
|
|
8692
9593
|
return {
|
|
8693
9594
|
runId,
|
|
@@ -8703,12 +9604,12 @@ function safeControlFileSegment(value) {
|
|
|
8703
9604
|
return sanitized.length > 0 ? sanitized : "developer-review";
|
|
8704
9605
|
}
|
|
8705
9606
|
function writeDeveloperReviewRunManifest(manifest) {
|
|
8706
|
-
|
|
9607
|
+
writePrivateJson3(manifest.manifestPath, manifest);
|
|
8707
9608
|
}
|
|
8708
|
-
function
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
|
|
9609
|
+
function writePrivateJson3(path, value, flag = "w") {
|
|
9610
|
+
mkdirSync7(dirname7(path), { recursive: true });
|
|
9611
|
+
writeFileSync5(path, JSON.stringify(value, null, 2), { mode: 384, flag });
|
|
9612
|
+
chmodSync3(path, 384);
|
|
8712
9613
|
}
|
|
8713
9614
|
function readDeveloperReviewRunManifest(path) {
|
|
8714
9615
|
const parsed = readJsonObject(path);
|
|
@@ -8730,7 +9631,7 @@ function readDeveloperReviewRunManifest(path) {
|
|
|
8730
9631
|
}
|
|
8731
9632
|
function readJsonObject(path) {
|
|
8732
9633
|
try {
|
|
8733
|
-
const parsed = JSON.parse(
|
|
9634
|
+
const parsed = JSON.parse(readFileSync8(path, "utf8"));
|
|
8734
9635
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : undefined;
|
|
8735
9636
|
} catch {
|
|
8736
9637
|
return;
|
|
@@ -8739,7 +9640,7 @@ function readJsonObject(path) {
|
|
|
8739
9640
|
function isDeveloperReviewPidAlive(pid) {
|
|
8740
9641
|
if (!pid || pid <= 0)
|
|
8741
9642
|
return false;
|
|
8742
|
-
return
|
|
9643
|
+
return isProcessAlive3(pid);
|
|
8743
9644
|
}
|
|
8744
9645
|
function cleanupErrorMessage(kind, error) {
|
|
8745
9646
|
return `${kind}: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -8750,6 +9651,8 @@ async function createStartedDaemon(deps = {}) {
|
|
|
8750
9651
|
return daemon;
|
|
8751
9652
|
}
|
|
8752
9653
|
function createProductionDaemon(options = {}) {
|
|
9654
|
+
if (!options.localStorePath)
|
|
9655
|
+
migrateLegacyLocalStoreIfNeeded2(options.root);
|
|
8753
9656
|
const deps = {
|
|
8754
9657
|
localStorePath: options.localStorePath ?? defaultLocalStorePath2(options.root),
|
|
8755
9658
|
maxRepoSessions: options.maxRepoSessions
|
|
@@ -8796,15 +9699,15 @@ function blockedProductionInjections(deps) {
|
|
|
8796
9699
|
}
|
|
8797
9700
|
function acquireDaemonLock(lockPath, root) {
|
|
8798
9701
|
try {
|
|
8799
|
-
const fd =
|
|
8800
|
-
|
|
9702
|
+
const fd = openSync5(lockPath, "wx", 384);
|
|
9703
|
+
writeFileSync5(fd, JSON.stringify({ pid: process.pid, root, startedAt: new Date().toISOString() }, null, 2), "utf8");
|
|
8801
9704
|
return fd;
|
|
8802
9705
|
} catch (error) {
|
|
8803
9706
|
const code = error.code;
|
|
8804
9707
|
if (code !== "EEXIST")
|
|
8805
9708
|
throw error;
|
|
8806
9709
|
if (isStaleLock(lockPath)) {
|
|
8807
|
-
|
|
9710
|
+
rmSync7(lockPath, { force: true });
|
|
8808
9711
|
return acquireDaemonLock(lockPath, root);
|
|
8809
9712
|
}
|
|
8810
9713
|
throw new Error(`archctxd already running for ${root}; lock=${lockPath}`);
|
|
@@ -8814,15 +9717,15 @@ function isValidRuntimeRpcConnection(value) {
|
|
|
8814
9717
|
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
9718
|
}
|
|
8816
9719
|
function staleConnectionFileReason(path, removeUnhealthyConnection) {
|
|
8817
|
-
if (!
|
|
9720
|
+
if (!existsSync8(path))
|
|
8818
9721
|
return;
|
|
8819
9722
|
try {
|
|
8820
9723
|
if (!isPrivateControlFile(path))
|
|
8821
9724
|
return "insecure-connection-file";
|
|
8822
|
-
const parsed = JSON.parse(
|
|
9725
|
+
const parsed = JSON.parse(readFileSync8(path, "utf8"));
|
|
8823
9726
|
if (!isValidRuntimeRpcConnection(parsed))
|
|
8824
9727
|
return "invalid-connection-file";
|
|
8825
|
-
if (!
|
|
9728
|
+
if (!isProcessAlive3(parsed.pid))
|
|
8826
9729
|
return "dead-connection-pid";
|
|
8827
9730
|
return removeUnhealthyConnection ? "unhealthy-connection-file" : undefined;
|
|
8828
9731
|
} catch {
|
|
@@ -8837,15 +9740,15 @@ function isPrivateControlFile(path) {
|
|
|
8837
9740
|
}
|
|
8838
9741
|
function isStaleLock(lockPath) {
|
|
8839
9742
|
try {
|
|
8840
|
-
const lock = JSON.parse(
|
|
9743
|
+
const lock = JSON.parse(readFileSync8(lockPath, "utf8"));
|
|
8841
9744
|
if (typeof lock.pid !== "number" || lock.pid <= 0)
|
|
8842
9745
|
return true;
|
|
8843
|
-
return !
|
|
9746
|
+
return !isProcessAlive3(lock.pid);
|
|
8844
9747
|
} catch {
|
|
8845
9748
|
return true;
|
|
8846
9749
|
}
|
|
8847
9750
|
}
|
|
8848
|
-
function
|
|
9751
|
+
function isProcessAlive3(pid) {
|
|
8849
9752
|
try {
|
|
8850
9753
|
process.kill(pid, 0);
|
|
8851
9754
|
return true;
|
|
@@ -9031,7 +9934,7 @@ init_src();
|
|
|
9031
9934
|
|
|
9032
9935
|
// packages/local-runtime/runtime-daemon/src/index.ts
|
|
9033
9936
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
9034
|
-
import { chmodSync as
|
|
9937
|
+
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
9938
|
import { createServer as createServer2 } from "node:http";
|
|
9036
9939
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
9037
9940
|
import { dirname as dirname8, join as join7, resolve as resolve12 } from "node:path";
|
|
@@ -9238,9 +10141,9 @@ class ArchctxDaemon2 {
|
|
|
9238
10141
|
tempRoot: input.tempRoot,
|
|
9239
10142
|
stateDir: input.stateDir
|
|
9240
10143
|
});
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
10144
|
+
mkdirSync8(paths.stateDir, { recursive: true });
|
|
10145
|
+
mkdirSync8(paths.runRoot, { recursive: true });
|
|
10146
|
+
mkdirSync8(paths.worktreeTempRoot, { recursive: true });
|
|
9244
10147
|
const preparing = {
|
|
9245
10148
|
schemaVersion: "archcontext.developer-review-run/v1",
|
|
9246
10149
|
runId: paths.runId,
|
|
@@ -9259,13 +10162,13 @@ class ArchctxDaemon2 {
|
|
|
9259
10162
|
cleanup: "remove-run-root"
|
|
9260
10163
|
}
|
|
9261
10164
|
};
|
|
9262
|
-
if (
|
|
9263
|
-
|
|
10165
|
+
if (existsSync9(paths.lockPath) || existsSync9(paths.manifestPath)) {
|
|
10166
|
+
rmSync8(paths.runRoot, { recursive: true, force: true });
|
|
9264
10167
|
throw new Error(`developer-review-run-already-active: ${input.challenge.challengeId}`);
|
|
9265
10168
|
}
|
|
9266
10169
|
let lockAcquired = false;
|
|
9267
10170
|
try {
|
|
9268
|
-
|
|
10171
|
+
writePrivateJson4(paths.lockPath, {
|
|
9269
10172
|
schemaVersion: "archcontext.developer-review-run-lock/v1",
|
|
9270
10173
|
runId: paths.runId,
|
|
9271
10174
|
challengeId: input.challenge.challengeId,
|
|
@@ -9295,7 +10198,7 @@ class ArchctxDaemon2 {
|
|
|
9295
10198
|
if (lockAcquired) {
|
|
9296
10199
|
this.cleanupDeveloperReviewRun(preparing);
|
|
9297
10200
|
} else {
|
|
9298
|
-
|
|
10201
|
+
rmSync8(paths.runRoot, { recursive: true, force: true });
|
|
9299
10202
|
}
|
|
9300
10203
|
throw error;
|
|
9301
10204
|
}
|
|
@@ -9316,7 +10219,7 @@ class ArchctxDaemon2 {
|
|
|
9316
10219
|
const errors = [];
|
|
9317
10220
|
if (run.worktree) {
|
|
9318
10221
|
try {
|
|
9319
|
-
const hadWorktree =
|
|
10222
|
+
const hadWorktree = existsSync9(run.worktree.worktreeRoot);
|
|
9320
10223
|
removeDetachedReviewWorktree(run.worktree);
|
|
9321
10224
|
if (hadWorktree)
|
|
9322
10225
|
removed.push("worktree");
|
|
@@ -9330,8 +10233,8 @@ class ArchctxDaemon2 {
|
|
|
9330
10233
|
["lock", run.lockPath]
|
|
9331
10234
|
]) {
|
|
9332
10235
|
try {
|
|
9333
|
-
const existed =
|
|
9334
|
-
|
|
10236
|
+
const existed = existsSync9(path);
|
|
10237
|
+
rmSync8(path, { recursive: true, force: true });
|
|
9335
10238
|
if (existed)
|
|
9336
10239
|
removed.push(kind);
|
|
9337
10240
|
} catch (error) {
|
|
@@ -9359,7 +10262,7 @@ class ArchctxDaemon2 {
|
|
|
9359
10262
|
removedLocks: [],
|
|
9360
10263
|
skippedActive: []
|
|
9361
10264
|
};
|
|
9362
|
-
if (!
|
|
10265
|
+
if (!existsSync9(stateDir))
|
|
9363
10266
|
return recovery;
|
|
9364
10267
|
for (const entry of readdirSync6(stateDir).sort()) {
|
|
9365
10268
|
if (!entry.endsWith(".json"))
|
|
@@ -9367,7 +10270,7 @@ class ArchctxDaemon2 {
|
|
|
9367
10270
|
const manifestPath = join7(stateDir, entry);
|
|
9368
10271
|
const manifest = readDeveloperReviewRunManifest2(manifestPath);
|
|
9369
10272
|
if (!manifest) {
|
|
9370
|
-
|
|
10273
|
+
rmSync8(manifestPath, { force: true });
|
|
9371
10274
|
continue;
|
|
9372
10275
|
}
|
|
9373
10276
|
if (!input.force && isDeveloperReviewPidAlive2(manifest.pid)) {
|
|
@@ -9387,7 +10290,7 @@ class ArchctxDaemon2 {
|
|
|
9387
10290
|
recovery.skippedActive.push(runId);
|
|
9388
10291
|
continue;
|
|
9389
10292
|
}
|
|
9390
|
-
|
|
10293
|
+
rmSync8(lockPath, { force: true });
|
|
9391
10294
|
recovery.removedLocks.push(lockPath);
|
|
9392
10295
|
}
|
|
9393
10296
|
return recovery;
|
|
@@ -9680,7 +10583,7 @@ class ArchctxDaemon2 {
|
|
|
9680
10583
|
const status = this.status();
|
|
9681
10584
|
if (!root)
|
|
9682
10585
|
return okEnvelope("status", status);
|
|
9683
|
-
const repositoryId =
|
|
10586
|
+
const repositoryId = repositoryFingerprint2(root);
|
|
9684
10587
|
const session = this.sessions.get(repositoryId);
|
|
9685
10588
|
return okEnvelope("status", {
|
|
9686
10589
|
...status,
|
|
@@ -9727,9 +10630,9 @@ class ArchctxDaemon2 {
|
|
|
9727
10630
|
}
|
|
9728
10631
|
async restoreRepositorySessions() {
|
|
9729
10632
|
for (const record of await this.localStore.listRepositorySessions()) {
|
|
9730
|
-
if (!record.root || !
|
|
10633
|
+
if (!record.root || !existsSync9(record.root))
|
|
9731
10634
|
continue;
|
|
9732
|
-
if (
|
|
10635
|
+
if (repositoryFingerprint2(record.root) !== record.repositoryId)
|
|
9733
10636
|
continue;
|
|
9734
10637
|
this.sessions.set(record.repositoryId, {
|
|
9735
10638
|
workspace: {
|
|
@@ -9996,14 +10899,11 @@ class RuntimeRpcClient2 {
|
|
|
9996
10899
|
return await response.json();
|
|
9997
10900
|
}
|
|
9998
10901
|
}
|
|
9999
|
-
function defaultDaemonControlDir2(root = process.cwd()) {
|
|
10000
|
-
return join7(root, ".archcontext", ".local");
|
|
10001
|
-
}
|
|
10002
10902
|
function defaultDeveloperReviewRunStateDir2(root = process.cwd()) {
|
|
10003
|
-
return
|
|
10903
|
+
return runtimeStatePaths2(root).developerReviewRunStateDir;
|
|
10004
10904
|
}
|
|
10005
10905
|
function defaultDaemonConnectionPath2(root = process.cwd()) {
|
|
10006
|
-
return
|
|
10906
|
+
return runtimeStatePaths2(root).daemonConnectionPath;
|
|
10007
10907
|
}
|
|
10008
10908
|
function unwrapRpcData2(result) {
|
|
10009
10909
|
if (!result.ok)
|
|
@@ -10015,7 +10915,7 @@ function readRuntimeRpcConnection2(root = process.cwd()) {
|
|
|
10015
10915
|
try {
|
|
10016
10916
|
if (!isPrivateControlFile2(path))
|
|
10017
10917
|
return;
|
|
10018
|
-
const parsed = JSON.parse(
|
|
10918
|
+
const parsed = JSON.parse(readFileSync9(path, "utf8"));
|
|
10019
10919
|
return isValidRuntimeRpcConnection2(parsed) ? parsed : undefined;
|
|
10020
10920
|
} catch {
|
|
10021
10921
|
return;
|
|
@@ -10116,7 +11016,7 @@ function createDeveloperReviewRunPaths2(input) {
|
|
|
10116
11016
|
const runId = `${safeChallengeId}-${randomBytes2(6).toString("hex")}`;
|
|
10117
11017
|
const stateDir = input.stateDir ? resolve12(input.stateDir) : defaultDeveloperReviewRunStateDir2(input.sourceRoot);
|
|
10118
11018
|
const tempParent = input.tempRoot ? resolve12(input.tempRoot) : tmpdir3();
|
|
10119
|
-
|
|
11019
|
+
mkdirSync8(tempParent, { recursive: true });
|
|
10120
11020
|
const runRoot = mkdtempSync4(join7(tempParent, `archctx-developer-review-${safeChallengeId.slice(0, 32)}-`));
|
|
10121
11021
|
return {
|
|
10122
11022
|
runId,
|
|
@@ -10132,12 +11032,12 @@ function safeControlFileSegment2(value) {
|
|
|
10132
11032
|
return sanitized.length > 0 ? sanitized : "developer-review";
|
|
10133
11033
|
}
|
|
10134
11034
|
function writeDeveloperReviewRunManifest2(manifest) {
|
|
10135
|
-
|
|
11035
|
+
writePrivateJson4(manifest.manifestPath, manifest);
|
|
10136
11036
|
}
|
|
10137
|
-
function
|
|
10138
|
-
|
|
10139
|
-
|
|
10140
|
-
|
|
11037
|
+
function writePrivateJson4(path, value, flag = "w") {
|
|
11038
|
+
mkdirSync8(dirname8(path), { recursive: true });
|
|
11039
|
+
writeFileSync6(path, JSON.stringify(value, null, 2), { mode: 384, flag });
|
|
11040
|
+
chmodSync4(path, 384);
|
|
10141
11041
|
}
|
|
10142
11042
|
function readDeveloperReviewRunManifest2(path) {
|
|
10143
11043
|
const parsed = readJsonObject2(path);
|
|
@@ -10159,7 +11059,7 @@ function readDeveloperReviewRunManifest2(path) {
|
|
|
10159
11059
|
}
|
|
10160
11060
|
function readJsonObject2(path) {
|
|
10161
11061
|
try {
|
|
10162
|
-
const parsed = JSON.parse(
|
|
11062
|
+
const parsed = JSON.parse(readFileSync9(path, "utf8"));
|
|
10163
11063
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : undefined;
|
|
10164
11064
|
} catch {
|
|
10165
11065
|
return;
|
|
@@ -10168,7 +11068,7 @@ function readJsonObject2(path) {
|
|
|
10168
11068
|
function isDeveloperReviewPidAlive2(pid) {
|
|
10169
11069
|
if (!pid || pid <= 0)
|
|
10170
11070
|
return false;
|
|
10171
|
-
return
|
|
11071
|
+
return isProcessAlive4(pid);
|
|
10172
11072
|
}
|
|
10173
11073
|
function cleanupErrorMessage2(kind, error) {
|
|
10174
11074
|
return `${kind}: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -10214,7 +11114,7 @@ function isPrivateControlFile2(path) {
|
|
|
10214
11114
|
const mode = statSync5(path).mode & 511;
|
|
10215
11115
|
return (mode & 63) === 0;
|
|
10216
11116
|
}
|
|
10217
|
-
function
|
|
11117
|
+
function isProcessAlive4(pid) {
|
|
10218
11118
|
try {
|
|
10219
11119
|
process.kill(pid, 0);
|
|
10220
11120
|
return true;
|
|
@@ -10401,7 +11301,7 @@ async function runStdioMcpLoop(input, output, log = (line) => process.stderr.wri
|
|
|
10401
11301
|
|
|
10402
11302
|
// packages/surfaces/renderer/src/index.ts
|
|
10403
11303
|
init_src();
|
|
10404
|
-
import { existsSync as
|
|
11304
|
+
import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync10 } from "node:fs";
|
|
10405
11305
|
import { resolve as resolve13 } from "node:path";
|
|
10406
11306
|
function normalizeNativeModel2(model) {
|
|
10407
11307
|
return {
|
|
@@ -10437,9 +11337,9 @@ function mermaidId(id) {
|
|
|
10437
11337
|
return stableId(id).replace(/-/g, "_").replace(/\./g, "_");
|
|
10438
11338
|
}
|
|
10439
11339
|
function readYamlObjects(dir) {
|
|
10440
|
-
if (!
|
|
11340
|
+
if (!existsSync10(dir))
|
|
10441
11341
|
return [];
|
|
10442
|
-
return readdirSync7(dir).filter((file) => /\.ya?ml$/.test(file)).sort().map((file) => parseFlatYaml(
|
|
11342
|
+
return readdirSync7(dir).filter((file) => /\.ya?ml$/.test(file)).sort().map((file) => parseFlatYaml(readFileSync10(resolve13(dir, file), "utf8")));
|
|
10443
11343
|
}
|
|
10444
11344
|
function parseFlatYaml(body) {
|
|
10445
11345
|
const out = {};
|
|
@@ -10464,7 +11364,7 @@ function escapeMermaid(value) {
|
|
|
10464
11364
|
// packages/surfaces/cli/src/main.ts
|
|
10465
11365
|
var [, , command, ...args] = process.argv;
|
|
10466
11366
|
var CLI_ENTRY = fileURLToPath(import.meta.url);
|
|
10467
|
-
var DAEMON_START_TIMEOUT_MS =
|
|
11367
|
+
var DAEMON_START_TIMEOUT_MS = 15000;
|
|
10468
11368
|
|
|
10469
11369
|
class RuntimeVersionUnsupportedError extends Error {
|
|
10470
11370
|
issue;
|
|
@@ -10679,6 +11579,8 @@ async function runCliUnchecked(command2 = "help", args2 = [], cwd, deps = {}) {
|
|
|
10679
11579
|
};
|
|
10680
11580
|
case "doctor":
|
|
10681
11581
|
return { schemaVersion: "archcontext.envelope/v1", ok: true, requestId: "doctor", data: await doctorReport(cwd) };
|
|
11582
|
+
case "paths":
|
|
11583
|
+
return { schemaVersion: "archcontext.envelope/v1", ok: true, requestId: "paths", data: runtimePathsReport(cwd) };
|
|
10682
11584
|
case "privacy-audit":
|
|
10683
11585
|
return {
|
|
10684
11586
|
schemaVersion: "archcontext.envelope/v1",
|
|
@@ -10724,8 +11626,8 @@ async function runCliUnchecked(command2 = "help", args2 = [], cwd, deps = {}) {
|
|
|
10724
11626
|
ok: true,
|
|
10725
11627
|
requestId: "help",
|
|
10726
11628
|
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"]
|
|
11629
|
+
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"],
|
|
11630
|
+
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
11631
|
}
|
|
10730
11632
|
};
|
|
10731
11633
|
}
|
|
@@ -10811,7 +11713,7 @@ async function runGithubCommand(args2, cwd, deps) {
|
|
|
10811
11713
|
tokenStore.clear(record.codeVerifierRef);
|
|
10812
11714
|
tokenStore.clear(record.refreshTokenRef);
|
|
10813
11715
|
keyStore.removeDevicePrivateKey(record.deviceKey.keyRef);
|
|
10814
|
-
|
|
11716
|
+
rmSync9(connectionPath, { force: true });
|
|
10815
11717
|
return okEnvelope("github.disconnect", {
|
|
10816
11718
|
disconnected: true,
|
|
10817
11719
|
connected: false,
|
|
@@ -11094,20 +11996,20 @@ function defaultGithubDeveloperReviewStatePath(cwd, pullRequestNumber) {
|
|
|
11094
11996
|
}
|
|
11095
11997
|
async function writeGithubDeveloperReviewState(cwd, state) {
|
|
11096
11998
|
const path = defaultGithubDeveloperReviewStatePath(cwd, state.challenge.pullRequestNumber);
|
|
11097
|
-
|
|
11999
|
+
mkdirSync9(dirname9(path), { recursive: true });
|
|
11098
12000
|
const serialized = `${JSON.stringify(state, null, 2)}
|
|
11099
12001
|
`;
|
|
11100
12002
|
assertNoCliSecretMaterial(serialized);
|
|
11101
|
-
|
|
12003
|
+
writeFileSync7(path, serialized, { mode: 384 });
|
|
11102
12004
|
if (process.platform !== "win32")
|
|
11103
|
-
|
|
12005
|
+
chmodSync5(path, 384);
|
|
11104
12006
|
return { state, path };
|
|
11105
12007
|
}
|
|
11106
12008
|
function readGithubDeveloperReviewState(path) {
|
|
11107
|
-
if (!
|
|
12009
|
+
if (!existsSync11(path))
|
|
11108
12010
|
return;
|
|
11109
12011
|
try {
|
|
11110
|
-
const parsed = JSON.parse(
|
|
12012
|
+
const parsed = JSON.parse(readFileSync11(path, "utf8"));
|
|
11111
12013
|
if (parsed.schemaVersion !== "archcontext.github-developer-review-state/v1")
|
|
11112
12014
|
return;
|
|
11113
12015
|
if (!parsed.challenge || typeof parsed.challenge.pullRequestNumber !== "number")
|
|
@@ -11156,10 +12058,10 @@ function defaultGithubConnectionPath(cwd) {
|
|
|
11156
12058
|
return join8(dirname9(defaultDaemonConnectionPath(cwd)), "github-connection.json");
|
|
11157
12059
|
}
|
|
11158
12060
|
function readGithubConnection(path) {
|
|
11159
|
-
if (!
|
|
12061
|
+
if (!existsSync11(path))
|
|
11160
12062
|
return;
|
|
11161
12063
|
try {
|
|
11162
|
-
const parsed = JSON.parse(
|
|
12064
|
+
const parsed = JSON.parse(readFileSync11(path, "utf8"));
|
|
11163
12065
|
if (parsed.schemaVersion !== "archcontext.github-connection/v1" || parsed.status !== "connected")
|
|
11164
12066
|
return;
|
|
11165
12067
|
return parsed;
|
|
@@ -11168,13 +12070,13 @@ function readGithubConnection(path) {
|
|
|
11168
12070
|
}
|
|
11169
12071
|
}
|
|
11170
12072
|
function writeGithubConnection(path, record) {
|
|
11171
|
-
|
|
12073
|
+
mkdirSync9(dirname9(path), { recursive: true });
|
|
11172
12074
|
const serialized = `${JSON.stringify(record, null, 2)}
|
|
11173
12075
|
`;
|
|
11174
12076
|
assertNoCliSecretMaterial(serialized);
|
|
11175
|
-
|
|
12077
|
+
writeFileSync7(path, serialized, { mode: 384 });
|
|
11176
12078
|
if (process.platform !== "win32")
|
|
11177
|
-
|
|
12079
|
+
chmodSync5(path, 384);
|
|
11178
12080
|
}
|
|
11179
12081
|
function sanitizeGithubConnection(record, connectionPath) {
|
|
11180
12082
|
return {
|
|
@@ -11203,7 +12105,7 @@ async function readReviewChallengeV2Arg(args2, cwd, commandName = "github verify
|
|
|
11203
12105
|
if (!inline && !challengePath)
|
|
11204
12106
|
return { ok: false, message: `${commandName} requires --challenge-json or --challenge-path` };
|
|
11205
12107
|
try {
|
|
11206
|
-
const raw = inline ??
|
|
12108
|
+
const raw = inline ?? readFileSync11(resolve14(cwd, challengePath), "utf8");
|
|
11207
12109
|
const parsed = JSON.parse(raw);
|
|
11208
12110
|
const attestation = await Promise.resolve().then(() => (init_src7(), exports_src3));
|
|
11209
12111
|
const assertReviewChallengeV23 = attestation.assertReviewChallengeV2;
|
|
@@ -11286,6 +12188,7 @@ function agentHostRemoveConfig(host) {
|
|
|
11286
12188
|
}
|
|
11287
12189
|
async function doctorReport(cwd) {
|
|
11288
12190
|
const product = productVersionManifest();
|
|
12191
|
+
const paths = runtimePathsReport(cwd);
|
|
11289
12192
|
const daemon = await doctorDaemon(cwd);
|
|
11290
12193
|
const git = doctorGit(cwd);
|
|
11291
12194
|
const sqlite = doctorSqlite(cwd);
|
|
@@ -11303,6 +12206,7 @@ async function doctorReport(cwd) {
|
|
|
11303
12206
|
},
|
|
11304
12207
|
daemon,
|
|
11305
12208
|
sqlite,
|
|
12209
|
+
paths,
|
|
11306
12210
|
codeGraph: product.runtime.codeGraph,
|
|
11307
12211
|
git,
|
|
11308
12212
|
permissions,
|
|
@@ -11350,23 +12254,46 @@ function doctorGit(cwd) {
|
|
|
11350
12254
|
}
|
|
11351
12255
|
}
|
|
11352
12256
|
function doctorSqlite(cwd) {
|
|
11353
|
-
const
|
|
12257
|
+
const paths = runtimeStatePaths(cwd);
|
|
12258
|
+
const path = paths.localStorePath;
|
|
12259
|
+
const legacyLocalStore = inspectLegacyLocalStoreMigration(cwd);
|
|
11354
12260
|
return {
|
|
11355
12261
|
path,
|
|
11356
|
-
exists:
|
|
11357
|
-
migrations: productVersionManifest().runtime.sqliteMigrations
|
|
12262
|
+
exists: existsSync11(path),
|
|
12263
|
+
migrations: productVersionManifest().runtime.sqliteMigrations,
|
|
12264
|
+
legacyPath: paths.legacyLocalStorePath,
|
|
12265
|
+
legacyExists: existsSync11(paths.legacyLocalStorePath),
|
|
12266
|
+
legacyLocalStore
|
|
11358
12267
|
};
|
|
11359
12268
|
}
|
|
11360
12269
|
function doctorPermissions(cwd) {
|
|
12270
|
+
const paths = runtimeStatePaths(cwd);
|
|
11361
12271
|
const controlDir = dirname9(defaultDaemonConnectionPath(cwd));
|
|
11362
12272
|
return {
|
|
11363
12273
|
workspace: pathAccess(cwd),
|
|
12274
|
+
stateRoot: pathAccess(paths.stateRoot),
|
|
12275
|
+
runtimeStateDir: pathAccess(paths.workspaceStateDir),
|
|
11364
12276
|
controlDir: pathAccess(controlDir),
|
|
11365
12277
|
sqlite: pathAccess(defaultLocalStorePath(cwd))
|
|
11366
12278
|
};
|
|
11367
12279
|
}
|
|
12280
|
+
function runtimePathsReport(cwd) {
|
|
12281
|
+
const paths = runtimeStatePaths(cwd);
|
|
12282
|
+
return {
|
|
12283
|
+
...paths,
|
|
12284
|
+
legacyLocalStore: inspectLegacyLocalStoreMigration(cwd),
|
|
12285
|
+
runtimeRepositoryId: repositoryFingerprint(paths.repositoryRoot),
|
|
12286
|
+
repositoryTruthDir: join8(paths.repositoryRoot, ".archcontext"),
|
|
12287
|
+
codeGraphIndexDir: join8(paths.repositoryRoot, ".codegraph"),
|
|
12288
|
+
npmGlobalInstallState: "forbidden",
|
|
12289
|
+
overrides: {
|
|
12290
|
+
stateRootEnv: "ARCHCONTEXT_STATE_DIR",
|
|
12291
|
+
localStorePathEnv: "ARCHCONTEXT_LOCAL_STORE_PATH"
|
|
12292
|
+
}
|
|
12293
|
+
};
|
|
12294
|
+
}
|
|
11368
12295
|
function pathAccess(path) {
|
|
11369
|
-
const exists =
|
|
12296
|
+
const exists = existsSync11(path);
|
|
11370
12297
|
return {
|
|
11371
12298
|
path,
|
|
11372
12299
|
exists,
|
|
@@ -11442,6 +12369,8 @@ async function createCliRuntime(cwd, deps) {
|
|
|
11442
12369
|
githubReviewSubmissionPort: _githubReviewSubmissionPort,
|
|
11443
12370
|
...runtimeDeps
|
|
11444
12371
|
} = deps;
|
|
12372
|
+
if (!runtimeDeps.localStorePath)
|
|
12373
|
+
migrateLegacyLocalStoreIfNeeded(cwd);
|
|
11445
12374
|
const daemon = await createStartedDaemon({
|
|
11446
12375
|
localStorePath: defaultLocalStorePath(cwd),
|
|
11447
12376
|
...runtimeDeps,
|
|
@@ -11544,8 +12473,8 @@ async function startBackgroundDaemon(args2, cwd) {
|
|
|
11544
12473
|
const connectionPath = defaultDaemonConnectionPath(cwd);
|
|
11545
12474
|
const controlDir = dirname9(connectionPath);
|
|
11546
12475
|
const logPath = join8(controlDir, "archctxd.log");
|
|
11547
|
-
|
|
11548
|
-
const logFd =
|
|
12476
|
+
mkdirSync9(controlDir, { recursive: true });
|
|
12477
|
+
const logFd = openSync7(logPath, "a", 384);
|
|
11549
12478
|
try {
|
|
11550
12479
|
const child = spawn(process.execPath, [
|
|
11551
12480
|
CLI_ENTRY,
|
|
@@ -11563,7 +12492,7 @@ async function startBackgroundDaemon(args2, cwd) {
|
|
|
11563
12492
|
child.unref();
|
|
11564
12493
|
const ready = await waitForDaemonReady(cwd, Number(readFlag(args2, "--timeout-ms") ?? DAEMON_START_TIMEOUT_MS));
|
|
11565
12494
|
if (!ready) {
|
|
11566
|
-
return errorEnvelope("daemon.start", "AC_RUNTIME_UNAVAILABLE", `archctxd did not become ready; log=${logPath}`);
|
|
12495
|
+
return errorEnvelope("daemon.start", "AC_RUNTIME_UNAVAILABLE", `archctxd did not become ready; log=${logPath}; logTail=${readFileTail(logPath)}`);
|
|
11567
12496
|
}
|
|
11568
12497
|
return okEnvelope("daemon.start", {
|
|
11569
12498
|
running: true,
|
|
@@ -11575,7 +12504,7 @@ async function startBackgroundDaemon(args2, cwd) {
|
|
|
11575
12504
|
...recoveryData(recovery)
|
|
11576
12505
|
});
|
|
11577
12506
|
} finally {
|
|
11578
|
-
|
|
12507
|
+
closeSync7(logFd);
|
|
11579
12508
|
}
|
|
11580
12509
|
}
|
|
11581
12510
|
async function upgradeDaemon(args2, cwd) {
|
|
@@ -11712,8 +12641,16 @@ async function waitForDaemonReady(cwd, timeoutMs) {
|
|
|
11712
12641
|
function sleep(ms) {
|
|
11713
12642
|
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
11714
12643
|
}
|
|
12644
|
+
function readFileTail(path, maxBytes = 4096) {
|
|
12645
|
+
try {
|
|
12646
|
+
const content = readFileSync11(path, "utf8");
|
|
12647
|
+
return content.slice(Math.max(0, content.length - maxBytes)).replace(/\s+/g, " ").trim();
|
|
12648
|
+
} catch {
|
|
12649
|
+
return "<unavailable>";
|
|
12650
|
+
}
|
|
12651
|
+
}
|
|
11715
12652
|
async function runForegroundDaemon(cwd, args2) {
|
|
11716
|
-
const daemon = await createStartedProductionDaemon({ root: cwd
|
|
12653
|
+
const daemon = await createStartedProductionDaemon({ root: cwd });
|
|
11717
12654
|
let resolveStopped;
|
|
11718
12655
|
const stopped = new Promise((resolve15) => {
|
|
11719
12656
|
resolveStopped = resolve15;
|