astrocode-workflow 0.3.5 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/config/schema.d.ts +1 -0
- package/dist/src/config/schema.js +1 -0
- package/dist/src/hooks/inject-provider.js +72 -13
- package/dist/src/state/repo-lock.d.ts +33 -0
- package/dist/src/state/repo-lock.js +97 -19
- package/dist/src/state/schema.d.ts +2 -2
- package/dist/src/state/schema.js +8 -1
- package/dist/src/state/workflow-repo-lock.d.ts +7 -0
- package/dist/src/state/workflow-repo-lock.js +45 -12
- package/dist/src/tools/index.js +3 -0
- package/dist/src/tools/lock.d.ts +4 -0
- package/dist/src/tools/lock.js +78 -0
- package/dist/src/tools/repair.js +40 -6
- package/dist/src/tools/workflow.js +1 -0
- package/dist/src/workflow/repair.js +2 -2
- package/package.json +1 -2
- package/src/config/schema.ts +1 -0
- package/src/hooks/inject-provider.ts +80 -16
- package/src/state/repo-lock.ts +129 -22
- package/src/state/schema.ts +8 -1
- package/src/state/workflow-repo-lock.ts +49 -12
- package/src/tools/index.ts +3 -0
- package/src/tools/lock.ts +75 -0
- package/src/tools/repair.ts +43 -6
- package/src/tools/workflow.ts +1 -0
- package/src/workflow/repair.ts +2 -2
package/src/tools/repair.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool";
|
|
2
3
|
import type { AstrocodeConfig } from "../config/schema";
|
|
3
4
|
import type { SqliteDb } from "../state/db";
|
|
@@ -5,28 +6,64 @@ import { withTx } from "../state/db";
|
|
|
5
6
|
import { repairState, formatRepairReport } from "../workflow/repair";
|
|
6
7
|
import { putArtifact } from "../workflow/artifacts";
|
|
7
8
|
import { nowISO } from "../shared/time";
|
|
9
|
+
import { getLockStatus, tryRemoveStaleLock } from "../state/repo-lock";
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
export function createAstroRepairTool(opts: { ctx: any; config: AstrocodeConfig; db: SqliteDb }): ToolDefinition {
|
|
11
13
|
const { ctx, config, db } = opts;
|
|
12
14
|
|
|
13
15
|
return tool({
|
|
14
|
-
description: "Repair Astrocode invariants and recover from inconsistent DB state. Writes a repair report artifact.",
|
|
16
|
+
description: "Repair Astrocode invariants and recover from inconsistent DB state. Also checks and repairs lock files. Writes a repair report artifact.",
|
|
15
17
|
args: {
|
|
16
18
|
write_report_artifact: tool.schema.boolean().default(true),
|
|
19
|
+
repair_lock: tool.schema.boolean().default(true).describe("Attempt to remove stale/dead lock files"),
|
|
17
20
|
},
|
|
18
|
-
execute: async ({ write_report_artifact }) => {
|
|
21
|
+
execute: async ({ write_report_artifact, repair_lock }) => {
|
|
19
22
|
const repoRoot = ctx.directory as string;
|
|
23
|
+
const lockPath = path.join(repoRoot, ".astro", "astro.lock");
|
|
24
|
+
|
|
25
|
+
// First, check and repair lock if requested
|
|
26
|
+
const lockLines: string[] = [];
|
|
27
|
+
const lockStatus = getLockStatus(lockPath);
|
|
28
|
+
|
|
29
|
+
if (lockStatus.exists) {
|
|
30
|
+
lockLines.push("## Lock Status");
|
|
31
|
+
lockLines.push(`- Lock found: ${lockPath}`);
|
|
32
|
+
lockLines.push(`- PID: ${lockStatus.pid} (${lockStatus.pidAlive ? 'alive' : 'dead'})`);
|
|
33
|
+
lockLines.push(`- Age: ${lockStatus.ageMs ? Math.floor(lockStatus.ageMs / 1000) : '?'}s`);
|
|
34
|
+
lockLines.push(`- Status: ${lockStatus.isStale ? 'stale' : 'fresh'}`);
|
|
35
|
+
|
|
36
|
+
if (repair_lock) {
|
|
37
|
+
const result = tryRemoveStaleLock(lockPath);
|
|
38
|
+
if (result.removed) {
|
|
39
|
+
lockLines.push(`- **Removed**: ${result.reason}`);
|
|
40
|
+
} else {
|
|
41
|
+
lockLines.push(`- **Not removed**: ${result.reason}`);
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
if (!lockStatus.pidAlive || lockStatus.isStale) {
|
|
45
|
+
lockLines.push(`- **Recommendation**: Run with repair_lock=true to remove this ${!lockStatus.pidAlive ? 'dead' : 'stale'} lock`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
lockLines.push("");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Then repair database state
|
|
20
52
|
const report = withTx(db, () => repairState(db, config));
|
|
21
|
-
const
|
|
53
|
+
const dbMd = formatRepairReport(report);
|
|
54
|
+
|
|
55
|
+
// Combine lock and DB repair
|
|
56
|
+
const fullMd = lockLines.length > 0
|
|
57
|
+
? `# Astrocode Repair Report\n\n${lockLines.join("\n")}\n${dbMd.replace(/^# Astrocode repair report\n*/i, "")}`
|
|
58
|
+
: dbMd;
|
|
22
59
|
|
|
23
60
|
if (write_report_artifact) {
|
|
24
61
|
const rel = `.astro/repair/repair_${nowISO().replace(/[:.]/g, "-")}.md`;
|
|
25
|
-
const a = putArtifact({ repoRoot, db, run_id: null, stage_key: null, type: "log", rel_path: rel, content:
|
|
26
|
-
return
|
|
62
|
+
const a = putArtifact({ repoRoot, db, run_id: null, stage_key: null, type: "log", rel_path: rel, content: fullMd, meta: { kind: "repair" } });
|
|
63
|
+
return fullMd + `\n\nReport saved: ${rel} (artifact=${a.artifact_id})`;
|
|
27
64
|
}
|
|
28
65
|
|
|
29
|
-
return
|
|
66
|
+
return fullMd;
|
|
30
67
|
},
|
|
31
68
|
});
|
|
32
69
|
}
|
package/src/tools/workflow.ts
CHANGED
|
@@ -199,6 +199,7 @@ export function createAstroWorkflowProceedTool(opts: { ctx: any; config: Astroco
|
|
|
199
199
|
repoRoot,
|
|
200
200
|
sessionId,
|
|
201
201
|
owner: "astro_workflow_proceed",
|
|
202
|
+
advisory: true, // Advisory mode: warn instead of blocking on lock contention
|
|
202
203
|
fn: async () => {
|
|
203
204
|
const steps = Math.min(max_steps, config.workflow.loop_max_steps_hard_cap);
|
|
204
205
|
|
package/src/workflow/repair.ts
CHANGED
|
@@ -72,11 +72,11 @@ export function repairState(db: SqliteDb, config: AstrocodeConfig): RepairReport
|
|
|
72
72
|
if (stageRuns.length < pipeline.length) {
|
|
73
73
|
const existingKeys = new Set(stageRuns.map((s) => s.stage_key));
|
|
74
74
|
const insert = db.prepare(
|
|
75
|
-
"INSERT INTO stage_runs (stage_run_id, run_id, stage_key, stage_index, status, updated_at) VALUES (?, ?, ?, ?, 'pending', ?)"
|
|
75
|
+
"INSERT INTO stage_runs (stage_run_id, run_id, stage_key, stage_index, status, created_at, updated_at) VALUES (?, ?, ?, ?, 'pending', ?, ?)"
|
|
76
76
|
);
|
|
77
77
|
pipeline.forEach((key, idx) => {
|
|
78
78
|
if (!existingKeys.has(key as any)) {
|
|
79
|
-
insert.run(newStageRunId(), active.run_id, key, idx, now);
|
|
79
|
+
insert.run(newStageRunId(), active.run_id, key, idx, now, now);
|
|
80
80
|
push(report, `Inserted missing stage_run ${key} for run ${active.run_id}`);
|
|
81
81
|
}
|
|
82
82
|
});
|