@archme/cli 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/dist/cli.js +166 -81
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -10005,9 +10005,23 @@ async function tailLog(opts = {}) {
|
|
|
10005
10005
|
await new Promise((resolve6, reject) => {
|
|
10006
10006
|
let fileWatcher = null;
|
|
10007
10007
|
let dirWatcher = null;
|
|
10008
|
+
let poller = null;
|
|
10009
|
+
let settled = false;
|
|
10008
10010
|
const dir = dirname7(path7);
|
|
10009
10011
|
const name = basename(path7);
|
|
10010
|
-
const
|
|
10012
|
+
const stop = () => {
|
|
10013
|
+
settled = true;
|
|
10014
|
+
fileWatcher?.close();
|
|
10015
|
+
dirWatcher?.close();
|
|
10016
|
+
if (poller) clearInterval(poller);
|
|
10017
|
+
process.removeListener("SIGINT", handleSigint);
|
|
10018
|
+
};
|
|
10019
|
+
const fail = (err2) => {
|
|
10020
|
+
if (settled) return;
|
|
10021
|
+
stop();
|
|
10022
|
+
reject(err2);
|
|
10023
|
+
};
|
|
10024
|
+
const readNew = async () => {
|
|
10011
10025
|
try {
|
|
10012
10026
|
const st = await stat(path7);
|
|
10013
10027
|
if (st.size < offset) offset = 0;
|
|
@@ -10020,61 +10034,50 @@ async function tailLog(opts = {}) {
|
|
|
10020
10034
|
});
|
|
10021
10035
|
offset = st.size;
|
|
10022
10036
|
}
|
|
10037
|
+
if (!fileWatcher && !settled) attachFileWatcher();
|
|
10023
10038
|
} catch (err2) {
|
|
10024
10039
|
if (err2.code === "ENOENT") return;
|
|
10025
|
-
|
|
10040
|
+
fail(err2);
|
|
10026
10041
|
}
|
|
10027
10042
|
};
|
|
10028
|
-
|
|
10029
|
-
|
|
10030
|
-
|
|
10031
|
-
|
|
10032
|
-
});
|
|
10033
|
-
} catch (err2) {
|
|
10034
|
-
if (err2.code !== "ENOENT") {
|
|
10035
|
-
reject(err2);
|
|
10036
|
-
return;
|
|
10037
|
-
}
|
|
10038
|
-
attachDirWatcher();
|
|
10039
|
-
}
|
|
10043
|
+
let chain = Promise.resolve();
|
|
10044
|
+
const drain = () => {
|
|
10045
|
+
if (settled) return;
|
|
10046
|
+
chain = chain.then(readNew);
|
|
10040
10047
|
};
|
|
10041
|
-
const
|
|
10048
|
+
const attachFileWatcher = () => {
|
|
10042
10049
|
try {
|
|
10043
|
-
|
|
10044
|
-
|
|
10045
|
-
|
|
10046
|
-
|
|
10047
|
-
|
|
10048
|
-
attachFileWatcher();
|
|
10049
|
-
});
|
|
10050
|
-
}
|
|
10051
|
-
});
|
|
10052
|
-
} catch (err2) {
|
|
10053
|
-
reject(err2);
|
|
10050
|
+
fileWatcher = watch(path7, () => drain());
|
|
10051
|
+
dirWatcher?.close();
|
|
10052
|
+
dirWatcher = null;
|
|
10053
|
+
} catch {
|
|
10054
|
+
fileWatcher = null;
|
|
10054
10055
|
}
|
|
10055
10056
|
};
|
|
10056
|
-
|
|
10057
|
-
() =>
|
|
10058
|
-
|
|
10059
|
-
|
|
10060
|
-
|
|
10061
|
-
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
}
|
|
10065
|
-
);
|
|
10057
|
+
try {
|
|
10058
|
+
dirWatcher = watch(dir, (_event, filename) => {
|
|
10059
|
+
if (filename === name) drain();
|
|
10060
|
+
});
|
|
10061
|
+
} catch (err2) {
|
|
10062
|
+
fail(err2);
|
|
10063
|
+
return;
|
|
10064
|
+
}
|
|
10066
10065
|
const handleSigint = () => {
|
|
10067
|
-
|
|
10068
|
-
|
|
10066
|
+
if (settled) return;
|
|
10067
|
+
stop();
|
|
10069
10068
|
resolve6();
|
|
10070
10069
|
};
|
|
10071
10070
|
process.once("SIGINT", handleSigint);
|
|
10071
|
+
poller = setInterval(drain, FOLLOW_POLL_MS);
|
|
10072
|
+
drain();
|
|
10072
10073
|
});
|
|
10073
10074
|
}
|
|
10075
|
+
var FOLLOW_POLL_MS;
|
|
10074
10076
|
var init_logs = __esm({
|
|
10075
10077
|
"src/daemon/service/logs.ts"() {
|
|
10076
10078
|
"use strict";
|
|
10077
10079
|
init_paths();
|
|
10080
|
+
FOLLOW_POLL_MS = 500;
|
|
10078
10081
|
}
|
|
10079
10082
|
});
|
|
10080
10083
|
|
|
@@ -13294,6 +13297,13 @@ var init_sequenceChapters = __esm({
|
|
|
13294
13297
|
}
|
|
13295
13298
|
});
|
|
13296
13299
|
|
|
13300
|
+
// ../shared/architectureDiff.ts
|
|
13301
|
+
var init_architectureDiff = __esm({
|
|
13302
|
+
"../shared/architectureDiff.ts"() {
|
|
13303
|
+
init_storyChanges();
|
|
13304
|
+
}
|
|
13305
|
+
});
|
|
13306
|
+
|
|
13297
13307
|
// ../shared/walkthrough.ts
|
|
13298
13308
|
var MAX_WALKTHROUGH_STEPS, MAX_STEP_TITLE_CHARS;
|
|
13299
13309
|
var init_walkthrough = __esm({
|
|
@@ -13301,6 +13311,7 @@ var init_walkthrough = __esm({
|
|
|
13301
13311
|
init_storyChanges();
|
|
13302
13312
|
init_sequenceChapters();
|
|
13303
13313
|
init_validationCodes();
|
|
13314
|
+
init_architectureDiff();
|
|
13304
13315
|
MAX_WALKTHROUGH_STEPS = 20;
|
|
13305
13316
|
MAX_STEP_TITLE_CHARS = 60;
|
|
13306
13317
|
}
|
|
@@ -13317,13 +13328,15 @@ var init_walkthroughGuidance = __esm({
|
|
|
13317
13328
|
- **Steps are in reading order, not build order.** This is not an implementation plan and not a restatement of your sequencing section \u2014 start where a reviewer should start, and put each step where it makes the next one make sense.
|
|
13318
13329
|
- **Group by idea.** One step is one coherent change, usually 3\u20138 elements. A one-element step is fine and often valuable: an independent fix that would otherwise be lost among the rest. Aim for a handful of steps; the cap is ${MAX_WALKTHROUGH_STEPS}.
|
|
13319
13330
|
- **Titles are terse** \u2014 a handle, not a summary. Under ${MAX_STEP_TITLE_CHARS} characters. \`Markers move per-repo\`, not \`This step explains how the source marker moves onto a per-repo table\`.
|
|
13320
|
-
- **
|
|
13321
|
-
-
|
|
13331
|
+
- **Every step gets a short description** \u2014 the narration a reader follows while the diagrams move beside it, one step at a time. Two or three plain sentences: what this part of the change is, why it exists, and how it fits the rest, at a high level. Example: \`This is the daemon picking up GitHub's answer after it posts a comment, review or approval on your behalf, and recording whether it worked.\`
|
|
13332
|
+
- Not prescriptive: describe the change, do not tell the reviewer what to check or what to think of it.
|
|
13333
|
+
- Not dense: no condensed paragraph of identifiers, flags and edge cases crammed into one sentence. Never restate the components, stories or connections the step already references \u2014 the reader is looking at them.
|
|
13334
|
+
- **Write it for someone meeting this change for the first time.** They know the system; they have NOT read your proposal body and were not in the design discussion. Say plainly what the step does and why it is there.
|
|
13322
13335
|
- Don't argue with objections the reader hasn't raised \u2014 no \`not just a nicety\`, no \`as discussed\`, no \`this is why it is its own step\`. They don't know how the walkthrough was assembled and haven't disagreed with you. A trade-off worth stating is information (\`X was considered; it breaks Y\`), not a rebuttal.
|
|
13323
13336
|
- Don't use the change's OWN new vocabulary undefined. If the step introduces a term, say what it means in the same breath or use plain words.
|
|
13324
13337
|
- Bad: \`A correctness item, not a phase-3 nicety: anchors are repo-relative paths, so two repos with the same conventional layout collide silently and seed stories from the wrong one.\`
|
|
13325
13338
|
- Good: \`With two repos in one project a path like src/index.ts exists in both, so looking up which component owns a changed file can return the wrong repo's. Each repo now records the container its code lives under, and lookups search only that subtree.\`
|
|
13326
|
-
- **Cover every story the change touches
|
|
13339
|
+
- **Cover every change** \u2014 every story, component and connection the change touches \u2014 or \`set_walkthrough\` rejects the walkthrough and lists what is missing. Guided mode walks the steps and nothing after them, so anything no step reaches is a change the reviewer never sees. A connection is covered when a step names it, names both of its components, or names a story page that draws it as an arrow. Name a connection inside a component as \`<component path>::<id>\` (\`daemon::session_auth->key_account\`); a root connection by its plain id.
|
|
13327
13340
|
- **Skip it for a small change.** A handful of elements needs no walkthrough; omit it rather than writing a step per element.
|
|
13328
13341
|
- **Prefer CHAPTERS to narrow a diagram.** If a story's sequence carries \`%% chapter: <id> | <Label>\` markers, name the ones your step covers as \`chapters: [{ story, chapter }]\`, using the **id**, never the label \u2014 ids are stable across a rename, labels are not. A chapter is a page break the story's author already wrote, so it is sharper than naming components, where one hub the step mentions decides the whole view.`;
|
|
13329
13342
|
}
|
|
@@ -16129,7 +16142,9 @@ function observeGithubPull(p) {
|
|
|
16129
16142
|
createdAt: p.created_at ?? null,
|
|
16130
16143
|
updatedAt: p.updated_at ?? null,
|
|
16131
16144
|
mergedAt: p.merged_at ?? null,
|
|
16132
|
-
closedAt: p.closed_at ?? null
|
|
16145
|
+
closedAt: p.closed_at ?? null,
|
|
16146
|
+
// Before the merge it is GitHub's throwaway test merge.
|
|
16147
|
+
mergeCommitSha: p.merged_at ? p.merge_commit_sha ?? null : null
|
|
16133
16148
|
};
|
|
16134
16149
|
}
|
|
16135
16150
|
function metaKey(o2) {
|
|
@@ -16164,6 +16179,7 @@ function toItem(o2, action, sent) {
|
|
|
16164
16179
|
created_at: o2.createdAt,
|
|
16165
16180
|
merged_at: o2.mergedAt,
|
|
16166
16181
|
closed_at: o2.closedAt,
|
|
16182
|
+
merge_commit_sha: o2.mergeCommitSha,
|
|
16167
16183
|
body: o2.body,
|
|
16168
16184
|
...action === "synchronize" && sent.seq !== void 0 ? { seq: sent.seq } : {}
|
|
16169
16185
|
};
|
|
@@ -16573,6 +16589,22 @@ var init_defaultBranch = __esm({
|
|
|
16573
16589
|
}
|
|
16574
16590
|
});
|
|
16575
16591
|
|
|
16592
|
+
// ../shared/prRefs.ts
|
|
16593
|
+
function pushCommitSubject(message) {
|
|
16594
|
+
if (message == null) return null;
|
|
16595
|
+
const [subject = "", ...body] = message.split("\n");
|
|
16596
|
+
const pr = MERGE_PR_SUBJECT.exec(subject.trim());
|
|
16597
|
+
const title = body.find((line) => line.trim())?.trim();
|
|
16598
|
+
return pr && title && !TRAILER_LINE.test(title) ? `${title} (#${pr[1]})` : subject;
|
|
16599
|
+
}
|
|
16600
|
+
var MERGE_PR_SUBJECT, TRAILER_LINE;
|
|
16601
|
+
var init_prRefs = __esm({
|
|
16602
|
+
"../shared/prRefs.ts"() {
|
|
16603
|
+
MERGE_PR_SUBJECT = /^Merge pull request #(\d+) from \S+$/;
|
|
16604
|
+
TRAILER_LINE = /^[A-Za-z0-9]+(?:-[A-Za-z0-9]+)+:\s/;
|
|
16605
|
+
}
|
|
16606
|
+
});
|
|
16607
|
+
|
|
16576
16608
|
// src/daemon/localGit.ts
|
|
16577
16609
|
var localGit_exports = {};
|
|
16578
16610
|
__export(localGit_exports, {
|
|
@@ -16641,13 +16673,14 @@ async function firstParentRange(dir, marker, tip) {
|
|
|
16641
16673
|
async function readCommitMeta(dir, sha) {
|
|
16642
16674
|
const { exitCode, stdout } = await git(
|
|
16643
16675
|
dir,
|
|
16644
|
-
|
|
16676
|
+
// %b last: it spans lines, and `pushCommitSubject` reads a merge commit's title from it.
|
|
16677
|
+
["show", "-s", "--format=%an%n%cI%n%s%n%b", sha],
|
|
16645
16678
|
{ allowFail: true }
|
|
16646
16679
|
);
|
|
16647
16680
|
if (exitCode !== 0) return { message: "", authorName: null, committedAt: null };
|
|
16648
|
-
const [
|
|
16681
|
+
const [authorName = "", committedAt = "", ...message] = stdout.split("\n");
|
|
16649
16682
|
return {
|
|
16650
|
-
message: message.trim(),
|
|
16683
|
+
message: (pushCommitSubject(message.join("\n")) ?? "").trim(),
|
|
16651
16684
|
authorName: authorName.trim() || null,
|
|
16652
16685
|
committedAt: committedAt.trim() || null
|
|
16653
16686
|
};
|
|
@@ -16676,6 +16709,7 @@ var init_localGit = __esm({
|
|
|
16676
16709
|
init_execa();
|
|
16677
16710
|
init_preflight();
|
|
16678
16711
|
init_defaultBranch();
|
|
16712
|
+
init_prRefs();
|
|
16679
16713
|
GIT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
16680
16714
|
}
|
|
16681
16715
|
});
|
|
@@ -17117,6 +17151,7 @@ var init_localPoller = __esm({
|
|
|
17117
17151
|
// src/daemon/run.ts
|
|
17118
17152
|
var run_exports = {};
|
|
17119
17153
|
__export(run_exports, {
|
|
17154
|
+
JOB_TOKEN_PREFIX: () => JOB_TOKEN_PREFIX,
|
|
17120
17155
|
MultipleAccountsError: () => MultipleAccountsError,
|
|
17121
17156
|
__testDispatch: () => __testDispatch,
|
|
17122
17157
|
affectedFieldForComplete: () => affectedFieldForComplete,
|
|
@@ -17128,6 +17163,7 @@ __export(run_exports, {
|
|
|
17128
17163
|
claudeFailureDetail: () => claudeFailureDetail,
|
|
17129
17164
|
computeDiff: () => computeDiff,
|
|
17130
17165
|
parseClaudeResult: () => parseClaudeResult,
|
|
17166
|
+
parseJobTokenResponse: () => parseJobTokenResponse,
|
|
17131
17167
|
probeMcpAuth: () => probeMcpAuth,
|
|
17132
17168
|
readBranchMarker: () => readBranchMarker,
|
|
17133
17169
|
routeShutdownInterrupt: () => routeShutdownInterrupt,
|
|
@@ -17220,6 +17256,56 @@ async function getRepoCredentials(ctx, jobId, deviceId) {
|
|
|
17220
17256
|
expires_at: typeof body.expires_at === "string" ? body.expires_at : null
|
|
17221
17257
|
};
|
|
17222
17258
|
}
|
|
17259
|
+
function parseJobTokenResponse(status, ok, body) {
|
|
17260
|
+
if (!ok) {
|
|
17261
|
+
throw new Error(`MCP job token unavailable: ${responseError(body, `job-token failed (${status})`)}`);
|
|
17262
|
+
}
|
|
17263
|
+
if (typeof body.token !== "string" || !body.token.startsWith(JOB_TOKEN_PREFIX) || body.token.length <= JOB_TOKEN_PREFIX.length) {
|
|
17264
|
+
throw new Error("MCP job token response did not carry an amj_ token");
|
|
17265
|
+
}
|
|
17266
|
+
const str2 = (v) => typeof v === "string" ? v : null;
|
|
17267
|
+
return {
|
|
17268
|
+
token: body.token,
|
|
17269
|
+
expires_at: str2(body.expires_at),
|
|
17270
|
+
project_id: str2(body.project_id),
|
|
17271
|
+
archme_branch: str2(body.archme_branch),
|
|
17272
|
+
scope: str2(body.scope)
|
|
17273
|
+
};
|
|
17274
|
+
}
|
|
17275
|
+
async function mintJobToken(ctx, jobId, deviceId) {
|
|
17276
|
+
const response = await fetchWithAuthRetry(
|
|
17277
|
+
"job-token",
|
|
17278
|
+
() => {
|
|
17279
|
+
const accessToken = ctx.auth.getAccessToken();
|
|
17280
|
+
if (!accessToken) throw new Error("daemon is not signed in with a registered device");
|
|
17281
|
+
return fetch(`${ctx.supabaseUrl.replace(/\/$/, "")}/functions/v1/agents-device/job-token`, {
|
|
17282
|
+
method: "POST",
|
|
17283
|
+
headers: {
|
|
17284
|
+
"Content-Type": "application/json",
|
|
17285
|
+
Authorization: `Bearer ${accessToken}`
|
|
17286
|
+
},
|
|
17287
|
+
body: JSON.stringify({
|
|
17288
|
+
device_id: deviceId,
|
|
17289
|
+
job_id: jobId
|
|
17290
|
+
})
|
|
17291
|
+
});
|
|
17292
|
+
},
|
|
17293
|
+
ctx.reLogin
|
|
17294
|
+
);
|
|
17295
|
+
const body = await parseJsonResponse(response);
|
|
17296
|
+
return parseJobTokenResponse(response.status, response.ok, body);
|
|
17297
|
+
}
|
|
17298
|
+
async function writeAgentMcpConfig(ctx, job, deviceId, projectSlug, source) {
|
|
17299
|
+
const { token } = await mintJobToken(ctx, job.id, deviceId);
|
|
17300
|
+
const { configPath, cleanup } = await writeMcpConfig({
|
|
17301
|
+
baseUrl: ctx.mcpBaseUrl,
|
|
17302
|
+
projectSlug,
|
|
17303
|
+
branch: job.archme_branch,
|
|
17304
|
+
bearer: token,
|
|
17305
|
+
source
|
|
17306
|
+
});
|
|
17307
|
+
return { configPath, cleanup, bearer: token };
|
|
17308
|
+
}
|
|
17223
17309
|
async function buildGitEnv(credentials) {
|
|
17224
17310
|
const dir = await mkdtemp3(join19(tmpdir3(), "archme-git-askpass-"));
|
|
17225
17311
|
const askpassPath = join19(dir, "askpass.sh");
|
|
@@ -17771,7 +17857,7 @@ async function probeMcpAuth(mcpUrl, bearer, opts = {}) {
|
|
|
17771
17857
|
body
|
|
17772
17858
|
});
|
|
17773
17859
|
lastStatus = res.status;
|
|
17774
|
-
if (res.status !== 401) {
|
|
17860
|
+
if (res.status !== 401 && res.status !== 403) {
|
|
17775
17861
|
return {
|
|
17776
17862
|
ok: true,
|
|
17777
17863
|
attempts: i2 + 1,
|
|
@@ -17779,13 +17865,13 @@ async function probeMcpAuth(mcpUrl, bearer, opts = {}) {
|
|
|
17779
17865
|
detail: `MCP accepted the daemon token (status ${res.status})`
|
|
17780
17866
|
};
|
|
17781
17867
|
}
|
|
17782
|
-
lastDetail = "MCP returned 401 \u2014 token rejected by JWKS verification";
|
|
17868
|
+
lastDetail = res.status === 403 ? "MCP returned 403 \u2014 job token rejected (job not running under this claim, or URL outside its binding)" : "MCP returned 401 \u2014 token rejected by JWKS verification";
|
|
17783
17869
|
} catch (err2) {
|
|
17784
17870
|
lastStatus = null;
|
|
17785
17871
|
lastDetail = `network error reaching MCP: ${err2 instanceof Error ? err2.message : String(err2)}`;
|
|
17786
17872
|
}
|
|
17787
17873
|
}
|
|
17788
|
-
if (lastStatus === 401) {
|
|
17874
|
+
if (lastStatus === 401 || lastStatus === 403) {
|
|
17789
17875
|
return { ok: false, attempts, lastStatus, detail: lastDetail };
|
|
17790
17876
|
}
|
|
17791
17877
|
return {
|
|
@@ -17860,19 +17946,12 @@ function parseClaudeResult(stdout) {
|
|
|
17860
17946
|
return { summary: raw, modelServed: null, usage: null, costUsd: null, isError: false, parsed: false };
|
|
17861
17947
|
}
|
|
17862
17948
|
}
|
|
17863
|
-
async function setupDigestMcp(ctx, job) {
|
|
17949
|
+
async function setupDigestMcp(ctx, job, deviceId) {
|
|
17864
17950
|
let cleanup;
|
|
17865
17951
|
try {
|
|
17952
|
+
if (!deviceId) throw new Error("daemon is not signed in with a registered device");
|
|
17866
17953
|
const projectSlug = await lookupProjectSlug(ctx.supabase, job.project_id);
|
|
17867
|
-
const mcpBearer = await ctx
|
|
17868
|
-
const { configPath, cleanup: cleanupFn } = await writeMcpConfig({
|
|
17869
|
-
baseUrl: ctx.mcpBaseUrl,
|
|
17870
|
-
projectSlug,
|
|
17871
|
-
branch: job.archme_branch,
|
|
17872
|
-
bearer: mcpBearer
|
|
17873
|
-
// No `source`: digest reads must not produce source-tagged commits — they
|
|
17874
|
-
// don't write at all, but leaving it off keeps the URL read-shaped.
|
|
17875
|
-
});
|
|
17954
|
+
const { configPath, cleanup: cleanupFn, bearer: mcpBearer } = await writeAgentMcpConfig(ctx, job, deviceId, projectSlug);
|
|
17876
17955
|
cleanup = cleanupFn;
|
|
17877
17956
|
const probe = await probeMcpAuth(
|
|
17878
17957
|
`${ctx.mcpBaseUrl}?project=${encodeURIComponent(projectSlug)}&branch=${encodeURIComponent(job.archme_branch)}`,
|
|
@@ -17895,7 +17974,7 @@ async function setupDigestMcp(ctx, job) {
|
|
|
17895
17974
|
async function runJob(ctx, job, claimDeviceId) {
|
|
17896
17975
|
if (job.job_kind === "digest") {
|
|
17897
17976
|
const digestDevice = claimDeviceId ?? ctx.deviceForOrg(job.org_id) ?? ctx.auth.getDeviceId() ?? void 0;
|
|
17898
|
-
const { mcp, cleanup } = await setupDigestMcp(ctx, job);
|
|
17977
|
+
const { mcp, cleanup } = await setupDigestMcp(ctx, job, digestDevice);
|
|
17899
17978
|
try {
|
|
17900
17979
|
await runDigestJob(ctx, job, digestDevice, inFlight, mcp);
|
|
17901
17980
|
} finally {
|
|
@@ -17927,6 +18006,27 @@ async function runJob(ctx, job, claimDeviceId) {
|
|
|
17927
18006
|
}, HEARTBEAT_INTERVAL_MS);
|
|
17928
18007
|
try {
|
|
17929
18008
|
const projectSlug = await lookupProjectSlug(ctx.supabase, job.project_id);
|
|
18009
|
+
const { configPath, cleanup: cleanupFn, bearer: mcpBearer } = await writeAgentMcpConfig(
|
|
18010
|
+
ctx,
|
|
18011
|
+
job,
|
|
18012
|
+
deviceId,
|
|
18013
|
+
projectSlug,
|
|
18014
|
+
{
|
|
18015
|
+
repo: `${job.repo_owner}/${job.repo_name}`,
|
|
18016
|
+
before_sha: job.before_sha ?? null,
|
|
18017
|
+
after_sha: job.after_sha,
|
|
18018
|
+
job_id: job.id,
|
|
18019
|
+
// Effective run mode — always diff or pr_update now (reconcile is gone).
|
|
18020
|
+
mode: job.source_mode === "pr_update" ? "pr_update" : "diff",
|
|
18021
|
+
commit_message: job.commit_message ?? null,
|
|
18022
|
+
committed_at: job.head_commit_timestamp ?? null,
|
|
18023
|
+
author_name: job.author_name ?? null,
|
|
18024
|
+
author_login: job.author_login ?? null
|
|
18025
|
+
}
|
|
18026
|
+
);
|
|
18027
|
+
activeCleanups.add(cleanupFn);
|
|
18028
|
+
cleanupFns.push(cleanupFn);
|
|
18029
|
+
markPhase("job_token");
|
|
17930
18030
|
let clone;
|
|
17931
18031
|
try {
|
|
17932
18032
|
clone = await ensureCloneAndFetch(ctx, job, deviceId);
|
|
@@ -18007,27 +18107,6 @@ async function runJob(ctx, job, claimDeviceId) {
|
|
|
18007
18107
|
throw new Error(`failed to materialize large diff to disk (${bytes} bytes): ${e.message}`);
|
|
18008
18108
|
}
|
|
18009
18109
|
}
|
|
18010
|
-
const mcpBearer = await ctx.auth.getMcpAccessToken();
|
|
18011
|
-
const { configPath, cleanup: cleanupFn } = await writeMcpConfig({
|
|
18012
|
-
baseUrl: ctx.mcpBaseUrl,
|
|
18013
|
-
projectSlug,
|
|
18014
|
-
branch: job.archme_branch,
|
|
18015
|
-
bearer: mcpBearer,
|
|
18016
|
-
source: {
|
|
18017
|
-
repo: `${job.repo_owner}/${job.repo_name}`,
|
|
18018
|
-
before_sha: job.before_sha ?? null,
|
|
18019
|
-
after_sha: job.after_sha,
|
|
18020
|
-
job_id: job.id,
|
|
18021
|
-
// Effective run mode — always diff or pr_update now (reconcile is gone).
|
|
18022
|
-
mode: effectiveMode,
|
|
18023
|
-
commit_message: job.commit_message ?? null,
|
|
18024
|
-
committed_at: job.head_commit_timestamp ?? null,
|
|
18025
|
-
author_name: job.author_name ?? null,
|
|
18026
|
-
author_login: job.author_login ?? null
|
|
18027
|
-
}
|
|
18028
|
-
});
|
|
18029
|
-
activeCleanups.add(cleanupFn);
|
|
18030
|
-
cleanupFns.push(cleanupFn);
|
|
18031
18110
|
let affectedSeed = "";
|
|
18032
18111
|
let affectedForComplete = null;
|
|
18033
18112
|
if (diff) {
|
|
@@ -18640,7 +18719,7 @@ ${signal} received, shutting down`);
|
|
|
18640
18719
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
18641
18720
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
18642
18721
|
}
|
|
18643
|
-
var HEARTBEAT_INTERVAL_MS, CLAUDE_TIMEOUT_MS, CLAUDE_LARGE_DIFF_TIMEOUT_MS, SAFETY_SWEEP_MS, LOG_STDOUT_CAP_BYTES, LOG_STDERR_CAP_BYTES, FAILURE_DETAIL_MAX, PRE_SPAWN_TOKEN_MIN_LIFETIME_MS, SUPABASE_URL, SUPABASE_ANON_KEY, ARCHME_DAEMON_CLIENT_ID, ARCHME_MCP_BASE_URL, CLONE_BASE_DIR, DAEMON_ID, LEASE_SECONDS, inFlight, cancelled, killReasons, activeCleanups, cloneMutex, shuttingDown, daemonVersion, pauseUntilMs, resumeTimer, currentSched, MAX_CONCURRENT_JOBS, ctxByJob, deviceByJob, DEFAULT_CLONE_BASE_DIR, INLINE_PATCH_BYTE_CAP, GIT_SHA_RE, CURATOR_PREAMBLE, CORE_PRINCIPLES, STORY_COVERAGE_GATE, NEW_STORY_GUIDANCE, STORY_DESCRIPTION_EDIT_RULE, PHASE_GUIDANCE, BASE_RULES, DIFF_RULES, finite, channelSeq, MultipleAccountsError, __testDispatch;
|
|
18722
|
+
var HEARTBEAT_INTERVAL_MS, CLAUDE_TIMEOUT_MS, CLAUDE_LARGE_DIFF_TIMEOUT_MS, SAFETY_SWEEP_MS, LOG_STDOUT_CAP_BYTES, LOG_STDERR_CAP_BYTES, FAILURE_DETAIL_MAX, PRE_SPAWN_TOKEN_MIN_LIFETIME_MS, SUPABASE_URL, SUPABASE_ANON_KEY, ARCHME_DAEMON_CLIENT_ID, ARCHME_MCP_BASE_URL, CLONE_BASE_DIR, DAEMON_ID, LEASE_SECONDS, inFlight, cancelled, killReasons, activeCleanups, cloneMutex, shuttingDown, daemonVersion, pauseUntilMs, resumeTimer, currentSched, MAX_CONCURRENT_JOBS, ctxByJob, deviceByJob, DEFAULT_CLONE_BASE_DIR, JOB_TOKEN_PREFIX, INLINE_PATCH_BYTE_CAP, GIT_SHA_RE, CURATOR_PREAMBLE, CORE_PRINCIPLES, STORY_COVERAGE_GATE, NEW_STORY_GUIDANCE, STORY_DESCRIPTION_EDIT_RULE, PHASE_GUIDANCE, BASE_RULES, DIFF_RULES, finite, channelSeq, MultipleAccountsError, __testDispatch;
|
|
18644
18723
|
var init_run = __esm({
|
|
18645
18724
|
"src/daemon/run.ts"() {
|
|
18646
18725
|
"use strict";
|
|
@@ -18702,6 +18781,7 @@ var init_run = __esm({
|
|
|
18702
18781
|
ctxByJob = /* @__PURE__ */ new Map();
|
|
18703
18782
|
deviceByJob = /* @__PURE__ */ new Map();
|
|
18704
18783
|
DEFAULT_CLONE_BASE_DIR = `${homedir6()}/.archme-job-runner/clones`;
|
|
18784
|
+
JOB_TOKEN_PREFIX = "amj_";
|
|
18705
18785
|
INLINE_PATCH_BYTE_CAP = 2e5;
|
|
18706
18786
|
GIT_SHA_RE = /^[0-9a-f]{40}$/;
|
|
18707
18787
|
CURATOR_PREAMBLE = `## Your role
|
|
@@ -18830,6 +18910,11 @@ ${ANCHOR_VERIFICATION}`;
|
|
|
18830
18910
|
return deviceByJob.get(jobId);
|
|
18831
18911
|
},
|
|
18832
18912
|
runJob,
|
|
18913
|
+
// R7: the job-bound `amj_` MCP token mint and the digest's MCP setup, so a
|
|
18914
|
+
// test can assert the agent's mcp.json carries the job token and never the
|
|
18915
|
+
// daemon's own session.
|
|
18916
|
+
mintJobToken,
|
|
18917
|
+
setupDigestMcp,
|
|
18833
18918
|
// Exposes the clone/fetch entrypoint so a test can assert that the repo-token
|
|
18834
18919
|
// mint (getRepoCredentials -> agents-device/repo-token, gated on
|
|
18835
18920
|
// claimed_by_device_id) uses the CLAIM-time device rather than recomputing via
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@archme/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Map your codebase into a living architecture diagram: run `archme init` in a repo to scan it, `archme daemon start` to keep it current on every commit.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|