chainlesschain 0.46.0 → 0.47.1

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 (60) hide show
  1. package/README.md +16 -5
  2. package/bin/chainlesschain.js +0 -0
  3. package/package.json +1 -1
  4. package/src/assets/web-panel/.build-hash +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-C1AnPdMx.js → Analytics-DgypYeUB.js} +2 -2
  6. package/src/assets/web-panel/assets/AppLayout-DQyDwGut.css +1 -0
  7. package/src/assets/web-panel/assets/AppLayout-ZHpCFO_p.js +1 -0
  8. package/src/assets/web-panel/assets/{Backup-D31iZX3l.js → Backup-Ba9UybpT.js} +1 -1
  9. package/src/assets/web-panel/assets/{Chat-DiXJ3TuK.js → Chat-BwXskT21.js} +1 -1
  10. package/src/assets/web-panel/assets/{Cowork-B8ZDdRm4.js → Cowork-UmOe7qvE.js} +1 -1
  11. package/src/assets/web-panel/assets/{Cron-DBt1ueXh.js → Cron-JHS-rc-4.js} +2 -2
  12. package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +1 -0
  13. package/src/assets/web-panel/assets/{Dashboard-jt6XPIjB.js → Dashboard-CpWz2g0n.js} +2 -2
  14. package/src/assets/web-panel/assets/{Git-hwQ1oZHj.js → Git-CSYO0_zk.js} +2 -2
  15. package/src/assets/web-panel/assets/{Logs-4D9p6PRM.js → Logs-Hxw_K0km.js} +2 -2
  16. package/src/assets/web-panel/assets/{McpTools-CyAUjbbs.js → McpTools-DIE75TrB.js} +2 -2
  17. package/src/assets/web-panel/assets/{Memory-BMqOR7S-.js → Memory-C4KVnLlp.js} +2 -2
  18. package/src/assets/web-panel/assets/{Notes-Cmas8i4E.js → Notes-DuzrHMAk.js} +2 -2
  19. package/src/assets/web-panel/assets/{Organization-DnSa58Tl.js → Organization-DTq6uF82.js} +4 -4
  20. package/src/assets/web-panel/assets/{P2P-BxksIBWs.js → P2P-C0hjlhsR.js} +2 -2
  21. package/src/assets/web-panel/assets/{Permissions-Bq5Qn2s3.js → Permissions-Ec0NH-xC.js} +4 -4
  22. package/src/assets/web-panel/assets/{Projects-B7EM0uPg.js → Projects-U8D0asCS.js} +2 -2
  23. package/src/assets/web-panel/assets/{Providers-DAwgG5KV.js → Providers-BngtTLvJ.js} +2 -2
  24. package/src/assets/web-panel/assets/{RssFeed-HSZoRXvS.js → RssFeed-B9NbwCKM.js} +3 -3
  25. package/src/assets/web-panel/assets/{Security-Cz17qBny.js → Security-BL5Rkr1T.js} +3 -3
  26. package/src/assets/web-panel/assets/{Services-D2EsLq-v.js → Services-D4MJzLld.js} +2 -2
  27. package/src/assets/web-panel/assets/{Skills-C9v-f3vZ.js → Skills-CQTOMDwF.js} +1 -1
  28. package/src/assets/web-panel/assets/{Tasks-yMEcU0n7.js → Tasks-DepbJMnL.js} +1 -1
  29. package/src/assets/web-panel/assets/{Templates-l7SvlKuB.js → Templates-C24PVZPu.js} +1 -1
  30. package/src/assets/web-panel/assets/{Wallet-BHWhLWn9.js → Wallet-PQoSpN_P.js} +3 -3
  31. package/src/assets/web-panel/assets/{WebAuthn-kWhFYaUK.js → WebAuthn-BcuyQ4Lr.js} +4 -4
  32. package/src/assets/web-panel/assets/WorkflowEditor-C-SvXbHW.js +1 -0
  33. package/src/assets/web-panel/assets/WorkflowEditor-D5bX6woe.css +1 -0
  34. package/src/assets/web-panel/assets/{antd-D6h4fDFf.js → antd-DEjZPGMj.js} +82 -82
  35. package/src/assets/web-panel/assets/index-CLmYSvow.js +2 -0
  36. package/src/assets/web-panel/assets/{markdown-BZsB-Dsv.js → markdown-CusdXFxb.js} +1 -1
  37. package/src/assets/web-panel/index.html +2 -2
  38. package/src/commands/cowork.js +213 -41
  39. package/src/gateways/ws/action-protocol.js +140 -0
  40. package/src/gateways/ws/message-dispatcher.js +5 -0
  41. package/src/gateways/ws/ws-server.js +21 -0
  42. package/src/lib/cowork-evomap-adapter.js +121 -0
  43. package/src/lib/cowork-observe-html.js +108 -0
  44. package/src/lib/cowork-observe.js +160 -0
  45. package/src/lib/cowork-share.js +114 -10
  46. package/src/lib/provider-options.js +133 -0
  47. package/src/lib/skill-loader.js +65 -0
  48. package/src/lib/sub-agent-context.js +16 -4
  49. package/src/lib/sub-agent-profiles.js +164 -0
  50. package/src/lib/todo-manager.js +108 -0
  51. package/src/lib/turn-context.js +95 -0
  52. package/src/lib/web-fetch.js +224 -0
  53. package/src/repl/agent-repl.js +4 -0
  54. package/src/runtime/agent-core.js +135 -3
  55. package/src/runtime/coding-agent-contract-shared.cjs +131 -0
  56. package/src/runtime/coding-agent-policy.cjs +30 -0
  57. package/src/assets/web-panel/assets/AppLayout-BnvARObz.js +0 -1
  58. package/src/assets/web-panel/assets/AppLayout-cxfKLu-m.css +0 -1
  59. package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +0 -1
  60. package/src/assets/web-panel/assets/index-C1SPm_5l.js +0 -2
