@archme/cli 0.11.1 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +204 -73
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -13127,6 +13127,21 @@ Everything a reader sees in a sequence \u2014 arrow labels, Note text, and the c
|
|
|
13127
13127
|
}
|
|
13128
13128
|
});
|
|
13129
13129
|
|
|
13130
|
+
// ../shared/waypoints.ts
|
|
13131
|
+
var init_waypoints = __esm({
|
|
13132
|
+
"../shared/waypoints.ts"() {
|
|
13133
|
+
}
|
|
13134
|
+
});
|
|
13135
|
+
|
|
13136
|
+
// ../shared/leafEndpoints.ts
|
|
13137
|
+
var LEAF_ENDPOINT_GUIDANCE;
|
|
13138
|
+
var init_leafEndpoints = __esm({
|
|
13139
|
+
"../shared/leafEndpoints.ts"() {
|
|
13140
|
+
init_waypoints();
|
|
13141
|
+
LEAF_ENDPOINT_GUIDANCE = `- **Arrows connect leaves.** Name the component that does the work, never the parent it sits in. A component with children (other than its input/output interface ports) is a group: an arrow to it lands on the group's box and can't be followed inside, while an arrow to a leaf still draws on the parent's box whenever the parent is collapsed. If a parent's own code acts, model that code as one more child and name that child. An arrow between a component and one of its own children is always this mistake.`;
|
|
13142
|
+
}
|
|
13143
|
+
});
|
|
13144
|
+
|
|
13130
13145
|
// ../shared/componentOrganization.ts
|
|
13131
13146
|
var COMPONENT_ORGANIZATION_GUIDANCE;
|
|
13132
13147
|
var init_componentOrganization = __esm({
|
|
@@ -13181,9 +13196,10 @@ ${draftBlock}Sequence diagram format:
|
|
|
13181
13196
|
- Use "as" aliases for display names: participant root.api.gateway as API Gateway
|
|
13182
13197
|
- Supports combined fragments: alt/else, opt, par/and, loop, critical/option, break
|
|
13183
13198
|
- All participant references MUST be absolute paths prefixed with "root." (e.g., root.backend.api, never backend.api). Scope-relative paths are rejected on write \u2014 they break move_architecture's path-rewriter and the UML grouping logic.
|
|
13199
|
+
${LEAF_ENDPOINT_GUIDANCE}
|
|
13184
13200
|
- Example: "sequenceDiagram\\n root.mobile_user->>root.client: opens app\\n root.client->>root.api.post_users: POST /users\\n root.api.post_users->>root.db: Save new user\\n root.db-->>root.api.post_users: user record\\n root.api.post_users-->>root.client: Account created"
|
|
13185
|
-
- With branching: "sequenceDiagram\\n root.client->>root.api: POST /upload\\n alt File accepted\\n root.api-->>root.client: Upload saved\\n else Wrong type or too large\\n root.api-->>root.client: Upload rejected\\n end"
|
|
13186
|
-
- With parallelism: "sequenceDiagram\\n root.api->>root.db: Record the upload\\n par\\n root.api->>root.storage: Store the file\\n and\\n root.api->>root.search: Make it searchable\\n end"
|
|
13201
|
+
- With branching: "sequenceDiagram\\n root.client->>root.api.upload: POST /upload\\n alt File accepted\\n root.api.upload-->>root.client: Upload saved\\n else Wrong type or too large\\n root.api.upload-->>root.client: Upload rejected\\n end"
|
|
13202
|
+
- With parallelism: "sequenceDiagram\\n root.api.upload->>root.db: Record the upload\\n par\\n root.api.upload->>root.storage: Store the file\\n and\\n root.api.upload->>root.search: Make it searchable\\n end"
|
|
13187
13203
|
|
|
13188
13204
|
${SEQUENCE_WORDING_GUIDANCE}
|
|
13189
13205
|
|
|
@@ -13221,6 +13237,7 @@ var init_scanPrompts = __esm({
|
|
|
13221
13237
|
init_storyNaming();
|
|
13222
13238
|
init_storyChapterGuidance();
|
|
13223
13239
|
init_sequenceWording();
|
|
13240
|
+
init_leafEndpoints();
|
|
13224
13241
|
init_componentOrganization();
|
|
13225
13242
|
BUILD_METHODOLOGY = `You are a build sub-agent. You are part of a multi-agent system that builds a comprehensive visual architecture of a codebase \u2014 components, connections, and user stories that trace data flow end-to-end. Your role is to translate research findings into actual architecture: creating components backed by real code references, wiring connections between them, and building story sequence diagrams that trace how data flows through the system.
|
|
13226
13243
|
|
|
@@ -13229,7 +13246,7 @@ var init_scanPrompts = __esm({
|
|
|
13229
13246
|
- Only build what the research findings and code evidence support. Do not invent components, connections, or user stories that aren't backed by real code. If findings are insufficient, flag gaps rather than guessing.
|
|
13230
13247
|
- Every connection MUST have a non-empty method
|
|
13231
13248
|
- Keep component hierarchies nested (e.g., backend > api > routes)
|
|
13232
|
-
-
|
|
13249
|
+
- Name connectionUpdates endpoints with "root."-prefixed paths; each connection is stored where its endpoints meet
|
|
13233
13250
|
- External services must be created as components with \`role: "external"\` (and \`kind: "human"\` for human actors), then referenced by dot-path in sequences like any other component
|
|
13234
13251
|
- Small utility modules are implementation details \u2014 omit unless architecturally significant
|
|
13235
13252
|
- Components must exist before non-draft stories can reference them. Add components first, then wire stories.
|
|
@@ -13341,8 +13358,9 @@ Component annotations:
|
|
|
13341
13358
|
- Example: { id: "user-org", from: "user", to: "org", method: "belongs_to", reference: true }
|
|
13342
13359
|
|
|
13343
13360
|
Connection format:
|
|
13344
|
-
-
|
|
13345
|
-
-
|
|
13361
|
+
- Name both endpoints with "root."-prefixed dot-paths, as in sequences (or paths relative to the entry's "at"); interface children too: "root.api_server.post_users"
|
|
13362
|
+
- You don't choose the scope: each connection is stored where its endpoints' parents meet, and the result says where and under which id \u2014 use that "at" + id to remove it
|
|
13363
|
+
- id is optional: omitted, it is minted as "<from>-><to>"
|
|
13346
13364
|
- method describes how components communicate (e.g., REST, gRPC, GraphQL, WebSocket, Kafka, function, event)`;
|
|
13347
13365
|
FORMAT_REF_DRAFT_STORIES_BLOCK = `Draft stories:
|
|
13348
13366
|
- Set draft: true for stories where the architecture isn't built yet
|
|
@@ -13571,6 +13589,16 @@ var init_validationCodes = __esm({
|
|
|
13571
13589
|
// ──────────────────────────────────────────────────────────────────────────
|
|
13572
13590
|
/** componentUpdates[].remove references an id that doesn't exist at scope. */
|
|
13573
13591
|
COMPONENT_REMOVE_ID_NOT_FOUND: "component.remove.id-not-found",
|
|
13592
|
+
/**
|
|
13593
|
+
* WARNING (non-blocking): this write removed components, so the connections
|
|
13594
|
+
* naming them (or their descendants), in any scope, were removed with them.
|
|
13595
|
+
*/
|
|
13596
|
+
COMPONENT_REMOVE_CONNECTIONS_REMOVED: "component.remove.connections-removed",
|
|
13597
|
+
/**
|
|
13598
|
+
* WARNING (non-blocking): this write removed components that story sequences
|
|
13599
|
+
* still name. The sequences are left as they are for the agent to fix.
|
|
13600
|
+
*/
|
|
13601
|
+
COMPONENT_REMOVE_STORY_STILL_REFERENCES: "component.remove.story-still-references",
|
|
13574
13602
|
// ──────────────────────────────────────────────────────────────────────────
|
|
13575
13603
|
// connection.add
|
|
13576
13604
|
// ──────────────────────────────────────────────────────────────────────────
|
|
@@ -13584,9 +13612,16 @@ var init_validationCodes = __esm({
|
|
|
13584
13612
|
CONNECTION_ADD_MISSING_TO: "connection.add.missing-to",
|
|
13585
13613
|
/** Missing or empty `method`. */
|
|
13586
13614
|
CONNECTION_ADD_MISSING_METHOD: "connection.add.missing-method",
|
|
13587
|
-
/**
|
|
13615
|
+
/**
|
|
13616
|
+
* @deprecated No longer emitted: a connection add is stored where its
|
|
13617
|
+
* endpoints meet (`resolveConnectionUpdates`), whatever scope it names.
|
|
13618
|
+
* Connection emitted at a parent scope outside the current scope.
|
|
13619
|
+
*/
|
|
13588
13620
|
CONNECTION_AT_WRONG_SCOPE_OUTSIDE: "connection.add.wrong-scope-outside",
|
|
13589
|
-
/**
|
|
13621
|
+
/**
|
|
13622
|
+
* @deprecated No longer emitted, as above.
|
|
13623
|
+
* Connection internal to a child but added at the parent level.
|
|
13624
|
+
*/
|
|
13590
13625
|
CONNECTION_AT_WRONG_SCOPE_INTERNAL: "connection.add.wrong-scope-internal",
|
|
13591
13626
|
// ──────────────────────────────────────────────────────────────────────────
|
|
13592
13627
|
// connection.remove
|
|
@@ -13606,6 +13641,12 @@ var init_validationCodes = __esm({
|
|
|
13606
13641
|
* One code, both sites: to an agent it's the same rule.
|
|
13607
13642
|
*/
|
|
13608
13643
|
CONNECTION_PAIR_ALREADY_CONNECTED: "connection.add.pair-already-connected",
|
|
13644
|
+
/**
|
|
13645
|
+
* WARNING (non-blocking): an explicit `connectionUpdates.add` was NOT stored —
|
|
13646
|
+
* a finer edge on the same pair (one endpoint a child of this one's) already
|
|
13647
|
+
* says it, and the subsumption prune keeps only the finer edge.
|
|
13648
|
+
*/
|
|
13649
|
+
CONNECTION_ADD_SUBSUMED_BY_CHILD: "connection.add.subsumed-by-child",
|
|
13609
13650
|
// ──────────────────────────────────────────────────────────────────────────
|
|
13610
13651
|
// story.add — story-add fields
|
|
13611
13652
|
// ──────────────────────────────────────────────────────────────────────────
|
|
@@ -13696,6 +13737,14 @@ var init_validationCodes = __esm({
|
|
|
13696
13737
|
PARTICIPANT_NOT_ROOT_PREFIXED: "sequence.participant.not-root-prefixed",
|
|
13697
13738
|
/** Participant dot-path references a component that doesn't exist. */
|
|
13698
13739
|
PARTICIPANT_COMPONENT_NOT_FOUND: "sequence.participant.component-not-found",
|
|
13740
|
+
/**
|
|
13741
|
+
* WARNING (non-blocking): an arrow in a sequence the write authored, or a
|
|
13742
|
+
* `connectionUpdates` add, ends on a component with children
|
|
13743
|
+
* (`LEAF_ENDPOINT_GUIDANCE`). The connection lands on the group's box instead
|
|
13744
|
+
* of the part that does the work. One code for both sites, as with
|
|
13745
|
+
* CONNECTION_PAIR_ALREADY_CONNECTED. Silent on a byte-identical re-send.
|
|
13746
|
+
*/
|
|
13747
|
+
PARTICIPANT_HAS_CHILDREN: "sequence.participant.has-children",
|
|
13699
13748
|
// ──────────────────────────────────────────────────────────────────────────
|
|
13700
13749
|
// sequence.ref — validateSequenceRefs
|
|
13701
13750
|
// ──────────────────────────────────────────────────────────────────────────
|
|
@@ -16540,7 +16589,19 @@ function stripPromptEcho(haystack, prompt) {
|
|
|
16540
16589
|
function rateLimitDeferPlanForExit(channels, nowMs = Date.now(), defaultTimeZone = localTimeZone()) {
|
|
16541
16590
|
const haystack = stripPromptEcho(`${channels.message}
|
|
16542
16591
|
${channels.stderr}`, channels.prompt);
|
|
16543
|
-
|
|
16592
|
+
const plan = rateLimitDeferPlan(haystack, nowMs, defaultTimeZone);
|
|
16593
|
+
if (plan?.parsed) return plan;
|
|
16594
|
+
return claudeErrorDeferPlan(channels.claudeError, nowMs, defaultTimeZone) ?? plan;
|
|
16595
|
+
}
|
|
16596
|
+
function claudeErrorDeferPlan(error, nowMs, defaultTimeZone) {
|
|
16597
|
+
if (!error) return null;
|
|
16598
|
+
const text = error.text.trim();
|
|
16599
|
+
const parsed = rateLimitPauseUntil(text, nowMs, defaultTimeZone);
|
|
16600
|
+
const opensWithLimit = parsed != null && text.startsWith(parsed.info.matched);
|
|
16601
|
+
if (error.apiErrorStatus === 429 || error.apiErrorStatus == null && opensWithLimit) {
|
|
16602
|
+
return parsed ? { parsed: true, pauseUntil: parsed.pauseUntil, info: parsed.info } : { parsed: false, pauseUntil: new Date(nowMs + RATE_LIMIT_TENTATIVE_PAUSE_MS), info: null };
|
|
16603
|
+
}
|
|
16604
|
+
return null;
|
|
16544
16605
|
}
|
|
16545
16606
|
var RATE_LIMIT_RE, MONTHS, RATE_LIMIT_TENTATIVE_PAUSE_MS, DATED_RESET_ROLLOVER_GRACE_MS, RATE_LIMIT_PAUSE_PADDING_MS, RATE_LIMIT_SHAPED_RE;
|
|
16546
16607
|
var init_rateLimit = __esm({
|
|
@@ -17840,16 +17901,20 @@ __export(run_exports, {
|
|
|
17840
17901
|
buildClaudeExitMessage: () => buildClaudeExitMessage,
|
|
17841
17902
|
buildDevices: () => buildDevices,
|
|
17842
17903
|
buildDiffPrompt: () => buildDiffPrompt,
|
|
17904
|
+
claudeErrorDetails: () => claudeErrorDetails,
|
|
17905
|
+
claudeErrorResult: () => claudeErrorResult,
|
|
17843
17906
|
claudeFailureDetail: () => claudeFailureDetail,
|
|
17844
17907
|
computeDiff: () => computeDiff,
|
|
17845
17908
|
forkRepoScope: () => forkRepoScope,
|
|
17909
|
+
isClaudeAuthFailure: () => isClaudeAuthFailure,
|
|
17846
17910
|
parseClaudeResult: () => parseClaudeResult,
|
|
17847
17911
|
parseJobTokenResponse: () => parseJobTokenResponse,
|
|
17848
17912
|
probeMcpAuth: () => probeMcpAuth,
|
|
17849
17913
|
readBranchMarker: () => readBranchMarker,
|
|
17850
17914
|
routeShutdownInterrupt: () => routeShutdownInterrupt,
|
|
17851
17915
|
runDaemon: () => runDaemon,
|
|
17852
|
-
teeStream: () => teeStream
|
|
17916
|
+
teeStream: () => teeStream,
|
|
17917
|
+
withFailureDetail: () => withFailureDetail
|
|
17853
17918
|
});
|
|
17854
17919
|
import { createClient } from "@supabase/supabase-js";
|
|
17855
17920
|
import { randomUUID } from "node:crypto";
|
|
@@ -17866,12 +17931,29 @@ function attributeKillCause(result, recorded) {
|
|
|
17866
17931
|
function buildClaudeExitMessage(exitCode, causeSuffix, stderr) {
|
|
17867
17932
|
return `claude exited ${exitCode}${causeSuffix}: ${stderr.slice(0, 4e3)}`;
|
|
17868
17933
|
}
|
|
17934
|
+
function claudeErrorResult(stdout) {
|
|
17935
|
+
return claudeErrorDetails(stdout)?.text || null;
|
|
17936
|
+
}
|
|
17937
|
+
function claudeErrorDetails(stdout) {
|
|
17938
|
+
const parsed = parseClaudeResult(stdout);
|
|
17939
|
+
return parsed.parsed && parsed.isError ? { text: parsed.summary.trim(), apiErrorStatus: parsed.apiErrorStatus } : null;
|
|
17940
|
+
}
|
|
17869
17941
|
function claudeFailureDetail(stderr, stdout) {
|
|
17942
|
+
const result = claudeErrorResult(stdout);
|
|
17943
|
+
if (result) return result.slice(-FAILURE_DETAIL_MAX);
|
|
17870
17944
|
if (stderr.trim() || !stdout.trim()) return null;
|
|
17871
17945
|
const parsed = parseClaudeResult(stdout);
|
|
17872
17946
|
const text = (parsed.parsed ? parsed.summary : stdout).trim();
|
|
17873
17947
|
return text ? text.slice(-FAILURE_DETAIL_MAX) : null;
|
|
17874
17948
|
}
|
|
17949
|
+
function isClaudeAuthFailure(errorResult, stderr) {
|
|
17950
|
+
return /failed to authenticate|oauth session expired|not logged in|please run \/login/i.test(`${errorResult ?? ""}
|
|
17951
|
+
${stderr}`);
|
|
17952
|
+
}
|
|
17953
|
+
function withFailureDetail(message, detail) {
|
|
17954
|
+
return detail ? `claude said: ${detail}
|
|
17955
|
+
${message}` : message;
|
|
17956
|
+
}
|
|
17875
17957
|
async function routeShutdownInterrupt(id, cancelled2, fail, exitCode) {
|
|
17876
17958
|
if (cancelled2.has(id)) return;
|
|
17877
17959
|
if (exitCode === 0) {
|
|
@@ -18625,10 +18707,11 @@ function parseClaudeResult(stdout) {
|
|
|
18625
18707
|
usage: modelUsage ? sumModelUsage(modelUsage) : null,
|
|
18626
18708
|
costUsd: typeof d.total_cost_usd === "number" ? d.total_cost_usd : null,
|
|
18627
18709
|
isError: d.is_error === true,
|
|
18710
|
+
apiErrorStatus: typeof d.api_error_status === "number" ? d.api_error_status : null,
|
|
18628
18711
|
parsed: true
|
|
18629
18712
|
};
|
|
18630
18713
|
} catch {
|
|
18631
|
-
return { summary: raw, modelServed: null, usage: null, costUsd: null, isError: false, parsed: false };
|
|
18714
|
+
return { summary: raw, modelServed: null, usage: null, costUsd: null, isError: false, apiErrorStatus: null, parsed: false };
|
|
18632
18715
|
}
|
|
18633
18716
|
}
|
|
18634
18717
|
async function setupDigestMcp(ctx, job, deviceId) {
|
|
@@ -18917,16 +19000,30 @@ async function runJob(ctx, job, claimDeviceId) {
|
|
|
18917
19000
|
if (result.exitCode !== 0) {
|
|
18918
19001
|
const causeSuffix = cause ? ` (${result.signal ?? "exit"}, cause: ${cause})` : "";
|
|
18919
19002
|
const stderr = result.stderr ? String(result.stderr) : "";
|
|
18920
|
-
const
|
|
19003
|
+
const stdout = result.stdout ? String(result.stdout) : "";
|
|
19004
|
+
const detail = claudeFailureDetail(stderr, stdout);
|
|
18921
19005
|
if (detail) log(`claude output: ${detail.replace(/\n/g, " ")}`);
|
|
18922
|
-
throw new
|
|
19006
|
+
throw new ClaudeRunError(
|
|
19007
|
+
buildClaudeExitMessage(result.exitCode, causeSuffix, stderr),
|
|
19008
|
+
detail,
|
|
19009
|
+
isClaudeAuthFailure(claudeErrorResult(stdout), stderr),
|
|
19010
|
+
claudeErrorDetails(stdout)
|
|
19011
|
+
);
|
|
18923
19012
|
}
|
|
18924
19013
|
const runResult = parseClaudeResult(result.stdout ? String(result.stdout) : "");
|
|
18925
19014
|
if (!runResult.parsed) {
|
|
18926
19015
|
log(`warning: claude --output-format json stdout was not a JSON result object; using raw text as summary`);
|
|
18927
19016
|
}
|
|
18928
19017
|
if (runResult.isError) {
|
|
18929
|
-
throw new
|
|
19018
|
+
throw new ClaudeRunError(
|
|
19019
|
+
`claude reported is_error in its JSON result (no successful completion)`,
|
|
19020
|
+
runResult.summary.trim().slice(-FAILURE_DETAIL_MAX) || null,
|
|
19021
|
+
isClaudeAuthFailure(
|
|
19022
|
+
claudeErrorResult(result.stdout ? String(result.stdout) : ""),
|
|
19023
|
+
result.stderr ? String(result.stderr) : ""
|
|
19024
|
+
),
|
|
19025
|
+
claudeErrorDetails(result.stdout ? String(result.stdout) : "")
|
|
19026
|
+
);
|
|
18930
19027
|
}
|
|
18931
19028
|
const summary2 = runResult.summary.trim().slice(0, 4e3);
|
|
18932
19029
|
if (summary2) log(`claude> ${summary2.slice(0, 500).replace(/\n/g, " ")}`);
|
|
@@ -18983,20 +19080,22 @@ async function runJob(ctx, job, claimDeviceId) {
|
|
|
18983
19080
|
return;
|
|
18984
19081
|
}
|
|
18985
19082
|
const msg = err4 instanceof Error ? err4.message : String(err4);
|
|
19083
|
+
const runErr = err4 instanceof ClaudeRunError ? err4 : null;
|
|
18986
19084
|
const haystack = stripPromptEcho(`${msg}
|
|
18987
19085
|
${logs.stderr}`, sentPrompt);
|
|
18988
19086
|
const plan = rateLimitDeferPlanForExit({
|
|
18989
19087
|
message: msg,
|
|
18990
19088
|
stderr: logs.stderr,
|
|
18991
19089
|
stdout: logs.stdout,
|
|
18992
|
-
prompt: sentPrompt
|
|
19090
|
+
prompt: sentPrompt,
|
|
19091
|
+
claudeError: runErr?.claudeError
|
|
18993
19092
|
});
|
|
18994
19093
|
if (plan) {
|
|
18995
19094
|
const { parsed, pauseUntil, info } = plan;
|
|
18996
19095
|
if (!parsed) {
|
|
18997
19096
|
void rpcReportEvent(ctx, job.org_id, "rate_limit_unmatched_exit", {
|
|
18998
19097
|
job_id: job.id,
|
|
18999
|
-
excerpt: haystack.slice(0, 500)
|
|
19098
|
+
excerpt: [runErr?.claudeError?.text, haystack].filter(Boolean).join("\n").slice(0, 500)
|
|
19000
19099
|
});
|
|
19001
19100
|
}
|
|
19002
19101
|
console.log(
|
|
@@ -19016,8 +19115,21 @@ ${logs.stderr}`, sentPrompt);
|
|
|
19016
19115
|
if (deferred) return;
|
|
19017
19116
|
console.warn(`[${job.id}] defer no-op \u2014 falling through to fail`);
|
|
19018
19117
|
}
|
|
19019
|
-
|
|
19020
|
-
|
|
19118
|
+
if (!plan && runErr?.authFailure) {
|
|
19119
|
+
const pauseUntil = new Date(Date.now() + AUTH_PAUSE_MS);
|
|
19120
|
+
console.error(
|
|
19121
|
+
`[${job.id}] CLAUDE CODE IS NOT AUTHENTICATED on this host (${runErr.detail ?? "auth failure"}). Run \`claude /login\` from this machine's DESKTOP session, not over SSH: on macOS an SSH login writes ~/.claude/.credentials.json while this daemon reads the keychain. Deferring job + pausing daemon until ${pauseUntil.toISOString()}.`
|
|
19122
|
+
);
|
|
19123
|
+
const detail = { job_id: job.id, claude_said: runErr.detail };
|
|
19124
|
+
await rpcSetDaemonPause(ctx, pauseUntil, "claude_auth_expired", detail);
|
|
19125
|
+
setLocalPause(pauseUntil.getTime());
|
|
19126
|
+
const deferred = await rpcDefer(ctx, job.id, deviceId, "claude_auth_expired", detail);
|
|
19127
|
+
if (deferred) return;
|
|
19128
|
+
console.warn(`[${job.id}] defer no-op \u2014 falling through to fail`);
|
|
19129
|
+
}
|
|
19130
|
+
const failMessage = withFailureDetail(msg, runErr?.detail ?? null);
|
|
19131
|
+
console.log(`[${job.id}] failed: ${failMessage}`);
|
|
19132
|
+
await rpcFail(ctx, job.id, deviceId, failMessage, metrics, logs);
|
|
19021
19133
|
} finally {
|
|
19022
19134
|
clearInterval(heartbeat);
|
|
19023
19135
|
inFlight.delete(job.id);
|
|
@@ -19422,7 +19534,7 @@ ${signal} received, shutting down`);
|
|
|
19422
19534
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
19423
19535
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
19424
19536
|
}
|
|
19425
|
-
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;
|
|
19537
|
+
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, AUTH_PAUSE_MS, ClaudeRunError, 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;
|
|
19426
19538
|
var init_run = __esm({
|
|
19427
19539
|
"src/daemon/run.ts"() {
|
|
19428
19540
|
"use strict";
|
|
@@ -19472,6 +19584,15 @@ var init_run = __esm({
|
|
|
19472
19584
|
LOG_STDOUT_CAP_BYTES = 30 * 1024;
|
|
19473
19585
|
LOG_STDERR_CAP_BYTES = 30 * 1024;
|
|
19474
19586
|
FAILURE_DETAIL_MAX = 400;
|
|
19587
|
+
AUTH_PAUSE_MS = 15 * 60 * 1e3;
|
|
19588
|
+
ClaudeRunError = class extends Error {
|
|
19589
|
+
constructor(message, detail, authFailure, claudeError) {
|
|
19590
|
+
super(message);
|
|
19591
|
+
this.detail = detail;
|
|
19592
|
+
this.authFailure = authFailure;
|
|
19593
|
+
this.claudeError = claudeError;
|
|
19594
|
+
}
|
|
19595
|
+
};
|
|
19475
19596
|
PRE_SPAWN_TOKEN_MIN_LIFETIME_MS = CLAUDE_LARGE_DIFF_TIMEOUT_MS + 5 * 60 * 1e3;
|
|
19476
19597
|
inFlight = /* @__PURE__ */ new Map();
|
|
19477
19598
|
cancelled = /* @__PURE__ */ new Set();
|
|
@@ -22514,9 +22635,9 @@ var init_run_init = __esm({
|
|
|
22514
22635
|
}
|
|
22515
22636
|
});
|
|
22516
22637
|
|
|
22517
|
-
// ../shared/
|
|
22518
|
-
var
|
|
22519
|
-
"../shared/
|
|
22638
|
+
// ../shared/connectionPair.ts
|
|
22639
|
+
var init_connectionPair = __esm({
|
|
22640
|
+
"../shared/connectionPair.ts"() {
|
|
22520
22641
|
}
|
|
22521
22642
|
});
|
|
22522
22643
|
|
|
@@ -22645,80 +22766,87 @@ function toAbsolute(endpoint, scopePath) {
|
|
|
22645
22766
|
if (scopePath.length === 0) return "root." + endpoint;
|
|
22646
22767
|
return "root." + scopePath.join(".") + "." + endpoint;
|
|
22647
22768
|
}
|
|
22648
|
-
function
|
|
22649
|
-
const
|
|
22650
|
-
|
|
22651
|
-
return absolute;
|
|
22652
|
-
}
|
|
22653
|
-
function rewriteAllConnections(arch, rewriter) {
|
|
22654
|
-
const entries = [];
|
|
22655
|
-
function walkConns(connections, scopePath) {
|
|
22769
|
+
function listAllConnections(arch) {
|
|
22770
|
+
const out = [];
|
|
22771
|
+
const walkConns = (connections, scopePath) => {
|
|
22656
22772
|
for (const conn of connections) {
|
|
22657
|
-
|
|
22658
|
-
const absTo = toAbsolute(conn.to, scopePath);
|
|
22659
|
-
const newAbsFrom = rewriter(absFrom);
|
|
22660
|
-
const newAbsTo = rewriter(absTo);
|
|
22661
|
-
const changed = newAbsFrom !== absFrom || newAbsTo !== absTo;
|
|
22662
|
-
entries.push({
|
|
22773
|
+
out.push({
|
|
22663
22774
|
conn,
|
|
22664
22775
|
scopePath,
|
|
22665
|
-
|
|
22666
|
-
|
|
22667
|
-
changed
|
|
22776
|
+
absFrom: toAbsolute(conn.from, scopePath),
|
|
22777
|
+
absTo: toAbsolute(conn.to, scopePath)
|
|
22668
22778
|
});
|
|
22669
22779
|
}
|
|
22670
|
-
}
|
|
22671
|
-
|
|
22780
|
+
};
|
|
22781
|
+
const walkComponents = (components, basePath) => {
|
|
22672
22782
|
for (const comp of components) {
|
|
22673
22783
|
const compPath = [...basePath, comp.id];
|
|
22674
22784
|
if (comp.connections) walkConns(comp.connections, compPath);
|
|
22675
22785
|
if (comp.components) walkComponents(comp.components, compPath);
|
|
22676
22786
|
}
|
|
22677
|
-
}
|
|
22787
|
+
};
|
|
22678
22788
|
walkConns(arch.connections, []);
|
|
22679
22789
|
walkComponents(arch.components, []);
|
|
22680
|
-
|
|
22681
|
-
|
|
22790
|
+
return out;
|
|
22791
|
+
}
|
|
22792
|
+
function dropConnections(arch, doomed) {
|
|
22793
|
+
const byScope = /* @__PURE__ */ new Map();
|
|
22794
|
+
for (const { conn, scopePath } of doomed) {
|
|
22795
|
+
const key = scopePath.join(".");
|
|
22796
|
+
const ids = byScope.get(key) ?? /* @__PURE__ */ new Set();
|
|
22797
|
+
ids.add(conn.id);
|
|
22798
|
+
byScope.set(key, ids);
|
|
22799
|
+
}
|
|
22682
22800
|
let result = arch;
|
|
22683
|
-
const
|
|
22684
|
-
for (const entry of entries) {
|
|
22685
|
-
if (!entry.changed) continue;
|
|
22686
|
-
const key = entry.scopePath.join(".");
|
|
22687
|
-
if (!removeByScopeKey.has(key)) removeByScopeKey.set(key, /* @__PURE__ */ new Set());
|
|
22688
|
-
removeByScopeKey.get(key).add(entry.conn.id);
|
|
22689
|
-
}
|
|
22690
|
-
for (const [scopeKey, connIds] of removeByScopeKey) {
|
|
22801
|
+
for (const [scopeKey, ids] of byScope) {
|
|
22691
22802
|
const scopePath = scopeKey === "" ? [] : scopeKey.split(".");
|
|
22692
22803
|
const nav = navigateToScope(result, scopePath);
|
|
22693
22804
|
if (!nav) continue;
|
|
22694
22805
|
result = replaceScopeAtPath(result, scopePath, {
|
|
22695
22806
|
...nav.scope,
|
|
22696
|
-
connections: nav.scope.connections.filter((c3) => !
|
|
22807
|
+
connections: nav.scope.connections.filter((c3) => !ids.has(c3.id))
|
|
22697
22808
|
});
|
|
22698
22809
|
}
|
|
22699
|
-
|
|
22700
|
-
|
|
22701
|
-
|
|
22702
|
-
|
|
22703
|
-
|
|
22704
|
-
|
|
22705
|
-
|
|
22706
|
-
|
|
22810
|
+
return result;
|
|
22811
|
+
}
|
|
22812
|
+
function connectionHome(absFrom, absTo) {
|
|
22813
|
+
const a2 = absFrom.split(".").slice(1);
|
|
22814
|
+
const b = absTo.split(".").slice(1);
|
|
22815
|
+
const scopePath = findLCA(a2.slice(0, -1), b.slice(0, -1));
|
|
22816
|
+
return { scopePath, from: a2.slice(scopePath.length).join("."), to: b.slice(scopePath.length).join(".") };
|
|
22817
|
+
}
|
|
22818
|
+
function rewriteAllConnections(arch, rewriter) {
|
|
22819
|
+
const changed = listAllConnections(arch).map((e) => ({ ...e, newFrom: rewriter(e.absFrom), newTo: rewriter(e.absTo) })).filter((e) => e.newFrom !== e.absFrom || e.newTo !== e.absTo);
|
|
22820
|
+
if (changed.length === 0) return { arch, connectionsUpdated: 0, notCarried: [] };
|
|
22821
|
+
let result = dropConnections(arch, changed);
|
|
22822
|
+
const notCarried = [];
|
|
22823
|
+
for (const entry of changed) {
|
|
22824
|
+
const home2 = connectionHome(entry.newFrom, entry.newTo);
|
|
22825
|
+
const nav = navigateToScope(result, home2.scopePath);
|
|
22826
|
+
if (!nav) continue;
|
|
22707
22827
|
const newConn = {
|
|
22708
22828
|
...entry.conn,
|
|
22709
|
-
from:
|
|
22710
|
-
to:
|
|
22711
|
-
id: `${
|
|
22712
|
-
};
|
|
22713
|
-
const existingConns =
|
|
22714
|
-
|
|
22715
|
-
|
|
22716
|
-
|
|
22717
|
-
|
|
22718
|
-
|
|
22829
|
+
from: home2.from,
|
|
22830
|
+
to: home2.to,
|
|
22831
|
+
id: `${home2.from}-${home2.to}`
|
|
22832
|
+
};
|
|
22833
|
+
const existingConns = nav.scope.connections;
|
|
22834
|
+
const taken = existingConns.find((c3) => c3.id === newConn.id);
|
|
22835
|
+
if (taken) {
|
|
22836
|
+
notCarried.push(
|
|
22837
|
+
`connection \`${entry.conn.id}\` (${newConn.from} \u2192 ${newConn.to}) was not carried over: ${scopeLabel(home2.scopePath)} already has \`${taken.id}\` (${taken.from} \u2192 ${taken.to}).`
|
|
22838
|
+
);
|
|
22839
|
+
continue;
|
|
22719
22840
|
}
|
|
22841
|
+
result = replaceScopeAtPath(result, home2.scopePath, {
|
|
22842
|
+
...nav.scope,
|
|
22843
|
+
connections: [...existingConns, newConn]
|
|
22844
|
+
});
|
|
22720
22845
|
}
|
|
22721
|
-
return { arch: result, connectionsUpdated:
|
|
22846
|
+
return { arch: result, connectionsUpdated: changed.length, notCarried };
|
|
22847
|
+
}
|
|
22848
|
+
function scopeLabel(scopePath) {
|
|
22849
|
+
return scopePath.length === 0 ? "root" : `root.${scopePath.join(".")}`;
|
|
22722
22850
|
}
|
|
22723
22851
|
function applyComponentMove(arch, move) {
|
|
22724
22852
|
const fromPath = move.from === "root" || move.from === "" ? [] : move.from.replace(/^root\./, "").split(".");
|
|
@@ -22767,7 +22895,7 @@ function applyComponentMove(arch, move) {
|
|
|
22767
22895
|
});
|
|
22768
22896
|
const rewriter = buildPathRewriter(oldAbsolute, newAbsolute);
|
|
22769
22897
|
result = rewriteArchPaths(result, rewriter);
|
|
22770
|
-
const { arch: afterConns, connectionsUpdated } = rewriteAllConnections(
|
|
22898
|
+
const { arch: afterConns, connectionsUpdated, notCarried } = rewriteAllConnections(
|
|
22771
22899
|
result,
|
|
22772
22900
|
rewriter
|
|
22773
22901
|
);
|
|
@@ -22793,13 +22921,16 @@ function applyComponentMove(arch, move) {
|
|
|
22793
22921
|
walk(a2.components);
|
|
22794
22922
|
};
|
|
22795
22923
|
countAllHops(result);
|
|
22796
|
-
const
|
|
22924
|
+
const notCarriedNote = notCarried.length > 0 ? ` Warning: ${notCarried.join(" ")}` : "";
|
|
22925
|
+
const summary2 = `Moved component '${move.id}' from ${move.from} to ${move.to}. As part of this move, ${hopsRewritten} story hop(s) and ${connectionsUpdated} connection(s) were updated to reflect the new path.${notCarriedNote}`;
|
|
22797
22926
|
return { arch: result, summary: summary2 };
|
|
22798
22927
|
}
|
|
22799
22928
|
var init_archUpdate = __esm({
|
|
22800
22929
|
"../shared/archUpdate.ts"() {
|
|
22801
22930
|
init_waypoints();
|
|
22802
22931
|
init_validationCodes();
|
|
22932
|
+
init_connectionPair();
|
|
22933
|
+
init_leafEndpoints();
|
|
22803
22934
|
}
|
|
22804
22935
|
});
|
|
22805
22936
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@archme/cli",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
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",
|