archctx 0.8.1 → 0.9.0
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/assets/catalog.yaml +2 -2
- package/bin/archctx.mjs +1260 -84
- package/package.json +1 -1
- package/schemas/runtime/archctx-capabilities.schema.json +1 -1
- package/schemas/runtime/projection-pr-verification.schema.json +34 -0
- package/schemas/runtime/projection-publish-run.schema.json +74 -0
- package/schemas/runtime/projection-publish-status.schema.json +36 -0
package/bin/archctx.mjs
CHANGED
|
@@ -1753,7 +1753,9 @@ var init_projection = __esm(() => {
|
|
|
1753
1753
|
"projection-preview-v1",
|
|
1754
1754
|
"projection-prior-committed-applies-v1",
|
|
1755
1755
|
"projection-protocol-v2",
|
|
1756
|
+
"projection-publish-v1",
|
|
1756
1757
|
"projection-snapshot-v1",
|
|
1758
|
+
"projection-verify-pr-v1",
|
|
1757
1759
|
"recommendation-scan-decision-v1",
|
|
1758
1760
|
"recommendation-v3",
|
|
1759
1761
|
"refactor-assessment-v1",
|
|
@@ -3363,6 +3365,10 @@ var init_ports = __esm(() => {
|
|
|
3363
3365
|
// packages/contracts/src/practices.ts
|
|
3364
3366
|
var PRACTICE_SCHEMA_VERSION = "archcontext.practice/v1", PRACTICE_PROFILE_SCHEMA_VERSION = "archcontext.practice-profile/v1", PRACTICE_SOURCE_SCHEMA_VERSION = "archcontext.practice-source/v1", PRACTICE_CATALOG_MANIFEST_SCHEMA_VERSION = "archcontext.practice-catalog-manifest/v1", PRACTICE_ENFORCEMENT_POLICY_SCHEMA_VERSION = "archcontext.practice-enforcement-policy/v1", PRACTICE_WAIVER_SCHEMA_VERSION = "archcontext.practice-waiver/v1";
|
|
3365
3367
|
|
|
3368
|
+
// packages/contracts/src/projection-writer.ts
|
|
3369
|
+
var PROJECTION_PUBLISH_RUN_SCHEMA_VERSION = "archcontext.projection-publish-run/v1", PROJECTION_PUBLISH_STATUS_SCHEMA_VERSION = "archcontext.projection-publish-status/v1", PROJECTION_PR_VERIFICATION_SCHEMA_VERSION = "archcontext.projection-pr-verification/v1", PROJECTION_WRITER_BRANCH_PREFIX = "archctx/", PROJECTION_WRITER_DEFAULT_BRANCH = "archctx/projection";
|
|
3370
|
+
var init_projection_writer = () => {};
|
|
3371
|
+
|
|
3366
3372
|
// packages/contracts/src/projection-manifest.ts
|
|
3367
3373
|
function architectureDocsProjectionManifestIssues(value) {
|
|
3368
3374
|
const issues = [];
|
|
@@ -3867,7 +3873,7 @@ function compareVersionTriples(left, right) {
|
|
|
3867
3873
|
}
|
|
3868
3874
|
return 0;
|
|
3869
3875
|
}
|
|
3870
|
-
var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.
|
|
3876
|
+
var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.9.0", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.4.3", ARCHCONTEXT_NODE_RANGE = ">=22.22 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v2", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-07-11.explorer-view-v2";
|
|
3871
3877
|
|
|
3872
3878
|
// packages/contracts/src/refactor.ts
|
|
3873
3879
|
function refactorNamespaceIssues(value, label = "product.id") {
|
|
@@ -4969,6 +4975,7 @@ var init_src = __esm(() => {
|
|
|
4969
4975
|
init_ports();
|
|
4970
4976
|
init_projection();
|
|
4971
4977
|
init_projection_recovery();
|
|
4978
|
+
init_projection_writer();
|
|
4972
4979
|
init_projection_manifest();
|
|
4973
4980
|
init_refactor();
|
|
4974
4981
|
init_schema();
|
|
@@ -15377,7 +15384,7 @@ var init_src6 = __esm(() => {
|
|
|
15377
15384
|
|
|
15378
15385
|
// packages/local-runtime/egress-admission/src/index.ts
|
|
15379
15386
|
var LOCAL_EGRESS_MODE_ENV = "ARCHCONTEXT_EGRESS_MODE";
|
|
15380
|
-
var LOCAL_EGRESS_CHANNELS = ["context7", "agent-audit", "github-issue-publishing", "npm-update-check", "codegraph-telemetry"];
|
|
15387
|
+
var LOCAL_EGRESS_CHANNELS = ["context7", "agent-audit", "github-issue-publishing", "npm-update-check", "projection-publishing", "codegraph-telemetry"];
|
|
15381
15388
|
|
|
15382
15389
|
class LocalEgressPolicyError extends Error {
|
|
15383
15390
|
}
|
|
@@ -21015,6 +21022,16 @@ ${wrapped}`;
|
|
|
21015
21022
|
}
|
|
21016
21023
|
return `${existing.slice(0, region.start)}${wrapped}${existing.slice(region.end)}`;
|
|
21017
21024
|
}
|
|
21025
|
+
function agentContextTextOutsideRegions(body, nodeIds) {
|
|
21026
|
+
let text = body;
|
|
21027
|
+
for (const nodeId of [...new Set(nodeIds)].sort()) {
|
|
21028
|
+
const region = findAgentContextRegion(text, nodeId);
|
|
21029
|
+
if (!region)
|
|
21030
|
+
throw new Error(`agent-context-marker-missing: ${nodeId}`);
|
|
21031
|
+
text = `${text.slice(0, region.start)}\x00archcontext-agent-context-region:${nodeId}\x00${text.slice(region.end)}`;
|
|
21032
|
+
}
|
|
21033
|
+
return text;
|
|
21034
|
+
}
|
|
21018
21035
|
function findAgentContextRegion(body, nodeId) {
|
|
21019
21036
|
const startPattern = new RegExp(`<!-- BEGIN ARCHCONTEXT AGENT CONTEXT id="${escapeRegExp(nodeId)}"[^>]*-->`);
|
|
21020
21037
|
const startMatch = startPattern.exec(body);
|
|
@@ -22998,7 +23015,7 @@ function isGitWorktreeError(error) {
|
|
|
22998
23015
|
|
|
22999
23016
|
// packages/local-runtime/egress-admission/src/index.ts
|
|
23000
23017
|
var LOCAL_EGRESS_MODE_ENV2 = "ARCHCONTEXT_EGRESS_MODE";
|
|
23001
|
-
var LOCAL_EGRESS_CHANNELS2 = ["context7", "agent-audit", "github-issue-publishing", "npm-update-check", "codegraph-telemetry"];
|
|
23018
|
+
var LOCAL_EGRESS_CHANNELS2 = ["context7", "agent-audit", "github-issue-publishing", "npm-update-check", "projection-publishing", "codegraph-telemetry"];
|
|
23002
23019
|
|
|
23003
23020
|
class LocalEgressPolicyError2 extends Error {
|
|
23004
23021
|
}
|
|
@@ -33880,6 +33897,19 @@ async function runAgentContextProjectionCommand(input, cwd, daemon) {
|
|
|
33880
33897
|
throw publicOperationFailure(error);
|
|
33881
33898
|
}
|
|
33882
33899
|
}
|
|
33900
|
+
function agentContextProjectionDifferingPaths(root, paths) {
|
|
33901
|
+
const wanted = new Set(paths);
|
|
33902
|
+
if (wanted.size === 0)
|
|
33903
|
+
return [];
|
|
33904
|
+
const projection = buildAgentContextProjection(root);
|
|
33905
|
+
const onDisk = new Map(projection.existingFiles.map((file) => [file.path, file.onDisk]));
|
|
33906
|
+
const written = projectionFilesBoundToRenderInput(projection.existingFiles, projection.plan.files);
|
|
33907
|
+
const rendered = new Set(written.map((file) => file.path));
|
|
33908
|
+
return [...new Set([
|
|
33909
|
+
...written.filter((file) => wanted.has(file.path) && onDisk.get(file.path) !== file.body).map((file) => file.path),
|
|
33910
|
+
...[...wanted].filter((path) => !rendered.has(path))
|
|
33911
|
+
])].sort();
|
|
33912
|
+
}
|
|
33883
33913
|
function buildAgentContextProjection(root) {
|
|
33884
33914
|
const model = loadNativeModelFromArchContext(root);
|
|
33885
33915
|
const sourceDigest = digestJson({ model });
|
|
@@ -34566,6 +34596,8 @@ function effectiveEgressChannels(env, live) {
|
|
|
34566
34596
|
}
|
|
34567
34597
|
if (live.updateCheckRequested)
|
|
34568
34598
|
channels.push({ channel: "npm-update-check", destination: "configured npm registry", trigger: "explicit update --check or doctor --check-updates", data: "archctx package version lookup", status: "enabled" });
|
|
34599
|
+
if (live.projectionPublishRequested)
|
|
34600
|
+
channels.push({ channel: "projection-publishing", destination: "configured git remote, plus GitHub via gh or the operator's publisher command", trigger: "explicit archctx projection publish", data: "one commit of generated projection outputs and a pull request title/body listing their paths", status: "enabled" });
|
|
34569
34601
|
if (env[CODEGRAPH_TELEMETRY_ENV] !== undefined && env[CODEGRAPH_TELEMETRY_ENV] !== CODEGRAPH_TELEMETRY_DISABLED_VALUE)
|
|
34570
34602
|
channels.push({ channel: "codegraph-telemetry", destination: "CodeGraph configured telemetry endpoint", trigger: "CodeGraph execution with telemetry enabled", data: "third-party telemetry", status: "enabled" });
|
|
34571
34603
|
return channels.map((channel) => localEgressAdmission2(channel.channel, env).allowed ? channel : { ...channel, status: "blocked-by-policy" });
|
|
@@ -34600,9 +34632,9 @@ function localEgressStatus(env = process.env, live = {}) {
|
|
|
34600
34632
|
}
|
|
34601
34633
|
|
|
34602
34634
|
// packages/surfaces/cli/src/main.ts
|
|
34603
|
-
import { spawn as spawn2, spawnSync as
|
|
34604
|
-
import { accessSync as accessSync3, closeSync as
|
|
34605
|
-
import { dirname as dirname16, join as
|
|
34635
|
+
import { spawn as spawn2, spawnSync as spawnSync6 } from "child_process";
|
|
34636
|
+
import { accessSync as accessSync3, closeSync as closeSync13, constants as constants3, existsSync as existsSync35, mkdirSync as mkdirSync14, openSync as openSync13, readFileSync as readFileSync32, rmSync as rmSync16, statSync as statSync11 } from "fs";
|
|
34637
|
+
import { dirname as dirname16, join as join27, parse, resolve as resolve35 } from "path";
|
|
34606
34638
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
34607
34639
|
|
|
34608
34640
|
// packages/contracts/src/index.ts
|
|
@@ -34617,6 +34649,7 @@ init_ownership_dependencies();
|
|
|
34617
34649
|
init_ports();
|
|
34618
34650
|
init_projection();
|
|
34619
34651
|
init_projection_recovery();
|
|
34652
|
+
init_projection_writer();
|
|
34620
34653
|
init_projection_manifest();
|
|
34621
34654
|
init_refactor();
|
|
34622
34655
|
init_schema();
|
|
@@ -54326,6 +54359,947 @@ function runtimeProjectionOwnedOutputDigest(projection) {
|
|
|
54326
54359
|
files: projection.files.map((file) => ({ path: file.path, body: file.body })).sort((left, right) => left.path.localeCompare(right.path))
|
|
54327
54360
|
});
|
|
54328
54361
|
}
|
|
54362
|
+
// packages/local-runtime/runtime-daemon/src/projection-writer.ts
|
|
54363
|
+
import { execFile as execFile2, spawnSync as spawnSync5 } from "node:child_process";
|
|
54364
|
+
import { createHash as createHash13, randomUUID as randomUUID3 } from "node:crypto";
|
|
54365
|
+
import { closeSync as closeSync9, existsSync as existsSync29, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync5, openSync as openSync10, readFileSync as readFileSync27, readSync as readSync5, realpathSync as realpathSync12, renameSync as renameSync7, rmSync as rmSync15, writeFileSync as writeFileSync11 } from "node:fs";
|
|
54366
|
+
import { tmpdir as tmpdir4 } from "node:os";
|
|
54367
|
+
import { extname as extname2, join as join23, resolve as resolve30 } from "node:path";
|
|
54368
|
+
init_src();
|
|
54369
|
+
var PROJECTION_PUBLISH_REQUEST_ID = "projection.publish";
|
|
54370
|
+
var PROJECTION_PUBLISH_STATUS_REQUEST_ID = "projection.publish-status";
|
|
54371
|
+
var PROJECTION_VERIFY_PR_REQUEST_ID = "projection.verify-pr";
|
|
54372
|
+
var PROJECTION_WRITER_DEFAULT_BASE = "main";
|
|
54373
|
+
var PROJECTION_WRITER_DEFAULT_REMOTE = "origin";
|
|
54374
|
+
var PROJECTION_PR_BODY_MARKER = "<!-- archctx-projection-writer -->";
|
|
54375
|
+
var PROJECTION_WRITER_STATE_SCHEMA_VERSION = "archcontext.projection-writer-state/v1";
|
|
54376
|
+
var PROJECTION_WRITER_MAX_ITERATIONS = 3;
|
|
54377
|
+
var PROJECTION_WRITER_TARGETS = ["agent-context", "architecture-docs"];
|
|
54378
|
+
var PUBLISHER_TIMEOUT_MS = 120000;
|
|
54379
|
+
var PR_BODY_MAX_LISTED_PATHS = 200;
|
|
54380
|
+
|
|
54381
|
+
class ProjectionWriterFailure extends Error {
|
|
54382
|
+
code;
|
|
54383
|
+
reasonCode;
|
|
54384
|
+
constructor(code, reasonCode, message) {
|
|
54385
|
+
super(message);
|
|
54386
|
+
this.code = code;
|
|
54387
|
+
this.reasonCode = reasonCode;
|
|
54388
|
+
this.name = "ProjectionWriterFailure";
|
|
54389
|
+
}
|
|
54390
|
+
}
|
|
54391
|
+
function projectionWriterPaths(repositoryRoot, remote, base, env = process.env) {
|
|
54392
|
+
const key = createHash13("sha256").update(`${remote}\x00${base}`, "utf8").digest("hex").slice(0, 16);
|
|
54393
|
+
const dir = join23(runtimeStatePaths(repositoryRoot, env).repositoryStateDir, "projection-writer", key);
|
|
54394
|
+
return { dir, worktree: join23(dir, "worktree"), state: join23(dir, "state.json"), lock: join23(dir, "lock") };
|
|
54395
|
+
}
|
|
54396
|
+
async function runProjectionPublishCommand(options) {
|
|
54397
|
+
const env = options.env ?? process.env;
|
|
54398
|
+
const now = options.now ?? (() => new Date);
|
|
54399
|
+
const remote = options.remote ?? PROJECTION_WRITER_DEFAULT_REMOTE;
|
|
54400
|
+
const base = options.base ?? PROJECTION_WRITER_DEFAULT_BASE;
|
|
54401
|
+
const branch = options.branch ?? PROJECTION_WRITER_DEFAULT_BRANCH;
|
|
54402
|
+
let repositoryRoot;
|
|
54403
|
+
try {
|
|
54404
|
+
repositoryRoot = findRepositoryRoot(options.cwd);
|
|
54405
|
+
} catch {
|
|
54406
|
+
return errorEnvelope(PROJECTION_PUBLISH_REQUEST_ID, "AC_REPO_NOT_FOUND", `projection publish requires a Git worktree at ${options.cwd}`, "git-worktree-required");
|
|
54407
|
+
}
|
|
54408
|
+
const invalid = projectionWriterTargetIssue(repositoryRoot, { remote, base, branch });
|
|
54409
|
+
if (invalid)
|
|
54410
|
+
return errorEnvelope(PROJECTION_PUBLISH_REQUEST_ID, "AC_SCHEMA_INVALID", invalid, "projection-writer-input-invalid");
|
|
54411
|
+
try {
|
|
54412
|
+
assertLocalEgressAllowed2("projection-publishing", env);
|
|
54413
|
+
} catch (error) {
|
|
54414
|
+
return errorEnvelope(PROJECTION_PUBLISH_REQUEST_ID, "AC_POLICY_VIOLATION", errorMessage2(error), "egress-policy-denied");
|
|
54415
|
+
}
|
|
54416
|
+
const startedAt = now().toISOString();
|
|
54417
|
+
const paths = projectionWriterPaths(repositoryRoot, remote, base, env);
|
|
54418
|
+
const blank = () => ({
|
|
54419
|
+
schemaVersion: PROJECTION_PUBLISH_RUN_SCHEMA_VERSION,
|
|
54420
|
+
result: "failed",
|
|
54421
|
+
remote,
|
|
54422
|
+
base,
|
|
54423
|
+
branch,
|
|
54424
|
+
baseSha: null,
|
|
54425
|
+
publishedCommit: null,
|
|
54426
|
+
pushed: false,
|
|
54427
|
+
pullRequest: null,
|
|
54428
|
+
publisher: options.publisher.kind,
|
|
54429
|
+
files: [],
|
|
54430
|
+
projectionStatus: null,
|
|
54431
|
+
humanActions: [],
|
|
54432
|
+
error: null,
|
|
54433
|
+
startedAt,
|
|
54434
|
+
finishedAt: startedAt,
|
|
54435
|
+
iterations: 0
|
|
54436
|
+
});
|
|
54437
|
+
let lock;
|
|
54438
|
+
try {
|
|
54439
|
+
mkdirSync12(paths.dir, { recursive: true, mode: 448 });
|
|
54440
|
+
lock = acquireProjectionWriterLock(paths.lock, startedAt);
|
|
54441
|
+
} catch (error) {
|
|
54442
|
+
const failure = asWriterFailure(error);
|
|
54443
|
+
return projectionPublishEnvelope({ ...blank(), finishedAt: now().toISOString(), error: writerError(failure) });
|
|
54444
|
+
}
|
|
54445
|
+
if (lock.kind === "busy") {
|
|
54446
|
+
return projectionPublishEnvelope({ ...blank(), result: "busy", holderPid: lock.pid, finishedAt: now().toISOString() });
|
|
54447
|
+
}
|
|
54448
|
+
try {
|
|
54449
|
+
let state;
|
|
54450
|
+
try {
|
|
54451
|
+
state = readProjectionWriterState(paths.state, remote, base);
|
|
54452
|
+
} catch (error) {
|
|
54453
|
+
return projectionPublishEnvelope({ ...blank(), finishedAt: now().toISOString(), error: writerError(asWriterFailure(error)) });
|
|
54454
|
+
}
|
|
54455
|
+
const outcome = await runLockedProjectionPublish({ options, env, now, repositoryRoot, paths, remote, base, branch, state, assertOwner: () => assertProjectionWriterOwner(paths.lock, lock.record), blank: blank() });
|
|
54456
|
+
const next = nextProjectionWriterState(state, outcome);
|
|
54457
|
+
try {
|
|
54458
|
+
writeProjectionWriterState(paths.state, next);
|
|
54459
|
+
} catch (error) {
|
|
54460
|
+
return projectionPublishEnvelope({
|
|
54461
|
+
...outcome.run,
|
|
54462
|
+
result: "failed",
|
|
54463
|
+
error: writerError(new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-state-unwritable", errorMessage2(error)))
|
|
54464
|
+
});
|
|
54465
|
+
}
|
|
54466
|
+
return projectionPublishEnvelope(outcome.run);
|
|
54467
|
+
} finally {
|
|
54468
|
+
lock.release();
|
|
54469
|
+
}
|
|
54470
|
+
}
|
|
54471
|
+
async function runLockedProjectionPublish(input) {
|
|
54472
|
+
const { options, env, now, repositoryRoot, paths, remote, base, branch, state } = input;
|
|
54473
|
+
const runSequence = state.runSequence + 1;
|
|
54474
|
+
const publisherIdentity = options.publisher.identity ?? options.publisher.kind;
|
|
54475
|
+
let iterations = 0;
|
|
54476
|
+
let baseSha = null;
|
|
54477
|
+
const finish = (result, fields = {}, extra = {}) => ({
|
|
54478
|
+
run: { ...input.blank, ...fields, result, baseSha, iterations, finishedAt: now().toISOString() },
|
|
54479
|
+
runSequence,
|
|
54480
|
+
publisherIdentity,
|
|
54481
|
+
...extra
|
|
54482
|
+
});
|
|
54483
|
+
const failed = (failure, fields = {}, extra = {}) => finish("failed", { ...fields, error: writerError(failure) }, extra);
|
|
54484
|
+
try {
|
|
54485
|
+
const pushRemote = projectionWriterPushUrl(repositoryRoot, remote);
|
|
54486
|
+
while (iterations < PROJECTION_WRITER_MAX_ITERATIONS) {
|
|
54487
|
+
iterations += 1;
|
|
54488
|
+
remoteGit(repositoryRoot, ["fetch", "--no-tags", remote, `+refs/heads/${base}:refs/remotes/${remote}/${base}`], env);
|
|
54489
|
+
baseSha = git(repositoryRoot, ["rev-parse", "--verify", `refs/remotes/${remote}/${base}^{commit}`]).trim();
|
|
54490
|
+
const successMatches = state.lastSuccessfulBranch === branch && state.lastSuccessfulSha === baseSha;
|
|
54491
|
+
const pending = state.publisherPending?.branch === branch && state.publisherPending.baseSha === baseSha ? state.publisherPending : successMatches && state.lastSuccessfulCommit && state.lastSuccessfulPublisher !== publisherIdentity ? { branch, baseSha, commitSha: state.lastSuccessfulCommit, files: state.lastSuccessfulFiles } : null;
|
|
54492
|
+
if (!pending && successMatches && state.lastSuccessfulPublisher === publisherIdentity && (state.lastSuccessfulCommit === null || lsRemoteBranch(repositoryRoot, pushRemote, branch, env) === state.lastSuccessfulCommit)) {
|
|
54493
|
+
return finish("already-processed", { publishedCommit: state.lastSuccessfulCommit });
|
|
54494
|
+
}
|
|
54495
|
+
input.assertOwner();
|
|
54496
|
+
prepareProjectionWriterWorktree(repositoryRoot, paths.worktree, baseSha);
|
|
54497
|
+
if (pending && lsRemoteBranch(repositoryRoot, pushRemote, branch, env) === pending.commitSha) {
|
|
54498
|
+
input.assertOwner();
|
|
54499
|
+
const published = await publishPullRequest(options.publisher, { worktree: paths.worktree, remote, base, branch, baseSha, commitSha: pending.commitSha, files: pending.files });
|
|
54500
|
+
if (!published.ok) {
|
|
54501
|
+
return failed(published.failure, { publishedCommit: pending.commitSha, files: pending.files }, { publisherPending: pending });
|
|
54502
|
+
}
|
|
54503
|
+
return finish("published", { publishedCommit: pending.commitSha, files: pending.files, pullRequest: published.pullRequest });
|
|
54504
|
+
}
|
|
54505
|
+
const ownedPatterns = projectionOwnedPaths(loadNativeModelFromArchContext(paths.worktree));
|
|
54506
|
+
const snapshot = runProjectionSnapshotCommand(paths.worktree);
|
|
54507
|
+
if (!snapshot.ok)
|
|
54508
|
+
throw envelopeFailure(snapshot, "projection-writer-snapshot-failed");
|
|
54509
|
+
const expected = snapshot.data.expected;
|
|
54510
|
+
const request = {
|
|
54511
|
+
schemaVersion: PROJECTION_REQUEST_SCHEMA_VERSION,
|
|
54512
|
+
requestId: `projection_publish.${baseSha.slice(0, 12)}.${runSequence}-${iterations}`,
|
|
54513
|
+
profile: "repo-harness/v1",
|
|
54514
|
+
mode: "apply",
|
|
54515
|
+
targets: [...PROJECTION_WRITER_TARGETS],
|
|
54516
|
+
changedPaths: [],
|
|
54517
|
+
expected,
|
|
54518
|
+
...options.acceptObservedMajorChange ? { acceptObservedMajorChange: true } : {}
|
|
54519
|
+
};
|
|
54520
|
+
const applied = await options.apply(paths.worktree, request);
|
|
54521
|
+
input.assertOwner();
|
|
54522
|
+
if (!applied.ok) {
|
|
54523
|
+
const failure = envelopeFailure(applied, "projection-apply-failed");
|
|
54524
|
+
if (failure.code === "AC_CODE_FACTS_UNAVAILABLE") {
|
|
54525
|
+
throw new ProjectionWriterFailure(failure.code, failure.reasonCode, `${failure.message}; the writer never initializes CodeGraph itself: run \`codegraph init ${paths.worktree}\` once, then publish again`);
|
|
54526
|
+
}
|
|
54527
|
+
throw failure;
|
|
54528
|
+
}
|
|
54529
|
+
const result = applied.data;
|
|
54530
|
+
const projectionStatus = result.status;
|
|
54531
|
+
if (projectionStatus === "human-action-required" || projectionStatus === "adoption-required") {
|
|
54532
|
+
return finish("human-action-required", { projectionStatus, humanActions: publishHumanActions(result) });
|
|
54533
|
+
}
|
|
54534
|
+
if (!["noop", "applied", "applied-partial"].includes(projectionStatus)) {
|
|
54535
|
+
throw new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-apply-status-unsupported", `projection apply ended with status ${projectionStatus}; the writer publishes only noop, applied and applied-partial results`);
|
|
54536
|
+
}
|
|
54537
|
+
const changed = worktreeChangedPaths(paths.worktree);
|
|
54538
|
+
if (changed.length === 0)
|
|
54539
|
+
return finish("up-to-date", { projectionStatus });
|
|
54540
|
+
const written = new Set(result.files.filter((file) => file.action !== "unchanged").map((file) => file.path));
|
|
54541
|
+
const offending = changed.filter((path) => !written.has(path) || !isProjectionOwnedPath(path, ownedPatterns));
|
|
54542
|
+
if (offending.length > 0) {
|
|
54543
|
+
return failed(new ProjectionWriterFailure("AC_POLICY_VIOLATION", "projection-writer-non-owned-change", `projection apply changed paths the writer may not publish: ${offending.join(", ")}`), { projectionStatus });
|
|
54544
|
+
}
|
|
54545
|
+
input.assertOwner();
|
|
54546
|
+
git(paths.worktree, ["add", "-A", "--", ...changed]);
|
|
54547
|
+
const title = projectionPublishTitle(base, baseSha);
|
|
54548
|
+
try {
|
|
54549
|
+
git(paths.worktree, ["commit", "--no-verify", "-q", "-m", title, "-m", `Archctx-Projection-Source: ${baseSha}`]);
|
|
54550
|
+
} catch (error) {
|
|
54551
|
+
return failed(new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-commit-failed", `git commit failed in the writer worktree; configure a git identity (user.name, user.email) for the repository or set GIT_AUTHOR_*/GIT_COMMITTER_*: ${errorMessage2(error)}`), { projectionStatus, files: changed });
|
|
54552
|
+
}
|
|
54553
|
+
const commitSha = git(paths.worktree, ["rev-parse", "HEAD"]).trim();
|
|
54554
|
+
const tip = lsRemoteBranch(repositoryRoot, remote, base, env);
|
|
54555
|
+
if (tip === null) {
|
|
54556
|
+
return failed(new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-base-missing", `${remote} has no branch ${base}`), { projectionStatus, files: changed });
|
|
54557
|
+
}
|
|
54558
|
+
if (tip !== baseSha)
|
|
54559
|
+
continue;
|
|
54560
|
+
const lease = lsRemoteBranch(repositoryRoot, pushRemote, branch, env);
|
|
54561
|
+
let publishedCommit = commitSha;
|
|
54562
|
+
let pushed = false;
|
|
54563
|
+
if (lease !== null && carriesSameProjection(paths.worktree, lease, commitSha, baseSha)) {
|
|
54564
|
+
publishedCommit = lease;
|
|
54565
|
+
} else {
|
|
54566
|
+
input.assertOwner();
|
|
54567
|
+
remoteGit(paths.worktree, ["push", "--no-verify", `--force-with-lease=refs/heads/${branch}:${lease ?? ""}`, "--", pushRemote, `${commitSha}:refs/heads/${branch}`], env);
|
|
54568
|
+
pushed = true;
|
|
54569
|
+
}
|
|
54570
|
+
input.assertOwner();
|
|
54571
|
+
const published = await publishPullRequest(options.publisher, { worktree: paths.worktree, remote, base, branch, baseSha, commitSha: publishedCommit, files: changed });
|
|
54572
|
+
if (!published.ok) {
|
|
54573
|
+
const pendingPublish = { baseSha, branch, commitSha: publishedCommit, files: changed };
|
|
54574
|
+
return failed(published.failure, { projectionStatus, files: changed, publishedCommit, pushed }, { publisherPending: pendingPublish });
|
|
54575
|
+
}
|
|
54576
|
+
return finish("published", { projectionStatus, files: changed, publishedCommit, pushed, pullRequest: published.pullRequest });
|
|
54577
|
+
}
|
|
54578
|
+
return failed(new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-base-unstable", `${remote}/${base} moved during each of ${PROJECTION_WRITER_MAX_ITERATIONS} attempts; nothing was pushed`));
|
|
54579
|
+
} catch (error) {
|
|
54580
|
+
return failed(asWriterFailure(error));
|
|
54581
|
+
}
|
|
54582
|
+
}
|
|
54583
|
+
function nextProjectionWriterState(state, outcome) {
|
|
54584
|
+
const run = outcome.run;
|
|
54585
|
+
const next = { ...state, branch: run.branch, runSequence: outcome.runSequence, lastRun: run };
|
|
54586
|
+
if (run.result === "already-processed")
|
|
54587
|
+
return next;
|
|
54588
|
+
if (run.baseSha !== null)
|
|
54589
|
+
next.lastProcessedSha = run.baseSha;
|
|
54590
|
+
if (run.result === "published" || run.result === "up-to-date") {
|
|
54591
|
+
next.lastSuccessfulSha = run.baseSha;
|
|
54592
|
+
next.lastSuccessfulBranch = run.branch;
|
|
54593
|
+
next.lastSuccessfulPublisher = outcome.publisherIdentity;
|
|
54594
|
+
next.lastSuccessfulFiles = run.files;
|
|
54595
|
+
next.lastSuccessfulCommit = run.result === "published" ? run.publishedCommit : null;
|
|
54596
|
+
next.publisherPending = null;
|
|
54597
|
+
} else if (outcome.publisherPending) {
|
|
54598
|
+
next.publisherPending = outcome.publisherPending;
|
|
54599
|
+
} else if (run.result === "human-action-required") {
|
|
54600
|
+
next.publisherPending = null;
|
|
54601
|
+
}
|
|
54602
|
+
return next;
|
|
54603
|
+
}
|
|
54604
|
+
function projectionPublishEnvelope(run) {
|
|
54605
|
+
if (run.result !== "failed")
|
|
54606
|
+
return okEnvelope(PROJECTION_PUBLISH_REQUEST_ID, run);
|
|
54607
|
+
const error = run.error ?? { code: "AC_UNCLASSIFIED_FAILURE", message: "projection publish failed" };
|
|
54608
|
+
return {
|
|
54609
|
+
...errorEnvelope(PROJECTION_PUBLISH_REQUEST_ID, error.code, error.message, error.reasonCode),
|
|
54610
|
+
data: run
|
|
54611
|
+
};
|
|
54612
|
+
}
|
|
54613
|
+
function publishHumanActions(result) {
|
|
54614
|
+
return result.humanActions.map((action) => ({ reasonCode: action.reasonCode, ...action.path ? { path: action.path } : {} }));
|
|
54615
|
+
}
|
|
54616
|
+
function projectionPublishTitle(base, baseSha) {
|
|
54617
|
+
return `chore(projection): refresh generated architecture outputs for ${base}@${baseSha.slice(0, 12)}`;
|
|
54618
|
+
}
|
|
54619
|
+
function projectionPublishBody(input) {
|
|
54620
|
+
const listed = input.files.slice(0, PR_BODY_MAX_LISTED_PATHS).map((path) => `- \`${path}\``);
|
|
54621
|
+
if (input.files.length > PR_BODY_MAX_LISTED_PATHS)
|
|
54622
|
+
listed.push(`- … and ${input.files.length - PR_BODY_MAX_LISTED_PATHS} more`);
|
|
54623
|
+
return [
|
|
54624
|
+
PROJECTION_PR_BODY_MARKER,
|
|
54625
|
+
"",
|
|
54626
|
+
`Base: \`${input.base}\` at \`${input.baseSha}\``,
|
|
54627
|
+
`Commit: \`${input.commitSha}\``,
|
|
54628
|
+
"",
|
|
54629
|
+
"Changed generated paths:",
|
|
54630
|
+
"",
|
|
54631
|
+
...listed,
|
|
54632
|
+
"",
|
|
54633
|
+
`Generated by \`archctx projection publish\`; touches generated paths only. Verify with \`archctx projection verify-pr --base ${input.base}\`.`,
|
|
54634
|
+
""
|
|
54635
|
+
].join(`
|
|
54636
|
+
`);
|
|
54637
|
+
}
|
|
54638
|
+
async function publishPullRequest(publisher, input) {
|
|
54639
|
+
if (publisher.kind === "none")
|
|
54640
|
+
return { ok: true, pullRequest: null };
|
|
54641
|
+
const dir = mkdtempSync5(join23(tmpdir4(), "archctx-projection-pr-body-"));
|
|
54642
|
+
try {
|
|
54643
|
+
const bodyFile = join23(dir, "body.md");
|
|
54644
|
+
writeFileSync11(bodyFile, projectionPublishBody(input), { mode: 384 });
|
|
54645
|
+
const pullRequest = await publisher.ensurePullRequest({
|
|
54646
|
+
worktree: input.worktree,
|
|
54647
|
+
remote: input.remote,
|
|
54648
|
+
base: input.base,
|
|
54649
|
+
branch: input.branch,
|
|
54650
|
+
baseSha: input.baseSha,
|
|
54651
|
+
commitSha: input.commitSha,
|
|
54652
|
+
title: projectionPublishTitle(input.base, input.baseSha),
|
|
54653
|
+
bodyFile
|
|
54654
|
+
});
|
|
54655
|
+
return { ok: true, pullRequest: { ...pullRequest.url ? { url: pullRequest.url } : {}, ...pullRequest.number !== undefined ? { number: pullRequest.number } : {} } };
|
|
54656
|
+
} catch (error) {
|
|
54657
|
+
if (error instanceof LocalEgressPolicyError2)
|
|
54658
|
+
return { ok: false, failure: new ProjectionWriterFailure("AC_POLICY_VIOLATION", "egress-policy-denied", error.message) };
|
|
54659
|
+
return { ok: false, failure: new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-publisher-failed", `${publisher.kind} publisher failed: ${errorMessage2(error)}`) };
|
|
54660
|
+
} finally {
|
|
54661
|
+
rmSync15(dir, { recursive: true, force: true });
|
|
54662
|
+
}
|
|
54663
|
+
}
|
|
54664
|
+
function runProjectionPublishStatusCommand(options) {
|
|
54665
|
+
const env = options.env ?? process.env;
|
|
54666
|
+
const remote = options.remote ?? PROJECTION_WRITER_DEFAULT_REMOTE;
|
|
54667
|
+
const base = options.base ?? PROJECTION_WRITER_DEFAULT_BASE;
|
|
54668
|
+
let repositoryRoot;
|
|
54669
|
+
try {
|
|
54670
|
+
repositoryRoot = findRepositoryRoot(options.cwd);
|
|
54671
|
+
} catch {
|
|
54672
|
+
return errorEnvelope(PROJECTION_PUBLISH_STATUS_REQUEST_ID, "AC_REPO_NOT_FOUND", `projection publish-status requires a Git worktree at ${options.cwd}`, "git-worktree-required");
|
|
54673
|
+
}
|
|
54674
|
+
const invalid = refNameIssue(repositoryRoot, "--base", base) ?? remoteNameIssue(remote);
|
|
54675
|
+
if (invalid)
|
|
54676
|
+
return errorEnvelope(PROJECTION_PUBLISH_STATUS_REQUEST_ID, "AC_SCHEMA_INVALID", invalid, "projection-writer-input-invalid");
|
|
54677
|
+
try {
|
|
54678
|
+
const paths = projectionWriterPaths(repositoryRoot, remote, base, env);
|
|
54679
|
+
const state = readProjectionWriterState(paths.state, remote, base);
|
|
54680
|
+
const holder = readProjectionWriterLock(paths.lock);
|
|
54681
|
+
const status = {
|
|
54682
|
+
schemaVersion: PROJECTION_PUBLISH_STATUS_SCHEMA_VERSION,
|
|
54683
|
+
remote,
|
|
54684
|
+
base,
|
|
54685
|
+
branch: state.branch,
|
|
54686
|
+
worktreePath: paths.worktree,
|
|
54687
|
+
running: holder && isProcessAlive(holder.pid) ? holder : null,
|
|
54688
|
+
lastProcessedSha: state.lastProcessedSha,
|
|
54689
|
+
lastSuccessfulSha: state.lastSuccessfulSha,
|
|
54690
|
+
lastRun: state.lastRun
|
|
54691
|
+
};
|
|
54692
|
+
return okEnvelope(PROJECTION_PUBLISH_STATUS_REQUEST_ID, status);
|
|
54693
|
+
} catch (error) {
|
|
54694
|
+
if (error instanceof ProjectionWriterFailure)
|
|
54695
|
+
return errorEnvelope(PROJECTION_PUBLISH_STATUS_REQUEST_ID, error.code, error.message, error.reasonCode);
|
|
54696
|
+
return publicOperationErrorEnvelope(PROJECTION_PUBLISH_STATUS_REQUEST_ID, error);
|
|
54697
|
+
}
|
|
54698
|
+
}
|
|
54699
|
+
async function runProjectionPublishFollow(options) {
|
|
54700
|
+
let runs = 0;
|
|
54701
|
+
while (!options.shouldStop()) {
|
|
54702
|
+
let envelope;
|
|
54703
|
+
try {
|
|
54704
|
+
envelope = await options.runOnce();
|
|
54705
|
+
} catch (error) {
|
|
54706
|
+
envelope = publicOperationErrorEnvelope(PROJECTION_PUBLISH_REQUEST_ID, error);
|
|
54707
|
+
}
|
|
54708
|
+
runs += 1;
|
|
54709
|
+
options.onResult(envelope);
|
|
54710
|
+
if (options.shouldStop())
|
|
54711
|
+
break;
|
|
54712
|
+
await options.sleep(options.intervalMs);
|
|
54713
|
+
}
|
|
54714
|
+
return runs;
|
|
54715
|
+
}
|
|
54716
|
+
function runProjectionVerifyPrCommand(options) {
|
|
54717
|
+
const requestId = PROJECTION_VERIFY_PR_REQUEST_ID;
|
|
54718
|
+
let root;
|
|
54719
|
+
try {
|
|
54720
|
+
root = findRepositoryRoot(options.cwd);
|
|
54721
|
+
} catch {
|
|
54722
|
+
return errorEnvelope(requestId, "AC_REPO_NOT_FOUND", `projection verify-pr requires a Git worktree at ${options.cwd}`, "git-worktree-required");
|
|
54723
|
+
}
|
|
54724
|
+
if (!isGitRevisionArgument(options.base))
|
|
54725
|
+
return errorEnvelope(requestId, "AC_SCHEMA_INVALID", `projection verify-pr --base is not a usable revision: ${JSON.stringify(options.base)}`, "projection-verify-pr-input-invalid");
|
|
54726
|
+
try {
|
|
54727
|
+
if (!isTrackedWorktreeClean(root)) {
|
|
54728
|
+
return errorEnvelope(requestId, "AC_PRECONDITION_FAILED", "projection verify-pr requires a clean tracked worktree; HEAD is the pull request head", "projection-verify-pr-worktree-dirty");
|
|
54729
|
+
}
|
|
54730
|
+
let baseSha;
|
|
54731
|
+
let mergeBase;
|
|
54732
|
+
const headSha = git(root, ["rev-parse", "--verify", "HEAD^{commit}"]).trim();
|
|
54733
|
+
try {
|
|
54734
|
+
baseSha = git(root, ["rev-parse", "--verify", `${options.base}^{commit}`]).trim();
|
|
54735
|
+
mergeBase = git(root, ["merge-base", baseSha, headSha]).trim();
|
|
54736
|
+
} catch (error) {
|
|
54737
|
+
return errorEnvelope(requestId, "AC_PRECONDITION_FAILED", `projection verify-pr cannot resolve ${options.base} against HEAD: ${errorMessage2(error)}`, "projection-verify-pr-base-unresolved");
|
|
54738
|
+
}
|
|
54739
|
+
const changedPaths = [...new Set(git(root, ["diff", "--name-only", "-z", "--no-renames", `${mergeBase}..${headSha}`, "--"]).split("\x00").filter(Boolean))].sort();
|
|
54740
|
+
const verification = (fields) => okEnvelope(requestId, {
|
|
54741
|
+
schemaVersion: PROJECTION_PR_VERIFICATION_SCHEMA_VERSION,
|
|
54742
|
+
base: options.base,
|
|
54743
|
+
baseSha,
|
|
54744
|
+
headSha,
|
|
54745
|
+
mergeBase,
|
|
54746
|
+
changedPaths,
|
|
54747
|
+
nonGeneratedPaths: [],
|
|
54748
|
+
humanEditedPaths: [],
|
|
54749
|
+
generatedOnly: false,
|
|
54750
|
+
renderEquivalent: null,
|
|
54751
|
+
differingPaths: [],
|
|
54752
|
+
projectionStatus: null,
|
|
54753
|
+
...fields
|
|
54754
|
+
});
|
|
54755
|
+
if (changedPaths.length === 0)
|
|
54756
|
+
return verification({ verdict: "no-changes" });
|
|
54757
|
+
const classified = classifyPullRequestPaths(root, mergeBase, headSha, changedPaths);
|
|
54758
|
+
if (classified.nonGeneratedPaths.length > 0) {
|
|
54759
|
+
return verification({ verdict: "touches-non-generated", nonGeneratedPaths: classified.nonGeneratedPaths, humanEditedPaths: classified.humanEditedPaths });
|
|
54760
|
+
}
|
|
54761
|
+
const snapshot = runProjectionSnapshotCommand(root);
|
|
54762
|
+
if (!snapshot.ok)
|
|
54763
|
+
return { ...snapshot, requestId };
|
|
54764
|
+
const request = {
|
|
54765
|
+
schemaVersion: PROJECTION_REQUEST_SCHEMA_VERSION,
|
|
54766
|
+
requestId: `projection_verify_pr.${headSha.slice(0, 12)}`,
|
|
54767
|
+
profile: "repo-harness/v1",
|
|
54768
|
+
mode: "check",
|
|
54769
|
+
targets: [...PROJECTION_WRITER_TARGETS],
|
|
54770
|
+
changedPaths: [],
|
|
54771
|
+
expected: snapshot.data.expected
|
|
54772
|
+
};
|
|
54773
|
+
const checked = (options.check ?? runProjectionCheckInvocation)(request, root);
|
|
54774
|
+
if (!checked.ok)
|
|
54775
|
+
return { ...checked, requestId };
|
|
54776
|
+
const result = checked.data;
|
|
54777
|
+
let agentContextDiffering;
|
|
54778
|
+
try {
|
|
54779
|
+
agentContextDiffering = agentContextProjectionDifferingPaths(root, classified.agentContextPaths);
|
|
54780
|
+
} catch {
|
|
54781
|
+
agentContextDiffering = [...classified.agentContextPaths];
|
|
54782
|
+
}
|
|
54783
|
+
const differingPaths = [...new Set([
|
|
54784
|
+
...result.files.filter((file) => file.action !== "unchanged").map((file) => file.path),
|
|
54785
|
+
...agentContextDiffering
|
|
54786
|
+
])].sort();
|
|
54787
|
+
const renderEquivalent = result.status === "noop" && agentContextDiffering.length === 0;
|
|
54788
|
+
return verification({
|
|
54789
|
+
verdict: renderEquivalent ? "generated-only-render-equivalent" : "generated-only-not-render-equivalent",
|
|
54790
|
+
generatedOnly: true,
|
|
54791
|
+
renderEquivalent,
|
|
54792
|
+
differingPaths,
|
|
54793
|
+
projectionStatus: result.status
|
|
54794
|
+
});
|
|
54795
|
+
} catch (error) {
|
|
54796
|
+
return publicOperationErrorEnvelope(requestId, error);
|
|
54797
|
+
}
|
|
54798
|
+
}
|
|
54799
|
+
function classifyPullRequestPaths(root, mergeBase, headSha, changedPaths) {
|
|
54800
|
+
const baseTargets = projectionTargetsAtRevision(root, mergeBase);
|
|
54801
|
+
const headTargets = projectionTargetsAtRevision(root, headSha);
|
|
54802
|
+
const nonGenerated = [];
|
|
54803
|
+
const humanEdited = [];
|
|
54804
|
+
const agentContext = [];
|
|
54805
|
+
for (const path of changedPaths) {
|
|
54806
|
+
if (path === ARCHITECTURE_DOCS_PROJECTION_MANIFEST_PATH)
|
|
54807
|
+
continue;
|
|
54808
|
+
const atBase = baseTargets.get(path);
|
|
54809
|
+
const atHead = headTargets.get(path);
|
|
54810
|
+
if (!atBase && !atHead) {
|
|
54811
|
+
nonGenerated.push(path);
|
|
54812
|
+
continue;
|
|
54813
|
+
}
|
|
54814
|
+
const before = readFileAtRevision(root, mergeBase, path);
|
|
54815
|
+
const after = readFileAtRevision(root, headSha, path);
|
|
54816
|
+
if (!atHead && after !== null) {
|
|
54817
|
+
nonGenerated.push(path);
|
|
54818
|
+
continue;
|
|
54819
|
+
}
|
|
54820
|
+
const target = {
|
|
54821
|
+
whole: (atBase?.whole ?? false) || (atHead?.whole ?? false),
|
|
54822
|
+
mixedDocument: (atBase?.mixedDocument ?? false) || (atHead?.mixedDocument ?? false),
|
|
54823
|
+
agentNodeIds: [...new Set([...atBase?.agentNodeIds ?? [], ...atHead?.agentNodeIds ?? []])].sort()
|
|
54824
|
+
};
|
|
54825
|
+
if (!target.mixedDocument && target.agentNodeIds.length === 0)
|
|
54826
|
+
continue;
|
|
54827
|
+
if (!mixedTargetTextUnchanged(before, after, target)) {
|
|
54828
|
+
humanEdited.push(path);
|
|
54829
|
+
nonGenerated.push(path);
|
|
54830
|
+
} else if (target.agentNodeIds.length > 0) {
|
|
54831
|
+
agentContext.push(path);
|
|
54832
|
+
}
|
|
54833
|
+
}
|
|
54834
|
+
return { nonGeneratedPaths: nonGenerated.sort(), humanEditedPaths: humanEdited.sort(), agentContextPaths: agentContext.sort() };
|
|
54835
|
+
}
|
|
54836
|
+
function projectionTargetsAtRevision(root, revision) {
|
|
54837
|
+
const targets = new Map;
|
|
54838
|
+
const add = (path, ownership) => {
|
|
54839
|
+
const known = targets.get(path) ?? { whole: false, mixedDocument: false, agentNodeIds: [] };
|
|
54840
|
+
targets.set(path, {
|
|
54841
|
+
whole: known.whole || ownership.whole === true,
|
|
54842
|
+
mixedDocument: known.mixedDocument || ownership.mixedDocument === true,
|
|
54843
|
+
agentNodeIds: [...known.agentNodeIds, ...ownership.agentNodeIds ?? []]
|
|
54844
|
+
});
|
|
54845
|
+
};
|
|
54846
|
+
try {
|
|
54847
|
+
const manifest = readFileAtRevision(root, revision, ARCHITECTURE_DOCS_PROJECTION_MANIFEST_PATH);
|
|
54848
|
+
const rows = manifest === null ? {} : JSON.parse(manifest).targets;
|
|
54849
|
+
if (rows && typeof rows === "object" && !Array.isArray(rows)) {
|
|
54850
|
+
for (const row of Object.values(rows)) {
|
|
54851
|
+
if (!row || typeof row.path !== "string")
|
|
54852
|
+
continue;
|
|
54853
|
+
add(row.path, row.ownership === "generated" ? { whole: true } : { mixedDocument: true });
|
|
54854
|
+
}
|
|
54855
|
+
}
|
|
54856
|
+
} catch {}
|
|
54857
|
+
try {
|
|
54858
|
+
const listed = git(root, ["ls-tree", "-r", "-z", "--name-only", revision, "--", ".archcontext/model"]).split("\x00").filter(Boolean);
|
|
54859
|
+
const files = new Map(listed.map((path) => [path, readFileAtRevision(root, revision, path) ?? ""]));
|
|
54860
|
+
for (const target of agentContextProjectionTargetPaths(loadNativeModelFromModelFiles(files)))
|
|
54861
|
+
add(target.path, { agentNodeIds: [target.nodeId] });
|
|
54862
|
+
} catch {}
|
|
54863
|
+
return targets;
|
|
54864
|
+
}
|
|
54865
|
+
function mixedTargetTextUnchanged(before, after, target) {
|
|
54866
|
+
if (before === null || after === null)
|
|
54867
|
+
return false;
|
|
54868
|
+
try {
|
|
54869
|
+
return JSON.stringify(textOutsideGeneratedRegions(before, target)) === JSON.stringify(textOutsideGeneratedRegions(after, target));
|
|
54870
|
+
} catch {
|
|
54871
|
+
return false;
|
|
54872
|
+
}
|
|
54873
|
+
}
|
|
54874
|
+
function textOutsideGeneratedRegions(body, target) {
|
|
54875
|
+
const text = target.agentNodeIds.length > 0 ? agentContextTextOutsideRegions(body, target.agentNodeIds) : body;
|
|
54876
|
+
if (!target.mixedDocument)
|
|
54877
|
+
return [text];
|
|
54878
|
+
const scan = scanDocumentRegions(text);
|
|
54879
|
+
if (scan.diagnostics.length > 0 || scan.generatedRegions.some((region) => !region.closed))
|
|
54880
|
+
throw new Error("projection-generated-regions-unbalanced");
|
|
54881
|
+
return scan.humanRegions.map((region) => region.text);
|
|
54882
|
+
}
|
|
54883
|
+
function readFileAtRevision(root, revision, path) {
|
|
54884
|
+
if (!gitSucceeds2(root, ["cat-file", "-e", `${revision}:${path}`]))
|
|
54885
|
+
return null;
|
|
54886
|
+
return git(root, ["cat-file", "blob", `${revision}:${path}`]);
|
|
54887
|
+
}
|
|
54888
|
+
function createNoneProjectionPublisher() {
|
|
54889
|
+
return { kind: "none", ensurePullRequest: async () => ({}) };
|
|
54890
|
+
}
|
|
54891
|
+
function createGhProjectionPublisher(options = {}) {
|
|
54892
|
+
const exec = options.exec ?? nodeProjectionPublisherExec;
|
|
54893
|
+
const admissionEnv = options.env ?? process.env;
|
|
54894
|
+
const timeoutMs = options.timeoutMs ?? PUBLISHER_TIMEOUT_MS;
|
|
54895
|
+
return {
|
|
54896
|
+
kind: "gh",
|
|
54897
|
+
async ensurePullRequest(input) {
|
|
54898
|
+
assertLocalEgressAllowed2("projection-publishing", admissionEnv);
|
|
54899
|
+
const repository = projectionPublisherRepository(input.worktree, input.remote);
|
|
54900
|
+
const env = { ...stringEnv(process.env), GH_PROMPT_DISABLED: "1" };
|
|
54901
|
+
delete env.GH_REPO;
|
|
54902
|
+
const gh = (args) => withLocalEgress2("projection-publishing", () => exec("gh", [...args, "--repo", repository], { cwd: input.worktree, env, timeoutMs }), admissionEnv);
|
|
54903
|
+
const listed = await gh(["pr", "list", "--head", input.branch, "--base", input.base, "--state", "open", "--json", "number,url", "--limit", "1"]);
|
|
54904
|
+
const open = parseGhPullRequestList(listed.stdout);
|
|
54905
|
+
if (open) {
|
|
54906
|
+
await gh(["pr", "edit", String(open.number), "--title", input.title, "--body-file", input.bodyFile]);
|
|
54907
|
+
return { number: open.number, ...open.url ? { url: open.url } : {} };
|
|
54908
|
+
}
|
|
54909
|
+
const created = await gh(["pr", "create", "--base", input.base, "--head", input.branch, "--title", input.title, "--body-file", input.bodyFile]);
|
|
54910
|
+
const url = created.stdout.trim().split(`
|
|
54911
|
+
`).map((line) => line.trim()).filter(Boolean).pop() ?? "";
|
|
54912
|
+
const number = /\/pull\/(\d+)(?:[/?#]|$)/.exec(url)?.[1];
|
|
54913
|
+
return { ...url ? { url } : {}, ...number ? { number: Number(number) } : {} };
|
|
54914
|
+
}
|
|
54915
|
+
};
|
|
54916
|
+
}
|
|
54917
|
+
function projectionWriterPushUrl(worktree, remote) {
|
|
54918
|
+
const urls = git(worktree, ["remote", "get-url", "--push", "--all", "--", remote]).trim().split(`
|
|
54919
|
+
`).filter(Boolean);
|
|
54920
|
+
if (urls.length !== 1) {
|
|
54921
|
+
throw new ProjectionWriterFailure("AC_SCHEMA_INVALID", "projection-writer-push-target-ambiguous", "projection publish requires exactly one push URL for the selected remote");
|
|
54922
|
+
}
|
|
54923
|
+
return urls[0];
|
|
54924
|
+
}
|
|
54925
|
+
function projectionPublisherRepository(worktree, remote) {
|
|
54926
|
+
const source = projectionWriterPushUrl(worktree, remote);
|
|
54927
|
+
const scp = /^[^/@:]+@([A-Za-z0-9.-]+):([^?#]+)$/.exec(source);
|
|
54928
|
+
let host;
|
|
54929
|
+
let path;
|
|
54930
|
+
if (scp) {
|
|
54931
|
+
host = scp[1];
|
|
54932
|
+
path = scp[2];
|
|
54933
|
+
} else {
|
|
54934
|
+
const url = new URL(source);
|
|
54935
|
+
if (!["https:", "ssh:"].includes(url.protocol) || url.search || url.hash || url.password || url.protocol === "https:" && url.username) {
|
|
54936
|
+
throw new Error("the selected remote must be an SSH or HTTPS GitHub repository URL");
|
|
54937
|
+
}
|
|
54938
|
+
host = url.protocol === "ssh:" ? url.hostname : url.host;
|
|
54939
|
+
path = url.pathname.replace(/^\//, "");
|
|
54940
|
+
}
|
|
54941
|
+
path = path.replace(/\.git$/, "");
|
|
54942
|
+
if (!/^[A-Za-z0-9.-]+(?::[0-9]+)?$/.test(host) || !/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(path) || path.split("/").some((part) => part === "." || part === "..")) {
|
|
54943
|
+
throw new Error("the selected remote must identify one GitHub host, owner, and repository");
|
|
54944
|
+
}
|
|
54945
|
+
return `${host.toLowerCase()}/${path}`;
|
|
54946
|
+
}
|
|
54947
|
+
function createCommandProjectionPublisher(options) {
|
|
54948
|
+
const exec = options.exec ?? nodeProjectionPublisherExec;
|
|
54949
|
+
const admissionEnv = options.env ?? process.env;
|
|
54950
|
+
const sourceEnv = options.sourceEnv ?? process.env;
|
|
54951
|
+
const platform = options.platform ?? process.platform;
|
|
54952
|
+
const timeoutMs = options.timeoutMs ?? PUBLISHER_TIMEOUT_MS;
|
|
54953
|
+
return {
|
|
54954
|
+
kind: "command",
|
|
54955
|
+
identity: `command:${resolve30(options.command)}`,
|
|
54956
|
+
async ensurePullRequest(input) {
|
|
54957
|
+
const env = {
|
|
54958
|
+
...commandPublisherBaseEnv(sourceEnv, platform),
|
|
54959
|
+
ARCHCTX_PUBLISH_REMOTE: input.remote,
|
|
54960
|
+
ARCHCTX_PUBLISH_BASE: input.base,
|
|
54961
|
+
ARCHCTX_PUBLISH_BRANCH: input.branch,
|
|
54962
|
+
ARCHCTX_PUBLISH_BASE_SHA: input.baseSha,
|
|
54963
|
+
ARCHCTX_PUBLISH_COMMIT: input.commitSha,
|
|
54964
|
+
ARCHCTX_PUBLISH_TITLE: input.title,
|
|
54965
|
+
ARCHCTX_PUBLISH_BODY_FILE: input.bodyFile
|
|
54966
|
+
};
|
|
54967
|
+
const { stdout } = await withLocalEgress2("projection-publishing", async () => {
|
|
54968
|
+
const invocation = commandPublisherInvocation(options.command, platform);
|
|
54969
|
+
try {
|
|
54970
|
+
return await exec(invocation.file, invocation.args, { cwd: input.worktree, env, timeoutMs });
|
|
54971
|
+
} catch (error) {
|
|
54972
|
+
if (invocation.interpreter !== undefined && error.code === "ENOENT") {
|
|
54973
|
+
throw new Error(`publisher command ${options.command} needs the interpreter "${invocation.interpreter}" from its #! line, which was not found on PATH`);
|
|
54974
|
+
}
|
|
54975
|
+
throw error;
|
|
54976
|
+
}
|
|
54977
|
+
}, admissionEnv);
|
|
54978
|
+
return parsePublisherCommandOutput(stdout);
|
|
54979
|
+
}
|
|
54980
|
+
};
|
|
54981
|
+
}
|
|
54982
|
+
var COMMAND_PUBLISHER_POSIX_ENV = ["PATH", "HOME"];
|
|
54983
|
+
var COMMAND_PUBLISHER_WINDOWS_ENV = ["PATH", "HOME", "SystemRoot", "windir", "ComSpec", "PATHEXT", "TEMP", "TMP", "USERPROFILE"];
|
|
54984
|
+
function commandPublisherBaseEnv(source, platform) {
|
|
54985
|
+
const env = {};
|
|
54986
|
+
if (platform !== "win32") {
|
|
54987
|
+
for (const name of COMMAND_PUBLISHER_POSIX_ENV) {
|
|
54988
|
+
const value = source[name];
|
|
54989
|
+
if (value)
|
|
54990
|
+
env[name] = value;
|
|
54991
|
+
}
|
|
54992
|
+
return env;
|
|
54993
|
+
}
|
|
54994
|
+
const allowed = new Set(COMMAND_PUBLISHER_WINDOWS_ENV.map((name) => name.toUpperCase()));
|
|
54995
|
+
const forwarded = new Set;
|
|
54996
|
+
for (const [name, value] of Object.entries(source)) {
|
|
54997
|
+
const key = name.toUpperCase();
|
|
54998
|
+
if (!value || !allowed.has(key) || forwarded.has(key))
|
|
54999
|
+
continue;
|
|
55000
|
+
forwarded.add(key);
|
|
55001
|
+
env[name] = value;
|
|
55002
|
+
}
|
|
55003
|
+
return env;
|
|
55004
|
+
}
|
|
55005
|
+
function commandPublisherInvocation(command, platform) {
|
|
55006
|
+
if (platform !== "win32")
|
|
55007
|
+
return { file: command, args: [] };
|
|
55008
|
+
const extension = extname2(command).toLowerCase();
|
|
55009
|
+
if (extension === ".cmd" || extension === ".bat") {
|
|
55010
|
+
throw new Error(`publisher command ${command} is a ${extension} script, which only runs through cmd.exe; the command publisher runs without a shell, so use an executable or a #! script`);
|
|
55011
|
+
}
|
|
55012
|
+
const firstLine = readCommandFirstLine(command);
|
|
55013
|
+
if (!firstLine.startsWith("#!"))
|
|
55014
|
+
return { file: command, args: [] };
|
|
55015
|
+
const tokens = firstLine.slice(2).trim().split(/\s+/).filter((token) => token !== "");
|
|
55016
|
+
if (tokens.length === 0)
|
|
55017
|
+
throw new Error(`publisher command ${command} has an empty #! line`);
|
|
55018
|
+
let interpreter = commandBasename(tokens[0]);
|
|
55019
|
+
let interpreterArgs = tokens.slice(1);
|
|
55020
|
+
if (interpreter === "env") {
|
|
55021
|
+
const next = interpreterArgs[0];
|
|
55022
|
+
if (next === undefined || next.startsWith("-")) {
|
|
55023
|
+
throw new Error(`publisher command ${command} has an unsupported #! line "${firstLine}": /usr/bin/env must be followed by an interpreter name`);
|
|
55024
|
+
}
|
|
55025
|
+
interpreter = commandBasename(next);
|
|
55026
|
+
interpreterArgs = interpreterArgs.slice(1);
|
|
55027
|
+
}
|
|
55028
|
+
return { file: interpreter, args: [...interpreterArgs, command], interpreter };
|
|
55029
|
+
}
|
|
55030
|
+
function commandBasename(token) {
|
|
55031
|
+
const name = token.split(/[\\/]/).pop() ?? token;
|
|
55032
|
+
return name.toLowerCase().endsWith(".exe") ? name.slice(0, -4) : name;
|
|
55033
|
+
}
|
|
55034
|
+
function readCommandFirstLine(command) {
|
|
55035
|
+
let fd;
|
|
55036
|
+
try {
|
|
55037
|
+
fd = openSync10(command, "r");
|
|
55038
|
+
} catch (error) {
|
|
55039
|
+
throw new Error(`publisher command ${command} cannot be read: ${error instanceof Error ? error.message : String(error)}`);
|
|
55040
|
+
}
|
|
55041
|
+
try {
|
|
55042
|
+
const buffer = Buffer.alloc(1024);
|
|
55043
|
+
const length = readSync5(fd, buffer, 0, buffer.length, 0);
|
|
55044
|
+
const head = buffer.subarray(0, length).toString("utf8");
|
|
55045
|
+
const newline = head.indexOf(`
|
|
55046
|
+
`);
|
|
55047
|
+
return (newline === -1 ? head : head.slice(0, newline)).replace(/\r$/, "");
|
|
55048
|
+
} finally {
|
|
55049
|
+
closeSync9(fd);
|
|
55050
|
+
}
|
|
55051
|
+
}
|
|
55052
|
+
function parseGhPullRequestList(stdout) {
|
|
55053
|
+
const parsed = JSON.parse(stdout.trim() === "" ? "[]" : stdout);
|
|
55054
|
+
if (!Array.isArray(parsed))
|
|
55055
|
+
throw new Error("gh pr list returned an unexpected shape");
|
|
55056
|
+
const first = parsed[0];
|
|
55057
|
+
if (!first)
|
|
55058
|
+
return;
|
|
55059
|
+
if (typeof first.number !== "number" || !Number.isInteger(first.number) || first.number <= 0)
|
|
55060
|
+
throw new Error("gh pr list returned a pull request without a number");
|
|
55061
|
+
return { number: first.number, ...typeof first.url === "string" && first.url !== "" ? { url: first.url } : {} };
|
|
55062
|
+
}
|
|
55063
|
+
function parsePublisherCommandOutput(stdout) {
|
|
55064
|
+
let parsed;
|
|
55065
|
+
try {
|
|
55066
|
+
parsed = JSON.parse(stdout.trim());
|
|
55067
|
+
} catch {
|
|
55068
|
+
return {};
|
|
55069
|
+
}
|
|
55070
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
55071
|
+
return {};
|
|
55072
|
+
const value = parsed;
|
|
55073
|
+
return {
|
|
55074
|
+
...typeof value.url === "string" && value.url !== "" ? { url: value.url } : {},
|
|
55075
|
+
...typeof value.number === "number" && Number.isInteger(value.number) && value.number > 0 ? { number: value.number } : {}
|
|
55076
|
+
};
|
|
55077
|
+
}
|
|
55078
|
+
var nodeProjectionPublisherExec = (file, args, options) => new Promise((resolvePromise, rejectPromise) => {
|
|
55079
|
+
execFile2(file, args, { cwd: options.cwd, env: options.env, timeout: options.timeoutMs, encoding: "utf8", maxBuffer: 16 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
55080
|
+
if (error) {
|
|
55081
|
+
rejectPromise(Object.assign(new Error(`${[file, ...args.slice(0, 2)].join(" ")} failed: ${(stderr ?? "").trim() || error.message}`), { code: error.code }));
|
|
55082
|
+
return;
|
|
55083
|
+
}
|
|
55084
|
+
resolvePromise({ stdout });
|
|
55085
|
+
});
|
|
55086
|
+
});
|
|
55087
|
+
function stringEnv(env) {
|
|
55088
|
+
return Object.fromEntries(Object.entries(env).filter((entry) => typeof entry[1] === "string"));
|
|
55089
|
+
}
|
|
55090
|
+
function projectionWriterTargetIssue(repositoryRoot, input) {
|
|
55091
|
+
const issue = refNameIssue(repositoryRoot, "--base", input.base) ?? refNameIssue(repositoryRoot, "--branch", input.branch) ?? remoteNameIssue(input.remote);
|
|
55092
|
+
if (issue)
|
|
55093
|
+
return issue;
|
|
55094
|
+
if (!input.branch.startsWith(PROJECTION_WRITER_BRANCH_PREFIX))
|
|
55095
|
+
return `projection publish --branch must start with ${PROJECTION_WRITER_BRANCH_PREFIX}; the writer never pushes to any other ref`;
|
|
55096
|
+
if (input.branch === input.base)
|
|
55097
|
+
return "projection publish --branch must differ from --base";
|
|
55098
|
+
if (!gitSucceeds2(repositoryRoot, ["remote", "get-url", "--", input.remote]))
|
|
55099
|
+
return `projection publish --remote names no configured remote: ${input.remote}`;
|
|
55100
|
+
return;
|
|
55101
|
+
}
|
|
55102
|
+
function refNameIssue(repositoryRoot, flag, name) {
|
|
55103
|
+
if (name === "" || name.startsWith("-") || name.includes("@{") || !gitSucceeds2(repositoryRoot, ["check-ref-format", "--branch", name])) {
|
|
55104
|
+
return `${flag} is not a valid branch name: ${JSON.stringify(name)}`;
|
|
55105
|
+
}
|
|
55106
|
+
return;
|
|
55107
|
+
}
|
|
55108
|
+
function remoteNameIssue(remote) {
|
|
55109
|
+
if (remote === "" || remote.startsWith("-") || !/^[A-Za-z0-9._-]+$/.test(remote))
|
|
55110
|
+
return `--remote is not a valid remote name: ${JSON.stringify(remote)}`;
|
|
55111
|
+
return;
|
|
55112
|
+
}
|
|
55113
|
+
function acquireProjectionWriterLock(lockPath, startedAt) {
|
|
55114
|
+
let ownership;
|
|
55115
|
+
try {
|
|
55116
|
+
ownership = acquireLocalStoreWriterOwnership(`${lockPath}.coordination`);
|
|
55117
|
+
} catch (error) {
|
|
55118
|
+
if (error instanceof FailureError && error.reasonCode === "runtime-writer-owned") {
|
|
55119
|
+
const holder = readProjectionWriterLock(lockPath);
|
|
55120
|
+
if (holder && isProcessAlive(holder.pid))
|
|
55121
|
+
return { kind: "busy", pid: holder.pid };
|
|
55122
|
+
throw new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-lock-unavailable", "another writer holds the OS lock; its diagnostic owner record is unavailable");
|
|
55123
|
+
}
|
|
55124
|
+
throw error;
|
|
55125
|
+
}
|
|
55126
|
+
try {
|
|
55127
|
+
const body = readPrivateControlFile(lockPath);
|
|
55128
|
+
if (body === undefined && existsSync29(lockPath)) {
|
|
55129
|
+
throw new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-lock-unverified", "the writer owner record has unverified permissions");
|
|
55130
|
+
}
|
|
55131
|
+
const holder = body === undefined ? undefined : parseProjectionWriterLock(body);
|
|
55132
|
+
if (holder && isProcessAlive(holder.pid)) {
|
|
55133
|
+
ownership.release();
|
|
55134
|
+
return { kind: "busy", pid: holder.pid };
|
|
55135
|
+
}
|
|
55136
|
+
rmSync15(lockPath, { force: true });
|
|
55137
|
+
const record = JSON.stringify({ pid: process.pid, startedAt, token: randomUUID3() }, null, 2);
|
|
55138
|
+
createPrivateControlFile(lockPath, record);
|
|
55139
|
+
return { kind: "acquired", record, release() {
|
|
55140
|
+
try {
|
|
55141
|
+
if (readPrivateControlFile(lockPath) === record)
|
|
55142
|
+
rmSync15(lockPath, { force: true });
|
|
55143
|
+
} finally {
|
|
55144
|
+
ownership.release();
|
|
55145
|
+
}
|
|
55146
|
+
} };
|
|
55147
|
+
} catch (error) {
|
|
55148
|
+
ownership.release();
|
|
55149
|
+
throw error;
|
|
55150
|
+
}
|
|
55151
|
+
}
|
|
55152
|
+
function assertProjectionWriterOwner(lockPath, record) {
|
|
55153
|
+
if (readPrivateControlFile(lockPath) !== record) {
|
|
55154
|
+
throw new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-lock-lost", "the writer owner record changed; publication stopped");
|
|
55155
|
+
}
|
|
55156
|
+
}
|
|
55157
|
+
function readProjectionWriterLock(lockPath) {
|
|
55158
|
+
const body = readPrivateControlFile(lockPath);
|
|
55159
|
+
return body === undefined ? undefined : parseProjectionWriterLock(body);
|
|
55160
|
+
}
|
|
55161
|
+
function parseProjectionWriterLock(body) {
|
|
55162
|
+
try {
|
|
55163
|
+
const value = JSON.parse(body);
|
|
55164
|
+
if (typeof value.pid !== "number" || !Number.isInteger(value.pid) || value.pid <= 0)
|
|
55165
|
+
return;
|
|
55166
|
+
return { pid: value.pid, startedAt: typeof value.startedAt === "string" ? value.startedAt : "" };
|
|
55167
|
+
} catch {
|
|
55168
|
+
return;
|
|
55169
|
+
}
|
|
55170
|
+
}
|
|
55171
|
+
function emptyProjectionWriterState(remote, base) {
|
|
55172
|
+
return {
|
|
55173
|
+
schemaVersion: PROJECTION_WRITER_STATE_SCHEMA_VERSION,
|
|
55174
|
+
remote,
|
|
55175
|
+
base,
|
|
55176
|
+
branch: null,
|
|
55177
|
+
runSequence: 0,
|
|
55178
|
+
lastProcessedSha: null,
|
|
55179
|
+
lastSuccessfulSha: null,
|
|
55180
|
+
lastSuccessfulCommit: null,
|
|
55181
|
+
lastSuccessfulBranch: null,
|
|
55182
|
+
lastSuccessfulPublisher: null,
|
|
55183
|
+
lastSuccessfulFiles: [],
|
|
55184
|
+
publisherPending: null,
|
|
55185
|
+
lastRun: null
|
|
55186
|
+
};
|
|
55187
|
+
}
|
|
55188
|
+
function readProjectionWriterState(path, remote, base) {
|
|
55189
|
+
if (!existsSync29(path))
|
|
55190
|
+
return emptyProjectionWriterState(remote, base);
|
|
55191
|
+
let value;
|
|
55192
|
+
try {
|
|
55193
|
+
value = JSON.parse(readFileSync27(path, "utf8"));
|
|
55194
|
+
} catch (error) {
|
|
55195
|
+
throw new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-state-invalid", `projection writer state is unreadable at ${path}: ${errorMessage2(error)}`);
|
|
55196
|
+
}
|
|
55197
|
+
if (value?.schemaVersion !== PROJECTION_WRITER_STATE_SCHEMA_VERSION || value.remote !== remote || value.base !== base || typeof value.runSequence !== "number") {
|
|
55198
|
+
throw new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-state-invalid", `projection writer state at ${path} does not belong to ${remote}/${base}`);
|
|
55199
|
+
}
|
|
55200
|
+
return { ...emptyProjectionWriterState(remote, base), ...value };
|
|
55201
|
+
}
|
|
55202
|
+
function writeProjectionWriterState(path, state) {
|
|
55203
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
55204
|
+
writeFileSync11(temporary, `${JSON.stringify(state, null, 2)}
|
|
55205
|
+
`, { encoding: "utf8", mode: 384 });
|
|
55206
|
+
renameSync7(temporary, path);
|
|
55207
|
+
}
|
|
55208
|
+
function prepareProjectionWriterWorktree(repositoryRoot, worktree, sha) {
|
|
55209
|
+
git(repositoryRoot, ["worktree", "prune"]);
|
|
55210
|
+
if (!isRegisteredWorktree(repositoryRoot, worktree)) {
|
|
55211
|
+
rmSync15(worktree, { recursive: true, force: true });
|
|
55212
|
+
git(repositoryRoot, ["worktree", "add", "--detach", worktree, sha]);
|
|
55213
|
+
}
|
|
55214
|
+
git(worktree, ["checkout", "-q", "--force", "--detach", sha]);
|
|
55215
|
+
git(worktree, ["reset", "-q", "--hard", sha]);
|
|
55216
|
+
git(worktree, ["clean", "-ffdq"]);
|
|
55217
|
+
}
|
|
55218
|
+
function isRegisteredWorktree(repositoryRoot, worktree) {
|
|
55219
|
+
if (!existsSync29(join23(worktree, ".git")))
|
|
55220
|
+
return false;
|
|
55221
|
+
const wanted = canonical(worktree);
|
|
55222
|
+
return git(repositoryRoot, ["worktree", "list", "--porcelain", "-z"]).split("\x00").filter((line) => line.startsWith("worktree ")).some((line) => canonical(line.slice("worktree ".length)) === wanted);
|
|
55223
|
+
}
|
|
55224
|
+
function canonical(path) {
|
|
55225
|
+
try {
|
|
55226
|
+
return realpathSync12.native(path);
|
|
55227
|
+
} catch {
|
|
55228
|
+
return resolve30(path);
|
|
55229
|
+
}
|
|
55230
|
+
}
|
|
55231
|
+
function worktreeChangedPaths(worktree) {
|
|
55232
|
+
const tokens = git(worktree, ["status", "--porcelain=v1", "-z", "--untracked-files=all"]).split("\x00");
|
|
55233
|
+
const paths = new Set;
|
|
55234
|
+
for (let index = 0;index < tokens.length; index += 1) {
|
|
55235
|
+
const entry = tokens[index];
|
|
55236
|
+
if (entry.length < 4)
|
|
55237
|
+
continue;
|
|
55238
|
+
paths.add(entry.slice(3));
|
|
55239
|
+
if (entry[0] === "R" || entry[0] === "C") {
|
|
55240
|
+
const source = tokens[index + 1];
|
|
55241
|
+
if (source)
|
|
55242
|
+
paths.add(source);
|
|
55243
|
+
index += 1;
|
|
55244
|
+
}
|
|
55245
|
+
}
|
|
55246
|
+
return [...paths].sort();
|
|
55247
|
+
}
|
|
55248
|
+
function isProjectionOwnedPath(path, patterns) {
|
|
55249
|
+
return patterns.some((pattern) => pattern.endsWith("/**") ? path.startsWith(pattern.slice(0, -2)) : path === pattern);
|
|
55250
|
+
}
|
|
55251
|
+
function carriesSameProjection(worktree, remoteTip, commitSha, baseSha) {
|
|
55252
|
+
if (!gitSucceeds2(worktree, ["cat-file", "-e", `${remoteTip}^{commit}`]))
|
|
55253
|
+
return false;
|
|
55254
|
+
const parents = git(worktree, ["rev-list", "--parents", "-n", "1", remoteTip]).trim().split(/\s+/).slice(1);
|
|
55255
|
+
if (parents.length !== 1 || parents[0] !== baseSha)
|
|
55256
|
+
return false;
|
|
55257
|
+
return git(worktree, ["rev-parse", `${remoteTip}^{tree}`]).trim() === git(worktree, ["rev-parse", `${commitSha}^{tree}`]).trim();
|
|
55258
|
+
}
|
|
55259
|
+
function lsRemoteBranch(repositoryRoot, remote, branch, env) {
|
|
55260
|
+
const line = remoteGit(repositoryRoot, ["ls-remote", "--", remote, `refs/heads/${branch}`], env).split(`
|
|
55261
|
+
`).find((entry) => entry.endsWith(` refs/heads/${branch}`));
|
|
55262
|
+
return line ? line.split("\t")[0] : null;
|
|
55263
|
+
}
|
|
55264
|
+
function remoteGit(cwd, args, env) {
|
|
55265
|
+
return withLocalEgress2("projection-publishing", () => git(cwd, args, { GIT_TERMINAL_PROMPT: "0" }), env);
|
|
55266
|
+
}
|
|
55267
|
+
function git(cwd, args, extraEnv = {}) {
|
|
55268
|
+
const result = spawnSync5("git", args, {
|
|
55269
|
+
cwd,
|
|
55270
|
+
encoding: "utf8",
|
|
55271
|
+
env: { ...process.env, ...extraEnv },
|
|
55272
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
55273
|
+
maxBuffer: 256 * 1024 * 1024
|
|
55274
|
+
});
|
|
55275
|
+
if (result.error)
|
|
55276
|
+
throw new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-git-failed", `git ${args[0]} could not run: ${result.error.message}`);
|
|
55277
|
+
if (result.status !== 0) {
|
|
55278
|
+
throw new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-git-failed", `git ${args[0]} failed: ${(result.stderr ?? "").trim() || `exit ${result.status}`}`);
|
|
55279
|
+
}
|
|
55280
|
+
return result.stdout ?? "";
|
|
55281
|
+
}
|
|
55282
|
+
function gitSucceeds2(cwd, args) {
|
|
55283
|
+
return spawnSync5("git", args, { cwd, stdio: ["ignore", "ignore", "ignore"] }).status === 0;
|
|
55284
|
+
}
|
|
55285
|
+
function envelopeFailure(envelope, fallbackReason) {
|
|
55286
|
+
const error = envelope.error;
|
|
55287
|
+
return new ProjectionWriterFailure(error?.code ?? "AC_UNCLASSIFIED_FAILURE", error?.reasonCode ?? fallbackReason, error?.message ?? fallbackReason);
|
|
55288
|
+
}
|
|
55289
|
+
function asWriterFailure(error) {
|
|
55290
|
+
if (error instanceof ProjectionWriterFailure)
|
|
55291
|
+
return error;
|
|
55292
|
+
if (error instanceof LocalEgressPolicyError2)
|
|
55293
|
+
return new ProjectionWriterFailure("AC_POLICY_VIOLATION", "egress-policy-denied", error.message);
|
|
55294
|
+
const envelope = publicOperationErrorEnvelope(PROJECTION_PUBLISH_REQUEST_ID, error);
|
|
55295
|
+
return envelopeFailure(envelope, "projection-writer-failed");
|
|
55296
|
+
}
|
|
55297
|
+
function writerError(failure) {
|
|
55298
|
+
return { code: failure.code, reasonCode: failure.reasonCode, message: failure.message };
|
|
55299
|
+
}
|
|
55300
|
+
function errorMessage2(error) {
|
|
55301
|
+
return error instanceof Error ? error.message : String(error);
|
|
55302
|
+
}
|
|
54329
55303
|
// packages/local-runtime/runtime-daemon/src/capability-dependencies.ts
|
|
54330
55304
|
init_src();
|
|
54331
55305
|
var CODEGRAPH_BINARY2 = "codegraph";
|
|
@@ -54449,8 +55423,8 @@ function runFlowSelectorCheckCommand(cwd, options = {}) {
|
|
|
54449
55423
|
}
|
|
54450
55424
|
}
|
|
54451
55425
|
// packages/local-runtime/runtime-daemon/src/recommendations.ts
|
|
54452
|
-
import { existsSync as
|
|
54453
|
-
import { join as
|
|
55426
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
55427
|
+
import { join as join24 } from "node:path";
|
|
54454
55428
|
|
|
54455
55429
|
// packages/local-runtime/runtime-daemon/src/refactor-verify.ts
|
|
54456
55430
|
init_src();
|
|
@@ -55052,7 +56026,7 @@ class RecommendationsService {
|
|
|
55052
56026
|
});
|
|
55053
56027
|
}
|
|
55054
56028
|
readGitOnlyDecision(repositoryRoot, recommendationId, surface) {
|
|
55055
|
-
if (!
|
|
56029
|
+
if (!existsSync30(join24(repositoryRoot, REFACTOR_DECISION_DIRECTORY)))
|
|
55056
56030
|
return;
|
|
55057
56031
|
let decisions;
|
|
55058
56032
|
try {
|
|
@@ -55722,7 +56696,7 @@ function lifecycleEnvelope(input) {
|
|
|
55722
56696
|
});
|
|
55723
56697
|
}
|
|
55724
56698
|
function refactorDecisionsInPlay(repositoryRoot, records) {
|
|
55725
|
-
return records.some(isRefactorRecommendation) ||
|
|
56699
|
+
return records.some(isRefactorRecommendation) || existsSync30(join24(repositoryRoot, REFACTOR_DECISION_DIRECTORY));
|
|
55726
56700
|
}
|
|
55727
56701
|
function ledgerAuditDecisionRefused(surface, record, recommendations, decisions) {
|
|
55728
56702
|
let head = record.recommendationId;
|
|
@@ -55843,15 +56817,15 @@ function recommendationActorKind(input) {
|
|
|
55843
56817
|
// packages/local-runtime/runtime-daemon/src/changeset-authority.ts
|
|
55844
56818
|
init_src();
|
|
55845
56819
|
init_src();
|
|
55846
|
-
import { existsSync as
|
|
55847
|
-
import { resolve as
|
|
56820
|
+
import { existsSync as existsSync31, readFileSync as readFileSync29 } from "node:fs";
|
|
56821
|
+
import { resolve as resolve32 } from "node:path";
|
|
55848
56822
|
init_src();
|
|
55849
56823
|
|
|
55850
56824
|
// packages/local-runtime/runtime-daemon/src/committed-change-acceptance.ts
|
|
55851
56825
|
init_src();
|
|
55852
56826
|
import { execFileSync as execFileSync10 } from "node:child_process";
|
|
55853
|
-
import { closeSync as
|
|
55854
|
-
import { resolve as
|
|
56827
|
+
import { closeSync as closeSync10, constants as fsConstants3, fstatSync as fstatSync4, lstatSync as lstatSync15, openSync as openSync11, readdirSync as readdirSync15, readFileSync as readFileSync28 } from "node:fs";
|
|
56828
|
+
import { resolve as resolve31 } from "node:path";
|
|
55855
56829
|
init_src();
|
|
55856
56830
|
var SEMANTIC_MODEL_DIRECTORIES = ["nodes", "relations", "flows"];
|
|
55857
56831
|
var SEMANTIC_MODEL_PATH = /^\.archcontext\/model\/(nodes|relations|flows)\/[^/]+\.ya?ml$/;
|
|
@@ -55916,7 +56890,7 @@ function readSemanticModelSnapshot(root, readEntry) {
|
|
|
55916
56890
|
}
|
|
55917
56891
|
for (const entry of entries.filter((name) => /\.ya?ml$/.test(name)).sort()) {
|
|
55918
56892
|
const path = `${relativeDirectory}/${entry}`;
|
|
55919
|
-
bodies.set(path, strictUtf8(readEntry(
|
|
56893
|
+
bodies.set(path, strictUtf8(readEntry(resolve31(absolute, entry), path), path));
|
|
55920
56894
|
}
|
|
55921
56895
|
}
|
|
55922
56896
|
const hashes = new Map([...bodies].map(([path, body]) => [path, digestJson({ body })]));
|
|
@@ -55925,13 +56899,13 @@ function readSemanticModelSnapshot(root, readEntry) {
|
|
|
55925
56899
|
function readRegularFileBytes(absolute, path) {
|
|
55926
56900
|
if (!lstatSync15(absolute).isFile())
|
|
55927
56901
|
throw new FailureError("AC_PRECONDITION_FAILED", "semantic-model-entry-not-regular");
|
|
55928
|
-
const fd =
|
|
56902
|
+
const fd = openSync11(absolute, fsConstants3.O_RDONLY | (fsConstants3.O_NOFOLLOW ?? 0) | (fsConstants3.O_NONBLOCK ?? 0));
|
|
55929
56903
|
try {
|
|
55930
56904
|
if (!fstatSync4(fd).isFile())
|
|
55931
56905
|
throw new FailureError("AC_PRECONDITION_FAILED", "semantic-model-entry-not-regular");
|
|
55932
|
-
return
|
|
56906
|
+
return readFileSync28(fd);
|
|
55933
56907
|
} finally {
|
|
55934
|
-
|
|
56908
|
+
closeSync10(fd);
|
|
55935
56909
|
}
|
|
55936
56910
|
}
|
|
55937
56911
|
function strictUtf8(bytes, path) {
|
|
@@ -56429,10 +57403,10 @@ class ChangeSetAuthorityService {
|
|
|
56429
57403
|
return publicFailureEnvelope("practices.waive", "AC_SCHEMA_INVALID", "waiver-input-invalid");
|
|
56430
57404
|
}
|
|
56431
57405
|
const path = `.archcontext/waivers/${waiverId}.json`;
|
|
56432
|
-
const absolute =
|
|
57406
|
+
const absolute = resolve32(session.workspace.root, path);
|
|
56433
57407
|
const body = `${JSON.stringify(waiver, null, 2)}
|
|
56434
57408
|
`;
|
|
56435
|
-
const expectedHash =
|
|
57409
|
+
const expectedHash = existsSync31(absolute) ? digestJson({ body: readFileSync29(absolute, "utf8") }) : "missing";
|
|
56436
57410
|
const draft = this.changeSetEngine.plan({
|
|
56437
57411
|
id: input.id ?? `changeset.practice-waiver-${waiverId.replace(/[^A-Za-z0-9_-]/g, "-")}`,
|
|
56438
57412
|
base: {
|
|
@@ -56868,7 +57842,7 @@ function architectureLedgerWriteAppendsEvents(mode) {
|
|
|
56868
57842
|
// packages/local-runtime/runtime-daemon/src/rpc-server.ts
|
|
56869
57843
|
init_src();
|
|
56870
57844
|
import { createServer as createServer2 } from "node:http";
|
|
56871
|
-
import { closeSync as
|
|
57845
|
+
import { closeSync as closeSync11, existsSync as existsSync32, mkdirSync as mkdirSync13 } from "node:fs";
|
|
56872
57846
|
import { dirname as dirname14 } from "node:path";
|
|
56873
57847
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
56874
57848
|
var DEFAULT_DAEMON_IDLE_TIMEOUT_MS = 30 * 60000;
|
|
@@ -56926,7 +57900,7 @@ class ArchctxRuntimeRpcServer {
|
|
|
56926
57900
|
if (connection)
|
|
56927
57901
|
await removePrivateControlFile(connection.connectionPath);
|
|
56928
57902
|
if (this.lockFd !== undefined) {
|
|
56929
|
-
|
|
57903
|
+
closeSync11(this.lockFd);
|
|
56930
57904
|
this.lockFd = undefined;
|
|
56931
57905
|
await removePrivateControlFile(this.options.lockPath ?? defaultDaemonLockPath(this.options.root ?? process.cwd()));
|
|
56932
57906
|
}
|
|
@@ -56939,8 +57913,8 @@ class ArchctxRuntimeRpcServer {
|
|
|
56939
57913
|
const root = this.options.root ?? process.cwd();
|
|
56940
57914
|
const connectionPath = this.options.connectionPath ?? defaultDaemonConnectionPath(root);
|
|
56941
57915
|
const lockPath = this.options.lockPath ?? defaultDaemonLockPath(root);
|
|
56942
|
-
|
|
56943
|
-
|
|
57916
|
+
mkdirSync13(dirname14(connectionPath), { recursive: true });
|
|
57917
|
+
mkdirSync13(dirname14(lockPath), { recursive: true });
|
|
56944
57918
|
this.lockFd = acquireDaemonLock(lockPath, root);
|
|
56945
57919
|
const token = this.options.token ?? randomBytes5(18).toString("base64url");
|
|
56946
57920
|
const server = createServer2((request, response) => {
|
|
@@ -56991,7 +57965,7 @@ class ArchctxRuntimeRpcServer {
|
|
|
56991
57965
|
await removePrivateControlFile(connection.connectionPath);
|
|
56992
57966
|
} finally {
|
|
56993
57967
|
if (this.lockFd !== undefined)
|
|
56994
|
-
|
|
57968
|
+
closeSync11(this.lockFd);
|
|
56995
57969
|
this.lockFd = undefined;
|
|
56996
57970
|
}
|
|
56997
57971
|
if (connection)
|
|
@@ -57033,7 +58007,7 @@ class ArchctxRuntimeRpcServer {
|
|
|
57033
58007
|
});
|
|
57034
58008
|
if (this.server && this.connection && (this.inFlightRpcRequests > 0 || this.authorizedRequests !== requestsBeforeRemoval)) {
|
|
57035
58009
|
try {
|
|
57036
|
-
if (!
|
|
58010
|
+
if (!existsSync32(connection.connectionPath))
|
|
57037
58011
|
createPrivateControlFile(connection.connectionPath, JSON.stringify(connection, null, 2));
|
|
57038
58012
|
this.idleExitPending = false;
|
|
57039
58013
|
this.armIdleTimer();
|
|
@@ -57246,8 +58220,8 @@ async function readRpcRequestBody(request, limits) {
|
|
|
57246
58220
|
}
|
|
57247
58221
|
|
|
57248
58222
|
// packages/local-runtime/runtime-daemon/src/index.ts
|
|
57249
|
-
import { existsSync as
|
|
57250
|
-
import { resolve as
|
|
58223
|
+
import { existsSync as existsSync33, readFileSync as readFileSync30 } from "node:fs";
|
|
58224
|
+
import { resolve as resolve33 } from "node:path";
|
|
57251
58225
|
init_src();
|
|
57252
58226
|
|
|
57253
58227
|
// packages/local-runtime/runtime-daemon/src/egress.ts
|
|
@@ -57283,6 +58257,8 @@ function effectiveEgressChannels2(env, live) {
|
|
|
57283
58257
|
}
|
|
57284
58258
|
if (live.updateCheckRequested)
|
|
57285
58259
|
channels.push({ channel: "npm-update-check", destination: "configured npm registry", trigger: "explicit update --check or doctor --check-updates", data: "archctx package version lookup", status: "enabled" });
|
|
58260
|
+
if (live.projectionPublishRequested)
|
|
58261
|
+
channels.push({ channel: "projection-publishing", destination: "configured git remote, plus GitHub via gh or the operator's publisher command", trigger: "explicit archctx projection publish", data: "one commit of generated projection outputs and a pull request title/body listing their paths", status: "enabled" });
|
|
57286
58262
|
if (env[CODEGRAPH_TELEMETRY_ENV] !== undefined && env[CODEGRAPH_TELEMETRY_ENV] !== CODEGRAPH_TELEMETRY_DISABLED_VALUE)
|
|
57287
58263
|
channels.push({ channel: "codegraph-telemetry", destination: "CodeGraph configured telemetry endpoint", trigger: "CodeGraph execution with telemetry enabled", data: "third-party telemetry", status: "enabled" });
|
|
57288
58264
|
return channels.map((channel) => localEgressAdmission2(channel.channel, env).allowed ? channel : { ...channel, status: "blocked-by-policy" });
|
|
@@ -57437,7 +58413,7 @@ class ArchctxDaemon {
|
|
|
57437
58413
|
const model = loadNativeModelFromArchContext(root);
|
|
57438
58414
|
const canonical = renderAgentContextProjection({ model, sourceDigest: digestJson({ model }), existingFiles: loadAgentContextProjectionFiles(root, model) });
|
|
57439
58415
|
const canonicalBody = canonical.files.find((candidate) => candidate.path === file.path)?.body;
|
|
57440
|
-
if (canonicalBody === undefined || projectionOwnedWriteBody(canonicalBody,
|
|
58416
|
+
if (canonicalBody === undefined || projectionOwnedWriteBody(canonicalBody, readFileSync30(resolve33(root, file.path), "utf8")) !== file.body) {
|
|
57441
58417
|
throw new Error(`Root contract requires canonical marker-only content: ${file.path}`);
|
|
57442
58418
|
}
|
|
57443
58419
|
}
|
|
@@ -57696,7 +58672,7 @@ class ArchctxDaemon {
|
|
|
57696
58672
|
if (this.auditRunAbortControllers.size > 0)
|
|
57697
58673
|
return true;
|
|
57698
58674
|
for (const [repositoryId, session] of this.sessions) {
|
|
57699
|
-
if (!
|
|
58675
|
+
if (!existsSync33(session.workspace.root)) {
|
|
57700
58676
|
this.sessions.delete(repositoryId);
|
|
57701
58677
|
continue;
|
|
57702
58678
|
}
|
|
@@ -57900,7 +58876,7 @@ class ArchctxDaemon {
|
|
|
57900
58876
|
try {
|
|
57901
58877
|
projectionDrift = completeTaskProjectionDrift(session.workspace.root);
|
|
57902
58878
|
projectionFreshness = completeTaskProjectionFreshness(session.workspace.root);
|
|
57903
|
-
if (model.valid || !
|
|
58879
|
+
if (model.valid || !existsSync33(resolve33(session.workspace.root, ".archcontext/manifest.yaml"))) {
|
|
57904
58880
|
architectureDebt = readCompleteTaskArchitectureDebt(session.workspace.root, practiceEnforcement);
|
|
57905
58881
|
}
|
|
57906
58882
|
} catch (error) {
|
|
@@ -58389,7 +59365,7 @@ class ArchctxDaemon {
|
|
|
58389
59365
|
}
|
|
58390
59366
|
async restoreRepositorySessions() {
|
|
58391
59367
|
for (const record of await this.localStore.listRepositorySessions()) {
|
|
58392
|
-
if (!record.root || !
|
|
59368
|
+
if (!record.root || !existsSync33(record.root))
|
|
58393
59369
|
continue;
|
|
58394
59370
|
if (repositoryFingerprint(record.root) !== record.repositoryId)
|
|
58395
59371
|
continue;
|
|
@@ -58687,7 +59663,7 @@ function blockedProductionInjections(deps) {
|
|
|
58687
59663
|
].filter((key) => (key in deps));
|
|
58688
59664
|
}
|
|
58689
59665
|
function completeTaskProjectionDrift(root) {
|
|
58690
|
-
if (!
|
|
59666
|
+
if (!existsSync33(resolve33(root, "docs/architecture/.projection-manifest.json")))
|
|
58691
59667
|
return;
|
|
58692
59668
|
const profile = loadArchitectureDocumentationProfile(root);
|
|
58693
59669
|
const loaded = loadArchitectureDocumentationInputs(root, profile);
|
|
@@ -58735,9 +59711,9 @@ function completeTaskProjectionFreshness(root) {
|
|
|
58735
59711
|
// packages/local-runtime/codegraph-adapter/src/index.ts
|
|
58736
59712
|
init_src();
|
|
58737
59713
|
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
58738
|
-
import { accessSync as accessSync2, closeSync as
|
|
59714
|
+
import { accessSync as accessSync2, closeSync as closeSync12, constants as fsConstants4, existsSync as existsSync34, openSync as openSync12, readFileSync as readFileSync31, readSync as readSync6, realpathSync as realpathSync13, statSync as statSync10 } from "node:fs";
|
|
58739
59715
|
import { createRequire as createRequire5 } from "node:module";
|
|
58740
|
-
import { basename as basename7, delimiter as delimiter2, dirname as dirname15, isAbsolute as isAbsolute10, join as
|
|
59716
|
+
import { basename as basename7, delimiter as delimiter2, dirname as dirname15, isAbsolute as isAbsolute10, join as join26, posix as posix4, resolve as resolve34 } from "node:path";
|
|
58741
59717
|
init_src();
|
|
58742
59718
|
var REQUIRED_CODEGRAPH_PACKAGE2 = "@colbymchenry/codegraph";
|
|
58743
59719
|
var REQUIRED_CODEGRAPH_VERSION2 = productVersionManifest().runtime.codeGraph.requiredVersion;
|
|
@@ -58774,15 +59750,15 @@ function codeGraphCliInvocation2(binary, cwd, pathValue = process.env.PATH ?? ""
|
|
|
58774
59750
|
}
|
|
58775
59751
|
function resolveExecutable2(binary, cwd, pathValue) {
|
|
58776
59752
|
if (binary.includes("/") || binary.includes("\\")) {
|
|
58777
|
-
const path = isAbsolute10(binary) ? binary :
|
|
58778
|
-
return
|
|
59753
|
+
const path = isAbsolute10(binary) ? binary : join26(cwd, binary);
|
|
59754
|
+
return existsSync34(path) ? realpathSync13.native(path) : undefined;
|
|
58779
59755
|
}
|
|
58780
59756
|
for (const dir of pathValue.split(delimiter2)) {
|
|
58781
59757
|
if (!dir)
|
|
58782
59758
|
continue;
|
|
58783
|
-
const path =
|
|
59759
|
+
const path = join26(dir, binary);
|
|
58784
59760
|
if (isExecutablePath2(path))
|
|
58785
|
-
return
|
|
59761
|
+
return realpathSync13.native(path);
|
|
58786
59762
|
}
|
|
58787
59763
|
return;
|
|
58788
59764
|
}
|
|
@@ -58800,12 +59776,12 @@ function isExecutablePath2(path) {
|
|
|
58800
59776
|
function resolvePackagedCodeGraphShim2() {
|
|
58801
59777
|
try {
|
|
58802
59778
|
const manifestPath = requireFromAdapter2.resolve(`${REQUIRED_CODEGRAPH_PACKAGE2}/package.json`);
|
|
58803
|
-
const manifest = JSON.parse(
|
|
59779
|
+
const manifest = JSON.parse(readFileSync31(manifestPath, "utf8"));
|
|
58804
59780
|
const relativeBin = manifest.bin?.codegraph;
|
|
58805
59781
|
if (manifest.version !== REQUIRED_CODEGRAPH_VERSION2 || !relativeBin)
|
|
58806
59782
|
return;
|
|
58807
|
-
const shimPath =
|
|
58808
|
-
return
|
|
59783
|
+
const shimPath = resolve34(dirname15(manifestPath), relativeBin);
|
|
59784
|
+
return existsSync34(shimPath) ? realpathSync13.native(shimPath) : undefined;
|
|
58809
59785
|
} catch {
|
|
58810
59786
|
return;
|
|
58811
59787
|
}
|
|
@@ -58819,15 +59795,15 @@ function isNodeRuntimeScript2(path) {
|
|
|
58819
59795
|
function readFilePrefix2(path, length) {
|
|
58820
59796
|
let fd;
|
|
58821
59797
|
try {
|
|
58822
|
-
fd =
|
|
59798
|
+
fd = openSync12(path, "r");
|
|
58823
59799
|
const buffer = Buffer.alloc(length);
|
|
58824
|
-
const bytes =
|
|
59800
|
+
const bytes = readSync6(fd, buffer, 0, length, 0);
|
|
58825
59801
|
return buffer.subarray(0, bytes).toString("utf8");
|
|
58826
59802
|
} catch {
|
|
58827
59803
|
return "";
|
|
58828
59804
|
} finally {
|
|
58829
59805
|
if (fd !== undefined)
|
|
58830
|
-
|
|
59806
|
+
closeSync12(fd);
|
|
58831
59807
|
}
|
|
58832
59808
|
}
|
|
58833
59809
|
var CODEGRAPH_IMPORT_NODE_OUTPUT_BYTES2 = 4 * 1024;
|
|
@@ -58865,7 +59841,7 @@ function sameRealPath2(left, right) {
|
|
|
58865
59841
|
if (typeof left !== "string")
|
|
58866
59842
|
return false;
|
|
58867
59843
|
try {
|
|
58868
|
-
return
|
|
59844
|
+
return realpathSync13.native(left) === realpathSync13.native(right);
|
|
58869
59845
|
} catch {
|
|
58870
59846
|
return false;
|
|
58871
59847
|
}
|
|
@@ -58955,7 +59931,7 @@ function inspectRepositoryCodeFacts2(root, options = {}) {
|
|
|
58955
59931
|
};
|
|
58956
59932
|
}
|
|
58957
59933
|
function codeGraphIndexAvailable2(workspaceRoot) {
|
|
58958
|
-
return
|
|
59934
|
+
return existsSync34(join26(workspaceRoot, ".codegraph"));
|
|
58959
59935
|
}
|
|
58960
59936
|
var NON_DECLARATION_KINDS2 = new Set(["file", "import", "export", "parameter"]);
|
|
58961
59937
|
var RUNTIME_BUILTIN_CALL_NAMES2 = new Set([
|
|
@@ -59880,6 +60856,12 @@ var AUDIT_RUN_VALUE_FLAGS = ["--task-session-id", "--reason", "--risk", "--uncer
|
|
|
59880
60856
|
var AUDIT_RUN_BOOLEAN_FLAGS = ["--no-wait", ...AUDIT_GLOBAL_BOOLEAN_FLAGS];
|
|
59881
60857
|
var AUDIT_APPROVE_VALUE_FLAGS = ["--run-id", "--confirm-public-repo", ...AUDIT_GLOBAL_VALUE_FLAGS];
|
|
59882
60858
|
var AUDIT_APPROVE_BOOLEAN_FLAGS = ["--resume", ...AUDIT_GLOBAL_BOOLEAN_FLAGS];
|
|
60859
|
+
var PROJECTION_PUBLISH_VALUE_FLAGS = ["--base", "--branch", "--interval", "--publisher", "--publisher-command", "--remote"];
|
|
60860
|
+
var PROJECTION_PUBLISH_BOOLEAN_FLAGS = ["--accept-observed-major-change", "--follow"];
|
|
60861
|
+
var PROJECTION_PUBLISH_STATUS_VALUE_FLAGS = ["--base", "--remote"];
|
|
60862
|
+
var PROJECTION_VERIFY_PR_VALUE_FLAGS = ["--base"];
|
|
60863
|
+
var PROJECTION_PUBLISH_DEFAULT_INTERVAL_SECONDS = 300;
|
|
60864
|
+
var PROJECTION_PUBLISH_MIN_INTERVAL_SECONDS = 30;
|
|
59883
60865
|
var CLI_COMMAND_FLAGS = {
|
|
59884
60866
|
debt: { value: ["--changed-path"], boolean: [] },
|
|
59885
60867
|
"agent-context": { value: ["--expected-worktree-digest", "--id", "--task-session-id"], boolean: ["--approved"] },
|
|
@@ -59993,7 +60975,10 @@ var CLI_COMMAND_FLAGS = {
|
|
|
59993
60975
|
recover: { value: ["--request-json"], boolean: [] },
|
|
59994
60976
|
readback: { value: ["--request-json"], boolean: [] },
|
|
59995
60977
|
snapshot: { value: [], boolean: [] },
|
|
59996
|
-
freshness: { value: ["--changed-path"], boolean: [] }
|
|
60978
|
+
freshness: { value: ["--changed-path"], boolean: [] },
|
|
60979
|
+
publish: { value: PROJECTION_PUBLISH_VALUE_FLAGS, boolean: PROJECTION_PUBLISH_BOOLEAN_FLAGS },
|
|
60980
|
+
"publish-status": { value: PROJECTION_PUBLISH_STATUS_VALUE_FLAGS, boolean: [] },
|
|
60981
|
+
"verify-pr": { value: PROJECTION_VERIFY_PR_VALUE_FLAGS, boolean: [] }
|
|
59997
60982
|
}
|
|
59998
60983
|
},
|
|
59999
60984
|
recommendations: {
|
|
@@ -60128,6 +61113,48 @@ var CLI_PROJECTION_FRESHNESS_HELP = {
|
|
|
60128
61113
|
writes: "none; no daemon, no local store, no CodeGraph sync. Outside a Git worktree it fails with AC_REPO_NOT_FOUND (git-worktree-required).",
|
|
60129
61114
|
capability: "projection-freshness-v1"
|
|
60130
61115
|
};
|
|
61116
|
+
var CLI_PROJECTION_PUBLISH_HELP = {
|
|
61117
|
+
usage: "archctx projection publish [--base <branch>] [--remote <name>] [--branch <archctx/...>] [--publisher gh|command|none] [--publisher-command <executable>] [--accept-observed-major-change] [--follow [--interval <seconds>]] [--json]",
|
|
61118
|
+
description: "Projection writer mode. Fetches the remote base tip, resets a writer-owned worktree to it, applies the projection through that worktree's archctxd, and pushes the result as exactly one commit on top of the base tip to a rolling archctx/* branch, then opens or updates its pull request. Only paths the apply wrote that are projection-owned outputs may change; anything else fails before a commit. One run per remote and base at a time (busy otherwise); the same base SHA is a no-op once published or up to date; a base that moves during the run is re-evaluated and the stale commit is never pushed.",
|
|
61119
|
+
options: {
|
|
61120
|
+
"--base <branch>": "The branch the projection is computed for (default: main).",
|
|
61121
|
+
"--remote <name>": "The remote to fetch the base from and push the rolling branch to (default: origin).",
|
|
61122
|
+
"--branch <archctx/...>": "The rolling branch (default: archctx/projection). It must start with archctx/ and differ from --base; the writer never pushes to any other ref.",
|
|
61123
|
+
"--publisher gh|command|none": "gh (default): the operator's own gh lists, then edits or creates the pull request. command: run --publisher-command. none: push only.",
|
|
61124
|
+
"--publisher-command <executable>": "With --publisher command: an executable run with no shell or arguments in the owned worktree, with PATH, HOME and ARCHCTX_PUBLISH_REMOTE/BASE/BRANCH/BASE_SHA/COMMIT/TITLE/BODY_FILE only (on Windows also SystemRoot, windir, ComSpec, PATHEXT, TEMP, TMP, USERPROFILE; #! scripts run through their interpreter on PATH; .cmd/.bat are rejected). Optional stdout: a JSON object {url?, number?}.",
|
|
61125
|
+
"--accept-observed-major-change": "Accept the major change the run observes (ProjectionRequestV1.acceptObservedMajorChange). Without it a major change stops at human-action-required and nothing is pushed.",
|
|
61126
|
+
"--follow": "Run as a foreground loop: one publish per --interval, one JSON envelope line per run on stdout, until SIGINT or SIGTERM. For launchd/systemd KeepAlive on an always-on host.",
|
|
61127
|
+
"--interval <seconds>": `With --follow: seconds between runs (default ${PROJECTION_PUBLISH_DEFAULT_INTERVAL_SECONDS}, minimum ${PROJECTION_PUBLISH_MIN_INTERVAL_SECONDS}).`,
|
|
61128
|
+
"--json": "Accepted for symmetry; output is always JSON.",
|
|
61129
|
+
"--help, -h": "Show help without fetching, pushing or starting a daemon."
|
|
61130
|
+
},
|
|
61131
|
+
results: "published, up-to-date, already-processed, human-action-required, busy (ok) or failed (ok: false with the run as data).",
|
|
61132
|
+
writes: "the writer state, lock and owned worktree under the repository runtime state directory; one commit on the owned worktree; a push to refs/heads/<--branch> only. Outbound: the projection-publishing egress channel (git fetch/ls-remote/push, gh, the publisher command); ARCHCONTEXT_EGRESS_MODE=local-only refuses it with AC_POLICY_VIOLATION. archctx stores no credentials. It never runs codegraph init; when code facts are required, run codegraph init <worktreePath> once (see projection publish-status).",
|
|
61133
|
+
capability: "projection-publish-v1"
|
|
61134
|
+
};
|
|
61135
|
+
var CLI_PROJECTION_PUBLISH_STATUS_HELP = {
|
|
61136
|
+
usage: "archctx projection publish-status [--base <branch>] [--remote <name>] [--json]",
|
|
61137
|
+
description: "Read-only state of the projection writer for one remote and base: the owned worktree path, a running writer's pid, the last processed and last successful base SHAs and the last run.",
|
|
61138
|
+
options: {
|
|
61139
|
+
"--base <branch>": "Default: main.",
|
|
61140
|
+
"--remote <name>": "Default: origin.",
|
|
61141
|
+
"--json": "Accepted for symmetry; output is always JSON.",
|
|
61142
|
+
"--help, -h": "Show help without reading anything."
|
|
61143
|
+
},
|
|
61144
|
+
writes: "none; no fetch, no daemon.",
|
|
61145
|
+
capability: "projection-publish-v1"
|
|
61146
|
+
};
|
|
61147
|
+
var CLI_PROJECTION_VERIFY_PR_HELP = {
|
|
61148
|
+
usage: "archctx projection verify-pr --base <ref> [--json]",
|
|
61149
|
+
description: "Checks the checked-out pull request head (HEAD, clean tracked worktree) against --base. A changed path is generated only when it is the projection manifest, a manifest target path or an agent-context target path at HEAD or at the merge base, and, for a mixed-ownership target, its text outside the generated regions is unchanged (otherwise it is listed in humanEditedPaths). Only a generated-only diff is rendered (projection check, plus the agent-context render for changed contract files). verdict is no-changes, touches-non-generated (answered from the diff alone, nothing rendered), generated-only-render-equivalent (generated files and regions equal a fresh render and no text outside them changed) or generated-only-not-render-equivalent. A mixed target that is added or deleted, or whose markers do not parse, is conservatively a human edit. The envelope is ok for every valid evaluation; the verdict carries the decision.",
|
|
61150
|
+
options: {
|
|
61151
|
+
"--base <ref>": "Required. The pull request base, for example origin/main.",
|
|
61152
|
+
"--json": "Accepted for symmetry; output is always JSON.",
|
|
61153
|
+
"--help, -h": "Show help without reading the repository."
|
|
61154
|
+
},
|
|
61155
|
+
writes: "none; no daemon, no local store. A dirty tracked worktree fails with AC_PRECONDITION_FAILED. A generated-only diff renders in-process like projection check, so it needs code facts when the manifest requires them.",
|
|
61156
|
+
capability: "projection-verify-pr-v1"
|
|
61157
|
+
};
|
|
60131
61158
|
function refactorFlagValue(args, flag) {
|
|
60132
61159
|
const index = args.indexOf(flag);
|
|
60133
61160
|
if (index === -1)
|
|
@@ -60264,14 +61291,14 @@ async function runCliUnchecked(command = "help", args = [], cwd, deps = {}) {
|
|
|
60264
61291
|
case "status":
|
|
60265
61292
|
return (await runtime()).runtimeStatus(cwd);
|
|
60266
61293
|
case "projection":
|
|
60267
|
-
return await runProjectionProtocolCommand2(args, cwd, runtime);
|
|
61294
|
+
return await runProjectionProtocolCommand2(args, cwd, runtime, deps);
|
|
60268
61295
|
case "flows":
|
|
60269
61296
|
return runFlowsCommand(args, cwd);
|
|
60270
61297
|
case "repo": {
|
|
60271
61298
|
const subcommand = args[0] ?? "list";
|
|
60272
61299
|
if (subcommand === "add") {
|
|
60273
61300
|
const root = readFlag(args, "--root") ?? cwd;
|
|
60274
|
-
if (
|
|
61301
|
+
if (resolve35(root) !== resolve35(cwd)) {
|
|
60275
61302
|
return errorEnvelope("repo.add", "AC_CAPABILITY_UNSUPPORTED", "Multi-repo architecture context is outside the Local Core MVP; run archctx inside one Git repository.");
|
|
60276
61303
|
}
|
|
60277
61304
|
const daemon = await runtime();
|
|
@@ -60615,6 +61642,12 @@ function cliHelpEnvelope(command, args) {
|
|
|
60615
61642
|
case "projection":
|
|
60616
61643
|
if (subcommand === "freshness")
|
|
60617
61644
|
return okEnvelope("help", CLI_PROJECTION_FRESHNESS_HELP);
|
|
61645
|
+
if (subcommand === "publish")
|
|
61646
|
+
return okEnvelope("help", CLI_PROJECTION_PUBLISH_HELP);
|
|
61647
|
+
if (subcommand === "publish-status")
|
|
61648
|
+
return okEnvelope("help", CLI_PROJECTION_PUBLISH_STATUS_HELP);
|
|
61649
|
+
if (subcommand === "verify-pr")
|
|
61650
|
+
return okEnvelope("help", CLI_PROJECTION_VERIFY_PR_HELP);
|
|
60618
61651
|
break;
|
|
60619
61652
|
case "daemon":
|
|
60620
61653
|
return okEnvelope("daemon", {
|
|
@@ -61331,8 +62364,10 @@ async function runAgentContextProjectionCommand2(args, cwd, daemon) {
|
|
|
61331
62364
|
expectedWorktreeDigest: readFlag(args, "--expected-worktree-digest")
|
|
61332
62365
|
});
|
|
61333
62366
|
}
|
|
61334
|
-
async function runProjectionProtocolCommand2(args, cwd, runtime) {
|
|
62367
|
+
async function runProjectionProtocolCommand2(args, cwd, runtime, deps) {
|
|
61335
62368
|
const action = args[0] ?? "run";
|
|
62369
|
+
if (action === "publish" || action === "publish-status" || action === "verify-pr")
|
|
62370
|
+
return runProjectionWriterCliCommand(action, args.slice(1), cwd, deps);
|
|
61336
62371
|
if (action === "snapshot") {
|
|
61337
62372
|
const unexpected = args.slice(1).filter((arg) => arg !== "--json");
|
|
61338
62373
|
if (unexpected.length > 0)
|
|
@@ -61342,7 +62377,7 @@ async function runProjectionProtocolCommand2(args, cwd, runtime) {
|
|
|
61342
62377
|
if (action === "freshness")
|
|
61343
62378
|
return runProjectionFreshnessCliCommand(args.slice(1), cwd);
|
|
61344
62379
|
if (!["run", "recover", "readback"].includes(action))
|
|
61345
|
-
return errorEnvelope("projection", "AC_SCHEMA_INVALID", "projection requires run|recover|readback --request-json <request>,
|
|
62380
|
+
return errorEnvelope("projection", "AC_SCHEMA_INVALID", "projection requires run|recover|readback --request-json <request>, snapshot|freshness [--json], or publish|publish-status|verify-pr");
|
|
61346
62381
|
const raw = readFlag(args, "--request-json");
|
|
61347
62382
|
if (!raw)
|
|
61348
62383
|
return errorEnvelope(`projection.${action}`, "AC_SCHEMA_INVALID", `projection ${action} requires --request-json`);
|
|
@@ -61379,6 +62414,145 @@ function runProjectionFreshnessCliCommand(args, cwd) {
|
|
|
61379
62414
|
}
|
|
61380
62415
|
return runProjectionFreshnessCommand(cwd, changedPaths.length > 0 ? { changedPaths } : {});
|
|
61381
62416
|
}
|
|
62417
|
+
async function runProjectionWriterCliCommand(action, args, cwd, deps) {
|
|
62418
|
+
const requestId = `projection.${action}`;
|
|
62419
|
+
const valueFlags = action === "publish" ? PROJECTION_PUBLISH_VALUE_FLAGS : action === "publish-status" ? PROJECTION_PUBLISH_STATUS_VALUE_FLAGS : PROJECTION_VERIFY_PR_VALUE_FLAGS;
|
|
62420
|
+
const stray = projectionWriterStrayToken(args, valueFlags);
|
|
62421
|
+
if (stray !== undefined)
|
|
62422
|
+
return errorEnvelope(requestId, "AC_SCHEMA_INVALID", `projection ${action} does not accept ${stray}; use --help`, "cli-input-invalid");
|
|
62423
|
+
if (action === "verify-pr") {
|
|
62424
|
+
const base = readFlag(args, "--base");
|
|
62425
|
+
if (!base)
|
|
62426
|
+
return errorEnvelope(requestId, "AC_SCHEMA_INVALID", "projection verify-pr requires --base <ref>", "cli-input-invalid");
|
|
62427
|
+
return runProjectionVerifyPrCommand({ cwd, base });
|
|
62428
|
+
}
|
|
62429
|
+
if (action === "publish-status") {
|
|
62430
|
+
return runProjectionPublishStatusCommand({ cwd, base: readFlag(args, "--base"), remote: readFlag(args, "--remote") });
|
|
62431
|
+
}
|
|
62432
|
+
const parsed = parseProjectionPublishCliInput(args, cwd);
|
|
62433
|
+
if (!parsed.ok)
|
|
62434
|
+
return parsed.envelope;
|
|
62435
|
+
if (parsed.input.follow) {
|
|
62436
|
+
return errorEnvelope(requestId, "AC_SCHEMA_INVALID", "projection publish --follow is a foreground process loop of the archctx executable; without --follow this command runs once", "cli-input-invalid");
|
|
62437
|
+
}
|
|
62438
|
+
return runProjectionPublishOnce(parsed.input, cwd, deps);
|
|
62439
|
+
}
|
|
62440
|
+
function projectionWriterStrayToken(args, valueFlags) {
|
|
62441
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
62442
|
+
const token = args[index];
|
|
62443
|
+
if (valueFlags.includes(token) || token === "--format") {
|
|
62444
|
+
index += 1;
|
|
62445
|
+
continue;
|
|
62446
|
+
}
|
|
62447
|
+
if (isCliFlagToken(token))
|
|
62448
|
+
continue;
|
|
62449
|
+
return token;
|
|
62450
|
+
}
|
|
62451
|
+
return;
|
|
62452
|
+
}
|
|
62453
|
+
function parseProjectionPublishCliInput(args, cwd) {
|
|
62454
|
+
const invalid = (message) => ({ ok: false, envelope: errorEnvelope("projection.publish", "AC_SCHEMA_INVALID", message, "cli-input-invalid") });
|
|
62455
|
+
const publisherKind = readFlag(args, "--publisher") ?? "gh";
|
|
62456
|
+
const publisherCommand = readFlag(args, "--publisher-command");
|
|
62457
|
+
const follow = args.includes("--follow");
|
|
62458
|
+
const intervalFlag = readFlag(args, "--interval");
|
|
62459
|
+
if (intervalFlag !== undefined && !follow)
|
|
62460
|
+
return invalid("projection publish --interval requires --follow");
|
|
62461
|
+
const intervalSeconds = intervalFlag === undefined ? PROJECTION_PUBLISH_DEFAULT_INTERVAL_SECONDS : Number(intervalFlag);
|
|
62462
|
+
if (!/^\d+$/.test(intervalFlag ?? "0") || !Number.isSafeInteger(intervalSeconds) || intervalSeconds < PROJECTION_PUBLISH_MIN_INTERVAL_SECONDS) {
|
|
62463
|
+
return invalid(`projection publish --interval must be an integer number of seconds, at least ${PROJECTION_PUBLISH_MIN_INTERVAL_SECONDS}`);
|
|
62464
|
+
}
|
|
62465
|
+
let publisher;
|
|
62466
|
+
if (publisherKind === "gh" || publisherKind === "none") {
|
|
62467
|
+
if (publisherCommand !== undefined)
|
|
62468
|
+
return invalid("projection publish --publisher-command requires --publisher command");
|
|
62469
|
+
publisher = publisherKind === "gh" ? createGhProjectionPublisher() : createNoneProjectionPublisher();
|
|
62470
|
+
} else if (publisherKind === "command") {
|
|
62471
|
+
if (!publisherCommand)
|
|
62472
|
+
return invalid("projection publish --publisher command requires --publisher-command <executable>");
|
|
62473
|
+
const command = resolve35(cwd, publisherCommand);
|
|
62474
|
+
if (!existsSync35(command) || !statSync11(command).isFile())
|
|
62475
|
+
return invalid(`projection publish --publisher-command is not a file: ${command}`);
|
|
62476
|
+
publisher = createCommandProjectionPublisher({ command });
|
|
62477
|
+
} else {
|
|
62478
|
+
return invalid("projection publish --publisher must be gh, command or none");
|
|
62479
|
+
}
|
|
62480
|
+
return {
|
|
62481
|
+
ok: true,
|
|
62482
|
+
input: {
|
|
62483
|
+
remote: readFlag(args, "--remote"),
|
|
62484
|
+
base: readFlag(args, "--base"),
|
|
62485
|
+
branch: readFlag(args, "--branch"),
|
|
62486
|
+
publisher,
|
|
62487
|
+
acceptObservedMajorChange: args.includes("--accept-observed-major-change"),
|
|
62488
|
+
follow,
|
|
62489
|
+
intervalSeconds
|
|
62490
|
+
}
|
|
62491
|
+
};
|
|
62492
|
+
}
|
|
62493
|
+
function runProjectionPublishOnce(input, cwd, deps) {
|
|
62494
|
+
const clients = new Map;
|
|
62495
|
+
return runProjectionPublishCommand({
|
|
62496
|
+
cwd,
|
|
62497
|
+
remote: input.remote,
|
|
62498
|
+
base: input.base,
|
|
62499
|
+
branch: input.branch,
|
|
62500
|
+
publisher: input.publisher,
|
|
62501
|
+
acceptObservedMajorChange: input.acceptObservedMajorChange,
|
|
62502
|
+
apply: async (worktree, request) => {
|
|
62503
|
+
let client = deps.runtimeClient ?? clients.get(worktree);
|
|
62504
|
+
if (!client) {
|
|
62505
|
+
client = await createOrStartRuntimeRpcClient(worktree);
|
|
62506
|
+
clients.set(worktree, client);
|
|
62507
|
+
}
|
|
62508
|
+
return client.projection(worktree, { action: "run", request });
|
|
62509
|
+
}
|
|
62510
|
+
});
|
|
62511
|
+
}
|
|
62512
|
+
async function runProjectionPublishFollowEntry(args, cwd) {
|
|
62513
|
+
const stray = projectionWriterStrayToken(args, PROJECTION_PUBLISH_VALUE_FLAGS);
|
|
62514
|
+
const parsed = stray === undefined ? parseProjectionPublishCliInput(args, cwd) : { ok: false, envelope: errorEnvelope("projection.publish", "AC_SCHEMA_INVALID", `projection publish does not accept ${stray}; use --help`, "cli-input-invalid") };
|
|
62515
|
+
if (!parsed.ok) {
|
|
62516
|
+
process.stdout.write(`${JSON.stringify(parsed.envelope)}
|
|
62517
|
+
`);
|
|
62518
|
+
return 1;
|
|
62519
|
+
}
|
|
62520
|
+
const controller = new AbortController;
|
|
62521
|
+
const stop = () => controller.abort();
|
|
62522
|
+
process.on("SIGINT", stop);
|
|
62523
|
+
process.on("SIGTERM", stop);
|
|
62524
|
+
try {
|
|
62525
|
+
await runProjectionPublishFollow({
|
|
62526
|
+
runOnce: () => runProjectionPublishOnce(parsed.input, cwd, {}),
|
|
62527
|
+
intervalMs: parsed.input.intervalSeconds * 1000,
|
|
62528
|
+
sleep: (ms) => sleepUnlessAborted(ms, controller.signal),
|
|
62529
|
+
shouldStop: () => controller.signal.aborted,
|
|
62530
|
+
onResult: (envelope) => {
|
|
62531
|
+
process.stdout.write(`${JSON.stringify(envelope)}
|
|
62532
|
+
`);
|
|
62533
|
+
}
|
|
62534
|
+
});
|
|
62535
|
+
} finally {
|
|
62536
|
+
process.off("SIGINT", stop);
|
|
62537
|
+
process.off("SIGTERM", stop);
|
|
62538
|
+
}
|
|
62539
|
+
return 0;
|
|
62540
|
+
}
|
|
62541
|
+
function sleepUnlessAborted(ms, signal) {
|
|
62542
|
+
return new Promise((resolvePromise) => {
|
|
62543
|
+
if (signal.aborted) {
|
|
62544
|
+
resolvePromise();
|
|
62545
|
+
return;
|
|
62546
|
+
}
|
|
62547
|
+
const timer = setTimeout(done, ms);
|
|
62548
|
+
function done() {
|
|
62549
|
+
clearTimeout(timer);
|
|
62550
|
+
signal.removeEventListener("abort", done);
|
|
62551
|
+
resolvePromise();
|
|
62552
|
+
}
|
|
62553
|
+
signal.addEventListener("abort", done, { once: true });
|
|
62554
|
+
});
|
|
62555
|
+
}
|
|
61382
62556
|
function runFlowsCommand(args, cwd) {
|
|
61383
62557
|
const formatAt = args.indexOf("--format");
|
|
61384
62558
|
const unexpected = args.slice(1).filter((arg, index) => arg !== "--json" && (formatAt < 0 || index + 1 !== formatAt && index !== formatAt));
|
|
@@ -61742,12 +62916,12 @@ function auditManifestGateRoot(cwd) {
|
|
|
61742
62916
|
}
|
|
61743
62917
|
}
|
|
61744
62918
|
function auditGithubIssuesEnabled(cwd) {
|
|
61745
|
-
const manifestPath =
|
|
61746
|
-
if (!
|
|
62919
|
+
const manifestPath = resolve35(auditManifestGateRoot(cwd), ".archcontext/manifest.yaml");
|
|
62920
|
+
if (!existsSync35(manifestPath))
|
|
61747
62921
|
return false;
|
|
61748
62922
|
let raw;
|
|
61749
62923
|
try {
|
|
61750
|
-
raw =
|
|
62924
|
+
raw = readFileSync32(manifestPath, "utf8");
|
|
61751
62925
|
} catch {
|
|
61752
62926
|
return false;
|
|
61753
62927
|
}
|
|
@@ -62032,8 +63206,8 @@ async function runGithubCommand(args, cwd, deps) {
|
|
|
62032
63206
|
}
|
|
62033
63207
|
if (subcommand === "disconnect") {
|
|
62034
63208
|
const connection = await deps.githubConnectionReader?.readVerifiedConnection({ repositoryRoot: cwd });
|
|
62035
|
-
const localRecordRemoved =
|
|
62036
|
-
|
|
63209
|
+
const localRecordRemoved = existsSync35(connectionPath);
|
|
63210
|
+
rmSync16(connectionPath, { force: true });
|
|
62037
63211
|
return okEnvelope("github.disconnect", {
|
|
62038
63212
|
connected: connection !== undefined,
|
|
62039
63213
|
localRecordRemoved,
|
|
@@ -62320,7 +63494,7 @@ async function digestReviewChallenge(challenge) {
|
|
|
62320
63494
|
return reviewChallengeV2Digest(challenge);
|
|
62321
63495
|
}
|
|
62322
63496
|
function defaultGithubConnectionPath(cwd) {
|
|
62323
|
-
return
|
|
63497
|
+
return join27(dirname16(defaultDaemonConnectionPath(cwd)), "github-connection.json");
|
|
62324
63498
|
}
|
|
62325
63499
|
function sanitizeGithubConnection(record, connectionPath) {
|
|
62326
63500
|
return {
|
|
@@ -62344,7 +63518,7 @@ async function readReviewChallengeV2Arg(args, cwd, commandName = "github verify-
|
|
|
62344
63518
|
if (!inline && !challengePath)
|
|
62345
63519
|
return { ok: false, message: `${commandName} requires --challenge-json or --challenge-path` };
|
|
62346
63520
|
try {
|
|
62347
|
-
const raw = inline ??
|
|
63521
|
+
const raw = inline ?? readFileSync32(resolve35(cwd, challengePath), "utf8");
|
|
62348
63522
|
const parsed = JSON.parse(raw);
|
|
62349
63523
|
const attestation = await Promise.resolve().then(() => (init_src6(), {}));
|
|
62350
63524
|
const assertReviewChallengeV2 = assertReviewChallengeV22;
|
|
@@ -62728,7 +63902,7 @@ function readLatestPackageVersion(env) {
|
|
|
62728
63902
|
if (env[LATEST_VERSION_ENV]) {
|
|
62729
63903
|
return { source: "env", version: env[LATEST_VERSION_ENV] };
|
|
62730
63904
|
}
|
|
62731
|
-
const result = withLocalEgress("npm-update-check", () =>
|
|
63905
|
+
const result = withLocalEgress("npm-update-check", () => spawnSync6("npm", ["view", RELEASE_PACKAGE_NAME, "version", "--json"], {
|
|
62732
63906
|
encoding: "utf8",
|
|
62733
63907
|
timeout: NPM_VIEW_TIMEOUT_MS,
|
|
62734
63908
|
shell: false
|
|
@@ -62822,10 +63996,10 @@ function doctorSqlite(cwd) {
|
|
|
62822
63996
|
const legacyLocalStore = inspectLegacyLocalStoreMigration(cwd);
|
|
62823
63997
|
return {
|
|
62824
63998
|
path,
|
|
62825
|
-
exists:
|
|
63999
|
+
exists: existsSync35(path),
|
|
62826
64000
|
migrations: productVersionManifest().runtime.sqliteMigrations,
|
|
62827
64001
|
legacyPath: paths.legacyLocalStorePath,
|
|
62828
|
-
legacyExists:
|
|
64002
|
+
legacyExists: existsSync35(paths.legacyLocalStorePath),
|
|
62829
64003
|
legacyLocalStore
|
|
62830
64004
|
};
|
|
62831
64005
|
}
|
|
@@ -62846,8 +64020,8 @@ function runtimePathsReport(cwd) {
|
|
|
62846
64020
|
...paths,
|
|
62847
64021
|
legacyLocalStore: inspectLegacyLocalStoreMigration(cwd),
|
|
62848
64022
|
runtimeRepositoryId: repositoryFingerprint2(paths.repositoryRoot),
|
|
62849
|
-
repositoryTruthDir:
|
|
62850
|
-
codeGraphIndexDir:
|
|
64023
|
+
repositoryTruthDir: join27(paths.repositoryRoot, ".archcontext"),
|
|
64024
|
+
codeGraphIndexDir: join27(paths.repositoryRoot, ".codegraph"),
|
|
62851
64025
|
npmGlobalInstallState: "forbidden",
|
|
62852
64026
|
overrides: {
|
|
62853
64027
|
stateRootEnv: "ARCHCONTEXT_STATE_DIR",
|
|
@@ -62856,7 +64030,7 @@ function runtimePathsReport(cwd) {
|
|
|
62856
64030
|
};
|
|
62857
64031
|
}
|
|
62858
64032
|
function pathAccess(path) {
|
|
62859
|
-
const exists =
|
|
64033
|
+
const exists = existsSync35(path);
|
|
62860
64034
|
return {
|
|
62861
64035
|
path,
|
|
62862
64036
|
exists,
|
|
@@ -63097,7 +64271,7 @@ function daemonStopStateDir(args, cwd) {
|
|
|
63097
64271
|
return { ok: false, envelope: errorEnvelope("daemon.stop", "AC_SCHEMA_INVALID", "daemon stop accepts at most one of --state-dir or --workspace-id") };
|
|
63098
64272
|
}
|
|
63099
64273
|
if (stateDir !== undefined)
|
|
63100
|
-
return { ok: true, stateDir:
|
|
64274
|
+
return { ok: true, stateDir: resolve35(cwd, stateDir) };
|
|
63101
64275
|
if (workspaceId === undefined)
|
|
63102
64276
|
return { ok: true };
|
|
63103
64277
|
if (!/^ws\.[0-9a-f]{16}$/.test(workspaceId)) {
|
|
@@ -63133,9 +64307,9 @@ async function startBackgroundDaemon(args, cwd) {
|
|
|
63133
64307
|
const recovery = discovered.recovery;
|
|
63134
64308
|
const connectionPath = defaultDaemonConnectionPath(cwd);
|
|
63135
64309
|
const controlDir = dirname16(connectionPath);
|
|
63136
|
-
const logPath =
|
|
63137
|
-
|
|
63138
|
-
const logFd =
|
|
64310
|
+
const logPath = join27(controlDir, "archctxd.log");
|
|
64311
|
+
mkdirSync14(controlDir, { recursive: true });
|
|
64312
|
+
const logFd = openSync13(logPath, "a", 384);
|
|
63139
64313
|
try {
|
|
63140
64314
|
let childExit;
|
|
63141
64315
|
let childError;
|
|
@@ -63175,7 +64349,7 @@ async function startBackgroundDaemon(args, cwd) {
|
|
|
63175
64349
|
...recoveryData(recovery)
|
|
63176
64350
|
});
|
|
63177
64351
|
} finally {
|
|
63178
|
-
|
|
64352
|
+
closeSync13(logFd);
|
|
63179
64353
|
}
|
|
63180
64354
|
}
|
|
63181
64355
|
async function upgradeDaemon(args, cwd) {
|
|
@@ -63184,7 +64358,7 @@ async function upgradeDaemon(args, cwd) {
|
|
|
63184
64358
|
const record = readRuntimeRpcConnectionFile(cwd);
|
|
63185
64359
|
const unchanged = () => JSON.stringify(readRuntimeRpcConnectionFile(cwd)) === JSON.stringify(record);
|
|
63186
64360
|
const refuse = (reason) => errorEnvelope("daemon.upgrade", "AC_RUNTIME_VERSION_UNSUPPORTED", `Daemon ownership is unproven (${reason}); no process was signalled and no replacement was started. Run the previous archctx daemon stop, or verify and stop the original daemon in its owning terminal, then run archctx daemon upgrade.`, "daemon-ownership-unproven");
|
|
63187
|
-
if (
|
|
64361
|
+
if (existsSync35(connectionPath) && (!record || typeof record.pid !== "number" || !Number.isInteger(record.pid) || record.pid <= 0)) {
|
|
63188
64362
|
return refuse("connection record cannot prove a daemon identity");
|
|
63189
64363
|
}
|
|
63190
64364
|
let lockOwner;
|
|
@@ -63234,9 +64408,9 @@ async function upgradeDaemon(args, cwd) {
|
|
|
63234
64408
|
if (!stopped.ok)
|
|
63235
64409
|
return refuse("live instance refused the expected identity");
|
|
63236
64410
|
const deadline = Date.now() + daemonStartTimeoutMs(args);
|
|
63237
|
-
while (
|
|
64411
|
+
while (existsSync35(connectionPath) || existsSync35(lockPath)) {
|
|
63238
64412
|
const current = readRuntimeRpcConnectionFile(cwd);
|
|
63239
|
-
if (current ? JSON.stringify(current) !== JSON.stringify(record) :
|
|
64413
|
+
if (current ? JSON.stringify(current) !== JSON.stringify(record) : existsSync35(connectionPath)) {
|
|
63240
64414
|
return refuse("connection changed or cannot be verified during self-stop");
|
|
63241
64415
|
}
|
|
63242
64416
|
const owner = liveDaemonLockOwnerPid(cwd);
|
|
@@ -63256,7 +64430,7 @@ async function upgradeDaemon(args, cwd) {
|
|
|
63256
64430
|
const started = await startBackgroundDaemon(args, cwd);
|
|
63257
64431
|
return started.ok ? { ...started, requestId: "daemon.upgrade", data: { ...started.data, upgraded: false, reason: "runtime-compatible" } } : started;
|
|
63258
64432
|
}
|
|
63259
|
-
if (selfStopped ?
|
|
64433
|
+
if (selfStopped ? existsSync35(connectionPath) || existsSync35(lockPath) : !unchanged() || record?.pid !== undefined && isPidAlive(record.pid) || liveDaemonLockOwnerPid(cwd) !== undefined) {
|
|
63260
64434
|
return refuse("ownership changed before replacement");
|
|
63261
64435
|
}
|
|
63262
64436
|
const recovery = recoverStaleDaemonControlFiles(cwd, { removeUnhealthyConnection: true });
|
|
@@ -63422,7 +64596,7 @@ function daemonStartTimeoutMs(args) {
|
|
|
63422
64596
|
}
|
|
63423
64597
|
function daemonStartFailureMessage(cwd, logPath, childPid, childExit, childError) {
|
|
63424
64598
|
const childState = childError ? `childError=${childError.message}` : childExit ? `childExit=${childExit.code ?? "null"}/${childExit.signal ?? "none"}` : "childState=still-running";
|
|
63425
|
-
const lockOwnerPid =
|
|
64599
|
+
const lockOwnerPid = existsSync35(defaultDaemonConnectionPath(cwd)) ? undefined : liveDaemonLockOwnerPid(cwd);
|
|
63426
64600
|
const owner = lockOwnerPid === undefined || lockOwnerPid === childPid ? "" : `; ${orphanedDaemonMessage(lockOwnerPid)}`;
|
|
63427
64601
|
return `archctxd did not become ready; ${childState}${owner}; log=${logPath}; logTail=${readFileTail(logPath)}`;
|
|
63428
64602
|
}
|
|
@@ -63443,7 +64617,7 @@ function sleep2(ms) {
|
|
|
63443
64617
|
}
|
|
63444
64618
|
function readFileTail(path, maxBytes = 4096) {
|
|
63445
64619
|
try {
|
|
63446
|
-
const content =
|
|
64620
|
+
const content = readFileSync32(path, "utf8");
|
|
63447
64621
|
return content.slice(Math.max(0, content.length - maxBytes)).replace(/\s+/g, " ").trim();
|
|
63448
64622
|
} catch {
|
|
63449
64623
|
return "<unavailable>";
|
|
@@ -63528,7 +64702,7 @@ function resolveCommandExitCode(result) {
|
|
|
63528
64702
|
return 2;
|
|
63529
64703
|
return 1;
|
|
63530
64704
|
}
|
|
63531
|
-
var CLI_HELP_EXAMPLES = ["archctx init --name MyApp", "archctx projection run --request-json '{...}'", "archctx projection snapshot --json", "archctx projection freshness --json", "archctx projection freshness --changed-path src/app.ts --json", "archctx plan --id changeset.<id> --op update_entity_fields --path .archcontext/model/nodes/<node>.yaml --expected-hash sha256:<64-hex> --body '<complete YAML>'", "archctx plan --id changeset.<id> --op delete_entity --path .archcontext/model/flows/<flow>.yaml --expected-hash sha256:<64-hex>", "archctx hash --path .archcontext/model/nodes/<node>.yaml", "archctx plan --id changeset.<id> --operations-file <operations.json>", "archctx plan --id changeset.<id> --op update_entity_fields --path .archcontext/model/nodes/<node>.yaml --expected-hash sha256:<64-hex> --body-file <entity.yaml>", "archctx apply --id changeset.<id> --approved --expected-worktree-digest <draft.base.worktreeDigest>", "archctx state recover --from-git", "archctx ledger migrate --from-yaml --dry-run", "archctx ledger promote --mode authoritative --preflight --rollback-plan", "archctx book recommendations --open --explain", "archctx recommendations accept --id recommendation.<id> --reason 'Accepted after local readback.'", "archctx recommendations metrics", "archctx recommendations list --status rejected", "archctx refactor scan --json", "archctx refactor verify --request-json '{...}' --json", "archctx practices validate --strict", "archctx practices list --json", "archctx practices waivers", "archctx practices waive --practice-id modularity.no-new-cycle --owner team-architecture --reason 'External migration window requires this edge until cutover.' --review-at 2026-07-10T00:00:00.000Z --expires-at 2026-07-24T00:00:00.000Z --evidence-digest sha256:<64-hex> --subject module.a->module.b", "archctx checkpoint --changed-path src/app.ts", "archctx practices checkpoint --task-session-id task_cli", "archctx investigate --runner-port codex", "archctx agents status --status queued,running", "archctx agents budget", "archctx hook enqueue --event post-edit --path src/app.ts", "archctx jobs list --status queued", "archctx audit consent", "archctx audit consent --revoke", "archctx audit run --reason 'quarterly architecture audit'", "archctx audit run --no-wait", "archctx audit list --status pending", "archctx audit show audit_run.<id>", "archctx audit approve audit_run.<id>", "archctx audit approve audit_run.<id> --confirm-public-repo public:<host>/<owner>/<repo>:<baseSha>:<runId>", "archctx audit approve audit_run.<id> --resume", "archctx hooks install --host codex", "archctx paths", "archctx update --check", "archctx doctor --check-updates", "archctx github connect", "archctx github status", "archctx daemon start", "archctx explore start --foreground", "archctx docs anchors --json", "archctx export native --worktree --json", "archctx export native --ref HEAD --profile repo-harness/v1 --json", "archctx export likec4", "archctx import structurizr --content '<json>'", "archctx resolve --path packages/core/projection-engine/src/index.ts", "archctx tunnel", "archctx refactor scan --import-node-limit 100000 --json", "archctx resolve --paths-from changed-paths.txt", "archctx flows check --json", "archctx recommendations defer --id recommendation.<id> --import-node-limit 100000 --reason 'Scanned with the same limit.'", "archctx deps --json", "archctx deps --into capability.<id> --json"];
|
|
64705
|
+
var CLI_HELP_EXAMPLES = ["archctx init --name MyApp", "archctx projection run --request-json '{...}'", "archctx projection snapshot --json", "archctx projection freshness --json", "archctx projection freshness --changed-path src/app.ts --json", "archctx projection publish --json", "archctx projection publish --publisher none --json", "archctx projection publish --follow --interval 300", "archctx projection publish-status --json", "archctx projection verify-pr --base origin/main --json", "archctx plan --id changeset.<id> --op update_entity_fields --path .archcontext/model/nodes/<node>.yaml --expected-hash sha256:<64-hex> --body '<complete YAML>'", "archctx plan --id changeset.<id> --op delete_entity --path .archcontext/model/flows/<flow>.yaml --expected-hash sha256:<64-hex>", "archctx hash --path .archcontext/model/nodes/<node>.yaml", "archctx plan --id changeset.<id> --operations-file <operations.json>", "archctx plan --id changeset.<id> --op update_entity_fields --path .archcontext/model/nodes/<node>.yaml --expected-hash sha256:<64-hex> --body-file <entity.yaml>", "archctx apply --id changeset.<id> --approved --expected-worktree-digest <draft.base.worktreeDigest>", "archctx state recover --from-git", "archctx ledger migrate --from-yaml --dry-run", "archctx ledger promote --mode authoritative --preflight --rollback-plan", "archctx book recommendations --open --explain", "archctx recommendations accept --id recommendation.<id> --reason 'Accepted after local readback.'", "archctx recommendations metrics", "archctx recommendations list --status rejected", "archctx refactor scan --json", "archctx refactor verify --request-json '{...}' --json", "archctx practices validate --strict", "archctx practices list --json", "archctx practices waivers", "archctx practices waive --practice-id modularity.no-new-cycle --owner team-architecture --reason 'External migration window requires this edge until cutover.' --review-at 2026-07-10T00:00:00.000Z --expires-at 2026-07-24T00:00:00.000Z --evidence-digest sha256:<64-hex> --subject module.a->module.b", "archctx checkpoint --changed-path src/app.ts", "archctx practices checkpoint --task-session-id task_cli", "archctx investigate --runner-port codex", "archctx agents status --status queued,running", "archctx agents budget", "archctx hook enqueue --event post-edit --path src/app.ts", "archctx jobs list --status queued", "archctx audit consent", "archctx audit consent --revoke", "archctx audit run --reason 'quarterly architecture audit'", "archctx audit run --no-wait", "archctx audit list --status pending", "archctx audit show audit_run.<id>", "archctx audit approve audit_run.<id>", "archctx audit approve audit_run.<id> --confirm-public-repo public:<host>/<owner>/<repo>:<baseSha>:<runId>", "archctx audit approve audit_run.<id> --resume", "archctx hooks install --host codex", "archctx paths", "archctx update --check", "archctx doctor --check-updates", "archctx github connect", "archctx github status", "archctx daemon start", "archctx explore start --foreground", "archctx docs anchors --json", "archctx export native --worktree --json", "archctx export native --ref HEAD --profile repo-harness/v1 --json", "archctx export likec4", "archctx import structurizr --content '<json>'", "archctx resolve --path packages/core/projection-engine/src/index.ts", "archctx tunnel", "archctx refactor scan --import-node-limit 100000 --json", "archctx resolve --paths-from changed-paths.txt", "archctx flows check --json", "archctx recommendations defer --id recommendation.<id> --import-node-limit 100000 --reason 'Scanned with the same limit.'", "archctx deps --json", "archctx deps --into capability.<id> --json"];
|
|
63532
64706
|
var CLI_RESOLVE_HELP = {
|
|
63533
64707
|
command: "archctx resolve (--path <repo-relative path> | --paths-from <file> | --paths-from -)",
|
|
63534
64708
|
rule: "ADR-0043. source.exclude applies first. When every matching node lies on one parent chain, the deepest node owns the path; refactor scan uses the same rule. When matching nodes lie on different chains, an ancestor yields to its own descendant, then the longest literal source.include prefix wins, and an equal-length tie is ambiguous.",
|
|
@@ -63537,7 +64711,7 @@ var CLI_RESOLVE_HELP = {
|
|
|
63537
64711
|
exitCodes: "--path: 0 matched, 1 no match or error, 2 ambiguous. --paths-from: 0 when no path is ambiguous (unowned paths included), 2 when any path is ambiguous, 1 on error."
|
|
63538
64712
|
};
|
|
63539
64713
|
function loadResolveModel(cwd) {
|
|
63540
|
-
if (!
|
|
64714
|
+
if (!existsSync35(join27(cwd, ".archcontext/manifest.yaml"))) {
|
|
63541
64715
|
throw new NativeContextError("native-context-model-missing", { path: ".archcontext/manifest.yaml" });
|
|
63542
64716
|
}
|
|
63543
64717
|
let model;
|
|
@@ -63593,7 +64767,7 @@ async function readResolvePathsFrom(args, cwd, deps) {
|
|
|
63593
64767
|
return { problem: "resolve --paths-from requires a file path or - for stdin" };
|
|
63594
64768
|
let content;
|
|
63595
64769
|
try {
|
|
63596
|
-
content = source === "-" ? await (deps.readStdin ? deps.readStdin() :
|
|
64770
|
+
content = source === "-" ? await (deps.readStdin ? deps.readStdin() : readFileSync32(0, "utf8")) : readFileSync32(resolve35(cwd, source), "utf8");
|
|
63597
64771
|
} catch (error) {
|
|
63598
64772
|
return { problem: `resolve --paths-from ${source} is not readable: ${error instanceof Error ? error.message : String(error)}` };
|
|
63599
64773
|
}
|
|
@@ -63681,7 +64855,7 @@ function readRepositoryInputFile(cwd, flag, file) {
|
|
|
63681
64855
|
const absolute = assertPathHasNoSymlinkSegments2(cwd, file);
|
|
63682
64856
|
if (!statSync11(absolute).isFile())
|
|
63683
64857
|
return { problem: `plan ${flag} ${file} is not a regular file` };
|
|
63684
|
-
return { content:
|
|
64858
|
+
return { content: readFileSync32(absolute, "utf8") };
|
|
63685
64859
|
} catch (error) {
|
|
63686
64860
|
return { problem: `plan ${flag} ${file} is not readable: ${error instanceof Error ? error.message : String(error)}` };
|
|
63687
64861
|
}
|
|
@@ -63699,10 +64873,10 @@ function runHashCommand(args, cwd) {
|
|
|
63699
64873
|
} catch (error) {
|
|
63700
64874
|
return errorEnvelope("hash", "AC_SCHEMA_INVALID", error instanceof Error ? error.message : String(error));
|
|
63701
64875
|
}
|
|
63702
|
-
if (!
|
|
64876
|
+
if (!existsSync35(absolute) || !statSync11(absolute).isFile()) {
|
|
63703
64877
|
return errorEnvelope("hash", "AC_SCHEMA_INVALID", `hash --path is not an existing file: ${path}`);
|
|
63704
64878
|
}
|
|
63705
|
-
return okEnvelope("hash", { path, hash: digestJson({ body:
|
|
64879
|
+
return okEnvelope("hash", { path, hash: digestJson({ body: readFileSync32(absolute, "utf8") }) });
|
|
63706
64880
|
}
|
|
63707
64881
|
async function readCliPlanOperations(args, cwd, deps) {
|
|
63708
64882
|
if (args.includes("--operations-file")) {
|
|
@@ -63712,7 +64886,7 @@ async function readCliPlanOperations(args, cwd, deps) {
|
|
|
63712
64886
|
const file = readFlag(args, "--operations-file");
|
|
63713
64887
|
if (!file)
|
|
63714
64888
|
return "plan --operations-file requires a path";
|
|
63715
|
-
const read = file === "-" ? { content: await (deps.readStdin ? deps.readStdin() :
|
|
64889
|
+
const read = file === "-" ? { content: await (deps.readStdin ? deps.readStdin() : readFileSync32(0, "utf8")) } : readRepositoryInputFile(cwd, "--operations-file", file);
|
|
63716
64890
|
if ("problem" in read)
|
|
63717
64891
|
return read.problem;
|
|
63718
64892
|
let parsed;
|
|
@@ -63774,7 +64948,7 @@ async function readCliPlanBody(args, cwd, deps) {
|
|
|
63774
64948
|
return { problem: "plan --body requires a value (use - to read stdin)" };
|
|
63775
64949
|
if (inline !== "-")
|
|
63776
64950
|
return inline;
|
|
63777
|
-
return deps.readStdin ? deps.readStdin() :
|
|
64951
|
+
return deps.readStdin ? deps.readStdin() : readFileSync32(0, "utf8");
|
|
63778
64952
|
}
|
|
63779
64953
|
function readFlag(args, flag) {
|
|
63780
64954
|
const index = args.indexOf(flag);
|
|
@@ -63866,6 +65040,8 @@ if (__require.main == __require.module) {
|
|
|
63866
65040
|
await runStdioMcpLoop(stdinLines(), (line) => process.stdout.write(`${line}
|
|
63867
65041
|
`), (line) => process.stderr.write(`${line}
|
|
63868
65042
|
`), { runtimeResolver: (root, options) => createOrStartRuntimeRpcClient(root, options?.preserveHealthFailure) });
|
|
65043
|
+
} else if (command === "projection" && args[0] === "publish" && args.includes("--follow") && preflightCliArguments(command, args) === undefined) {
|
|
65044
|
+
process.exitCode = await runProjectionPublishFollowEntry(args.slice(1), process.cwd());
|
|
63869
65045
|
} else if (command === "daemon" && args[0] === "start" && args.includes("--foreground") && preflightCliArguments(command, args) === undefined) {
|
|
63870
65046
|
await runForegroundDaemon(process.cwd(), args).catch((error) => {
|
|
63871
65047
|
const message = error instanceof Error ? error.message : String(error);
|