@13rac1/openclaw-plugin-claude-code 1.0.3 → 1.0.4
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/package.json +2 -4
- package/dist/index.d.ts +0 -50
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -481
- package/dist/index.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@13rac1/openclaw-plugin-claude-code",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "OpenClaw plugin for running Claude Code CLI in isolated Podman containers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -51,10 +51,8 @@
|
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"openclaw": ">=2025.1.0"
|
|
53
53
|
},
|
|
54
|
-
"dependencies": {
|
|
55
|
-
"@sinclair/typebox": "^0.34.48"
|
|
56
|
-
},
|
|
57
54
|
"devDependencies": {
|
|
55
|
+
"@sinclair/typebox": "^0.34.48",
|
|
58
56
|
"@eslint/js": "^9.39.2",
|
|
59
57
|
"@types/node": "^22.0.0",
|
|
60
58
|
"@vitest/coverage-v8": "^3.2.4",
|
package/dist/index.d.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Plugin configuration interface
|
|
3
|
-
*/
|
|
4
|
-
export interface ClaudeCodePluginConfig {
|
|
5
|
-
image: string;
|
|
6
|
-
runtime: string;
|
|
7
|
-
startupTimeout: number;
|
|
8
|
-
idleTimeout: number;
|
|
9
|
-
memory: string;
|
|
10
|
-
cpus: string;
|
|
11
|
-
network: string;
|
|
12
|
-
sessionsDir: string;
|
|
13
|
-
workspacesDir: string;
|
|
14
|
-
sessionIdleTimeout: number;
|
|
15
|
-
apparmorProfile?: string;
|
|
16
|
-
maxOutputSize: number;
|
|
17
|
-
}
|
|
18
|
-
/** Tool response content item */
|
|
19
|
-
interface ContentItem {
|
|
20
|
-
type: string;
|
|
21
|
-
text: string;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* OpenClaw Plugin API interface
|
|
25
|
-
*/
|
|
26
|
-
interface PluginApi {
|
|
27
|
-
config: Record<string, unknown>;
|
|
28
|
-
registerTool(config: {
|
|
29
|
-
name: string;
|
|
30
|
-
description: string;
|
|
31
|
-
parameters: unknown;
|
|
32
|
-
execute: (id: string, params: Record<string, unknown>) => Promise<{
|
|
33
|
-
content: ContentItem[];
|
|
34
|
-
}>;
|
|
35
|
-
}): void;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Format milliseconds as human-readable duration
|
|
39
|
-
*/
|
|
40
|
-
export declare function formatDuration(ms: number): string;
|
|
41
|
-
/**
|
|
42
|
-
* Claude Code Plugin for OpenClaw
|
|
43
|
-
*
|
|
44
|
-
* Registers tools that execute prompts in isolated Podman containers
|
|
45
|
-
* running Claude Code CLI.
|
|
46
|
-
*/
|
|
47
|
-
export default function register(api: PluginApi): void;
|
|
48
|
-
export { SessionManager } from "./session-manager.js";
|
|
49
|
-
export { PodmanRunner } from "./podman-runner.js";
|
|
50
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;CACvB;AAoBD,iCAAiC;AACjC,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,UAAU,SAAS;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,YAAY,CAAC,MAAM,EAAE;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,OAAO,CAAC;QACpB,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;YAAE,OAAO,EAAE,WAAW,EAAE,CAAA;SAAE,CAAC,CAAC;KAC/F,GAAG,IAAI,CAAC;CACV;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAgBjD;AAED;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAydrD;AAoDD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
DELETED
|
@@ -1,481 +0,0 @@
|
|
|
1
|
-
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import * as fs from "node:fs/promises";
|
|
3
|
-
import * as path from "node:path";
|
|
4
|
-
import { homedir } from "node:os";
|
|
5
|
-
import { SessionManager } from "./session-manager.js";
|
|
6
|
-
import { PodmanRunner } from "./podman-runner.js";
|
|
7
|
-
/**
|
|
8
|
-
* Default configuration values
|
|
9
|
-
*/
|
|
10
|
-
const DEFAULT_CONFIG = {
|
|
11
|
-
image: "ghcr.io/13rac1/openclaw-claude-code:latest",
|
|
12
|
-
runtime: "podman",
|
|
13
|
-
startupTimeout: 30, // Container must produce output within 30s
|
|
14
|
-
idleTimeout: 120, // Container silent for 120s = hung
|
|
15
|
-
memory: "512m",
|
|
16
|
-
cpus: "1.0",
|
|
17
|
-
network: "bridge", // Needs network for Anthropic API access
|
|
18
|
-
sessionsDir: "~/.openclaw/claude-sessions",
|
|
19
|
-
workspacesDir: "~/.openclaw/workspaces",
|
|
20
|
-
sessionIdleTimeout: 3600, // Clean up sessions after 1hr idle
|
|
21
|
-
apparmorProfile: "", // Disabled by default
|
|
22
|
-
maxOutputSize: 10 * 1024 * 1024, // 10MB default
|
|
23
|
-
};
|
|
24
|
-
/**
|
|
25
|
-
* Format milliseconds as human-readable duration
|
|
26
|
-
*/
|
|
27
|
-
export function formatDuration(ms) {
|
|
28
|
-
const seconds = Math.floor(ms / 1000);
|
|
29
|
-
const minutes = Math.floor(seconds / 60);
|
|
30
|
-
const hours = Math.floor(minutes / 60);
|
|
31
|
-
const days = Math.floor(hours / 24);
|
|
32
|
-
if (days > 0) {
|
|
33
|
-
return `${String(days)}d ${String(hours % 24)}h`;
|
|
34
|
-
}
|
|
35
|
-
if (hours > 0) {
|
|
36
|
-
return `${String(hours)}h ${String(minutes % 60)}m`;
|
|
37
|
-
}
|
|
38
|
-
if (minutes > 0) {
|
|
39
|
-
return `${String(minutes)}m ${String(seconds % 60)}s`;
|
|
40
|
-
}
|
|
41
|
-
return `${String(seconds)}s`;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Claude Code Plugin for OpenClaw
|
|
45
|
-
*
|
|
46
|
-
* Registers tools that execute prompts in isolated Podman containers
|
|
47
|
-
* running Claude Code CLI.
|
|
48
|
-
*/
|
|
49
|
-
export default function register(api) {
|
|
50
|
-
const pluginConfig = api.config;
|
|
51
|
-
const config = {
|
|
52
|
-
...DEFAULT_CONFIG,
|
|
53
|
-
...pluginConfig,
|
|
54
|
-
};
|
|
55
|
-
const sessionManager = new SessionManager({
|
|
56
|
-
sessionsDir: config.sessionsDir,
|
|
57
|
-
workspacesDir: config.workspacesDir,
|
|
58
|
-
idleTimeout: config.sessionIdleTimeout,
|
|
59
|
-
});
|
|
60
|
-
const podmanRunner = new PodmanRunner({
|
|
61
|
-
runtime: config.runtime,
|
|
62
|
-
image: config.image,
|
|
63
|
-
startupTimeout: config.startupTimeout,
|
|
64
|
-
idleTimeout: config.idleTimeout,
|
|
65
|
-
memory: config.memory,
|
|
66
|
-
cpus: config.cpus,
|
|
67
|
-
network: config.network,
|
|
68
|
-
apparmorProfile: config.apparmorProfile,
|
|
69
|
-
maxOutputSize: config.maxOutputSize,
|
|
70
|
-
});
|
|
71
|
-
// Helper to check authentication
|
|
72
|
-
async function getAuth() {
|
|
73
|
-
const hostCredsPath = path.join(homedir(), ".claude", ".credentials.json");
|
|
74
|
-
let hasCredsFile = false;
|
|
75
|
-
try {
|
|
76
|
-
await fs.access(hostCredsPath);
|
|
77
|
-
hasCredsFile = true;
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
// No credentials file
|
|
81
|
-
}
|
|
82
|
-
const apiKey = hasCredsFile ? undefined : process.env.ANTHROPIC_API_KEY;
|
|
83
|
-
if (!apiKey && !hasCredsFile) {
|
|
84
|
-
throw new Error("No authentication available. Set ANTHROPIC_API_KEY or have ~/.claude/.credentials.json");
|
|
85
|
-
}
|
|
86
|
-
return { apiKey, hasCredsFile };
|
|
87
|
-
}
|
|
88
|
-
// Helper to copy credentials to session directory
|
|
89
|
-
async function copyCredentials(claudeDir) {
|
|
90
|
-
const hostCredsPath = path.join(homedir(), ".claude", ".credentials.json");
|
|
91
|
-
const sessionCredsPath = path.join(claudeDir, ".credentials.json");
|
|
92
|
-
try {
|
|
93
|
-
await fs.mkdir(claudeDir, { recursive: true });
|
|
94
|
-
await fs.copyFile(hostCredsPath, sessionCredsPath);
|
|
95
|
-
}
|
|
96
|
-
catch (err) {
|
|
97
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
98
|
-
throw new Error(`Failed to copy credentials file: ${message}`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
// Register claude_code_start tool
|
|
102
|
-
api.registerTool({
|
|
103
|
-
name: "claude_code_start",
|
|
104
|
-
description: "Start a Claude Code task in the background. Returns a job ID immediately. " +
|
|
105
|
-
"Use claude_code_status to check progress and claude_code_output to read results.",
|
|
106
|
-
parameters: Type.Object({
|
|
107
|
-
prompt: Type.String({ description: "The prompt or task to send to Claude Code" }),
|
|
108
|
-
session_id: Type.Optional(Type.String({ description: "Optional session ID to continue a previous session" })),
|
|
109
|
-
}),
|
|
110
|
-
async execute(id, params) {
|
|
111
|
-
const prompt = params.prompt;
|
|
112
|
-
if (!prompt) {
|
|
113
|
-
throw new Error("prompt parameter is required");
|
|
114
|
-
}
|
|
115
|
-
const sessionKey = params.session_id ?? `session-${id}`;
|
|
116
|
-
// Check authentication
|
|
117
|
-
const { apiKey, hasCredsFile } = await getAuth();
|
|
118
|
-
// Verify container image exists
|
|
119
|
-
const imageExists = await podmanRunner.checkImage();
|
|
120
|
-
if (!imageExists) {
|
|
121
|
-
throw new Error(`Container image not found: ${config.image}. ` +
|
|
122
|
-
`Build it with: podman build -t ${config.image} .`);
|
|
123
|
-
}
|
|
124
|
-
// Get or create session
|
|
125
|
-
const session = await sessionManager.getOrCreateSession(sessionKey);
|
|
126
|
-
// Check for existing active job
|
|
127
|
-
const activeJob = await sessionManager.getActiveJob(sessionKey);
|
|
128
|
-
if (activeJob && (activeJob.status === "pending" || activeJob.status === "running")) {
|
|
129
|
-
throw new Error(`Session already has an active job: ${activeJob.jobId} (status: ${activeJob.status})`);
|
|
130
|
-
}
|
|
131
|
-
// Get paths for volume mounts
|
|
132
|
-
const claudeDir = `${config.sessionsDir.replace("~", process.env.HOME ?? "")}/${sessionKey}/.claude`;
|
|
133
|
-
const workspaceDir = sessionManager.workspaceDir(sessionKey);
|
|
134
|
-
// Copy credentials file if needed
|
|
135
|
-
if (hasCredsFile) {
|
|
136
|
-
await copyCredentials(claudeDir);
|
|
137
|
-
}
|
|
138
|
-
// Create job record
|
|
139
|
-
const containerName = podmanRunner.containerNameFromSessionKey(sessionKey);
|
|
140
|
-
const job = await sessionManager.createJob(sessionKey, { prompt, containerName });
|
|
141
|
-
try {
|
|
142
|
-
// Start container in detached mode
|
|
143
|
-
await podmanRunner.startDetached({
|
|
144
|
-
sessionKey,
|
|
145
|
-
prompt,
|
|
146
|
-
claudeDir,
|
|
147
|
-
workspaceDir,
|
|
148
|
-
resumeSessionId: session.claudeSessionId ?? undefined,
|
|
149
|
-
apiKey,
|
|
150
|
-
});
|
|
151
|
-
// Update job status to running
|
|
152
|
-
await sessionManager.updateJob(sessionKey, job.jobId, {
|
|
153
|
-
status: "running",
|
|
154
|
-
startedAt: new Date().toISOString(),
|
|
155
|
-
});
|
|
156
|
-
// Set as active job
|
|
157
|
-
await sessionManager.setActiveJob(sessionKey, job.jobId);
|
|
158
|
-
return {
|
|
159
|
-
content: [
|
|
160
|
-
{
|
|
161
|
-
type: "text",
|
|
162
|
-
text: JSON.stringify({
|
|
163
|
-
jobId: job.jobId,
|
|
164
|
-
sessionKey,
|
|
165
|
-
status: "running",
|
|
166
|
-
message: "Job started. Use claude_code_status to check progress.",
|
|
167
|
-
}, null, 2),
|
|
168
|
-
},
|
|
169
|
-
],
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
catch (err) {
|
|
173
|
-
// Update job as failed
|
|
174
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
175
|
-
await sessionManager.updateJob(sessionKey, job.jobId, {
|
|
176
|
-
status: "failed",
|
|
177
|
-
completedAt: new Date().toISOString(),
|
|
178
|
-
errorMessage: message,
|
|
179
|
-
});
|
|
180
|
-
throw err;
|
|
181
|
-
}
|
|
182
|
-
},
|
|
183
|
-
});
|
|
184
|
-
// Register claude_code_status tool
|
|
185
|
-
api.registerTool({
|
|
186
|
-
name: "claude_code_status",
|
|
187
|
-
description: "Check the status of a Claude Code job. Returns status, elapsed time, output size, and metrics.",
|
|
188
|
-
parameters: Type.Object({
|
|
189
|
-
job_id: Type.String({ description: "The job ID to check" }),
|
|
190
|
-
session_id: Type.Optional(Type.String({ description: "Session ID (if job was started with one)" })),
|
|
191
|
-
}),
|
|
192
|
-
async execute(id, params) {
|
|
193
|
-
const jobId = params.job_id;
|
|
194
|
-
if (!jobId) {
|
|
195
|
-
throw new Error("job_id parameter is required");
|
|
196
|
-
}
|
|
197
|
-
// Find the job - try provided session_id first, then search all sessions
|
|
198
|
-
let sessionKey = params.session_id;
|
|
199
|
-
let job = sessionKey ? await sessionManager.getJob(sessionKey, jobId) : null;
|
|
200
|
-
if (!job) {
|
|
201
|
-
// Search all sessions for the job
|
|
202
|
-
const sessions = await sessionManager.listSessions();
|
|
203
|
-
for (const session of sessions) {
|
|
204
|
-
job = await sessionManager.getJob(session.sessionKey, jobId);
|
|
205
|
-
if (job) {
|
|
206
|
-
sessionKey = session.sessionKey;
|
|
207
|
-
break;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
if (!job || !sessionKey) {
|
|
212
|
-
throw new Error(`Job not found: ${jobId}`);
|
|
213
|
-
}
|
|
214
|
-
// If job is running, check container status
|
|
215
|
-
if (job.status === "running") {
|
|
216
|
-
const containerStatus = await podmanRunner.getContainerStatus(job.containerName);
|
|
217
|
-
if (containerStatus && !containerStatus.running) {
|
|
218
|
-
// Container finished - update job
|
|
219
|
-
const logs = await podmanRunner.getContainerLogs(job.containerName);
|
|
220
|
-
if (logs) {
|
|
221
|
-
await sessionManager.appendJobOutput(sessionKey, jobId, logs);
|
|
222
|
-
}
|
|
223
|
-
const status = containerStatus.exitCode === 0 ? "completed" : "failed";
|
|
224
|
-
job = await sessionManager.updateJob(sessionKey, jobId, {
|
|
225
|
-
status,
|
|
226
|
-
completedAt: containerStatus.finishedAt ?? new Date().toISOString(),
|
|
227
|
-
exitCode: containerStatus.exitCode,
|
|
228
|
-
errorType: containerStatus.exitCode === 137
|
|
229
|
-
? "oom"
|
|
230
|
-
: containerStatus.exitCode !== 0
|
|
231
|
-
? "crash"
|
|
232
|
-
: null,
|
|
233
|
-
});
|
|
234
|
-
// Clear active job
|
|
235
|
-
await sessionManager.setActiveJob(sessionKey, null);
|
|
236
|
-
// Clean up container
|
|
237
|
-
await podmanRunner.killContainer(sessionKey);
|
|
238
|
-
}
|
|
239
|
-
else if (containerStatus) {
|
|
240
|
-
// Still running - get latest logs and metrics
|
|
241
|
-
const logs = await podmanRunner.getContainerLogs(job.containerName);
|
|
242
|
-
if (logs) {
|
|
243
|
-
await sessionManager.appendJobOutput(sessionKey, jobId, logs);
|
|
244
|
-
}
|
|
245
|
-
const metrics = await podmanRunner.getContainerStats(job.containerName);
|
|
246
|
-
if (metrics) {
|
|
247
|
-
await sessionManager.updateJob(sessionKey, jobId, { metrics });
|
|
248
|
-
job.metrics = metrics;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
// Calculate elapsed time
|
|
253
|
-
const startTime = job.startedAt
|
|
254
|
-
? new Date(job.startedAt).getTime()
|
|
255
|
-
: new Date(job.createdAt).getTime();
|
|
256
|
-
const endTime = job.completedAt ? new Date(job.completedAt).getTime() : Date.now();
|
|
257
|
-
const elapsedSeconds = (endTime - startTime) / 1000;
|
|
258
|
-
// Get current output size
|
|
259
|
-
const outputResult = await sessionManager.readJobOutput(sessionKey, jobId, {
|
|
260
|
-
offset: 0,
|
|
261
|
-
limit: 0,
|
|
262
|
-
});
|
|
263
|
-
const response = {
|
|
264
|
-
jobId: job.jobId,
|
|
265
|
-
sessionKey,
|
|
266
|
-
status: job.status,
|
|
267
|
-
elapsedSeconds: Math.round(elapsedSeconds * 10) / 10,
|
|
268
|
-
outputSize: outputResult.totalSize,
|
|
269
|
-
exitCode: job.exitCode,
|
|
270
|
-
error: job.errorMessage,
|
|
271
|
-
metrics: job.metrics,
|
|
272
|
-
};
|
|
273
|
-
return {
|
|
274
|
-
content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
|
|
275
|
-
};
|
|
276
|
-
},
|
|
277
|
-
});
|
|
278
|
-
// Register claude_code_output tool
|
|
279
|
-
api.registerTool({
|
|
280
|
-
name: "claude_code_output",
|
|
281
|
-
description: "Read output from a Claude Code job. Supports reading partial output while job is running.",
|
|
282
|
-
parameters: Type.Object({
|
|
283
|
-
job_id: Type.String({ description: "The job ID" }),
|
|
284
|
-
session_id: Type.Optional(Type.String({ description: "Session ID" })),
|
|
285
|
-
offset: Type.Optional(Type.Number({ description: "Byte offset to start reading from (default: 0)" })),
|
|
286
|
-
limit: Type.Optional(Type.Number({ description: "Maximum bytes to read (default: 64KB)" })),
|
|
287
|
-
}),
|
|
288
|
-
async execute(id, params) {
|
|
289
|
-
const jobId = params.job_id;
|
|
290
|
-
if (!jobId) {
|
|
291
|
-
throw new Error("job_id parameter is required");
|
|
292
|
-
}
|
|
293
|
-
// Find the job
|
|
294
|
-
let sessionKey = params.session_id;
|
|
295
|
-
let job = sessionKey ? await sessionManager.getJob(sessionKey, jobId) : null;
|
|
296
|
-
if (!job) {
|
|
297
|
-
const sessions = await sessionManager.listSessions();
|
|
298
|
-
for (const session of sessions) {
|
|
299
|
-
job = await sessionManager.getJob(session.sessionKey, jobId);
|
|
300
|
-
if (job) {
|
|
301
|
-
sessionKey = session.sessionKey;
|
|
302
|
-
break;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
if (!job || !sessionKey) {
|
|
307
|
-
throw new Error(`Job not found: ${jobId}`);
|
|
308
|
-
}
|
|
309
|
-
// If running, capture latest logs first
|
|
310
|
-
if (job.status === "running") {
|
|
311
|
-
const logs = await podmanRunner.getContainerLogs(job.containerName);
|
|
312
|
-
if (logs) {
|
|
313
|
-
await sessionManager.appendJobOutput(sessionKey, jobId, logs);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
const offset = params.offset ?? 0;
|
|
317
|
-
const limit = params.limit ?? 65536;
|
|
318
|
-
const result = await sessionManager.readJobOutput(sessionKey, jobId, { offset, limit });
|
|
319
|
-
const header = `[job: ${jobId}] [status: ${job.status}] [bytes ${String(offset)}-${String(offset + result.size)} of ${String(result.totalSize)}]${result.hasMore ? " [more available]" : ""}`;
|
|
320
|
-
return {
|
|
321
|
-
content: [{ type: "text", text: `${header}\n\n${result.content}` }],
|
|
322
|
-
};
|
|
323
|
-
},
|
|
324
|
-
});
|
|
325
|
-
// Register claude_code_cancel tool
|
|
326
|
-
api.registerTool({
|
|
327
|
-
name: "claude_code_cancel",
|
|
328
|
-
description: "Cancel a running Claude Code job.",
|
|
329
|
-
parameters: Type.Object({
|
|
330
|
-
job_id: Type.String({ description: "The job ID to cancel" }),
|
|
331
|
-
session_id: Type.Optional(Type.String({ description: "Session ID" })),
|
|
332
|
-
}),
|
|
333
|
-
async execute(id, params) {
|
|
334
|
-
const jobId = params.job_id;
|
|
335
|
-
if (!jobId) {
|
|
336
|
-
throw new Error("job_id parameter is required");
|
|
337
|
-
}
|
|
338
|
-
// Find the job
|
|
339
|
-
let sessionKey = params.session_id;
|
|
340
|
-
let job = sessionKey ? await sessionManager.getJob(sessionKey, jobId) : null;
|
|
341
|
-
if (!job) {
|
|
342
|
-
const sessions = await sessionManager.listSessions();
|
|
343
|
-
for (const session of sessions) {
|
|
344
|
-
job = await sessionManager.getJob(session.sessionKey, jobId);
|
|
345
|
-
if (job) {
|
|
346
|
-
sessionKey = session.sessionKey;
|
|
347
|
-
break;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
if (!job || !sessionKey) {
|
|
352
|
-
throw new Error(`Job not found: ${jobId}`);
|
|
353
|
-
}
|
|
354
|
-
if (job.status !== "running" && job.status !== "pending") {
|
|
355
|
-
return {
|
|
356
|
-
content: [
|
|
357
|
-
{ type: "text", text: `Job ${jobId} is already ${job.status}, cannot cancel.` },
|
|
358
|
-
],
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
// Kill the container
|
|
362
|
-
await podmanRunner.killContainer(sessionKey);
|
|
363
|
-
// Update job status
|
|
364
|
-
await sessionManager.updateJob(sessionKey, jobId, {
|
|
365
|
-
status: "cancelled",
|
|
366
|
-
completedAt: new Date().toISOString(),
|
|
367
|
-
});
|
|
368
|
-
// Clear active job
|
|
369
|
-
await sessionManager.setActiveJob(sessionKey, null);
|
|
370
|
-
return {
|
|
371
|
-
content: [{ type: "text", text: `Job ${jobId} cancelled.` }],
|
|
372
|
-
};
|
|
373
|
-
},
|
|
374
|
-
});
|
|
375
|
-
// Register the cleanup tool
|
|
376
|
-
api.registerTool({
|
|
377
|
-
name: "claude_code_cleanup",
|
|
378
|
-
description: "Clean up idle Claude Code sessions. " +
|
|
379
|
-
"Removes sessions that have been inactive longer than the configured timeout.",
|
|
380
|
-
parameters: Type.Object({}),
|
|
381
|
-
async execute() {
|
|
382
|
-
const deleted = await sessionManager.cleanupIdleSessions();
|
|
383
|
-
const text = deleted.length === 0
|
|
384
|
-
? "No idle sessions to clean up."
|
|
385
|
-
: `Cleaned up ${String(deleted.length)} idle session(s): ${deleted.join(", ")}`;
|
|
386
|
-
return {
|
|
387
|
-
content: [{ type: "text", text }],
|
|
388
|
-
};
|
|
389
|
-
},
|
|
390
|
-
});
|
|
391
|
-
// Register the sessions listing tool
|
|
392
|
-
api.registerTool({
|
|
393
|
-
name: "claude_code_sessions",
|
|
394
|
-
description: "List all active Claude Code sessions with their age, message count, and active jobs. " +
|
|
395
|
-
"Useful for understanding which sessions exist before resuming or cleaning up.",
|
|
396
|
-
parameters: Type.Object({}),
|
|
397
|
-
async execute() {
|
|
398
|
-
const sessions = await sessionManager.listSessions();
|
|
399
|
-
if (sessions.length === 0) {
|
|
400
|
-
return {
|
|
401
|
-
content: [{ type: "text", text: "No active sessions." }],
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
const now = Date.now();
|
|
405
|
-
const lines = await Promise.all(sessions.map(async (session) => {
|
|
406
|
-
const ageMs = now - new Date(session.createdAt).getTime();
|
|
407
|
-
const ageFormatted = formatDuration(ageMs);
|
|
408
|
-
const lastActiveMs = now - new Date(session.lastActivity).getTime();
|
|
409
|
-
const lastActiveFormatted = formatDuration(lastActiveMs);
|
|
410
|
-
const parts = [
|
|
411
|
-
`Session: ${session.sessionKey}`,
|
|
412
|
-
` Age: ${ageFormatted}`,
|
|
413
|
-
` Last Active: ${lastActiveFormatted} ago`,
|
|
414
|
-
` Messages: ${String(session.messageCount)}`,
|
|
415
|
-
];
|
|
416
|
-
if (session.activeJobId) {
|
|
417
|
-
const activeJob = await sessionManager.getJob(session.sessionKey, session.activeJobId);
|
|
418
|
-
if (activeJob) {
|
|
419
|
-
parts.push(` Active Job: ${activeJob.jobId} (${activeJob.status})`);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
if (session.claudeSessionId) {
|
|
423
|
-
parts.push(` Claude Session: ${session.claudeSessionId}`);
|
|
424
|
-
}
|
|
425
|
-
return parts.join("\n");
|
|
426
|
-
}));
|
|
427
|
-
const text = `Found ${String(sessions.length)} session(s):\n\n${lines.join("\n\n")}`;
|
|
428
|
-
return {
|
|
429
|
-
content: [{ type: "text", text }],
|
|
430
|
-
};
|
|
431
|
-
},
|
|
432
|
-
});
|
|
433
|
-
// Recovery: find orphaned containers on startup and reconcile with job state
|
|
434
|
-
void recoverOrphanedJobs(sessionManager, podmanRunner);
|
|
435
|
-
}
|
|
436
|
-
/**
|
|
437
|
-
* Recover orphaned containers on startup.
|
|
438
|
-
* Reconciles running containers with job state.
|
|
439
|
-
*/
|
|
440
|
-
async function recoverOrphanedJobs(sessionManager, podmanRunner) {
|
|
441
|
-
try {
|
|
442
|
-
const containers = await podmanRunner.listContainersByPrefix("claude-");
|
|
443
|
-
for (const container of containers) {
|
|
444
|
-
const sessionKey = podmanRunner.sessionKeyFromContainerName(container.name);
|
|
445
|
-
if (!sessionKey)
|
|
446
|
-
continue;
|
|
447
|
-
const activeJob = await sessionManager.getActiveJob(sessionKey);
|
|
448
|
-
if (activeJob?.containerName === container.name) {
|
|
449
|
-
// Job exists for this container
|
|
450
|
-
if (!container.running) {
|
|
451
|
-
// Container finished while plugin was down - update job
|
|
452
|
-
const status = await podmanRunner.getContainerStatus(container.name);
|
|
453
|
-
const logs = await podmanRunner.getContainerLogs(container.name);
|
|
454
|
-
if (logs) {
|
|
455
|
-
await sessionManager.appendJobOutput(sessionKey, activeJob.jobId, logs);
|
|
456
|
-
}
|
|
457
|
-
await sessionManager.updateJob(sessionKey, activeJob.jobId, {
|
|
458
|
-
status: status?.exitCode === 0 ? "completed" : "failed",
|
|
459
|
-
completedAt: status?.finishedAt ?? new Date().toISOString(),
|
|
460
|
-
exitCode: status?.exitCode ?? null,
|
|
461
|
-
errorType: status?.exitCode === 137 ? "oom" : status?.exitCode !== 0 ? "crash" : null,
|
|
462
|
-
});
|
|
463
|
-
await sessionManager.setActiveJob(sessionKey, null);
|
|
464
|
-
await podmanRunner.killContainer(sessionKey);
|
|
465
|
-
}
|
|
466
|
-
// If still running, leave it alone - normal polling will handle it
|
|
467
|
-
}
|
|
468
|
-
else {
|
|
469
|
-
// Orphaned container with no matching job - kill it
|
|
470
|
-
await podmanRunner.killContainer(sessionKey);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
catch {
|
|
475
|
-
// Ignore recovery errors on startup
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
// Also export components for testing
|
|
479
|
-
export { SessionManager } from "./session-manager.js";
|
|
480
|
-
export { PodmanRunner } from "./podman-runner.js";
|
|
481
|
-
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|