@calltelemetry/openclaw-linear 0.5.1 → 0.6.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/README.md +359 -195
- package/index.ts +10 -10
- package/openclaw.plugin.json +4 -1
- package/package.json +9 -2
- package/src/agent/agent.test.ts +127 -0
- package/src/{agent.ts → agent/agent.ts} +84 -7
- package/src/agent/watchdog.test.ts +266 -0
- package/src/agent/watchdog.ts +176 -0
- package/src/{cli.ts → infra/cli.ts} +5 -5
- package/src/{codex-worktree.ts → infra/codex-worktree.ts} +4 -0
- package/src/infra/notify.test.ts +169 -0
- package/src/{notify.ts → infra/notify.ts} +6 -1
- package/src/pipeline/active-session.test.ts +154 -0
- package/src/pipeline/artifacts.test.ts +383 -0
- package/src/pipeline/artifacts.ts +273 -0
- package/src/{dispatch-service.ts → pipeline/dispatch-service.ts} +1 -1
- package/src/pipeline/dispatch-state.test.ts +382 -0
- package/src/{dispatch-state.ts → pipeline/dispatch-state.ts} +1 -0
- package/src/pipeline/pipeline.test.ts +226 -0
- package/src/{pipeline.ts → pipeline/pipeline.ts} +134 -10
- package/src/{tier-assess.ts → pipeline/tier-assess.ts} +1 -1
- package/src/{webhook.test.ts → pipeline/webhook.test.ts} +1 -1
- package/src/{webhook.ts → pipeline/webhook.ts} +30 -8
- package/src/{claude-tool.ts → tools/claude-tool.ts} +31 -5
- package/src/{cli-shared.ts → tools/cli-shared.ts} +5 -4
- package/src/{code-tool.ts → tools/code-tool.ts} +2 -2
- package/src/{codex-tool.ts → tools/codex-tool.ts} +31 -5
- package/src/{gemini-tool.ts → tools/gemini-tool.ts} +31 -5
- package/src/{orchestration-tools.ts → tools/orchestration-tools.ts} +1 -1
- package/src/client.ts +0 -94
- /package/src/{auth.ts → api/auth.ts} +0 -0
- /package/src/{linear-api.ts → api/linear-api.ts} +0 -0
- /package/src/{oauth-callback.ts → api/oauth-callback.ts} +0 -0
- /package/src/{active-session.ts → pipeline/active-session.ts} +0 -0
- /package/src/{tools.ts → tools/tools.ts} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { createInterface } from "node:readline";
|
|
3
3
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
4
|
-
import type { ActivityContent } from "
|
|
4
|
+
import type { ActivityContent } from "../api/linear-api.js";
|
|
5
5
|
import {
|
|
6
6
|
buildLinearApi,
|
|
7
7
|
resolveSession,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
type CliToolParams,
|
|
12
12
|
type CliResult,
|
|
13
13
|
} from "./cli-shared.js";
|
|
14
|
+
import { InactivityWatchdog, resolveWatchdogConfig } from "../agent/watchdog.js";
|
|
14
15
|
|
|
15
16
|
const GEMINI_BIN = "/home/claw/.npm-global/bin/gemini";
|
|
16
17
|
|
|
@@ -98,7 +99,9 @@ export async function runGemini(
|
|
|
98
99
|
|
|
99
100
|
api.logger.info(`gemini_run: session=${agentSessionId ?? "none"}, issue=${issueIdentifier ?? "none"}`);
|
|
100
101
|
|
|
101
|
-
const
|
|
102
|
+
const agentId = (params as any).agentId ?? (pluginConfig?.defaultAgentId as string) ?? "default";
|
|
103
|
+
const wdConfig = resolveWatchdogConfig(agentId, pluginConfig ?? undefined);
|
|
104
|
+
const timeout = timeoutMs ?? (pluginConfig?.geminiTimeoutMs as number) ?? wdConfig.toolTimeoutMs;
|
|
102
105
|
const workingDir = params.workingDir ?? (pluginConfig?.geminiBaseRepo as string) ?? DEFAULT_BASE_REPO;
|
|
103
106
|
|
|
104
107
|
const linearApi = buildLinearApi(api, agentSessionId);
|
|
@@ -131,12 +134,27 @@ export async function runGemini(
|
|
|
131
134
|
});
|
|
132
135
|
|
|
133
136
|
let killed = false;
|
|
137
|
+
let killedByWatchdog = false;
|
|
134
138
|
const timer = setTimeout(() => {
|
|
135
139
|
killed = true;
|
|
136
140
|
child.kill("SIGTERM");
|
|
137
141
|
setTimeout(() => { if (!child.killed) child.kill("SIGKILL"); }, 5_000);
|
|
138
142
|
}, timeout);
|
|
139
143
|
|
|
144
|
+
const watchdog = new InactivityWatchdog({
|
|
145
|
+
inactivityMs: wdConfig.inactivityMs,
|
|
146
|
+
label: `gemini:${agentSessionId ?? "unknown"}`,
|
|
147
|
+
logger: api.logger,
|
|
148
|
+
onKill: () => {
|
|
149
|
+
killedByWatchdog = true;
|
|
150
|
+
killed = true;
|
|
151
|
+
clearTimeout(timer);
|
|
152
|
+
child.kill("SIGTERM");
|
|
153
|
+
setTimeout(() => { if (!child.killed) child.kill("SIGKILL"); }, 5_000);
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
watchdog.start();
|
|
157
|
+
|
|
140
158
|
const collectedMessages: string[] = [];
|
|
141
159
|
const collectedCommands: string[] = [];
|
|
142
160
|
let stderrOutput = "";
|
|
@@ -144,6 +162,7 @@ export async function runGemini(
|
|
|
144
162
|
const rl = createInterface({ input: child.stdout! });
|
|
145
163
|
rl.on("line", (line) => {
|
|
146
164
|
if (!line.trim()) return;
|
|
165
|
+
watchdog.tick();
|
|
147
166
|
|
|
148
167
|
let event: any;
|
|
149
168
|
try {
|
|
@@ -187,11 +206,13 @@ export async function runGemini(
|
|
|
187
206
|
});
|
|
188
207
|
|
|
189
208
|
child.stderr?.on("data", (chunk) => {
|
|
209
|
+
watchdog.tick();
|
|
190
210
|
stderrOutput += chunk.toString();
|
|
191
211
|
});
|
|
192
212
|
|
|
193
213
|
child.on("close", (code) => {
|
|
194
214
|
clearTimeout(timer);
|
|
215
|
+
watchdog.stop();
|
|
195
216
|
rl.close();
|
|
196
217
|
|
|
197
218
|
const parts: string[] = [];
|
|
@@ -200,11 +221,15 @@ export async function runGemini(
|
|
|
200
221
|
const output = parts.join("\n\n") || stderrOutput || "(no output)";
|
|
201
222
|
|
|
202
223
|
if (killed) {
|
|
203
|
-
|
|
224
|
+
const errorType = killedByWatchdog ? "inactivity_timeout" : "timeout";
|
|
225
|
+
const reason = killedByWatchdog
|
|
226
|
+
? `Gemini killed by inactivity watchdog (no I/O for ${Math.round(wdConfig.inactivityMs / 1000)}s)`
|
|
227
|
+
: `Gemini timed out after ${Math.round(timeout / 1000)}s`;
|
|
228
|
+
api.logger.warn(reason);
|
|
204
229
|
resolve({
|
|
205
230
|
success: false,
|
|
206
|
-
output:
|
|
207
|
-
error:
|
|
231
|
+
output: `${reason}. Partial output:\n${output}`,
|
|
232
|
+
error: errorType,
|
|
208
233
|
});
|
|
209
234
|
return;
|
|
210
235
|
}
|
|
@@ -225,6 +250,7 @@ export async function runGemini(
|
|
|
225
250
|
|
|
226
251
|
child.on("error", (err) => {
|
|
227
252
|
clearTimeout(timer);
|
|
253
|
+
watchdog.stop();
|
|
228
254
|
rl.close();
|
|
229
255
|
api.logger.error(`Gemini spawn error: ${err}`);
|
|
230
256
|
resolve({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AnyAgentTool, OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
2
|
import { jsonResult } from "openclaw/plugin-sdk";
|
|
3
|
-
import { runAgent } from "
|
|
3
|
+
import { runAgent } from "../agent/agent.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Create orchestration tools that let agents delegate work to other crew agents.
|
package/src/client.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
const LINEAR_GRAPHQL_URL = "https://api.linear.app/graphql";
|
|
2
|
-
|
|
3
|
-
export class LinearClient {
|
|
4
|
-
constructor(private accessToken: string) {}
|
|
5
|
-
|
|
6
|
-
async request<T = any>(query: string, variables?: Record<string, any>): Promise<T> {
|
|
7
|
-
const res = await fetch(LINEAR_GRAPHQL_URL, {
|
|
8
|
-
method: "POST",
|
|
9
|
-
headers: {
|
|
10
|
-
"Content-Type": "application/json",
|
|
11
|
-
"Authorization": `Bearer ${this.accessToken}`,
|
|
12
|
-
},
|
|
13
|
-
body: JSON.stringify({ query, variables }),
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
if (!res.ok) {
|
|
17
|
-
const text = await res.text();
|
|
18
|
-
throw new Error(`Linear API request failed: ${res.status} ${text}`);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const payload = await res.json();
|
|
22
|
-
if (payload.errors) {
|
|
23
|
-
throw new Error(`Linear API returned errors: ${JSON.stringify(payload.errors)}`);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return payload.data as T;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async getViewer() {
|
|
30
|
-
const query = `
|
|
31
|
-
query {
|
|
32
|
-
viewer {
|
|
33
|
-
id
|
|
34
|
-
name
|
|
35
|
-
email
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
`;
|
|
39
|
-
return this.request(query);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async listIssues(params: { limit: number; teamId?: string }) {
|
|
43
|
-
const query = `
|
|
44
|
-
query ListIssues($limit: Int, $teamId: String) {
|
|
45
|
-
issues(first: $limit, filter: { team: { id: { eq: $teamId } } }) {
|
|
46
|
-
nodes {
|
|
47
|
-
id
|
|
48
|
-
identifier
|
|
49
|
-
title
|
|
50
|
-
description
|
|
51
|
-
state {
|
|
52
|
-
name
|
|
53
|
-
}
|
|
54
|
-
assignee {
|
|
55
|
-
name
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
`;
|
|
61
|
-
return this.request(query, params);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async createIssue(params: { title: string; description?: string; teamId: string }) {
|
|
65
|
-
const query = `
|
|
66
|
-
mutation CreateIssue($title: String!, $description: String, $teamId: String!) {
|
|
67
|
-
issueCreate(input: { title: $title, description: $description, teamId: $teamId }) {
|
|
68
|
-
success
|
|
69
|
-
issue {
|
|
70
|
-
id
|
|
71
|
-
identifier
|
|
72
|
-
title
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
`;
|
|
77
|
-
return this.request(query, params);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async addComment(params: { issueId: string; body: string }) {
|
|
81
|
-
const query = `
|
|
82
|
-
mutation AddComment($issueId: String!, $body: String!) {
|
|
83
|
-
commentCreate(input: { issueId: $issueId, body: $body }) {
|
|
84
|
-
success
|
|
85
|
-
comment {
|
|
86
|
-
id
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
`;
|
|
91
|
-
return this.request(query, params);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|