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.
Files changed (37) hide show
  1. package/dist/assets/{AgentsPage-B-AAmsMK.js → AgentsPage-DqjDAcIw.js} +1 -1
  2. package/dist/assets/{AssistantPage-BV1Mfwdt.js → AssistantPage-C50CQFSB.js} +1 -1
  3. package/dist/assets/{BusinessPage-tLpNEz19.js → BusinessPage-AY70tf1k.js} +1 -1
  4. package/dist/assets/{CronManager-B-K_n3Jg.js → CronManager-Dt7LLuRr.js} +1 -1
  5. package/dist/assets/{HelpPage-Bhf_j6Xr.js → HelpPage-tlGx7fQF.js} +1 -1
  6. package/dist/assets/{IntegrationsPage-DAMjs9tM.js → IntegrationsPage-B4XOuHXu.js} +1 -1
  7. package/dist/assets/{JarvisHUD-C_TGXCCn.js → JarvisHUD-BDvuRd0I.js} +1 -1
  8. package/dist/assets/{MediaPage-C48HTTrt.js → MediaPage-CofV9Rd-.js} +1 -1
  9. package/dist/assets/{MemoryPage-JkC-qtgp.js → MemoryPage-Cj7FeqmJ.js} +1 -1
  10. package/dist/assets/{PlatformDashboard-AUo7tNnE.js → PlatformDashboard-B9kXAlH1.js} +1 -1
  11. package/dist/assets/{Playground-AzNMsRBL.js → Playground-Cka-pRkP.js} +1 -1
  12. package/dist/assets/{ProcessPanel-DpE_2sX3.js → ProcessPanel-BqhQgfYj.js} +1 -1
  13. package/dist/assets/{PromptsPage-C2RQOs6p.js → PromptsPage-VveKc9uX.js} +1 -1
  14. package/dist/assets/RunsPage-DXVEk0AZ.js +1 -0
  15. package/dist/assets/{SandboxManager-jHvYjwfh.js → SandboxManager-DACcwfDF.js} +1 -1
  16. package/dist/assets/{SettingsPage-BBJax6gt.js → SettingsPage-jfuQh8Tu.js} +1 -1
  17. package/dist/assets/{SkillsMarketplace-IjmjfdjD.js → SkillsMarketplace-DrigiApe.js} +1 -1
  18. package/dist/assets/{SocialMediaPage-DoPZHhr2.js → SocialMediaPage-DOh3IPe8.js} +1 -1
  19. package/dist/assets/{TailscalePage-DDEY7ckO.js → TailscalePage-DLhJWATT.js} +1 -1
  20. package/dist/assets/{TelephonyPage-OPNBZYKt.js → TelephonyPage-9C4C3_ot.js} +1 -1
  21. package/dist/assets/{TerminalPage-BjMbHHW3.js → TerminalPage-ChX-8Wu7.js} +1 -1
  22. package/dist/assets/index-C6Q5UQHD.js +229 -0
  23. package/dist/assets/index-ZxGXgiV3.css +32 -0
  24. package/dist/assets/sw-register-BBYuk-kw.js +1 -0
  25. package/dist/assets/workbox-window.prod.es5-BBnX5xw4.js +2 -0
  26. package/dist/index.html +2 -2
  27. package/dist/sw.js +1 -1
  28. package/dist/{workbox-d2a0910a.js → workbox-080c8b91.js} +1 -1
  29. package/package.json +1 -1
  30. package/server/agent-executor.ts +65 -0
  31. package/server/execution-store.ts +54 -1
  32. package/server/routes/agent-routes.ts +42 -0
  33. package/dist/assets/RunsPage-B9UOyO79.js +0 -1
  34. package/dist/assets/index-BgYM4wXw.js +0 -205
  35. package/dist/assets/index-BkjSoVgn.css +0 -32
  36. package/dist/assets/sw-register-C7NOHtIu.js +0 -1
  37. package/dist/assets/workbox-window.prod.es5-BIl4cyR9.js +0 -2
@@ -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};