heyhank 0.2.0 → 0.3.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/dist/assets/{AgentsPage-B-AAmsMK.js → AgentsPage-DqjDAcIw.js} +1 -1
- package/dist/assets/{AssistantPage-BV1Mfwdt.js → AssistantPage-C50CQFSB.js} +1 -1
- package/dist/assets/{BusinessPage-tLpNEz19.js → BusinessPage-AY70tf1k.js} +1 -1
- package/dist/assets/{CronManager-B-K_n3Jg.js → CronManager-Dt7LLuRr.js} +1 -1
- package/dist/assets/{HelpPage-Bhf_j6Xr.js → HelpPage-tlGx7fQF.js} +1 -1
- package/dist/assets/{IntegrationsPage-DAMjs9tM.js → IntegrationsPage-B4XOuHXu.js} +1 -1
- package/dist/assets/{JarvisHUD-C_TGXCCn.js → JarvisHUD-BDvuRd0I.js} +1 -1
- package/dist/assets/{MediaPage-C48HTTrt.js → MediaPage-CofV9Rd-.js} +1 -1
- package/dist/assets/{MemoryPage-JkC-qtgp.js → MemoryPage-Cj7FeqmJ.js} +1 -1
- package/dist/assets/{PlatformDashboard-AUo7tNnE.js → PlatformDashboard-B9kXAlH1.js} +1 -1
- package/dist/assets/{Playground-AzNMsRBL.js → Playground-Cka-pRkP.js} +1 -1
- package/dist/assets/{ProcessPanel-DpE_2sX3.js → ProcessPanel-BqhQgfYj.js} +1 -1
- package/dist/assets/{PromptsPage-C2RQOs6p.js → PromptsPage-VveKc9uX.js} +1 -1
- package/dist/assets/RunsPage-DXVEk0AZ.js +1 -0
- package/dist/assets/{SandboxManager-jHvYjwfh.js → SandboxManager-DACcwfDF.js} +1 -1
- package/dist/assets/{SettingsPage-BBJax6gt.js → SettingsPage-jfuQh8Tu.js} +1 -1
- package/dist/assets/{SkillsMarketplace-IjmjfdjD.js → SkillsMarketplace-DrigiApe.js} +1 -1
- package/dist/assets/{SocialMediaPage-DoPZHhr2.js → SocialMediaPage-DOh3IPe8.js} +1 -1
- package/dist/assets/{TailscalePage-DDEY7ckO.js → TailscalePage-DLhJWATT.js} +1 -1
- package/dist/assets/{TelephonyPage-OPNBZYKt.js → TelephonyPage-9C4C3_ot.js} +1 -1
- package/dist/assets/{TerminalPage-BjMbHHW3.js → TerminalPage-ChX-8Wu7.js} +1 -1
- package/dist/assets/index-C6Q5UQHD.js +229 -0
- package/dist/assets/index-ZxGXgiV3.css +32 -0
- package/dist/assets/sw-register-BBYuk-kw.js +1 -0
- package/dist/assets/workbox-window.prod.es5-BBnX5xw4.js +2 -0
- package/dist/index.html +2 -2
- package/dist/sw.js +1 -1
- package/dist/{workbox-d2a0910a.js → workbox-080c8b91.js} +1 -1
- package/package.json +1 -1
- package/server/agent-executor.ts +65 -0
- package/server/execution-store.ts +54 -1
- package/server/routes/agent-routes.ts +42 -0
- package/dist/assets/RunsPage-B9UOyO79.js +0 -1
- package/dist/assets/index-BgYM4wXw.js +0 -205
- package/dist/assets/index-BkjSoVgn.css +0 -32
- package/dist/assets/sw-register-C7NOHtIu.js +0 -1
- package/dist/assets/workbox-window.prod.es5-BIl4cyR9.js +0 -2
package/server/agent-executor.ts
CHANGED
|
@@ -351,6 +351,71 @@ export class AgentExecutor {
|
|
|
351
351
|
return this.executionStore.list(opts);
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
+
/**
|
|
355
|
+
* Cancel a running execution. Marks the in-memory + persisted record as
|
|
356
|
+
* completed (success=false). Returns true if a matching in-memory execution
|
|
357
|
+
* was found and updated, false if only the on-disk record was touched (e.g.
|
|
358
|
+
* for zombie sessions whose CLI process is long gone).
|
|
359
|
+
*/
|
|
360
|
+
cancelExecution(sessionId: string, reason = "Stopped by user"): boolean {
|
|
361
|
+
for (const [, execs] of this.executions) {
|
|
362
|
+
const exec = execs.find((e) => e.sessionId === sessionId && !e.completedAt);
|
|
363
|
+
if (exec) {
|
|
364
|
+
exec.completedAt = Date.now();
|
|
365
|
+
exec.success = false;
|
|
366
|
+
exec.error = exec.error || reason;
|
|
367
|
+
this.executionStore.update(sessionId, {
|
|
368
|
+
completedAt: exec.completedAt,
|
|
369
|
+
success: exec.success,
|
|
370
|
+
error: exec.error,
|
|
371
|
+
});
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// Fallback: touch the on-disk record directly (zombie cleanup).
|
|
376
|
+
this.executionStore.update(sessionId, {
|
|
377
|
+
completedAt: Date.now(),
|
|
378
|
+
success: false,
|
|
379
|
+
error: reason,
|
|
380
|
+
});
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Permanently delete execution records (single or bulk). Filters by status
|
|
386
|
+
* if provided, or by an explicit list of sessionIds. Returns the count of
|
|
387
|
+
* removed records. Refuses to delete records that are still running.
|
|
388
|
+
*/
|
|
389
|
+
deleteExecutions(opts: { sessionIds?: string[]; status?: "success" | "error" }): number {
|
|
390
|
+
let targets: string[] = [];
|
|
391
|
+
|
|
392
|
+
if (opts.sessionIds && opts.sessionIds.length > 0) {
|
|
393
|
+
// Filter out running ones — caller must cancel first.
|
|
394
|
+
const all = this.executionStore.list({ limit: 10000 }).executions;
|
|
395
|
+
const byId = new Map(all.map((e) => [e.sessionId, e] as const));
|
|
396
|
+
for (const sid of opts.sessionIds) {
|
|
397
|
+
const exec = byId.get(sid);
|
|
398
|
+
if (!exec || exec.completedAt) targets.push(sid);
|
|
399
|
+
}
|
|
400
|
+
} else if (opts.status) {
|
|
401
|
+
const matching = this.executionStore.list({ status: opts.status, limit: 10000 }).executions;
|
|
402
|
+
targets = matching.map((e) => e.sessionId);
|
|
403
|
+
} else {
|
|
404
|
+
return 0;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (targets.length === 0) return 0;
|
|
408
|
+
|
|
409
|
+
// Also drop them from each agent's in-memory list so getExecutions stays consistent.
|
|
410
|
+
const targetSet = new Set(targets);
|
|
411
|
+
for (const [agentId, list] of this.executions) {
|
|
412
|
+
const next = list.filter((e) => !targetSet.has(e.sessionId));
|
|
413
|
+
if (next.length !== list.length) this.executions.set(agentId, next);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return this.executionStore.deleteBySessionIds(targets);
|
|
417
|
+
}
|
|
418
|
+
|
|
354
419
|
/** Handle session exit: mark the corresponding execution as completed. */
|
|
355
420
|
handleSessionExited(sessionId: string, exitCode: number | null): void {
|
|
356
421
|
for (const [, execs] of this.executions) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Persists AgentExecution records to disk as JSONL (one file per day).
|
|
3
3
|
// Used by the Runs view to display execution history across server restarts.
|
|
4
4
|
|
|
5
|
-
import { mkdirSync, appendFileSync, readdirSync, readFileSync } from "node:fs";
|
|
5
|
+
import { mkdirSync, appendFileSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import type { AgentExecution } from "./agent-types.js";
|
|
@@ -72,6 +72,59 @@ export class ExecutionStore {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Permanently delete executions by sessionId. Rewrites any daily JSONL file
|
|
77
|
+
* that contained matching records and removes them from the in-memory cache.
|
|
78
|
+
* Returns the number of records that were removed (counts cache hits — the
|
|
79
|
+
* on-disk file may contain extra historical lines for the same sessionId
|
|
80
|
+
* which are all stripped too).
|
|
81
|
+
*/
|
|
82
|
+
deleteBySessionIds(sessionIds: string[]): number {
|
|
83
|
+
if (sessionIds.length === 0) return 0;
|
|
84
|
+
const toDelete = new Set(sessionIds);
|
|
85
|
+
|
|
86
|
+
// Rewrite each daily JSONL file, stripping any line whose sessionId matches.
|
|
87
|
+
try {
|
|
88
|
+
const files = readdirSync(this.dir)
|
|
89
|
+
.filter((f) => f.startsWith("executions-") && f.endsWith(".jsonl"));
|
|
90
|
+
for (const file of files) {
|
|
91
|
+
const filepath = join(this.dir, file);
|
|
92
|
+
const content = readFileSync(filepath, "utf-8");
|
|
93
|
+
const lines = content.split("\n").filter(Boolean);
|
|
94
|
+
const kept: string[] = [];
|
|
95
|
+
let touched = false;
|
|
96
|
+
for (const line of lines) {
|
|
97
|
+
try {
|
|
98
|
+
const exec = JSON.parse(line) as AgentExecution;
|
|
99
|
+
if (toDelete.has(exec.sessionId)) {
|
|
100
|
+
touched = true;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
} catch {
|
|
104
|
+
// Keep malformed lines as-is rather than risk data loss.
|
|
105
|
+
}
|
|
106
|
+
kept.push(line);
|
|
107
|
+
}
|
|
108
|
+
if (touched) {
|
|
109
|
+
writeFileSync(filepath, kept.length ? kept.join("\n") + "\n" : "", "utf-8");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
} catch (err) {
|
|
113
|
+
console.error("[execution-store] Failed to delete executions from disk:", err);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Remove from in-memory cache and count.
|
|
117
|
+
let removed = 0;
|
|
118
|
+
this.recentCache = this.recentCache.filter((e) => {
|
|
119
|
+
if (toDelete.has(e.sessionId)) {
|
|
120
|
+
removed++;
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
return true;
|
|
124
|
+
});
|
|
125
|
+
return removed;
|
|
126
|
+
}
|
|
127
|
+
|
|
75
128
|
/** Query executions with pagination and filtering. */
|
|
76
129
|
list(opts?: ExecutionQuery): ExecutionListResult {
|
|
77
130
|
const limit = opts?.limit ?? 50;
|
|
@@ -197,6 +197,48 @@ export function registerAgentRoutes(
|
|
|
197
197
|
return c.json(agentExecutor?.listAllExecutions({ agentId, triggerType, status, limit, offset }) ?? { executions: [], total: 0 });
|
|
198
198
|
});
|
|
199
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Delete a single completed execution record by sessionId. Running
|
|
202
|
+
* executions must be cancelled first.
|
|
203
|
+
*/
|
|
204
|
+
api.delete("/executions/:sessionId", (c) => {
|
|
205
|
+
const sessionId = c.req.param("sessionId");
|
|
206
|
+
if (!agentExecutor) return c.json({ error: "Executor not available" }, 503);
|
|
207
|
+
const removed = agentExecutor.deleteExecutions({ sessionIds: [sessionId] });
|
|
208
|
+
return c.json({ ok: true, removed });
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Bulk delete executions. Body accepts either an explicit sessionIds list
|
|
213
|
+
* or a status filter ("success" | "error") to clear matching records.
|
|
214
|
+
*/
|
|
215
|
+
api.post("/executions/bulk-delete", async (c) => {
|
|
216
|
+
if (!agentExecutor) return c.json({ error: "Executor not available" }, 503);
|
|
217
|
+
const body = (await c.req.json().catch(() => ({}))) as { sessionIds?: unknown; status?: unknown };
|
|
218
|
+
const status = body?.status === "success" || body?.status === "error" ? body.status : undefined;
|
|
219
|
+
const sessionIds = Array.isArray(body?.sessionIds)
|
|
220
|
+
? (body.sessionIds as unknown[]).filter((s): s is string => typeof s === "string")
|
|
221
|
+
: undefined;
|
|
222
|
+
if (!status && (!sessionIds || sessionIds.length === 0)) {
|
|
223
|
+
return c.json({ error: "Provide either sessionIds or status" }, 400);
|
|
224
|
+
}
|
|
225
|
+
const removed = agentExecutor.deleteExecutions({ sessionIds, status });
|
|
226
|
+
return c.json({ ok: true, removed });
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Cancel a running execution by sessionId. Works for both live runs and
|
|
231
|
+
* zombies (executions whose CLI process is gone but DB still says "running").
|
|
232
|
+
*/
|
|
233
|
+
api.post("/executions/:sessionId/cancel", async (c) => {
|
|
234
|
+
const sessionId = c.req.param("sessionId");
|
|
235
|
+
const body = await c.req.json().catch(() => ({} as { reason?: string }));
|
|
236
|
+
const reason = typeof body?.reason === "string" && body.reason.trim() ? body.reason.trim() : "Stopped by user";
|
|
237
|
+
if (!agentExecutor) return c.json({ error: "Executor not available" }, 503);
|
|
238
|
+
const wasLive = agentExecutor.cancelExecution(sessionId, reason);
|
|
239
|
+
return c.json({ ok: true, wasLive });
|
|
240
|
+
});
|
|
241
|
+
|
|
200
242
|
// ── Import / Export ────────────────────────────────────────────────────
|
|
201
243
|
|
|
202
244
|
api.post("/agents/import", async (c) => {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r,b as N,j as e}from"./index-BgYM4wXw.js";import{t as I}from"./time-ago-B6r_l9u1.js";function g(c){switch(c){case"manual":return"Manual";case"webhook":return"Webhook";case"schedule":return"Schedule";default:return c}}function v(c){switch(c){case"manual":return"bg-blue-500/10 text-blue-600 dark:text-blue-400";case"webhook":return"bg-purple-500/10 text-purple-600 dark:text-purple-400";case"schedule":return"bg-amber-500/10 text-amber-600 dark:text-amber-400";default:return"bg-cc-hover text-cc-muted"}}function E(c){return c.error?{label:"Error",color:"text-cc-error"}:c.success?{label:"Success",color:"text-cc-success"}:c.completedAt?{label:"Unknown",color:"text-cc-muted"}:{label:"Running",color:"text-cc-warning"}}function y(c,x){const a=(x||Date.now())-c;if(a<1e3)return"<1s";if(a<6e4)return`${Math.round(a/1e3)}s`;const u=Math.floor(a/6e4),p=Math.round(a%6e4/1e3);return`${u}m ${p}s`}function D(){const[c,x]=r.useState([]),[m,a]=r.useState([]),[u,p]=r.useState(0),[w,k]=r.useState(!0),[n,A]=r.useState("all"),[i,S]=r.useState("all"),[o,C]=r.useState(""),[s,f]=r.useState(null),h=r.useCallback(async()=>{try{const t={limit:100};n!=="all"&&(t.triggerType=n),i!=="all"&&(t.status=i),o&&(t.agentId=o);const l=await N.listExecutions(t);x(l.executions),p(l.total)}catch(t){console.error("[runs] Failed to fetch executions:",t)}finally{k(!1)}},[n,i,o]),b=r.useCallback(async()=>{try{const t=await N.listAgents();a(t)}catch(t){console.error("[runs] Failed to fetch agents:",t)}},[]);r.useEffect(()=>{b()},[b]),r.useEffect(()=>{h();const t=setInterval(h,5e3);return()=>clearInterval(t)},[h]);const j=t=>{const l=m.find(d=>d.id===t);return(l==null?void 0:l.name)||t};return e.jsxs("div",{className:"h-full flex flex-col bg-cc-bg",children:[e.jsx("div",{className:"shrink-0 border-b border-cc-border",children:e.jsxs("div",{className:"max-w-4xl mx-auto w-full px-4 sm:px-8 py-4",children:[e.jsx("h1",{className:"text-lg font-semibold text-cc-fg",children:"Runs"}),e.jsx("p",{className:"text-sm text-cc-muted mt-1",children:"Monitor agent executions across all triggers"})]})}),e.jsx("div",{className:"shrink-0 border-b border-cc-border",children:e.jsxs("div",{className:"max-w-4xl mx-auto w-full px-4 sm:px-8 py-3 flex items-center gap-4 flex-wrap",children:[e.jsxs("select",{value:o,onChange:t=>C(t.target.value),className:"text-sm rounded-md border border-cc-border bg-cc-input-bg text-cc-fg px-2 py-1","aria-label":"Filter by agent",children:[e.jsx("option",{value:"",children:"All agents"}),m.map(t=>e.jsx("option",{value:t.id,children:t.name},t.id))]}),e.jsx("div",{className:"flex items-center gap-1",children:["all","manual","webhook","schedule"].map(t=>e.jsx("button",{onClick:()=>A(t),className:`text-xs px-2.5 py-1 rounded-full transition-colors ${n===t?"bg-cc-fg text-cc-bg":"bg-cc-hover text-cc-muted hover:text-cc-fg"}`,children:t==="all"?"All triggers":g(t)},t))}),e.jsx("div",{className:"flex items-center gap-1",children:["all","running","success","error"].map(t=>e.jsx("button",{onClick:()=>S(t),className:`text-xs px-2.5 py-1 rounded-full transition-colors ${i===t?"bg-cc-fg text-cc-bg":"bg-cc-hover text-cc-muted hover:text-cc-fg"}`,children:t==="all"?"All statuses":t.charAt(0).toUpperCase()+t.slice(1)},t))}),e.jsxs("span",{className:"text-xs text-cc-muted ml-auto",children:[u," total"]})]})}),e.jsx("div",{className:"flex-1 overflow-auto",children:e.jsx("div",{className:"max-w-4xl mx-auto w-full px-4 sm:px-8 py-6 pb-safe",children:w?e.jsx("div",{className:"text-sm text-cc-muted text-center py-12",children:"Loading..."}):c.length===0?e.jsxs("div",{className:"text-center py-16",children:[e.jsx("div",{className:"mb-3 flex justify-center text-cc-muted",children:e.jsx("svg",{viewBox:"0 0 16 16",fill:"currentColor",className:"w-8 h-8",children:e.jsx("path",{d:"M8 1a7 7 0 100 14A7 7 0 008 1zm-.75 3.5a.75.75 0 011.5 0v3.19l2.03 2.03a.75.75 0 01-1.06 1.06l-2.25-2.25A.75.75 0 017.25 8V4.5z"})})}),e.jsx("p",{className:"text-sm text-cc-muted",children:"No executions found"}),e.jsx("p",{className:"text-xs text-cc-muted mt-1",children:"Run an agent to see executions here"})]}):e.jsxs("table",{className:"w-full text-sm",role:"table",children:[e.jsx("thead",{className:"sticky top-0 bg-cc-card backdrop-blur",children:e.jsxs("tr",{className:"text-left text-xs text-cc-muted uppercase tracking-wider",children:[e.jsx("th",{className:"px-6 py-2 font-medium",children:"Agent"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Trigger"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Status"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Started"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Duration"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Session"})]})}),e.jsx("tbody",{className:"divide-y divide-cc-border/50",children:c.map(t=>{const l=E(t),d=(s==null?void 0:s.sessionId)===t.sessionId;return e.jsxs("tr",{onClick:()=>f(d?null:t),className:`cursor-pointer transition-colors ${d?"bg-cc-active":"hover:bg-cc-hover"}`,children:[e.jsx("td",{className:"px-6 py-3",children:e.jsx("div",{className:"flex items-center gap-2",children:e.jsx("span",{className:"text-cc-fg font-medium",children:j(t.agentId)})})}),e.jsx("td",{className:"px-4 py-3",children:e.jsx("span",{className:`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium ${v(t.triggerType)}`,children:g(t.triggerType)})}),e.jsx("td",{className:"px-4 py-3",children:e.jsxs("span",{className:`font-medium ${l.color}`,children:[!t.completedAt&&!t.error&&e.jsx("span",{className:"inline-block w-1.5 h-1.5 rounded-full bg-cc-warning mr-1.5 animate-pulse"}),l.label]})}),e.jsx("td",{className:"px-4 py-3 text-cc-muted",children:I(t.startedAt)}),e.jsx("td",{className:"px-4 py-3 text-cc-muted font-mono text-xs",children:y(t.startedAt,t.completedAt)}),e.jsx("td",{className:"px-4 py-3",children:t.sessionId&&e.jsx("a",{href:`#/session/${t.sessionId}`,onClick:$=>$.stopPropagation(),className:"text-cc-primary hover:text-cc-primary-hover text-xs font-mono underline",children:"Open"})})]},`${t.sessionId}-${t.startedAt}`)})})]})})}),s&&e.jsxs("div",{className:"shrink-0 border-t border-cc-border bg-cc-card px-6 py-4",children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("h3",{className:"text-sm font-semibold text-cc-fg",children:"Execution Details"}),e.jsx("button",{onClick:()=>f(null),className:"text-cc-muted hover:text-cc-fg text-sm transition-colors","aria-label":"Close details",children:"Close"})]}),e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-4 text-sm",children:[e.jsxs("div",{children:[e.jsx("span",{className:"text-cc-muted",children:"Agent"}),e.jsx("p",{className:"text-cc-fg font-medium",children:j(s.agentId)})]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-cc-muted",children:"Trigger"}),e.jsx("p",{className:`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium ${v(s.triggerType)}`,children:g(s.triggerType)})]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-cc-muted",children:"Started"}),e.jsx("p",{className:"text-cc-fg",children:new Date(s.startedAt).toLocaleString()})]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-cc-muted",children:"Duration"}),e.jsx("p",{className:"text-cc-fg font-mono",children:y(s.startedAt,s.completedAt)})]})]}),s.error&&e.jsx("div",{className:"mt-3 p-2 bg-cc-error/10 rounded text-sm text-cc-error font-mono whitespace-pre-wrap",children:s.error}),s.sessionId&&e.jsx("a",{href:`#/session/${s.sessionId}`,className:"mt-3 inline-flex items-center gap-1 text-sm text-cc-primary hover:text-cc-primary-hover",children:"Open session to view live output"})]})]})}export{D as RunsPage};
|