granclaw 0.0.1-beta.96 → 0.0.1-beta.97
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/backend/agent/browser-bin.js +38 -0
- package/dist/backend/agent/runner-pi.js +15 -4
- package/dist/backend/orchestrator/server.js +13 -0
- package/dist/backend/workflows/runner.js +30 -0
- package/dist/frontend/assets/{index-2w83F8pA.js → index-6s2NVOlC.js} +44 -44
- package/dist/frontend/index.html +1 -1
- package/package.json +1 -1
|
@@ -6,9 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.registerBrowserProvider = registerBrowserProvider;
|
|
7
7
|
exports._resetBrowserProvidersForTests = _resetBrowserProvidersForTests;
|
|
8
8
|
exports.buildArgv = buildArgv;
|
|
9
|
+
exports.cdpNavigate = cdpNavigate;
|
|
9
10
|
exports.resolveBrowserBinary = resolveBrowserBinary;
|
|
10
11
|
const fs_1 = __importDefault(require("fs"));
|
|
11
12
|
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const ws_1 = require("ws");
|
|
12
14
|
const stealth_js_1 = require("../browser/stealth.js");
|
|
13
15
|
const providers = [];
|
|
14
16
|
function registerBrowserProvider(provider) {
|
|
@@ -20,6 +22,41 @@ function _resetBrowserProvidersForTests() {
|
|
|
20
22
|
function buildArgv(res, command, args) {
|
|
21
23
|
return [...res.preCommandArgs, command, ...args, ...res.postCommandArgs];
|
|
22
24
|
}
|
|
25
|
+
function cdpNavigate(port, url) {
|
|
26
|
+
return new Promise(async (resolve, reject) => {
|
|
27
|
+
const timer = setTimeout(() => reject(new Error('cdpNavigate: timeout')), 15_000);
|
|
28
|
+
try {
|
|
29
|
+
const res = await fetch(`http://127.0.0.1:${port}/json`);
|
|
30
|
+
const targets = (await res.json());
|
|
31
|
+
const page = targets.find(t => t.type === 'page');
|
|
32
|
+
if (!page) {
|
|
33
|
+
clearTimeout(timer);
|
|
34
|
+
reject(new Error('cdpNavigate: no page target'));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const ws = new ws_1.WebSocket(page.webSocketDebuggerUrl);
|
|
38
|
+
ws.on('open', () => {
|
|
39
|
+
ws.send(JSON.stringify({ id: 1, method: 'Page.navigate', params: { url } }));
|
|
40
|
+
});
|
|
41
|
+
ws.on('message', (data) => {
|
|
42
|
+
const msg = JSON.parse(data.toString());
|
|
43
|
+
if (msg.id === 1) {
|
|
44
|
+
clearTimeout(timer);
|
|
45
|
+
ws.close();
|
|
46
|
+
if (msg.error)
|
|
47
|
+
reject(new Error(`cdpNavigate: ${msg.error.message}`));
|
|
48
|
+
else
|
|
49
|
+
resolve(url);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
ws.on('error', (err) => { clearTimeout(timer); reject(err); });
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
clearTimeout(timer);
|
|
56
|
+
reject(err);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
23
60
|
async function resolveBrowserBinary(agentId, workspaceDir) {
|
|
24
61
|
for (const provider of providers) {
|
|
25
62
|
const resolution = await provider(agentId, workspaceDir);
|
|
@@ -39,6 +76,7 @@ async function resolveBrowserBinary(agentId, workspaceDir) {
|
|
|
39
76
|
env: {},
|
|
40
77
|
isRemote: false,
|
|
41
78
|
recordingSupported: true,
|
|
79
|
+
cdpPort: port,
|
|
42
80
|
};
|
|
43
81
|
}
|
|
44
82
|
}
|
|
@@ -413,7 +413,7 @@ async function runAgent(agent, message, onChunk, options) {
|
|
|
413
413
|
},
|
|
414
414
|
},
|
|
415
415
|
async execute(_toolCallId, params) {
|
|
416
|
-
const apiUrl = process.env.GRANCLAW_API_URL ??
|
|
416
|
+
const apiUrl = process.env.GRANCLAW_API_URL ?? `http://localhost:${process.env.PORT ?? 3001}`;
|
|
417
417
|
const url = new URL(`${apiUrl}/agents/${agent.id}/messages`);
|
|
418
418
|
if (params.contains)
|
|
419
419
|
url.searchParams.set('contains', params.contains);
|
|
@@ -564,7 +564,7 @@ async function runAgent(agent, message, onChunk, options) {
|
|
|
564
564
|
});
|
|
565
565
|
extensionFactories.push((pi) => {
|
|
566
566
|
const taskBase = () => {
|
|
567
|
-
const apiUrl = process.env.GRANCLAW_API_URL ??
|
|
567
|
+
const apiUrl = process.env.GRANCLAW_API_URL ?? `http://localhost:${process.env.PORT ?? 3001}`;
|
|
568
568
|
return `${apiUrl}/agents/${agent.id}/tasks`;
|
|
569
569
|
};
|
|
570
570
|
const fetchJson = async (url, init) => {
|
|
@@ -585,7 +585,7 @@ async function runAgent(agent, message, onChunk, options) {
|
|
|
585
585
|
parameters: {
|
|
586
586
|
type: 'object',
|
|
587
587
|
properties: {
|
|
588
|
-
status: { type: 'string', enum: ['backlog', 'in_progress', 'scheduled', 'to_review', 'done'], description: 'Filter by status (omit for all tasks)' },
|
|
588
|
+
status: { type: 'string', enum: ['backlog', 'in_progress', 'scheduled', 'to_review', 'done', 'cancelled'], description: 'Filter by status (omit for all tasks)' },
|
|
589
589
|
},
|
|
590
590
|
},
|
|
591
591
|
async execute(_id, params) {
|
|
@@ -788,6 +788,17 @@ async function runAgent(agent, message, onChunk, options) {
|
|
|
788
788
|
if (browser.recordingSupported && !browserState.handle.recordingStarted) {
|
|
789
789
|
await (0, session_manager_js_1.startRecording)(browserState.handle, browser);
|
|
790
790
|
}
|
|
791
|
+
if (command === 'open' && browser.cdpPort && args.length > 0) {
|
|
792
|
+
try {
|
|
793
|
+
await (0, browser_bin_js_1.cdpNavigate)(browser.cdpPort, args[0]);
|
|
794
|
+
(0, session_manager_js_1.appendCommand)(browserState.handle, `${command} ${args.join(' ')}`.trim());
|
|
795
|
+
return { content: [{ type: 'text', text: `Navigated to ${args[0]}` }] };
|
|
796
|
+
}
|
|
797
|
+
catch (err) {
|
|
798
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
799
|
+
return { content: [{ type: 'text', text: `browser open failed (CDP navigate): ${msg}` }] };
|
|
800
|
+
}
|
|
801
|
+
}
|
|
791
802
|
const argv = (0, browser_bin_js_1.buildArgv)(browser, command, args);
|
|
792
803
|
try {
|
|
793
804
|
const { stdout, stderr } = await execFileAsync(browser.bin, argv, {
|
|
@@ -943,7 +954,7 @@ async function runAgent(agent, message, onChunk, options) {
|
|
|
943
954
|
required: ['query'],
|
|
944
955
|
},
|
|
945
956
|
async execute(_toolCallId, params) {
|
|
946
|
-
const apiUrl = process.env.GRANCLAW_API_URL ??
|
|
957
|
+
const apiUrl = process.env.GRANCLAW_API_URL ?? `http://localhost:${process.env.PORT ?? 3001}`;
|
|
947
958
|
const url = `${apiUrl}/search?q=${encodeURIComponent(params.query)}`;
|
|
948
959
|
try {
|
|
949
960
|
const res = await fetch(url);
|
|
@@ -1203,6 +1203,19 @@ function createServer() {
|
|
|
1203
1203
|
}
|
|
1204
1204
|
res.json(run);
|
|
1205
1205
|
});
|
|
1206
|
+
app.post('/agents/:id/workflows/:wfId/runs/:runId/cancel', (req, res) => {
|
|
1207
|
+
const managed = (0, agent_manager_js_1.getManagedAgent)(req.params.id);
|
|
1208
|
+
if (!managed) {
|
|
1209
|
+
res.status(404).json({ error: 'Agent not found' });
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
const cancelled = (0, runner_js_1.cancelWorkflowRun)(req.params.id, req.params.runId);
|
|
1213
|
+
if (!cancelled) {
|
|
1214
|
+
res.status(404).json({ error: 'Run not active or not found' });
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1217
|
+
res.json({ ok: true });
|
|
1218
|
+
});
|
|
1206
1219
|
app.get('/agents/:id/schedules', (req, res) => {
|
|
1207
1220
|
const managed = (0, agent_manager_js_1.getManagedAgent)(req.params.id);
|
|
1208
1221
|
if (!managed) {
|
|
@@ -3,6 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.cancelWorkflowRun = cancelWorkflowRun;
|
|
7
|
+
exports.getActiveRunIds = getActiveRunIds;
|
|
6
8
|
exports.executeWorkflow = executeWorkflow;
|
|
7
9
|
const child_process_1 = require("child_process");
|
|
8
10
|
const util_1 = require("util");
|
|
@@ -15,6 +17,18 @@ const runner_pi_js_1 = require("../agent/runner-pi.js");
|
|
|
15
17
|
const crypto_1 = require("crypto");
|
|
16
18
|
const messages_db_js_1 = require("../messages-db.js");
|
|
17
19
|
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
20
|
+
const activeRuns = new Map();
|
|
21
|
+
function cancelWorkflowRun(agentId, runId) {
|
|
22
|
+
const key = `${agentId}:${runId}`;
|
|
23
|
+
const ctrl = activeRuns.get(key);
|
|
24
|
+
if (!ctrl)
|
|
25
|
+
return false;
|
|
26
|
+
ctrl.abort();
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
function getActiveRunIds() {
|
|
30
|
+
return Array.from(activeRuns.keys());
|
|
31
|
+
}
|
|
18
32
|
function resolveTemplates(prompt, prevOutput, allResults) {
|
|
19
33
|
let resolved = prompt;
|
|
20
34
|
resolved = resolved.replace(/\{\{prev\.output\}\}/g, prevOutput !== null ? JSON.stringify(prevOutput) : 'null');
|
|
@@ -170,6 +184,9 @@ async function executeWorkflow(agentId, workflowId, trigger) {
|
|
|
170
184
|
const workspaceDir = path_1.default.resolve(config_js_1.REPO_ROOT, agent.workspaceDir);
|
|
171
185
|
const run = (0, workflows_db_js_1.createRun)(agentId, workflowId, trigger);
|
|
172
186
|
const allResults = [];
|
|
187
|
+
const abortCtrl = new AbortController();
|
|
188
|
+
const runKey = `${agentId}:${run.id}`;
|
|
189
|
+
activeRuns.set(runKey, abortCtrl);
|
|
173
190
|
const runStepMap = new Map();
|
|
174
191
|
for (const step of workflow.steps) {
|
|
175
192
|
const runStep = (0, workflows_db_js_1.createRunStep)(agentId, { runId: run.id, stepId: step.id });
|
|
@@ -178,6 +195,17 @@ async function executeWorkflow(agentId, workflowId, trigger) {
|
|
|
178
195
|
let currentStep = workflow.steps[0];
|
|
179
196
|
let prevOutput = null;
|
|
180
197
|
while (currentStep) {
|
|
198
|
+
if (abortCtrl.signal.aborted) {
|
|
199
|
+
for (const step of workflow.steps) {
|
|
200
|
+
const rsId = runStepMap.get(step.id);
|
|
201
|
+
if (!allResults.some(r => r.stepId === step.id)) {
|
|
202
|
+
(0, workflows_db_js_1.updateRunStep)(agentId, rsId, { status: 'skipped' });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
(0, workflows_db_js_1.updateRun)(agentId, run.id, { status: 'cancelled', finishedAt: Date.now() });
|
|
206
|
+
activeRuns.delete(runKey);
|
|
207
|
+
return run.id;
|
|
208
|
+
}
|
|
181
209
|
const runStepId = runStepMap.get(currentStep.id);
|
|
182
210
|
const startedAt = Date.now();
|
|
183
211
|
(0, workflows_db_js_1.updateRunStep)(agentId, runStepId, { status: 'running', startedAt });
|
|
@@ -266,12 +294,14 @@ async function executeWorkflow(agentId, workflowId, trigger) {
|
|
|
266
294
|
}
|
|
267
295
|
}
|
|
268
296
|
(0, workflows_db_js_1.updateRun)(agentId, run.id, { status: 'failed', finishedAt });
|
|
297
|
+
activeRuns.delete(runKey);
|
|
269
298
|
console.error(`[workflow-runner] step "${currentStep?.name}" failed:`, message);
|
|
270
299
|
const failSummary = `**Workflow "${workflow.name}" failed** at step "${currentStep?.name}"\n\nError: ${message}`;
|
|
271
300
|
(0, messages_db_js_1.saveMessage)({ id: (0, crypto_1.randomUUID)(), agentId, channelId: 'ui', role: 'assistant', content: failSummary });
|
|
272
301
|
return run.id;
|
|
273
302
|
}
|
|
274
303
|
}
|
|
304
|
+
activeRuns.delete(runKey);
|
|
275
305
|
(0, workflows_db_js_1.updateRun)(agentId, run.id, { status: 'completed', finishedAt: Date.now() });
|
|
276
306
|
const lastOutput = allResults[allResults.length - 1];
|
|
277
307
|
const outputPreview = typeof lastOutput?.output === 'string'
|