@smithers-orchestrator/cli 0.16.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/LICENSE +21 -0
- package/package.json +55 -0
- package/src/AgentAvailability.ts +13 -0
- package/src/AgentAvailabilityStatus.ts +5 -0
- package/src/AggregateNodeDetailParams.ts +5 -0
- package/src/AskOptions.ts +12 -0
- package/src/ChatAttemptMeta.ts +7 -0
- package/src/ChatAttemptRow.ts +12 -0
- package/src/ChatOutputEvent.ts +6 -0
- package/src/DiffBundleLike.ts +6 -0
- package/src/DiscoveredWorkflow.ts +9 -0
- package/src/EnrichedNodeDetail.ts +60 -0
- package/src/EventCategory.ts +18 -0
- package/src/FindDbWaitOptions.ts +4 -0
- package/src/FormatEventLineOptions.ts +4 -0
- package/src/HijackCandidate.ts +11 -0
- package/src/HijackLaunchSpec.ts +6 -0
- package/src/InitWorkflowPackOptions.ts +4 -0
- package/src/InitWorkflowPackResult.ts +6 -0
- package/src/NativeHijackEngine.ts +8 -0
- package/src/NodeDetailAttempt.ts +22 -0
- package/src/NodeDetailTokenUsage.ts +11 -0
- package/src/NodeDetailToolCall.ts +12 -0
- package/src/ParsedNodeOutputEvent.ts +9 -0
- package/src/RenderNodeDetailOptions.ts +4 -0
- package/src/RunAutoResumeSkipReason.ts +4 -0
- package/src/RunDiffCommandInput.ts +13 -0
- package/src/RunDiffCommandResult.ts +3 -0
- package/src/RunOutputCommandInput.ts +12 -0
- package/src/RunOutputCommandResult.ts +3 -0
- package/src/RunRewindCommandInput.ts +14 -0
- package/src/RunRewindCommandResult.ts +3 -0
- package/src/RunTreeCommandInput.ts +14 -0
- package/src/RunTreeCommandResult.ts +3 -0
- package/src/SmithersEventType.ts +3 -0
- package/src/SupervisorOptions.ts +33 -0
- package/src/SupervisorPollSummary.ts +6 -0
- package/src/TreeRenderOptions.ts +5 -0
- package/src/WatchLoopOptions.ts +9 -0
- package/src/WatchLoopResult.ts +8 -0
- package/src/WatchRenderContext.ts +4 -0
- package/src/WhyBlocker.ts +17 -0
- package/src/WhyBlockerKind.ts +9 -0
- package/src/WhyDiagnosis.ts +10 -0
- package/src/WorkflowCta.ts +4 -0
- package/src/WorkflowSourceType.ts +1 -0
- package/src/agent-detection.js +257 -0
- package/src/ask.js +491 -0
- package/src/chat.js +226 -0
- package/src/diff.js +221 -0
- package/src/event-categories.js +141 -0
- package/src/find-db.js +93 -0
- package/src/format.js +272 -0
- package/src/hijack-session.js +207 -0
- package/src/hijack.js +226 -0
- package/src/index.d.ts +1 -0
- package/src/index.js +4868 -0
- package/src/mcp/SemanticMcpServerOptions.ts +4 -0
- package/src/mcp/SemanticToolCallResult.ts +14 -0
- package/src/mcp/SemanticToolContext.ts +6 -0
- package/src/mcp/SemanticToolDefinition.ts +13 -0
- package/src/mcp/SemanticToolError.ts +6 -0
- package/src/mcp/semantic-server.js +41 -0
- package/src/mcp/semantic-tools.js +1242 -0
- package/src/node-detail.js +682 -0
- package/src/output.js +111 -0
- package/src/resume-detached.js +37 -0
- package/src/rewind.js +88 -0
- package/src/scheduler.js +112 -0
- package/src/smithersRuntime.js +63 -0
- package/src/supervisor.js +418 -0
- package/src/tree.js +307 -0
- package/src/tui/app.jsx +139 -0
- package/src/tui/app.tsx +5 -0
- package/src/tui/components/AskModal.jsx +109 -0
- package/src/tui/components/AskModal.tsx +3 -0
- package/src/tui/components/AttentionPane.jsx +112 -0
- package/src/tui/components/AttentionPane.tsx +6 -0
- package/src/tui/components/ChatPane.jsx +57 -0
- package/src/tui/components/ChatPane.tsx +7 -0
- package/src/tui/components/CronList.jsx +87 -0
- package/src/tui/components/CronList.tsx +5 -0
- package/src/tui/components/DetailsPane.jsx +96 -0
- package/src/tui/components/DetailsPane.tsx +7 -0
- package/src/tui/components/FramesPane.jsx +147 -0
- package/src/tui/components/FramesPane.tsx +8 -0
- package/src/tui/components/LogsPane.jsx +46 -0
- package/src/tui/components/LogsPane.tsx +6 -0
- package/src/tui/components/MetricsPane.jsx +108 -0
- package/src/tui/components/MetricsPane.tsx +5 -0
- package/src/tui/components/NodeDetailView.jsx +284 -0
- package/src/tui/components/NodeDetailView.tsx +7 -0
- package/src/tui/components/NodeInspector.jsx +51 -0
- package/src/tui/components/NodeInspector.tsx +7 -0
- package/src/tui/components/RunDetailView.jsx +190 -0
- package/src/tui/components/RunDetailView.tsx +7 -0
- package/src/tui/components/RunsList.jsx +184 -0
- package/src/tui/components/RunsList.tsx +7 -0
- package/src/tui/components/SqliteBrowser.jsx +131 -0
- package/src/tui/components/SqliteBrowser.tsx +5 -0
- package/src/tui/components/WorkflowLauncher.jsx +63 -0
- package/src/tui/components/WorkflowLauncher.tsx +3 -0
- package/src/util/CliErrorMapping.ts +7 -0
- package/src/util/CliExitCode.ts +10 -0
- package/src/util/errorMessage.js +212 -0
- package/src/util/exitCodes.js +18 -0
- package/src/watch.js +128 -0
- package/src/why-diagnosis.js +1000 -0
- package/src/workflow-pack.js +2151 -0
- package/src/workflows.js +122 -0
package/src/hijack.js
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { SmithersError } from "@smithers-orchestrator/errors";
|
|
3
|
+
/** @typedef {import("./HijackCandidate.ts").HijackCandidate} HijackCandidate */
|
|
4
|
+
/** @typedef {import("./HijackLaunchSpec.ts").HijackLaunchSpec} HijackLaunchSpec */
|
|
5
|
+
/** @typedef {import("./NativeHijackEngine.ts").NativeHijackEngine} NativeHijackEngine */
|
|
6
|
+
/** @typedef {import("@smithers-orchestrator/db/adapter").SmithersDb} SmithersDb */
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {string | null} [metaJson]
|
|
10
|
+
* @returns {Record<string, unknown>}
|
|
11
|
+
*/
|
|
12
|
+
function parseAttemptMeta(metaJson) {
|
|
13
|
+
if (!metaJson)
|
|
14
|
+
return {};
|
|
15
|
+
try {
|
|
16
|
+
const parsed = JSON.parse(metaJson);
|
|
17
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
18
|
+
? parsed
|
|
19
|
+
: {};
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* @param {unknown} value
|
|
27
|
+
* @returns {NativeHijackEngine | undefined}
|
|
28
|
+
*/
|
|
29
|
+
function asNativeHijackEngine(value) {
|
|
30
|
+
return value === "claude-code" ||
|
|
31
|
+
value === "codex" ||
|
|
32
|
+
value === "gemini" ||
|
|
33
|
+
value === "pi" ||
|
|
34
|
+
value === "kimi" ||
|
|
35
|
+
value === "forge" ||
|
|
36
|
+
value === "amp"
|
|
37
|
+
? value
|
|
38
|
+
: undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* @param {unknown} value
|
|
42
|
+
* @returns {unknown[] | undefined}
|
|
43
|
+
*/
|
|
44
|
+
function asConversationMessages(value) {
|
|
45
|
+
return Array.isArray(value) ? value : undefined;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* @param {Record<string, unknown>} meta
|
|
49
|
+
* @returns {| { engine: string; mode: "native-cli"; resume: string } | { engine: string; mode: "conversation"; messages: unknown[] } | null}
|
|
50
|
+
*/
|
|
51
|
+
function extractContinuationFromMeta(meta) {
|
|
52
|
+
const handoff = meta.hijackHandoff;
|
|
53
|
+
if (handoff && typeof handoff === "object" && !Array.isArray(handoff)) {
|
|
54
|
+
const engine = typeof handoff.engine === "string"
|
|
55
|
+
? handoff.engine
|
|
56
|
+
: undefined;
|
|
57
|
+
const mode = handoff.mode === "conversation" ? "conversation" : "native-cli";
|
|
58
|
+
const resume = typeof handoff.resume === "string" ? handoff.resume : undefined;
|
|
59
|
+
const messages = asConversationMessages(handoff.messages);
|
|
60
|
+
if (engine && mode === "native-cli" && resume) {
|
|
61
|
+
return { engine, mode: "native-cli", resume };
|
|
62
|
+
}
|
|
63
|
+
if (engine && mode === "conversation" && messages?.length) {
|
|
64
|
+
return { engine, mode: "conversation", messages };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const engine = typeof meta.agentEngine === "string" ? meta.agentEngine : undefined;
|
|
68
|
+
const resume = typeof meta.agentResume === "string" ? meta.agentResume : undefined;
|
|
69
|
+
if (engine && resume) {
|
|
70
|
+
return { engine, mode: "native-cli", resume };
|
|
71
|
+
}
|
|
72
|
+
const messages = asConversationMessages(meta.agentConversation);
|
|
73
|
+
if (engine && messages?.length) {
|
|
74
|
+
return { engine, mode: "conversation", messages };
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* @param {SmithersDb} adapter
|
|
80
|
+
* @param {string} runId
|
|
81
|
+
* @param {string} [target]
|
|
82
|
+
* @returns {Promise<HijackCandidate | null>}
|
|
83
|
+
*/
|
|
84
|
+
export async function resolveHijackCandidate(adapter, runId, target) {
|
|
85
|
+
const attempts = await adapter.listAttemptsForRun(runId);
|
|
86
|
+
const sortedAttempts = [...attempts].sort((a, b) => {
|
|
87
|
+
const aMs = a.startedAtMs ?? 0;
|
|
88
|
+
const bMs = b.startedAtMs ?? 0;
|
|
89
|
+
if (aMs !== bMs)
|
|
90
|
+
return bMs - aMs;
|
|
91
|
+
if ((a.iteration ?? 0) !== (b.iteration ?? 0))
|
|
92
|
+
return (b.iteration ?? 0) - (a.iteration ?? 0);
|
|
93
|
+
return (b.attempt ?? 0) - (a.attempt ?? 0);
|
|
94
|
+
});
|
|
95
|
+
for (const attempt of sortedAttempts) {
|
|
96
|
+
const meta = parseAttemptMeta(attempt.metaJson);
|
|
97
|
+
const extracted = extractContinuationFromMeta(meta);
|
|
98
|
+
if (!extracted)
|
|
99
|
+
continue;
|
|
100
|
+
if (target && target !== extracted.engine && target !== attempt.nodeId)
|
|
101
|
+
continue;
|
|
102
|
+
return {
|
|
103
|
+
runId,
|
|
104
|
+
nodeId: attempt.nodeId,
|
|
105
|
+
iteration: attempt.iteration ?? 0,
|
|
106
|
+
attempt: attempt.attempt,
|
|
107
|
+
engine: extracted.engine,
|
|
108
|
+
mode: extracted.mode,
|
|
109
|
+
resume: extracted.mode === "native-cli" ? extracted.resume : undefined,
|
|
110
|
+
messages: extracted.mode === "conversation" ? extracted.messages : undefined,
|
|
111
|
+
cwd: attempt.jjCwd ?? process.cwd(),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* @param {SmithersDb} adapter
|
|
118
|
+
* @param {string} runId
|
|
119
|
+
* @param {{ target?: string; timeoutMs?: number }} [options]
|
|
120
|
+
* @returns {Promise<HijackCandidate>}
|
|
121
|
+
*/
|
|
122
|
+
export async function waitForHijackCandidate(adapter, runId, options = {}) {
|
|
123
|
+
const timeoutMs = options.timeoutMs ?? 30_000;
|
|
124
|
+
const deadline = Date.now() + timeoutMs;
|
|
125
|
+
while (Date.now() <= deadline) {
|
|
126
|
+
const run = await adapter.getRun(runId);
|
|
127
|
+
const candidate = await resolveHijackCandidate(adapter, runId, options.target);
|
|
128
|
+
if (run && run.status !== "running" && candidate) {
|
|
129
|
+
return candidate;
|
|
130
|
+
}
|
|
131
|
+
await Bun.sleep(200);
|
|
132
|
+
}
|
|
133
|
+
throw new SmithersError("HIJACK_TIMEOUT", `Timed out waiting for Smithers to hand off run ${runId}`, { runId, timeoutMs });
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* @param {HijackCandidate} candidate
|
|
137
|
+
* @returns {HijackLaunchSpec}
|
|
138
|
+
*/
|
|
139
|
+
export function buildHijackLaunchSpec(candidate) {
|
|
140
|
+
if (candidate.mode !== "native-cli" || !candidate.resume) {
|
|
141
|
+
throw new SmithersError("HIJACK_LAUNCH_MODE", `Candidate ${candidate.engine} requires the Smithers conversation hijack flow, not a native CLI launch`, candidate);
|
|
142
|
+
}
|
|
143
|
+
const env = { ...process.env };
|
|
144
|
+
if (candidate.engine === "claude-code") {
|
|
145
|
+
if (env.CLAUDE_CODE_ENTRYPOINT)
|
|
146
|
+
env.CLAUDE_CODE_ENTRYPOINT = "";
|
|
147
|
+
if (env.CLAUDECODE)
|
|
148
|
+
env.CLAUDECODE = "";
|
|
149
|
+
return {
|
|
150
|
+
command: "claude",
|
|
151
|
+
args: ["--resume", candidate.resume],
|
|
152
|
+
cwd: candidate.cwd,
|
|
153
|
+
env,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (candidate.engine === "gemini") {
|
|
157
|
+
return {
|
|
158
|
+
command: "gemini",
|
|
159
|
+
args: ["--resume", candidate.resume],
|
|
160
|
+
cwd: candidate.cwd,
|
|
161
|
+
env,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (candidate.engine === "pi") {
|
|
165
|
+
return {
|
|
166
|
+
command: "pi",
|
|
167
|
+
args: ["--session", candidate.resume],
|
|
168
|
+
cwd: candidate.cwd,
|
|
169
|
+
env,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (candidate.engine === "kimi") {
|
|
173
|
+
return {
|
|
174
|
+
command: "kimi",
|
|
175
|
+
args: ["--session", candidate.resume, "--work-dir", candidate.cwd],
|
|
176
|
+
cwd: candidate.cwd,
|
|
177
|
+
env,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
if (candidate.engine === "forge") {
|
|
181
|
+
return {
|
|
182
|
+
command: "forge",
|
|
183
|
+
args: ["--conversation-id", candidate.resume, "-C", candidate.cwd],
|
|
184
|
+
cwd: candidate.cwd,
|
|
185
|
+
env,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
if (candidate.engine === "amp") {
|
|
189
|
+
return {
|
|
190
|
+
command: "amp",
|
|
191
|
+
args: ["threads", "continue", candidate.resume],
|
|
192
|
+
cwd: candidate.cwd,
|
|
193
|
+
env,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
command: "codex",
|
|
198
|
+
args: ["resume", candidate.resume, "-C", candidate.cwd],
|
|
199
|
+
cwd: candidate.cwd,
|
|
200
|
+
env,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* @param {HijackCandidate} candidate
|
|
205
|
+
* @returns {candidate is HijackCandidate & { mode: "native-cli"; engine: NativeHijackEngine; resume: string }}
|
|
206
|
+
*/
|
|
207
|
+
export function isNativeHijackCandidate(candidate) {
|
|
208
|
+
return candidate.mode === "native-cli" &&
|
|
209
|
+
typeof candidate.resume === "string" &&
|
|
210
|
+
Boolean(asNativeHijackEngine(candidate.engine));
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* @param {HijackLaunchSpec} spec
|
|
214
|
+
* @returns {Promise<number>}
|
|
215
|
+
*/
|
|
216
|
+
export function launchHijackSession(spec) {
|
|
217
|
+
return new Promise((resolve, reject) => {
|
|
218
|
+
const child = spawn(spec.command, spec.args, {
|
|
219
|
+
cwd: spec.cwd,
|
|
220
|
+
env: spec.env,
|
|
221
|
+
stdio: "inherit",
|
|
222
|
+
});
|
|
223
|
+
child.on("error", reject);
|
|
224
|
+
child.on("close", (code) => resolve(code ?? 0));
|
|
225
|
+
});
|
|
226
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|