@@ -0,0 +1,2 @@
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./AppLayout-ZHpCFO_p.js","./vendor-CN0Iv_qZ.js","./ws-Dma34ig_.js","./_plugin-vue_export-helper-DlAUqK2U.js","./antd-DEjZPGMj.js","./AppLayout-DQyDwGut.css","./Dashboard-CpWz2g0n.js","./chat-BYmuDvol.js","./Dashboard-BS-tzGNj.css","./Chat-BwXskT21.js","./markdown-CusdXFxb.js","./Chat-DfR76jyX.css","./github-dark-Dfs9RUU9.css","./Cowork-UmOe7qvE.js","./Cowork-CXuhlHew.css","./Services-D4MJzLld.js","./Services-C8Qs6KXv.css","./Logs-Hxw_K0km.js","./Logs-Gf_Mv9Nx.css","./Skills-CQTOMDwF.js","./parsers-DftYMnlk.js","./Skills-BdjRyorN.css","./Providers-BngtTLvJ.js","./Providers-BEakqcO5.css","./McpTools-DIE75TrB.js","./McpTools-CyhSLDwf.css","./Notes-DuzrHMAk.js","./Notes-BG69sJKi.css","./Memory-C4KVnLlp.js","./Memory-DRghrGJr.css","./Cron-JHS-rc-4.js","./WorkflowEditor-C-SvXbHW.js","./WorkflowEditor-D5bX6woe.css","./Tasks-DepbJMnL.js","./Tasks-BJjN_YEm.css","./Security-BL5Rkr1T.js","./Security-Dwxw7rfP.css","./Permissions-Ec0NH-xC.js","./Permissions-C9WlkGl-.css","./P2P-C0hjlhsR.js","./P2P-OEzOeMZX.css","./Git-CSYO0_zk.js","./Git-DGcuBXST.css","./Projects-U8D0asCS.js","./Projects-DxKelI5h.css","./Wallet-PQoSpN_P.js","./Wallet-DnIumafl.css","./Organization-DTq6uF82.js","./Organization-DdOOM4ic.css","./Analytics-DgypYeUB.js","./Analytics-B4OM8S8X.css","./Templates-C24PVZPu.js","./Templates-DOY_oZnm.css","./Backup-Ba9UybpT.js","./Backup-fZqtfC1m.css","./RssFeed-B9NbwCKM.js","./RssFeed-BlFC20eg.css","./WebAuthn-BcuyQ4Lr.js","./WebAuthn-CNPl2VQR.css"])))=>i.map(i=>d[i]);
2
+ import{S as L,U as B,V as I,f as T,c as E,o as R,W as O,u as k,X as y,Y as V,Z as D,k as x,R as S,_ as w}from"./vendor-CN0Iv_qZ.js";import{a as g,A as M}from"./antd-DEjZPGMj.js";(function(){const a=document.createElement("link").relList;if(a&&a.supports&&a.supports("modulepreload"))return;for(const t of document.querySelectorAll('link[rel="modulepreload"]'))m(t);new MutationObserver(t=>{for(const o of t)if(o.type==="childList")for(const n of o.addedNodes)n.tagName==="LINK"&&n.rel==="modulepreload"&&m(n)}).observe(document,{childList:!0,subtree:!0});function c(t){const o={};return t.integrity&&(o.integrity=t.integrity),t.referrerPolicy&&(o.referrerPolicy=t.referrerPolicy),t.crossOrigin==="use-credentials"?o.credentials="include":t.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function m(t){if(t.ep)return;t.ep=!0;const o=c(t);fetch(t.href,o)}})();const C="modulepreload",N=function(s,a){return new URL(s,a).href},P={},e=function(a,c,m){let t=Promise.resolve();if(c&&c.length>0){let b=function(i){return Promise.all(i.map(d=>Promise.resolve(d).then(u=>({status:"fulfilled",value:u}),u=>({status:"rejected",reason:u}))))};const n=document.getElementsByTagName("link"),r=document.querySelector("meta[property=csp-nonce]"),p=r?.nonce||r?.getAttribute("nonce");t=b(c.map(i=>{if(i=N(i,m),i in P)return;P[i]=!0;const d=i.endsWith(".css"),u=d?'[rel="stylesheet"]':"";if(m)for(let f=n.length-1;f>=0;f--){const _=n[f];if(_.href===i&&(!d||_.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${i}"]${u}`))return;const l=document.createElement("link");if(l.rel=d?"stylesheet":C,d||(l.as="script"),l.crossOrigin="",l.href=i,p&&l.setAttribute("nonce",p),document.head.appendChild(l),d)return new Promise((f,_)=>{l.addEventListener("load",f),l.addEventListener("error",()=>_(new Error(`Unable to preload CSS for ${i}`)))})}))}function o(n){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=n,window.dispatchEvent(r),!r.defaultPrevented)throw n}return t.then(n=>{for(const r of n||[])r.status==="rejected"&&o(r.reason);return a().catch(o)})},U=[{path:"/",component:()=>e(()=>import("./AppLayout-ZHpCFO_p.js"),__vite__mapDeps([0,1,2,3,4,5]),import.meta.url),children:[{path:"",redirect:"/dashboard"},{path:"dashboard",name:"Dashboard",component:()=>e(()=>import("./Dashboard-CpWz2g0n.js"),__vite__mapDeps([6,1,2,7,3,4,8]),import.meta.url)},{path:"chat",name:"Chat",component:()=>e(()=>import("./Chat-BwXskT21.js"),__vite__mapDeps([9,1,10,4,7,2,3,11,12]),import.meta.url)},{path:"cowork",name:"Cowork",component:()=>e(()=>import("./Cowork-UmOe7qvE.js"),__vite__mapDeps([13,1,10,4,2,7,3,14,12]),import.meta.url)},{path:"services",name:"Services",component:()=>e(()=>import("./Services-D4MJzLld.js"),__vite__mapDeps([15,2,1,3,4,16]),import.meta.url)},{path:"logs",name:"Logs",component:()=>e(()=>import("./Logs-Hxw_K0km.js"),__vite__mapDeps([17,2,1,3,4,18]),import.meta.url)},{path:"skills",name:"Skills",component:()=>e(()=>import("./Skills-CQTOMDwF.js"),__vite__mapDeps([19,1,2,20,7,3,4,21]),import.meta.url)},{path:"providers",name:"Providers",component:()=>e(()=>import("./Providers-BngtTLvJ.js"),__vite__mapDeps([22,1,2,20,3,4,23]),import.meta.url)},{path:"mcp",name:"McpTools",component:()=>e(()=>import("./McpTools-DIE75TrB.js"),__vite__mapDeps([24,2,1,3,4,25]),import.meta.url)},{path:"notes",name:"Notes",component:()=>e(()=>import("./Notes-DuzrHMAk.js"),__vite__mapDeps([26,2,1,3,4,27]),import.meta.url)},{path:"memory",name:"Memory",component:()=>e(()=>import("./Memory-C4KVnLlp.js"),__vite__mapDeps([28,2,1,3,4,29]),import.meta.url)},{path:"cron",name:"Cron",component:()=>e(()=>import("./Cron-JHS-rc-4.js"),__vite__mapDeps([30,2,1,4]),import.meta.url)},{path:"workflow",name:"Workflow",component:()=>e(()=>import("./WorkflowEditor-C-SvXbHW.js"),__vite__mapDeps([31,1,2,3,4,32]),import.meta.url)},{path:"tasks",name:"Tasks",component:()=>e(()=>import("./Tasks-DepbJMnL.js"),__vite__mapDeps([33,1,2,3,4,34]),import.meta.url)},{path:"security",name:"Security",component:()=>e(()=>import("./Security-BL5Rkr1T.js"),__vite__mapDeps([35,2,1,3,4,36]),import.meta.url)},{path:"permissions",name:"Permissions",component:()=>e(()=>import("./Permissions-Ec0NH-xC.js"),__vite__mapDeps([37,2,1,3,4,38]),import.meta.url)},{path:"p2p",name:"P2P",component:()=>e(()=>import("./P2P-C0hjlhsR.js"),__vite__mapDeps([39,2,1,3,4,40]),import.meta.url)},{path:"git",name:"Git",component:()=>e(()=>import("./Git-CSYO0_zk.js"),__vite__mapDeps([41,2,1,3,4,42]),import.meta.url)},{path:"projects",name:"Projects",component:()=>e(()=>import("./Projects-U8D0asCS.js"),__vite__mapDeps([43,2,1,3,4,44]),import.meta.url)},{path:"wallet",name:"Wallet",component:()=>e(()=>import("./Wallet-PQoSpN_P.js"),__vite__mapDeps([45,2,1,3,4,46]),import.meta.url)},{path:"organization",name:"Organization",component:()=>e(()=>import("./Organization-DTq6uF82.js"),__vite__mapDeps([47,1,2,3,4,48]),import.meta.url)},{path:"analytics",name:"Analytics",component:()=>e(()=>import("./Analytics-DgypYeUB.js"),__vite__mapDeps([49,2,1,3,4,50]),import.meta.url)},{path:"templates",name:"Templates",component:()=>e(()=>import("./Templates-C24PVZPu.js"),__vite__mapDeps([51,1,2,3,4,52]),import.meta.url)},{path:"backup",name:"Backup",component:()=>e(()=>import("./Backup-Ba9UybpT.js"),__vite__mapDeps([53,2,1,3,4,54]),import.meta.url)},{path:"rssfeed",name:"RssFeed",component:()=>e(()=>import("./RssFeed-B9NbwCKM.js"),__vite__mapDeps([55,1,2,3,4,56]),import.meta.url)},{path:"webauthn",name:"WebAuthn",component:()=>e(()=>import("./WebAuthn-BcuyQ4Lr.js"),__vite__mapDeps([57,2,1,3,4,58]),import.meta.url)}]}],F=L({history:B(),routes:U}),A="cc_theme",v={dark:{label:"暗黑",icon:"🌑",antd:{algorithm:g.darkAlgorithm,token:{colorPrimary:"#1677ff",colorBgBase:"#141414",colorBgContainer:"#1f1f1f",colorBgElevated:"#2a2a2a",borderRadius:8,fontFamily:'system-ui, -apple-system, "Segoe UI", sans-serif'},components:{Layout:{siderBg:"#1c1c1c",headerBg:"#1c1c1c",bodyBg:"#141414"},Menu:{darkItemBg:"#1c1c1c",darkSubMenuItemBg:"#171717"}}},vars:{"--bg-base":"#141414","--bg-sidebar":"#1c1c1c","--bg-header":"#1c1c1c","--bg-card":"#1f1f1f","--bg-card-hover":"#262626","--border-color":"#252525","--border-subtle":"#1e1e1e","--text-primary":"#e0e0e0","--text-secondary":"#888","--text-muted":"#444","--logo-text":"#ffffff","--menu-mode":"dark","--shadow-card":"0 2px 8px rgba(0,0,0,.45)","--group-title":"#3a3a3a"}},light:{label:"亮白",icon:"☀️",antd:{algorithm:g.defaultAlgorithm,token:{colorPrimary:"#1677ff",colorBgBase:"#ffffff",colorBgContainer:"#ffffff",colorBgElevated:"#ffffff",borderRadius:8,fontFamily:'system-ui, -apple-system, "Segoe UI", sans-serif'},components:{Layout:{siderBg:"#ffffff",headerBg:"#ffffff",bodyBg:"#f4f6fb"},Menu:{itemBg:"#ffffff"}}},vars:{"--bg-base":"#f4f6fb","--bg-sidebar":"#ffffff","--bg-header":"#ffffff","--bg-card":"#ffffff","--bg-card-hover":"#f0f4ff","--border-color":"#e8edf5","--border-subtle":"#f0f0f0","--text-primary":"#1a1a2e","--text-secondary":"#5a6474","--text-muted":"#b0b8c8","--logo-text":"#1a1a2e","--menu-mode":"light","--shadow-card":"0 2px 12px rgba(0,0,0,.07)","--group-title":"#aab0bc"}},blue:{label:"深蓝",icon:"🌊",antd:{algorithm:g.darkAlgorithm,token:{colorPrimary:"#2f80ed",colorBgBase:"#0d1117",colorBgContainer:"#161b22",colorBgElevated:"#1c2230",borderRadius:8,fontFamily:'system-ui, -apple-system, "Segoe UI", sans-serif'},components:{Layout:{siderBg:"#0f1923",headerBg:"#0f1923",bodyBg:"#0d1117"},Menu:{darkItemBg:"#0f1923",darkSubMenuItemBg:"#0b1520"}}},vars:{"--bg-base":"#0d1117","--bg-sidebar":"#0f1923","--bg-header":"#0f1923","--bg-card":"#161b22","--bg-card-hover":"#1c2230","--border-color":"#21303f","--border-subtle":"#182030","--text-primary":"#c9d8ef","--text-secondary":"#6e8caa","--text-muted":"#2d4060","--logo-text":"#e0eeff","--menu-mode":"dark","--shadow-card":"0 2px 8px rgba(0,40,80,.5)","--group-title":"#2d4060"}},green:{label:"翠绿",icon:"🌿",antd:{algorithm:g.darkAlgorithm,token:{colorPrimary:"#29a270",colorBgBase:"#0a1a12",colorBgContainer:"#0f2318",colorBgElevated:"#152e20",borderRadius:8,fontFamily:'system-ui, -apple-system, "Segoe UI", sans-serif'},components:{Layout:{siderBg:"#0c1e14",headerBg:"#0c1e14",bodyBg:"#0a1a12"},Menu:{darkItemBg:"#0c1e14",darkSubMenuItemBg:"#091810"}}},vars:{"--bg-base":"#0a1a12","--bg-sidebar":"#0c1e14","--bg-header":"#0c1e14","--bg-card":"#0f2318","--bg-card-hover":"#152e20","--border-color":"#1a3828","--border-subtle":"#122a1c","--text-primary":"#c0e8c8","--text-secondary":"#5a9a6a","--text-muted":"#1e4028","--logo-text":"#d8f0e0","--menu-mode":"dark","--shadow-card":"0 2px 8px rgba(0,40,20,.5)","--group-title":"#1e4028"}}},W=I("theme",()=>{const s=T(localStorage.getItem(A)||"light"),a=E(()=>v[s.value]||v.dark),c=E(()=>a.value.antd),m=E(()=>s.value!=="light");function t(){const r=a.value.vars,p=document.documentElement;for(const[b,i]of Object.entries(r))p.style.setProperty(b,i);p.setAttribute("data-theme",s.value)}function o(r){v[r]&&(s.value=r,localStorage.setItem(A,r),t())}function n(){t()}return{current:s,config:a,antdTheme:c,isDark:m,setTheme:o,init:n}}),j={__name:"App",setup(s){const a=W();return R(()=>a.init()),(c,m)=>{const t=y("router-view"),o=y("a-config-provider");return V(),O(o,{theme:k(a).antdTheme},{default:D(()=>[x(t)]),_:1},8,["theme"])}}},h=S(j);h.use(w());h.use(F);h.use(M);h.mount("#app");export{v as T,W as u};
@@ -1,4 +1,4 @@
1
- import{g as Yc}from"./antd-D6h4fDFf.js";function Ni(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}let He=Ni();function Mc(a){He=a}const Lc=/[&<>"']/,qc=new RegExp(Lc.source,"g"),xc=/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,Hc=new RegExp(xc.source,"g"),Vc={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},$i=a=>Vc[a];function Ne(a,e){if(e){if(Lc.test(a))return a.replace(qc,$i)}else if(xc.test(a))return a.replace(Hc,$i);return a}const zc=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;function Wc(a){return a.replace(zc,(e,t)=>(t=t.toLowerCase(),t==="colon"?":":t.charAt(0)==="#"?t.charAt(1)==="x"?String.fromCharCode(parseInt(t.substring(2),16)):String.fromCharCode(+t.substring(1)):""))}const $c=/(^|[^\[])\^/g;function Z(a,e){let t=typeof a=="string"?a:a.source;e=e||"";const n={replace:(r,i)=>{let o=typeof i=="string"?i:i.source;return o=o.replace($c,"$1"),t=t.replace(r,o),n},getRegex:()=>new RegExp(t,e)};return n}function Ki(a){try{a=encodeURI(a).replace(/%25/g,"%")}catch{return null}return a}const pt={exec:()=>null};function Qi(a,e){const t=a.replace(/\|/g,(i,o,s)=>{let l=!1,_=o;for(;--_>=0&&s[_]==="\\";)l=!l;return l?"|":" |"}),n=t.split(/ \|/);let r=0;if(n[0].trim()||n.shift(),n.length>0&&!n[n.length-1].trim()&&n.pop(),e)if(n.length>e)n.splice(e);else for(;n.length<e;)n.push("");for(;r<n.length;r++)n[r]=n[r].trim().replace(/\\\|/g,"|");return n}function yt(a,e,t){const n=a.length;if(n===0)return"";let r=0;for(;r<n&&a.charAt(n-r-1)===e;)r++;return a.slice(0,n-r)}function Kc(a,e){if(a.indexOf(e[1])===-1)return-1;let t=0;for(let n=0;n<a.length;n++)if(a[n]==="\\")n++;else if(a[n]===e[0])t++;else if(a[n]===e[1]&&(t--,t<0))return n;return-1}function Xi(a,e,t,n){const r=e.href,i=e.title?Ne(e.title):null,o=a[1].replace(/\\([\[\]])/g,"$1");if(a[0].charAt(0)!=="!"){n.state.inLink=!0;const s={type:"link",raw:t,href:r,title:i,text:o,tokens:n.inlineTokens(o)};return n.state.inLink=!1,s}return{type:"image",raw:t,href:r,title:i,text:Ne(o)}}function Qc(a,e){const t=a.match(/^(\s+)(?:```)/);if(t===null)return e;const n=t[1];return e.split(`
1
+ import{g as Yc}from"./antd-DEjZPGMj.js";function Ni(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}let He=Ni();function Mc(a){He=a}const Lc=/[&<>"']/,qc=new RegExp(Lc.source,"g"),xc=/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,Hc=new RegExp(xc.source,"g"),Vc={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},$i=a=>Vc[a];function Ne(a,e){if(e){if(Lc.test(a))return a.replace(qc,$i)}else if(xc.test(a))return a.replace(Hc,$i);return a}const zc=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;function Wc(a){return a.replace(zc,(e,t)=>(t=t.toLowerCase(),t==="colon"?":":t.charAt(0)==="#"?t.charAt(1)==="x"?String.fromCharCode(parseInt(t.substring(2),16)):String.fromCharCode(+t.substring(1)):""))}const $c=/(^|[^\[])\^/g;function Z(a,e){let t=typeof a=="string"?a:a.source;e=e||"";const n={replace:(r,i)=>{let o=typeof i=="string"?i:i.source;return o=o.replace($c,"$1"),t=t.replace(r,o),n},getRegex:()=>new RegExp(t,e)};return n}function Ki(a){try{a=encodeURI(a).replace(/%25/g,"%")}catch{return null}return a}const pt={exec:()=>null};function Qi(a,e){const t=a.replace(/\|/g,(i,o,s)=>{let l=!1,_=o;for(;--_>=0&&s[_]==="\\";)l=!l;return l?"|":" |"}),n=t.split(/ \|/);let r=0;if(n[0].trim()||n.shift(),n.length>0&&!n[n.length-1].trim()&&n.pop(),e)if(n.length>e)n.splice(e);else for(;n.length<e;)n.push("");for(;r<n.length;r++)n[r]=n[r].trim().replace(/\\\|/g,"|");return n}function yt(a,e,t){const n=a.length;if(n===0)return"";let r=0;for(;r<n&&a.charAt(n-r-1)===e;)r++;return a.slice(0,n-r)}function Kc(a,e){if(a.indexOf(e[1])===-1)return-1;let t=0;for(let n=0;n<a.length;n++)if(a[n]==="\\")n++;else if(a[n]===e[0])t++;else if(a[n]===e[1]&&(t--,t<0))return n;return-1}function Xi(a,e,t,n){const r=e.href,i=e.title?Ne(e.title):null,o=a[1].replace(/\\([\[\]])/g,"$1");if(a[0].charAt(0)!=="!"){n.state.inLink=!0;const s={type:"link",raw:t,href:r,title:i,text:o,tokens:n.inlineTokens(o)};return n.state.inLink=!1,s}return{type:"image",raw:t,href:r,title:i,text:Ne(o)}}function Qc(a,e){const t=a.match(/^(\s+)(?:```)/);if(t===null)return e;const n=t[1];return e.split(`
2
2
  `).map(r=>{const i=r.match(/^\s+/);if(i===null)return r;const[o]=i;return o.length>=n.length?r.slice(n.length):r}).join(`
3
3
  `)}class Lt{options;rules;lexer;constructor(e){this.options=e||He}space(e){const t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:"space",raw:t[0]}}code(e){const t=this.rules.block.code.exec(e);if(t){const n=t[0].replace(/^ {1,4}/gm,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?n:yt(n,`
4
4
  `)}}}fences(e){const t=this.rules.block.fences.exec(e);if(t){const n=t[0],r=Qc(n,t[3]||"");return{type:"code",raw:n,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:r}}}heading(e){const t=this.rules.block.heading.exec(e);if(t){let n=t[2].trim();if(/#$/.test(n)){const r=yt(n,"#");(this.options.pedantic||!r||/ $/.test(r))&&(n=r.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(e){const t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:t[0]}}blockquote(e){const t=this.rules.block.blockquote.exec(e);if(t){let n=t[0].replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,`
@@ -8,9 +8,9 @@
8
8
  // Injected by web-ui-server.js at serve time
9
9
  window.__CC_CONFIG__ = __CC_CONFIG_PLACEHOLDER__;
10
10
  </script>
11
- <script type="module" crossorigin src="./assets/index-C1SPm_5l.js"></script>
11
+ <script type="module" crossorigin src="./assets/index-CLmYSvow.js"></script>
12
12
  <link rel="modulepreload" crossorigin href="./assets/vendor-CN0Iv_qZ.js">
13
- <link rel="modulepreload" crossorigin href="./assets/antd-D6h4fDFf.js">
13
+ <link rel="modulepreload" crossorigin href="./assets/antd-DEjZPGMj.js">
14
14
  <link rel="stylesheet" crossorigin href="./assets/index-CyGyEIVX.css">
15
15
  </head>
16
16
  <body>
@@ -300,17 +300,15 @@ export function registerCoworkCommand(program) {
300
300
  tpl
301
301
  .command("search [query]")
302
302
  .description("Search for Cowork templates on the EvoMap hub")
303
+ .option("--hub <url>", "EvoMap hub URL")
303
304
  .option("--limit <n>", "Max results", "20")
304
305
  .option("--json", "Output as JSON")
305
306
  .action(async (query, options) => {
306
- const [{ searchTemplates, _deps: mpDeps }, { EvoMapClient }] =
307
- await Promise.all([
308
- import("../lib/cowork-template-marketplace.js"),
309
- import("../lib/evomap-client.js"),
310
- ]);
311
- mpDeps.evomapClient = new EvoMapClient();
307
+ const { searchTemplatesInHub } =
308
+ await import("../lib/cowork-evomap-adapter.js");
312
309
  try {
313
- const results = await searchTemplates(query || "", {
310
+ const results = await searchTemplatesInHub(query || "", {
311
+ hubUrl: options.hub,
314
312
  limit: parseInt(options.limit, 10) || 20,
315
313
  });
316
314
  if (options.json) {
@@ -345,15 +343,22 @@ export function registerCoworkCommand(program) {
345
343
  tpl
346
344
  .command("install <geneId>")
347
345
  .description("Install a Cowork template from the EvoMap hub")
348
- .action(async (geneId) => {
349
- const [{ installTemplate, _deps: mpDeps }, { EvoMapClient }] =
350
- await Promise.all([
351
- import("../lib/cowork-template-marketplace.js"),
352
- import("../lib/evomap-client.js"),
353
- ]);
354
- mpDeps.evomapClient = new EvoMapClient();
346
+ .option("--hub <url>", "EvoMap hub URL")
347
+ .option("--require-signed", "Reject genes without an Ed25519 signature")
348
+ .option(
349
+ "--trust <did>",
350
+ "Only accept genes signed by this DID (repeatable)",
351
+ (value, prev) => (prev ? [...prev, value] : [value]),
352
+ )
353
+ .action(async (geneId, options) => {
354
+ const { installTemplateFromHub } =
355
+ await import("../lib/cowork-evomap-adapter.js");
355
356
  try {
356
- const template = await installTemplate(process.cwd(), geneId);
357
+ const template = await installTemplateFromHub(process.cwd(), geneId, {
358
+ hubUrl: options.hub,
359
+ requireSigned: !!options.requireSigned,
360
+ trustedDids: options.trust || null,
361
+ });
357
362
  logger.log(
358
363
  chalk.green(
359
364
  `✓ Installed template '${template.id}' (${template.name})`,
@@ -407,38 +412,39 @@ export function registerCoworkCommand(program) {
407
412
  tpl
408
413
  .command("publish <templateId>")
409
414
  .description("Publish a built-in or installed Cowork template to EvoMap")
410
- .requiredOption("--author <name>", "Author name for the published gene")
411
- .option("--version <v>", "Gene version", "1.0.0")
412
- .option("--description <text>", "Gene description")
413
- .option("--tags <list>", "Comma-separated tags")
415
+ .option("--hub <url>", "EvoMap hub URL")
416
+ .option("--api-key <key>", "EvoMap API key (or set EVOMAP_API_KEY)")
417
+ .option(
418
+ "--sign <did>",
419
+ "Sign the gene with this DID from the local identity store",
420
+ )
414
421
  .action(async (templateId, options) => {
415
- const [
416
- { publishTemplate, toShareableTemplate, _deps: mpDeps },
417
- { getTemplate },
418
- { EvoMapClient },
419
- ] = await Promise.all([
420
- import("../lib/cowork-template-marketplace.js"),
422
+ const [{ publishTemplateToHub }, { getTemplate }] = await Promise.all([
423
+ import("../lib/cowork-evomap-adapter.js"),
421
424
  import("../lib/cowork-task-templates.js"),
422
- import("../lib/evomap-client.js"),
423
425
  ]);
424
- mpDeps.evomapClient = new EvoMapClient();
425
426
 
426
427
  const template = getTemplate(templateId);
427
- if (template.id === "free") {
428
+ if (template.id === "free" && templateId !== "free") {
428
429
  logger.error(`Unknown template: ${templateId}`);
429
430
  process.exit(1);
430
431
  }
431
- const shareable = toShareableTemplate(template);
432
+
433
+ let signer;
434
+ if (options.sign) {
435
+ signer = await _resolveSigner(options.sign);
436
+ }
437
+
432
438
  try {
433
- const result = await publishTemplate(shareable, {
434
- author: options.author,
435
- version: options.version,
436
- description: options.description,
437
- tags: options.tags
438
- ? options.tags.split(",").map((t) => t.trim())
439
- : [],
439
+ const result = await publishTemplateToHub(template, {
440
+ hubUrl: options.hub,
441
+ apiKey: options.apiKey,
442
+ signer,
440
443
  });
441
- logger.log(chalk.green(`✓ Published ${result?.id || shareable.id}`));
444
+ logger.log(
445
+ chalk.green(`✓ Published ${result?.id || template.id}`) +
446
+ (signer ? chalk.gray(` (signed by ${signer.did})`) : ""),
447
+ );
442
448
  } catch (err) {
443
449
  logger.error(`Publish failed: ${err.message}`);
444
450
  process.exit(1);
@@ -594,11 +600,28 @@ export function registerCoworkCommand(program) {
594
600
  "Export/import signed Cowork packets (templates or task results)",
595
601
  );
596
602
 
603
+ async function _resolveSigner(didQuery) {
604
+ if (!didQuery) return null;
605
+ const { getConnection } = await import("../lib/db-connection.js");
606
+ const { getIdentity } = await import("../lib/did-manager.js");
607
+ const db = await getConnection();
608
+ const identity = getIdentity(db, didQuery);
609
+ if (!identity) {
610
+ throw new Error(`DID identity not found: ${didQuery}`);
611
+ }
612
+ return {
613
+ did: identity.did,
614
+ publicKey: identity.public_key,
615
+ privateKey: identity.secret_key,
616
+ };
617
+ }
618
+
597
619
  share
598
620
  .command("export-template <id>")
599
621
  .description("Export an installed template as a signed packet")
600
622
  .requiredOption("--out <file>", "Output packet file (.json)")
601
623
  .option("--author <name>", "Author name", "anonymous")
624
+ .option("--sign <did>", "Sign packet with a local DID identity")
602
625
  .action(async (id, options) => {
603
626
  const [{ listUserTemplates }, { exportTemplatePacket, writePacket }] =
604
627
  await Promise.all([
@@ -611,9 +634,20 @@ export function registerCoworkCommand(program) {
611
634
  logger.error(`Template not installed: ${id}`);
612
635
  process.exit(1);
613
636
  }
614
- const packet = exportTemplatePacket(tpl, { author: options.author });
637
+ let signer = null;
638
+ try {
639
+ signer = await _resolveSigner(options.sign);
640
+ } catch (err) {
641
+ logger.error(err.message);
642
+ process.exit(1);
643
+ }
644
+ const packet = exportTemplatePacket(tpl, {
645
+ author: options.author,
646
+ signer,
647
+ });
615
648
  writePacket(options.out, packet);
616
649
  logger.log(chalk.green(`✓ Wrote template packet to ${options.out}`));
650
+ if (signer) logger.log(chalk.gray(` signed by: ${signer.did}`));
617
651
  });
618
652
 
619
653
  share
@@ -621,6 +655,7 @@ export function registerCoworkCommand(program) {
621
655
  .description("Export a historical Cowork task result as a signed packet")
622
656
  .requiredOption("--out <file>", "Output packet file (.json)")
623
657
  .option("--author <name>", "Author name", "anonymous")
658
+ .option("--sign <did>", "Sign packet with a local DID identity")
624
659
  .action(async (taskId, options) => {
625
660
  const { findHistoryRecord, exportResultPacket, writePacket } =
626
661
  await import("../lib/cowork-share.js");
@@ -629,9 +664,20 @@ export function registerCoworkCommand(program) {
629
664
  logger.error(`Task not found in history: ${taskId}`);
630
665
  process.exit(1);
631
666
  }
632
- const packet = exportResultPacket(rec, { author: options.author });
667
+ let signer = null;
668
+ try {
669
+ signer = await _resolveSigner(options.sign);
670
+ } catch (err) {
671
+ logger.error(err.message);
672
+ process.exit(1);
673
+ }
674
+ const packet = exportResultPacket(rec, {
675
+ author: options.author,
676
+ signer,
677
+ });
633
678
  writePacket(options.out, packet);
634
679
  logger.log(chalk.green(`✓ Wrote result packet to ${options.out}`));
680
+ if (signer) logger.log(chalk.gray(` signed by: ${signer.did}`));
635
681
  });
636
682
 
637
683
  share
@@ -639,11 +685,20 @@ export function registerCoworkCommand(program) {
639
685
  .description(
640
686
  "Import a signed packet (auto-detects template vs result by kind)",
641
687
  )
642
- .action(async (file) => {
688
+ .option("--require-signed", "Reject unsigned packets")
689
+ .option(
690
+ "--trust <did>",
691
+ "Accept only packets signed by one of these DIDs (repeatable)",
692
+ (value, prev) => (prev ? [...prev, value] : [value]),
693
+ )
694
+ .action(async (file, options) => {
643
695
  const { readPacket, importTemplatePacket, importResultPacket } =
644
696
  await import("../lib/cowork-share.js");
645
697
  try {
646
- const packet = readPacket(file);
698
+ const packet = readPacket(file, {
699
+ requireSigned: !!options.requireSigned,
700
+ trustedDids: options.trust || null,
701
+ });
647
702
  if (packet.kind === "template") {
648
703
  const tpl = importTemplatePacket(process.cwd(), packet);
649
704
  logger.log(chalk.green(`✓ Imported template '${tpl.id}'`));
@@ -674,6 +729,11 @@ export function registerCoworkCommand(program) {
674
729
  logger.log(chalk.gray(` author: ${pkt.meta.author}`));
675
730
  logger.log(chalk.gray(` createdAt: ${pkt.meta.createdAt}`));
676
731
  logger.log(chalk.gray(` checksum: ${pkt.checksum.slice(0, 16)}…`));
732
+ if (pkt.signature) {
733
+ logger.log(chalk.gray(` signed by: ${pkt.signature.did}`));
734
+ } else {
735
+ logger.log(chalk.gray(` signed: no`));
736
+ }
677
737
  } catch (err) {
678
738
  logger.error(err.message);
679
739
  process.exit(1);
@@ -808,6 +868,118 @@ export function registerCoworkCommand(program) {
808
868
  }
809
869
  });
810
870
 
871
+ // cowork observe — unified dashboard over tasks/workflows/schedules
872
+ const observe = cowork
873
+ .command("observe")
874
+ .description(
875
+ "Aggregate view over Cowork history, workflows, and schedules",
876
+ );
877
+
878
+ observe
879
+ .command("report", { isDefault: true })
880
+ .description("Print the aggregate report (default)")
881
+ .option("--days <n>", "Window size in days", "7")
882
+ .option("--json", "Output as JSON")
883
+ .action(async (options) => {
884
+ const { aggregate } = await import("../lib/cowork-observe.js");
885
+ const windowDays = parseInt(options.days, 10) || 7;
886
+ const data = aggregate(process.cwd(), { windowDays });
887
+ if (options.json) {
888
+ console.log(JSON.stringify(data, null, 2));
889
+ return;
890
+ }
891
+ const pct = (x) => `${Math.round((x || 0) * 100)}%`;
892
+ logger.log(chalk.bold(`Cowork Observe — last ${data.window.days}d`));
893
+ logger.log(chalk.gray(` ${data.window.from} → ${data.window.to}`));
894
+ logger.log("");
895
+ logger.log(chalk.cyan("Tasks"));
896
+ logger.log(` total: ${data.tasks.total}`);
897
+ logger.log(` completed: ${data.tasks.completed}`);
898
+ logger.log(` failed: ${data.tasks.failed}`);
899
+ logger.log(` success rate: ${pct(data.tasks.successRate)}`);
900
+ logger.log(` avg tokens: ${data.tasks.avgTokens}`);
901
+ logger.log("");
902
+ logger.log(chalk.cyan(`Templates (${data.templates.length})`));
903
+ for (const t of data.templates.slice(0, 5)) {
904
+ logger.log(
905
+ ` ${t.templateName || t.templateId} runs=${t.runs} success=${pct(t.successRate)}`,
906
+ );
907
+ }
908
+ logger.log("");
909
+ logger.log(chalk.cyan("Schedules"));
910
+ logger.log(` active: ${data.schedules.active}`);
911
+ for (const n of data.schedules.nextTriggers) {
912
+ logger.log(
913
+ chalk.gray(` next: ${n.at} ${n.cron} (${n.scheduleId || "-"})`),
914
+ );
915
+ }
916
+ if (data.failures.length > 0) {
917
+ logger.log("");
918
+ logger.log(chalk.yellow(`Failures (${data.failures.length})`));
919
+ for (const f of data.failures.slice(0, 3)) {
920
+ const top = f.commonSummaries?.[0]?.summary || "—";
921
+ logger.log(
922
+ ` ${f.templateName || f.templateId} ×${f.failureCount} ${top.slice(0, 60)}`,
923
+ );
924
+ }
925
+ }
926
+ });
927
+
928
+ observe
929
+ .command("serve")
930
+ .description("Start a read-only HTTP dashboard")
931
+ .option("--port <n>", "HTTP port (0 for random)", "18820")
932
+ .option("--host <addr>", "Bind address", "127.0.0.1")
933
+ .option("--days <n>", "Window size in days", "7")
934
+ .action(async (options) => {
935
+ const http = await import("node:http");
936
+ const { aggregate } = await import("../lib/cowork-observe.js");
937
+ const { buildHtml } = await import("../lib/cowork-observe-html.js");
938
+ const windowDays = parseInt(options.days, 10) || 7;
939
+
940
+ const server = http.createServer((req, res) => {
941
+ const urlPath = (req.url || "/").split("?")[0];
942
+ try {
943
+ if (urlPath === "/" || urlPath === "/index.html") {
944
+ const data = aggregate(process.cwd(), { windowDays });
945
+ const html = buildHtml(data);
946
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
947
+ res.end(html);
948
+ return;
949
+ }
950
+ if (urlPath === "/api/observe") {
951
+ const data = aggregate(process.cwd(), { windowDays });
952
+ res.writeHead(200, {
953
+ "content-type": "application/json; charset=utf-8",
954
+ });
955
+ res.end(JSON.stringify(data));
956
+ return;
957
+ }
958
+ res.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
959
+ res.end("Not found");
960
+ } catch (err) {
961
+ res.writeHead(500, { "content-type": "text/plain; charset=utf-8" });
962
+ res.end(`Error: ${err.message}`);
963
+ }
964
+ });
965
+
966
+ const port = parseInt(options.port, 10);
967
+ server.listen(Number.isFinite(port) ? port : 18820, options.host, () => {
968
+ const addr = server.address();
969
+ const actualPort = typeof addr === "object" && addr ? addr.port : port;
970
+ logger.log(
971
+ chalk.green(
972
+ `✓ Cowork Observe dashboard at http://${options.host}:${actualPort}/`,
973
+ ),
974
+ );
975
+ logger.log(chalk.gray(" Press Ctrl+C to stop."));
976
+ });
977
+
978
+ process.on("SIGINT", () => {
979
+ server.close(() => process.exit(0));
980
+ });
981
+ });
982
+
811
983
  // cowork learning — analyze historical runs
812
984
  const learning = cowork
813
985
  .command("learning")
@@ -275,3 +275,143 @@ export async function handleOrchestrate(server, id, ws, message) {
275
275
  });
276
276
  }
277
277
  }
278
+
279
+ // ─── Workflow (N1) ───────────────────────────────────────────────────────────
280
+
281
+ function _cwd(server) {
282
+ return server.projectRoot || process.cwd();
283
+ }
284
+
285
+ function _sendError(server, ws, id, code, message) {
286
+ server._send(ws, { id, type: "error", code, message });
287
+ }
288
+
289
+ export async function handleWorkflowList(server, id, ws) {
290
+ try {
291
+ const { listWorkflows } = await import("../../lib/cowork-workflow.js");
292
+ const workflows = listWorkflows(_cwd(server));
293
+ server._send(ws, { id, type: "workflow:list", workflows });
294
+ } catch (err) {
295
+ _sendError(server, ws, id, "WORKFLOW_LIST_FAILED", err.message);
296
+ }
297
+ }
298
+
299
+ export async function handleWorkflowGet(server, id, ws, message) {
300
+ const { id: wfId } = message || {};
301
+ if (!wfId) return _sendError(server, ws, id, "MISSING_ID", "id required");
302
+ try {
303
+ const { getWorkflow } = await import("../../lib/cowork-workflow.js");
304
+ const workflow = getWorkflow(_cwd(server), wfId);
305
+ server._send(ws, { id, type: "workflow:get", workflow: workflow || null });
306
+ } catch (err) {
307
+ _sendError(server, ws, id, "WORKFLOW_GET_FAILED", err.message);
308
+ }
309
+ }
310
+
311
+ export async function handleWorkflowSave(server, id, ws, message) {
312
+ const { workflow } = message || {};
313
+ if (!workflow || typeof workflow !== "object") {
314
+ return _sendError(
315
+ server,
316
+ ws,
317
+ id,
318
+ "INVALID_WORKFLOW",
319
+ "workflow object required",
320
+ );
321
+ }
322
+ try {
323
+ const { validateWorkflow, saveWorkflow } =
324
+ await import("../../lib/cowork-workflow.js");
325
+ const result = validateWorkflow(workflow);
326
+ if (!result.valid) {
327
+ return _sendError(
328
+ server,
329
+ ws,
330
+ id,
331
+ "WORKFLOW_INVALID",
332
+ result.errors.join("; "),
333
+ );
334
+ }
335
+ saveWorkflow(_cwd(server), workflow);
336
+ server._send(ws, {
337
+ id,
338
+ type: "workflow:save",
339
+ saved: true,
340
+ workflowId: workflow.id,
341
+ });
342
+ } catch (err) {
343
+ _sendError(server, ws, id, "WORKFLOW_SAVE_FAILED", err.message);
344
+ }
345
+ }
346
+
347
+ export async function handleWorkflowRemove(server, id, ws, message) {
348
+ const { id: wfId } = message || {};
349
+ if (!wfId) return _sendError(server, ws, id, "MISSING_ID", "id required");
350
+ try {
351
+ const { removeWorkflow } = await import("../../lib/cowork-workflow.js");
352
+ const removed = removeWorkflow(_cwd(server), wfId);
353
+ server._send(ws, { id, type: "workflow:remove", removed });
354
+ } catch (err) {
355
+ _sendError(server, ws, id, "WORKFLOW_REMOVE_FAILED", err.message);
356
+ }
357
+ }
358
+
359
+ export async function handleWorkflowRun(server, id, ws, message) {
360
+ const { id: wfId } = message || {};
361
+ if (!wfId) return _sendError(server, ws, id, "MISSING_ID", "id required");
362
+
363
+ try {
364
+ const { getWorkflow, executeWorkflow } =
365
+ await import("../../lib/cowork-workflow.js");
366
+ const workflow = getWorkflow(_cwd(server), wfId);
367
+ if (!workflow) {
368
+ return _sendError(
369
+ server,
370
+ ws,
371
+ id,
372
+ "WORKFLOW_NOT_FOUND",
373
+ `No workflow: ${wfId}`,
374
+ );
375
+ }
376
+
377
+ const runId = `wf-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
378
+ server._send(ws, { id, type: "workflow:started", runId, workflowId: wfId });
379
+
380
+ const onStepStart = ({ stepId, message: stepMessage }) => {
381
+ server._send(ws, {
382
+ id,
383
+ type: "workflow:step-start",
384
+ runId,
385
+ stepId,
386
+ message: stepMessage,
387
+ });
388
+ };
389
+ const onStepComplete = (outcome) => {
390
+ server._send(ws, {
391
+ id,
392
+ type: "workflow:step-complete",
393
+ runId,
394
+ stepId: outcome?.id,
395
+ status: outcome?.status,
396
+ summary: outcome?.result?.summary,
397
+ });
398
+ };
399
+
400
+ const record = await executeWorkflow({
401
+ workflow,
402
+ cwd: _cwd(server),
403
+ onStepStart,
404
+ onStepComplete,
405
+ });
406
+
407
+ server._send(ws, {
408
+ id,
409
+ type: "workflow:done",
410
+ runId,
411
+ status: record?.status || "completed",
412
+ steps: record?.steps || [],
413
+ });
414
+ } catch (err) {
415
+ _sendError(server, ws, id, "WORKFLOW_RUN_FAILED", err.message);
416
+ }
417
+ }
@@ -47,6 +47,11 @@ export function createWsMessageDispatcher(server) {
47
47
  "cowork-cancel": () => server._handleCoworkCancel(id, ws, message),
48
48
  "cowork-templates": () => server._handleCoworkTemplates(id, ws),
49
49
  "cowork-history": () => server._handleCoworkHistory(id, ws, message),
50
+ "workflow-list": () => server._handleWorkflowList(id, ws),
51
+ "workflow-get": () => server._handleWorkflowGet(id, ws, message),
52
+ "workflow-save": () => server._handleWorkflowSave(id, ws, message),
53
+ "workflow-remove": () => server._handleWorkflowRemove(id, ws, message),
54
+ "workflow-run": () => server._handleWorkflowRun(id, ws, message),
50
55
  "tasks-list": () => server._handleTasksList(id, ws),
51
56
  "tasks-stop": () => server._handleTasksStop(id, ws, message),
52
57
  "tasks-detail": () => server._handleTaskDetail(id, ws, message),
@@ -55,6 +55,11 @@ import {
55
55
  handleCoworkCancel,
56
56
  handleCoworkTemplates,
57
57
  handleCoworkHistory,
58
+ handleWorkflowList,
59
+ handleWorkflowGet,
60
+ handleWorkflowSave,
61
+ handleWorkflowRemove,
62
+ handleWorkflowRun,
58
63
  } from "./action-protocol.js";
59
64
  import {
60
65
  handleWorktreeDiff,
@@ -321,6 +326,22 @@ export class ChainlessChainWSServer extends EventEmitter {
321
326
  return handleCoworkHistory(this, id, ws, message);
322
327
  }
323
328
 
329
+ _handleWorkflowList(id, ws) {
330
+ return handleWorkflowList(this, id, ws);
331
+ }
332
+ _handleWorkflowGet(id, ws, message) {
333
+ return handleWorkflowGet(this, id, ws, message);
334
+ }
335
+ _handleWorkflowSave(id, ws, message) {
336
+ return handleWorkflowSave(this, id, ws, message);
337
+ }
338
+ _handleWorkflowRemove(id, ws, message) {
339
+ return handleWorkflowRemove(this, id, ws, message);
340
+ }
341
+ _handleWorkflowRun(id, ws, message) {
342
+ return handleWorkflowRun(this, id, ws, message);
343
+ }
344
+
324
345
  /** @private – list background tasks */
325
346
  async _handleTasksList(id, ws) {
326
347
  try {