aitable-workflow-cli 0.1.13-beta.1 → 0.1.14-beta.1785913842374
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 +1 -1
- package/dist/cli.js +35 -34
- package/package.json +3 -3
- package/templates/support-qa/workflows/config-sync/recipes/config_sync.recipe.ts +10 -1
- package/templates/support-qa/workflows/group-qa/recipes/conversation_handler.recipe.ts +5 -2
- package/templates/support-qa/workflows/group-qa/recipes/on-action.recipe.ts +37 -1
- package/templates/support-qa/workflows/group-qa/recipes/reply-to-group.recipe.ts +1 -1
- package/templates/support-qa/workflows/group-qa/workflow.yml +5 -0
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ aitable-workflow init --template support-qa --profile aitable
|
|
|
65
65
|
|------|------|
|
|
66
66
|
| `aitable-workflow init` | 交互式向导,生成项目配置骨架;`--template <name>` 从内置模板脚手架 |
|
|
67
67
|
| `aitable-workflow design` | 调 Coding Agent 生成 `workflow.yml` 与 `recipes/` |
|
|
68
|
-
| `aitable-workflow setup` | 先交互式校验/补齐配置(含 Coding Agent 选择),再在 AI 表格创建字段、状态选项并编译知识库;`--skip-config`
|
|
68
|
+
| `aitable-workflow setup` | 先交互式校验/补齐配置(含 Coding Agent 选择),再在 AI 表格创建字段、状态选项并编译知识库;`--skip-config-if-valid` 在无阻断项时不打开配置界面并直接搭建,`--skip-config` 完全跳过前置校验(CI 用),`--no-kb` / `--kb-full` 控制知识库 |
|
|
69
69
|
| `aitable-workflow run` | 启动编排引擎,轮询并推进工单 |
|
|
70
70
|
| `aitable-workflow validate` | 校验配置与工作流模板,输出步骤链摘要 |
|
|
71
71
|
| `aitable-workflow config` | 单独校验项目配置完整性并交互式修改参数(`--json` / `--no-interactive`);`setup` 已内置该步骤,通常无需单跑 |
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{b as O,d as x,e as V,f as ie,g as M}from"./chunk-U773UWB7.js";import{a as c}from"./chunk-EI7MMDWY.js";import{config as Ee}from"dotenv";import{Command as Y}from"commander";import{createApp as ht,loadSettings as re,loadWorkflowTemplate as Ie,logger as g,activateGlobalDwsOnPath as wt,ensureTsxRegistered as yt,runInit as kt,runDesignCommand as At,runSetupCommand as Rt,runKbCompileCommand as vt,runKbUpdateCommand as bt,runKbStatusCommand as St,runKbIngestCommand as Et,runSync as It,checkWorkflowRecipes as $t,listCodingAgents as Tt,Tracer as xt,saveCredential as _t,listCredentials as se,setActiveAgent as Lt,getActiveCredential as k,readCredentials as Ct,findConflictingCredential as Dt,maskSecret as K}from"aitable-workflow-core";import{startServer as Pt,resolveServeConfig as Ot}from"aitable-workflow-server";import I from"os";import A from"path";import{mkdir as Fe,readFile as Be}from"fs/promises";import{mkdirSync as We}from"fs";import{execSync as Ne}from"child_process";import{logger as f,detectInstalledAgents as Ye,runCodingAgent as Ge,DwsClient as Ke,getActiveCredential as He}from"aitable-workflow-core";import J from"ws";import{logger as b,RELAY_REJECTED_CODE as xe,isRelayMessage as _e}from"aitable-workflow-core";var S="relay:conn",ce=1e3,Le=3e4,Ce=Number(process.env.AITABLE_RELAY_SERVER_STALE_MS??9e4),U=class{static{c(this,"RelayConnection")}ws=null;delay=ce;timer=null;watchdog=null;should=!0;permanentlyRejected=!1;accepted=!1;lastServerFrameAt=0;serverUrl;key;token;onMessage;onOpen;onAccepted;onPermanentReject;mkWs;staleMs;constructor(e){this.serverUrl=e.serverUrl,this.key=e.key??"",this.token=e.token??"",this.onMessage=e.onMessage,this.onOpen=e.onOpen,this.onAccepted=e.onAccepted,this.onPermanentReject=e.onPermanentReject,this.mkWs=e.mkWs??(n=>new J(n)),this.staleMs=e.staleMs??Ce}connect(){this.permanentlyRejected||(this.should=!0,this.doConnect())}send(e){this.ws&&this.ws.readyState===J.OPEN&&this.ws.send(JSON.stringify(e))}close(){this.should=!1,this.timer&&(clearTimeout(this.timer),this.timer=null),this.watchdog&&(clearTimeout(this.watchdog),this.watchdog=null),this.ws?.close()}isPermanentlyRejected(){return this.permanentlyRejected}currentSocket(){return this.ws}buildUrl(){let e=this.serverUrl.replace(/\/+$/,"").replace(/^http/,"ws"),n=this.token?`token=${encodeURIComponent(this.token)}`:`key=${encodeURIComponent(this.key)}`;return`${e}/api/relay/connect?${n}`}doConnect(){if(!this.should||this.permanentlyRejected)return;let e=this.buildUrl();b.info(S,"connecting",{serverUrl:this.serverUrl}),this.watchdog&&(clearTimeout(this.watchdog),this.watchdog=null),this.accepted=!1,this.lastServerFrameAt=0;let n=this.mkWs(e);this.ws=n,n.on("open",()=>{b.info(S,"connected"),this.onOpen?.(n)}),n.on("message",o=>{this.accepted||(this.accepted=!0,this.delay=ce,this.onAccepted?.(n)),this.lastServerFrameAt=Date.now(),this.armWatchdog();let r;try{r=JSON.parse(typeof o=="string"?o:String(o))}catch{return}_e(r)&&(r.type==="ping"&&this.send({type:"pong"}),this.onMessage(r))}),n.on("close",(o,r)=>{this.watchdog&&(clearTimeout(this.watchdog),this.watchdog=null);let s=r==null?"":String(r);if(o===xe){this.permanentlyRejected=!0,this.should=!1,b.error(S,"server permanently rejected this machine: its connection key is unknown or was removed; re-issue the relay connect command and restart with the new key",{code:o,reason:s}),this.onPermanentReject?.({code:o,reason:s});return}b.warn(S,"disconnected",{code:o}),this.scheduleReconnect()}),n.on("error",o=>{b.error(S,"ws error",{detail:o instanceof Error?o.message:String(o)})})}armWatchdog(){this.watchdog&&clearTimeout(this.watchdog),this.watchdog=setTimeout(()=>{if(!(!this.accepted||!this.lastServerFrameAt||this.ws?.readyState!==J.OPEN)){b.warn(S,"server heartbeat stale; closing socket to reconnect",{staleMs:Date.now()-this.lastServerFrameAt});try{this.ws.close()}catch{}}},this.staleMs+1),this.watchdog.unref?.()}scheduleReconnect(){!this.should||this.permanentlyRejected||this.timer||(b.info(S,"reconnecting",{ms:this.delay}),this.timer=setTimeout(()=>{this.timer=null,this.doConnect()},this.delay),this.timer.unref?.(),this.delay=Math.min(this.delay*2,Le))}};import{createHash as De}from"crypto";import{readdir as Pe,readFile as Oe,stat as Me}from"fs/promises";import le from"path";import{isAllowedArtifactPath as Ue,normalizeArtifactRelPath as je}from"aitable-workflow-core";var ge={async upload(t,e,n){let o=await fetch(t,{method:"PUT",body:e,headers:{...n}});if(!o.ok)throw new Error(`artifact PUT failed: ${o.status} ${o.statusText}`)}};async function de(t){let e=[];return await me(t,t,e),e.sort((n,o)=>n.relPath.localeCompare(o.relPath))}c(de,"scanArtifacts");async function me(t,e,n){let o;try{o=await Pe(e,{withFileTypes:!0})}catch{return}for(let r of o){if(r.name==="node_modules"||r.name.startsWith("."))continue;let s=le.join(e,r.name);if(r.isDirectory())await me(t,s,n);else if(r.isFile()){let a=le.relative(t,s).replace(/\\/g,"/");if(!Ue(a))continue;let i=await Me(s),l=await Oe(s),m=De("sha256").update(l).digest("hex");n.push({relPath:je(a),absPath:s,size:i.size,sha256:m})}}}c(me,"walk");var u="relay:daemon",pe=["aitable"],qe="dws";function Ve(t,e=pe){if(!Array.isArray(t)||t.length===0)return{ok:!1,reason:"dws args must be a non-empty array"};for(let o of t){if(typeof o!="string")return{ok:!1,reason:"dws args must all be strings"};if(o.length===0)return{ok:!1,reason:"dws args must not contain empty tokens"};if(/[\u0000-\u001f]/.test(o))return{ok:!1,reason:"dws args must not contain control characters"}}let n=t[0];return n.startsWith("-")?{ok:!1,reason:"first dws arg must be a subcommand, not a flag"}:e.length>0&&!e.includes(n)?{ok:!1,reason:`dws command "${n}" is not allowed`}:{ok:!0}}c(Ve,"validateDwsArgs");function Je(t){try{return Ne(`${t} --version`,{encoding:"utf8",timeout:5e3,stdio:["pipe","pipe","pipe"]}),!0}catch{return!1}}c(Je,"detectDwsAvailable");var z=class{static{c(this,"RelayDaemon")}config;allowedDwsCommands;conn;runAgent;createDwsClient;detectAgents;detectDws;hostname;osString;onPermanentReject;artifactUploader;grantTimeoutMs;pendingGrants=new Map;maxTasks;inflight=0;taskQueue=[];machineId;constructor(e,n={}){this.config={...e,dwsBin:e.dwsBin??qe},this.machineId=e.machineId,this.allowedDwsCommands=e.allowedDwsCommands??pe,this.runAgent=n.runAgent??Ge,this.createDwsClient=n.createDwsClient??((r,s)=>new Ke({bin:r,timeoutMs:s})),this.detectAgents=n.detectAgents??(()=>Ye()),this.detectDws=n.detectDws??(r=>Je(r)),this.hostname=n.hostname??(()=>I.hostname()),this.osString=n.osString??(()=>`${I.platform()} ${I.arch()}`),this.onPermanentReject=n.onPermanentReject,this.artifactUploader=n.artifactUploader??ge,this.grantTimeoutMs=n.grantTimeoutMs??3e4,this.maxTasks=Math.max(1,Math.floor(e.maxTasks??1));let o=n.createConnection??(r=>new U(r));this.conn=o({serverUrl:this.config.serverUrl,key:this.config.key,token:this.config.token,onMessage:c(r=>this.handleMessage(r),"onMessage"),onOpen:c(()=>this.handleOpen(),"onOpen"),onPermanentReject:c(r=>this.handlePermanentReject(r),"onPermanentReject")})}start(){f.info(u,"starting",{serverUrl:this.config.serverUrl,workspaceRoot:this.config.workspaceRoot}),this.config.name||f.warn(u,"\u672A\u6307\u5B9A --name\uFF0C\u5C06\u4F7F\u7528\u4E3B\u673A\u540D\u4F5C\u4E3A\u673A\u5668\u6CE8\u518C\u540D\uFF1B\u591A\u673A\u573A\u666F\u4E0B hostname \u82E5\u91CD\u590D\u4F1A\u5BFC\u81F4\u8FDE\u63A5\u4E92\u76F8\u9876\u66FF\uFF0C\u5EFA\u8BAE\u663E\u5F0F\u6307\u5B9A --name",{hostname:this.hostname()}),this.conn.connect()}stop(){f.info(u,"stopping"),this.conn.close()}buildReady(){let n={type:"ready",runtimes:Array.from(new Set(this.detectAgents().map(o=>o.type))),hasDws:this.detectDws(this.config.dwsBin),hostname:this.hostname(),os:this.osString()};return this.machineId&&(n.machineId=this.machineId),this.config.name&&(n.name=this.config.name),this.config.displayName&&(n.displayName=this.config.displayName),this.config.description&&(n.description=this.config.description),this.maxTasks>1&&(n.maxTasks=this.maxTasks),n}handleOpen(){let e=this.buildReady();f.info(u,"connected; sending ready",{runtimes:e.runtimes,hasDws:e.hasDws}),this.conn.send(e)}handlePermanentReject(e){f.error(u,"permanently rejected by gateway",{...e}),this.onPermanentReject?.(e)}handleMessage(e){switch(e.type){case"run:agent":this.enqueueTask(()=>this.handleRunAgent(e));break;case"run:dws":this.enqueueTask(()=>this.handleRunDws(e));break;case"upload:grant":this.resolveGrant(e);break;case"ready:ack":e.machineId&&(this.machineId=e.machineId,f.info(u,"machine id assigned",{machineId:e.machineId}));break;default:break}}enqueueTask(e){let n=c(()=>{this.inflight+=1,e().finally(()=>{this.inflight-=1;let o=this.taskQueue.shift();o&&o()})},"exec");this.inflight<this.maxTasks?n():this.taskQueue.push(n)}async handleRunAgent(e){let{requestId:n}=e;f.info(u,"run:agent received",{requestId:n,projectId:e.projectId,agent:e.agent});try{let o=this.resolveWorkDir(e.projectId,e.workDir);await Fe(o,{recursive:!0});let r=this.rewritePromptWorkDir(e.prompt,e.workDir,o),s={agent:e.agent,workDir:o,onEvent:c(l=>this.conn.send({type:"event",requestId:n,event:l}),"onEvent")};e.sessionId&&(s.sessionId=e.sessionId),e.permissionMode&&(s.adapterOptions={permissionMode:e.permissionMode}),this.config.agentTimeoutMs!=null&&(s.timeoutMs=this.config.agentTimeoutMs);let a=await this.runAgent(r,s),i;try{i=await this.collectAndUploadArtifacts(n,e.projectId,o)}catch(l){let m=l instanceof Error?l.message:String(l);f.error(u,"artifact upload failed",{requestId:n,error:m}),this.conn.send({type:"error",requestId:n,code:"artifact_upload_failed",message:m});return}this.conn.send({type:"result",requestId:n,ok:!0,response:a.response,sessionId:a.sessionId,...i?{artifacts:i}:{}}),f.info(u,"run:agent done",{requestId:n,sessionId:a.sessionId,artifacts:i?i.files.length:0})}catch(o){let r=o instanceof Error?o.message:String(o);f.error(u,"run:agent failed",{requestId:n,error:r}),this.conn.send({type:"error",requestId:n,code:"agent_failed",message:r})}}async collectAndUploadArtifacts(e,n,o){let r=await de(o);if(r.length===0)return;this.conn.send({type:"upload:request",requestId:e,projectId:n,files:r.map(i=>({path:i.relPath,size:i.size,sha256:i.sha256}))});let s=await this.awaitGrant(e);if(!s)throw new Error("upload:grant \u7B49\u5F85\u8D85\u65F6");if(s.grants.length===0){f.warn(u,"upload:grant \u65E0\u6388\u6743\u9879\uFF08gateway \u672A\u914D\u7F6E store \u6216\u5168\u88AB\u62D2\uFF09",{requestId:e,rejected:(s.rejected??[]).length});return}let a=[];for(let i of s.grants){let l=r.find(h=>h.relPath===i.path);if(!l)continue;let m=await Be(l.absPath);await this.artifactUploader.upload(i.url,m,i.headers),a.push({path:i.path,key:i.key,sha256:l.sha256,size:l.size})}return{store:"oss",files:a}}awaitGrant(e){return new Promise(n=>{let o=setTimeout(()=>{this.pendingGrants.delete(e),n(void 0)},this.grantTimeoutMs);o.unref?.(),this.pendingGrants.set(e,r=>{clearTimeout(o),n(r)})})}resolveGrant(e){let n=this.pendingGrants.get(e.requestId);n&&(this.pendingGrants.delete(e.requestId),n(e))}async handleRunDws(e){let{requestId:n}=e,o=Ve(e.args,this.allowedDwsCommands);if(!o.ok){f.warn(u,"run:dws rejected",{requestId:n,reason:o.reason}),this.conn.send({type:"error",requestId:n,code:"dws_rejected",message:o.reason});return}f.info(u,"run:dws received",{requestId:n,command:e.args[0]});try{let s=await this.createDwsClient(this.config.dwsBin,e.timeoutMs).runJson(e.args,e.timeoutMs!=null?{timeoutMs:e.timeoutMs}:void 0);this.conn.send({type:"result",requestId:n,ok:!0,response:JSON.stringify(s)}),f.info(u,"run:dws done",{requestId:n})}catch(r){let s=r instanceof Error?r.message:String(r);f.error(u,"run:dws failed",{requestId:n,error:s}),this.conn.send({type:"error",requestId:n,code:"dws_failed",message:s})}}resolveWorkDir(e,n){if(n&&n.trim()!=="")if(A.isAbsolute(n))f.warn(u,"\u5FFD\u7565\u670D\u52A1\u7AEF\u4E0B\u53D1\u7684\u7EDD\u5BF9\u8DEF\u5F84 workDir\uFF0C\u56DE\u9000\u5230 workspaceRoot/projectId",{workDir:n,projectId:e});else return A.resolve(this.config.workspaceRoot,n);return A.join(this.config.workspaceRoot,e)}rewritePromptWorkDir(e,n,o){let r=n?.trim();return!r||!A.isAbsolute(r)||r===o?e:e.split(r).join(o)}};function X(t,e){let n,o=t?.trim();if(o)n=A.isAbsolute(o)?A.resolve(o):A.resolve(I.homedir(),o);else{let r=e?.trim()?e.trim():"default";n=A.join(I.homedir(),".aitable-workflow","workspaces",r)}return We(n,{recursive:!0}),n}c(X,"resolveWorkspaceRoot");function ze(t,e=process.env){let n=t.serverUrl??e.AITABLE_RELAY_SERVER_URL,o=t.key??e.AITABLE_RELAY_BOOTSTRAP_KEY,r=t.token??e.AITABLE_RELAY_TOKEN,s=t.workspaceRoot??e.AITABLE_RELAY_WORKSPACE_ROOT,a=t.machineId??e.AITABLE_RELAY_MACHINE_ID,i=t.name??e.AITABLE_RELAY_NAME,l=t.desc??e.AITABLE_RELAY_DESC,m=t.maxTasks??e.AITABLE_RELAY_MAX_TASKS,h=[];if(n||h.push("--server-url (\u6216 AITABLE_RELAY_SERVER_URL)"),!o&&!r&&h.push("--key/--token (\u6216 AITABLE_RELAY_BOOTSTRAP_KEY/AITABLE_RELAY_TOKEN)"),h.length>0)throw new Error(`relay \u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${h.join(", ")}
|
|
3
|
-
\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 \`aitable-workflow connect\` \u6CE8\u518C\u540E\u53EF\u7701\u7565\u8FD9\u4E9B\u53C2\u6570`);let w=
|
|
4
|
-
\u{1F4A1} ${
|
|
5
|
-
\u{1F4A1} ${
|
|
6
|
-
\u{1F4A1} ${
|
|
7
|
-
`),m={PATH:process.env.PATH??"/usr/local/bin:/usr/bin:/bin",...e?.relayEnv??{}},h=Object.entries(m).map(([w,
|
|
8
|
-
<string>${$(
|
|
2
|
+
import{b as U,d as x,e as z,f as le,g as j}from"./chunk-U773UWB7.js";import{a as c}from"./chunk-EI7MMDWY.js";import{config as xe}from"dotenv";import{Command as G}from"commander";import{createApp as vt,loadSettings as K,loadWorkflowTemplate as Ce,logger as g,activateGlobalDwsOnPath as bt,createExecShellToolkit as St,ensureTsxRegistered as Et,runInit as It,runDesignCommand as $t,runCheck as Ie,runSetupCommand as Tt,runKbCompileCommand as xt,runKbUpdateCommand as Ct,runKbStatusCommand as _t,runKbIngestCommand as Lt,runSync as Dt,checkWorkflowRecipes as Pt,listCodingAgents as Ot,Tracer as Mt,saveCredential as Ut,listCredentials as ae,setActiveAgent as jt,getActiveCredential as A,readCredentials as Nt,findConflictingCredential as Ft,maskSecret as H,runMigrate as Bt,checkMigrateHint as _e}from"aitable-workflow-core";import{startServer as Wt,resolveServeConfig as Vt}from"aitable-workflow-server";import I from"os";import R from"path";import{mkdir as Ge,readFile as Ke}from"fs/promises";import{mkdirSync as He}from"fs";import{execSync as qe}from"child_process";import{logger as p,detectInstalledAgents as Je,runCodingAgent as ze,DwsClient as Xe,getActiveCredential as Qe}from"aitable-workflow-core";import X from"ws";import{logger as b,RELAY_REJECTED_CODE as Oe,isRelayMessage as Me}from"aitable-workflow-core";var S="relay:conn",ge=1e3,Ue=3e4,je=Number(process.env.AITABLE_RELAY_SERVER_STALE_MS??9e4),N=class{static{c(this,"RelayConnection")}ws=null;delay=ge;timer=null;watchdog=null;should=!0;permanentlyRejected=!1;accepted=!1;lastServerFrameAt=0;serverUrl;key;token;onMessage;onOpen;onAccepted;onPermanentReject;mkWs;staleMs;constructor(e){this.serverUrl=e.serverUrl,this.key=e.key??"",this.token=e.token??"",this.onMessage=e.onMessage,this.onOpen=e.onOpen,this.onAccepted=e.onAccepted,this.onPermanentReject=e.onPermanentReject,this.mkWs=e.mkWs??(n=>new X(n)),this.staleMs=e.staleMs??je}connect(){this.permanentlyRejected||(this.should=!0,this.doConnect())}send(e){this.ws&&this.ws.readyState===X.OPEN&&this.ws.send(JSON.stringify(e))}close(){this.should=!1,this.timer&&(clearTimeout(this.timer),this.timer=null),this.watchdog&&(clearTimeout(this.watchdog),this.watchdog=null),this.ws?.close()}isPermanentlyRejected(){return this.permanentlyRejected}currentSocket(){return this.ws}buildUrl(){let e=this.serverUrl.replace(/\/+$/,"").replace(/^http/,"ws"),n=this.token?`token=${encodeURIComponent(this.token)}`:`key=${encodeURIComponent(this.key)}`;return`${e}/api/relay/connect?${n}`}doConnect(){if(!this.should||this.permanentlyRejected)return;let e=this.buildUrl();b.info(S,"connecting",{serverUrl:this.serverUrl}),this.watchdog&&(clearTimeout(this.watchdog),this.watchdog=null),this.accepted=!1,this.lastServerFrameAt=0;let n=this.mkWs(e);this.ws=n,n.on("open",()=>{b.info(S,"connected"),this.onOpen?.(n)}),n.on("message",o=>{this.accepted||(this.accepted=!0,this.delay=ge,this.onAccepted?.(n)),this.lastServerFrameAt=Date.now(),this.armWatchdog();let r;try{r=JSON.parse(typeof o=="string"?o:String(o))}catch{return}Me(r)&&(r.type==="ping"&&this.send({type:"pong"}),this.onMessage(r))}),n.on("close",(o,r)=>{this.watchdog&&(clearTimeout(this.watchdog),this.watchdog=null);let i=r==null?"":String(r);if(o===Oe){this.permanentlyRejected=!0,this.should=!1,b.error(S,"server permanently rejected this machine: its connection key is unknown or was removed; re-issue the relay connect command and restart with the new key",{code:o,reason:i}),this.onPermanentReject?.({code:o,reason:i});return}b.warn(S,"disconnected",{code:o}),this.scheduleReconnect()}),n.on("error",o=>{b.error(S,"ws error",{detail:o instanceof Error?o.message:String(o)})})}armWatchdog(){this.watchdog&&clearTimeout(this.watchdog),this.watchdog=setTimeout(()=>{if(!(!this.accepted||!this.lastServerFrameAt||this.ws?.readyState!==X.OPEN)){b.warn(S,"server heartbeat stale; closing socket to reconnect",{staleMs:Date.now()-this.lastServerFrameAt});try{this.ws.close()}catch{}}},this.staleMs+1),this.watchdog.unref?.()}scheduleReconnect(){!this.should||this.permanentlyRejected||this.timer||(b.info(S,"reconnecting",{ms:this.delay}),this.timer=setTimeout(()=>{this.timer=null,this.doConnect()},this.delay),this.timer.unref?.(),this.delay=Math.min(this.delay*2,Ue))}};import{createHash as Ne}from"crypto";import{readdir as Fe,readFile as Be,stat as We}from"fs/promises";import de from"path";import{isAllowedArtifactPath as Ve,normalizeArtifactRelPath as Ye}from"aitable-workflow-core";var me={async upload(t,e,n){let o=await fetch(t,{method:"PUT",body:e,headers:{...n}});if(!o.ok)throw new Error(`artifact PUT failed: ${o.status} ${o.statusText}`)}};async function fe(t){let e=[];return await pe(t,t,e),e.sort((n,o)=>n.relPath.localeCompare(o.relPath))}c(fe,"scanArtifacts");async function pe(t,e,n){let o;try{o=await Fe(e,{withFileTypes:!0})}catch{return}for(let r of o){if(r.name==="node_modules"||r.name.startsWith("."))continue;let i=de.join(e,r.name);if(r.isDirectory())await pe(t,i,n);else if(r.isFile()){let s=de.relative(t,i).replace(/\\/g,"/");if(!Ve(s))continue;let a=await We(i),l=await Be(i),m=Ne("sha256").update(l).digest("hex");n.push({relPath:Ye(s),absPath:i,size:a.size,sha256:m})}}}c(pe,"walk");var u="relay:daemon",ue=["aitable"],Ze="dws";function et(t,e=ue){if(!Array.isArray(t)||t.length===0)return{ok:!1,reason:"dws args must be a non-empty array"};for(let o of t){if(typeof o!="string")return{ok:!1,reason:"dws args must all be strings"};if(o.length===0)return{ok:!1,reason:"dws args must not contain empty tokens"};if(/[\u0000-\u001f]/.test(o))return{ok:!1,reason:"dws args must not contain control characters"}}let n=t[0];return n.startsWith("-")?{ok:!1,reason:"first dws arg must be a subcommand, not a flag"}:e.length>0&&!e.includes(n)?{ok:!1,reason:`dws command "${n}" is not allowed`}:{ok:!0}}c(et,"validateDwsArgs");function tt(t){try{return qe(`${t} --version`,{encoding:"utf8",timeout:5e3,stdio:["pipe","pipe","pipe"]}),!0}catch{return!1}}c(tt,"detectDwsAvailable");var Q=class{static{c(this,"RelayDaemon")}config;allowedDwsCommands;conn;runAgent;createDwsClient;detectAgents;detectDws;hostname;osString;onPermanentReject;artifactUploader;grantTimeoutMs;pendingGrants=new Map;maxTasks;inflight=0;taskQueue=[];machineId;constructor(e,n={}){this.config={...e,dwsBin:e.dwsBin??Ze},this.machineId=e.machineId,this.allowedDwsCommands=e.allowedDwsCommands??ue,this.runAgent=n.runAgent??ze,this.createDwsClient=n.createDwsClient??((r,i)=>new Xe({bin:r,timeoutMs:i})),this.detectAgents=n.detectAgents??(()=>Je()),this.detectDws=n.detectDws??(r=>tt(r)),this.hostname=n.hostname??(()=>I.hostname()),this.osString=n.osString??(()=>`${I.platform()} ${I.arch()}`),this.onPermanentReject=n.onPermanentReject,this.artifactUploader=n.artifactUploader??me,this.grantTimeoutMs=n.grantTimeoutMs??3e4,this.maxTasks=Math.max(1,Math.floor(e.maxTasks??1));let o=n.createConnection??(r=>new N(r));this.conn=o({serverUrl:this.config.serverUrl,key:this.config.key,token:this.config.token,onMessage:c(r=>this.handleMessage(r),"onMessage"),onOpen:c(()=>this.handleOpen(),"onOpen"),onPermanentReject:c(r=>this.handlePermanentReject(r),"onPermanentReject")})}start(){p.info(u,"starting",{serverUrl:this.config.serverUrl,workspaceRoot:this.config.workspaceRoot}),this.config.name||p.warn(u,"\u672A\u6307\u5B9A --name\uFF0C\u5C06\u4F7F\u7528\u4E3B\u673A\u540D\u4F5C\u4E3A\u673A\u5668\u6CE8\u518C\u540D\uFF1B\u591A\u673A\u573A\u666F\u4E0B hostname \u82E5\u91CD\u590D\u4F1A\u5BFC\u81F4\u8FDE\u63A5\u4E92\u76F8\u9876\u66FF\uFF0C\u5EFA\u8BAE\u663E\u5F0F\u6307\u5B9A --name",{hostname:this.hostname()}),this.conn.connect()}stop(){p.info(u,"stopping"),this.conn.close()}buildReady(){let n={type:"ready",runtimes:Array.from(new Set(this.detectAgents().map(o=>o.type))),hasDws:this.detectDws(this.config.dwsBin),hostname:this.hostname(),os:this.osString()};return this.machineId&&(n.machineId=this.machineId),this.config.name&&(n.name=this.config.name),this.config.displayName&&(n.displayName=this.config.displayName),this.config.description&&(n.description=this.config.description),this.maxTasks>1&&(n.maxTasks=this.maxTasks),n}handleOpen(){let e=this.buildReady();p.info(u,"connected; sending ready",{runtimes:e.runtimes,hasDws:e.hasDws}),this.conn.send(e)}handlePermanentReject(e){p.error(u,"permanently rejected by gateway",{...e}),this.onPermanentReject?.(e)}handleMessage(e){switch(e.type){case"run:agent":this.enqueueTask(()=>this.handleRunAgent(e));break;case"run:dws":this.enqueueTask(()=>this.handleRunDws(e));break;case"upload:grant":this.resolveGrant(e);break;case"ready:ack":e.machineId&&(this.machineId=e.machineId,p.info(u,"machine id assigned",{machineId:e.machineId}));break;default:break}}enqueueTask(e){let n=c(()=>{this.inflight+=1,e().finally(()=>{this.inflight-=1;let o=this.taskQueue.shift();o&&o()})},"exec");this.inflight<this.maxTasks?n():this.taskQueue.push(n)}async handleRunAgent(e){let{requestId:n}=e;p.info(u,"run:agent received",{requestId:n,projectId:e.projectId,agent:e.agent});try{let o=this.resolveWorkDir(e.projectId,e.workDir);await Ge(o,{recursive:!0});let r=this.rewritePromptWorkDir(e.prompt,e.workDir,o),i={agent:e.agent,workDir:o,onEvent:c(l=>this.conn.send({type:"event",requestId:n,event:l}),"onEvent")};e.sessionId&&(i.sessionId=e.sessionId),e.permissionMode&&(i.adapterOptions={permissionMode:e.permissionMode}),this.config.agentTimeoutMs!=null&&(i.timeoutMs=this.config.agentTimeoutMs);let s=await this.runAgent(r,i),a;try{a=await this.collectAndUploadArtifacts(n,e.projectId,o)}catch(l){let m=l instanceof Error?l.message:String(l);p.error(u,"artifact upload failed",{requestId:n,error:m}),this.conn.send({type:"error",requestId:n,code:"artifact_upload_failed",message:m});return}this.conn.send({type:"result",requestId:n,ok:!0,response:s.response,sessionId:s.sessionId,...a?{artifacts:a}:{}}),p.info(u,"run:agent done",{requestId:n,sessionId:s.sessionId,artifacts:a?a.files.length:0})}catch(o){let r=o instanceof Error?o.message:String(o);p.error(u,"run:agent failed",{requestId:n,error:r}),this.conn.send({type:"error",requestId:n,code:"agent_failed",message:r})}}async collectAndUploadArtifacts(e,n,o){let r=await fe(o);if(r.length===0)return;this.conn.send({type:"upload:request",requestId:e,projectId:n,files:r.map(a=>({path:a.relPath,size:a.size,sha256:a.sha256}))});let i=await this.awaitGrant(e);if(!i)throw new Error("upload:grant \u7B49\u5F85\u8D85\u65F6");if(i.grants.length===0){p.warn(u,"upload:grant \u65E0\u6388\u6743\u9879\uFF08gateway \u672A\u914D\u7F6E store \u6216\u5168\u88AB\u62D2\uFF09",{requestId:e,rejected:(i.rejected??[]).length});return}let s=[];for(let a of i.grants){let l=r.find(h=>h.relPath===a.path);if(!l)continue;let m=await Ke(l.absPath);await this.artifactUploader.upload(a.url,m,a.headers),s.push({path:a.path,key:a.key,sha256:l.sha256,size:l.size})}return{store:"oss",files:s}}awaitGrant(e){return new Promise(n=>{let o=setTimeout(()=>{this.pendingGrants.delete(e),n(void 0)},this.grantTimeoutMs);o.unref?.(),this.pendingGrants.set(e,r=>{clearTimeout(o),n(r)})})}resolveGrant(e){let n=this.pendingGrants.get(e.requestId);n&&(this.pendingGrants.delete(e.requestId),n(e))}async handleRunDws(e){let{requestId:n}=e,o=et(e.args,this.allowedDwsCommands);if(!o.ok){p.warn(u,"run:dws rejected",{requestId:n,reason:o.reason}),this.conn.send({type:"error",requestId:n,code:"dws_rejected",message:o.reason});return}p.info(u,"run:dws received",{requestId:n,command:e.args[0]});try{let i=await this.createDwsClient(this.config.dwsBin,e.timeoutMs).runJson(e.args,e.timeoutMs!=null?{timeoutMs:e.timeoutMs}:void 0);this.conn.send({type:"result",requestId:n,ok:!0,response:JSON.stringify(i)}),p.info(u,"run:dws done",{requestId:n})}catch(r){let i=r instanceof Error?r.message:String(r);p.error(u,"run:dws failed",{requestId:n,error:i}),this.conn.send({type:"error",requestId:n,code:"dws_failed",message:i})}}resolveWorkDir(e,n){if(n&&n.trim()!=="")if(R.isAbsolute(n))p.warn(u,"\u5FFD\u7565\u670D\u52A1\u7AEF\u4E0B\u53D1\u7684\u7EDD\u5BF9\u8DEF\u5F84 workDir\uFF0C\u56DE\u9000\u5230 workspaceRoot/projectId",{workDir:n,projectId:e});else return R.resolve(this.config.workspaceRoot,n);return R.join(this.config.workspaceRoot,e)}rewritePromptWorkDir(e,n,o){let r=n?.trim();return!r||!R.isAbsolute(r)||r===o?e:e.split(r).join(o)}};function Z(t,e){let n,o=t?.trim();if(o)n=R.isAbsolute(o)?R.resolve(o):R.resolve(I.homedir(),o);else{let r=e?.trim()?e.trim():"default";n=R.join(I.homedir(),".aitable-workflow","workspaces",r)}return He(n,{recursive:!0}),n}c(Z,"resolveWorkspaceRoot");function nt(t,e=process.env){let n=t.serverUrl??e.AITABLE_RELAY_SERVER_URL,o=t.key??e.AITABLE_RELAY_BOOTSTRAP_KEY,r=t.token??e.AITABLE_RELAY_TOKEN,i=t.workspaceRoot??e.AITABLE_RELAY_WORKSPACE_ROOT,s=t.machineId??e.AITABLE_RELAY_MACHINE_ID,a=t.name??e.AITABLE_RELAY_NAME,l=t.desc??e.AITABLE_RELAY_DESC,m=t.maxTasks??e.AITABLE_RELAY_MAX_TASKS,h=[];if(n||h.push("--server-url (\u6216 AITABLE_RELAY_SERVER_URL)"),!o&&!r&&h.push("--key/--token (\u6216 AITABLE_RELAY_BOOTSTRAP_KEY/AITABLE_RELAY_TOKEN)"),h.length>0)throw new Error(`relay \u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${h.join(", ")}
|
|
3
|
+
\u{1F4A1} \u63D0\u793A: \u4F7F\u7528 \`aitable-workflow connect\` \u6CE8\u518C\u540E\u53EF\u7701\u7565\u8FD9\u4E9B\u53C2\u6570`);let w=a??I.hostname()??"default",f={serverUrl:n,workspaceRoot:Z(i,w)};if(r?f.token=r:o&&(f.key=o),s&&(f.machineId=s),a&&(f.name=a,f.displayName=a),l&&(f.description=l),m!=null&&m!==""){let T=Math.floor(Number(m));Number.isFinite(T)&&T>=1&&(f.maxTasks=T)}return f}c(nt,"resolveRelayCliConfig");async function C(t,e=process.env){let n=null;try{n=await Qe()}catch{n=null}let o={serverUrl:t.serverUrl??e.AITABLE_RELAY_SERVER_URL??n?.serverUrl,key:t.key??e.AITABLE_RELAY_BOOTSTRAP_KEY??n?.key,token:t.token??e.AITABLE_RELAY_TOKEN??n?.token,name:t.name??e.AITABLE_RELAY_NAME??n?.name,desc:t.desc??e.AITABLE_RELAY_DESC,workspaceRoot:t.workspaceRoot??e.AITABLE_RELAY_WORKSPACE_ROOT??n?.workspaceRoot,machineId:t.machineId??e.AITABLE_RELAY_MACHINE_ID??n?.machineId,maxTasks:t.maxTasks??e.AITABLE_RELAY_MAX_TASKS};return nt(o,e)}c(C,"resolveDaemonConfig");async function ee(t){let e=await C(t),n=new Q(e,{onPermanentReject:c(()=>{process.exit(1)},"onPermanentReject")}),o=c(()=>{n.stop(),process.exit(0)},"shutdown");return process.on("SIGINT",o),process.on("SIGTERM",o),n.start(),n}c(ee,"runRelayDaemon");import{spawn as rt}from"child_process";import{existsSync as st}from"fs";import L from"path";import{fileURLToPath as it}from"url";import{appendFileSync as Sn,statSync as En,renameSync as In,mkdirSync as $n,existsSync as Tn}from"fs";import ot from"path";var _n=10*1024*1024;function _(t){return ot.join(U(t),"daemon.log")}c(_,"logFilePath");var F=L.dirname(it(import.meta.url));function at(){let t=[L.resolve(F,"..","..","bin","aitable-workflow.mjs"),L.resolve(F,"..","bin","aitable-workflow.mjs"),L.resolve(F,"..","..","dist","cli.js"),L.resolve(F,"cli.js")];return t.find(e=>st(e))??t[0]}c(at,"resolveDaemonEntry");var ct=at();async function B(t){await le(t.agentId);let e=["relay"];t.serverUrl&&e.push("--server-url",t.serverUrl),t.token&&e.push("--token",t.token),t.key&&e.push("--key",t.key),t.name&&e.push("--name",t.name),t.workspaceRoot&&e.push("--workspace-root",t.workspaceRoot),t.machineId&&e.push("--machine-id",t.machineId),t.maxTasks&&e.push("--max-tasks",String(t.maxTasks));let n=await import("fs").then(s=>{let a=_(t.agentId);return s.openSync(a,"a")}),o=rt(process.execPath,[ct,...e],{detached:!0,stdio:["ignore",n,n],env:{...process.env}});try{let{closeSync:s}=await import("fs");s(n)}catch{}o.unref();let{writeFile:r}=await import("fs/promises"),{pidFilePath:i}=await import("./pid-file-HPTFI54H.js");return o.pid&&await r(i(t.agentId),String(o.pid),"utf8"),{pid:o.pid??0}}c(B,"startDaemon");async function W(t,e=5e3){let n=await z(t);if(n===null||!x(n))return await j(t),!1;try{process.kill(n,"SIGTERM")}catch{return await j(t),!1}let o=Date.now()+e;for(;Date.now()<o&&x(n);)await new Promise(r=>setTimeout(r,200));if(x(n))try{process.kill(n,"SIGKILL")}catch{}return await j(t),!0}c(W,"stopDaemon");async function V(t){let e=await z(t),n=e!==null&&x(e),o=null;if(n&&e!==null)try{let{readFile:r}=await import("fs/promises"),i=await r(`/proc/${e}/stat`,"utf8"),s=parseInt(i.split(" ")[21]??"0",10),a=await r("/proc/uptime","utf8"),h=parseFloat(a.split(" ")[0]??"0")-s/100;o=Math.round(h*1e3)}catch{}return{agentId:t,running:n,pid:n?e:null,uptimeMs:o,logFile:_(t)}}c(V,"getDaemonStatus");import{readFile as lt,writeFile as Yn,mkdir as Gn}from"fs/promises";import gt from"path";var he={connectionState:"disconnected",lastHeartbeatAt:null,machineId:null,serverUrl:"",runningTasks:0,queuedTasks:0,lastDisconnectReason:null,updatedAt:new Date().toISOString()};function dt(t){return gt.join(U(t),"state.json")}c(dt,"stateFilePath");async function te(t){try{let e=await lt(dt(t),"utf8");return{...he,...JSON.parse(e)}}catch{return{...he}}}c(te,"readState");import{execSync as D}from"child_process";import{writeFile as mt,rm as ft,mkdir as pt,access as ut}from"fs/promises";import v from"path";import we from"os";import{readCredentials as ht}from"aitable-workflow-core";var wt="com.alibaba.aitable-workflow.daemon",ne="\u8BF7\u5148\u5168\u5C40\u5B89\u88C5\uFF1Anpm i -g @ali/aitable-workflow";function P(t){return`${wt}.${t}`}c(P,"plistLabel");function oe(t){return v.join(we.homedir(),"Library","LaunchAgents",`${P(t)}.plist`)}c(oe,"plistPath");function E(){return process.platform==="darwin"}c(E,"isMacOS");function ye(){try{return D("which aitable-workflow",{encoding:"utf8"}).trim()||null}catch{return null}}c(ye,"resolveGlobalCliEntry");function ke(t){if(!t||!t.trim())throw new Error(`\u672A\u627E\u5230\u5168\u5C40\u5B89\u88C5\u7684 aitable-workflow CLI \u5165\u53E3\uFF0C\u65E0\u6CD5\u914D\u7F6E\u5F00\u673A\u81EA\u542F\u3002
|
|
4
|
+
\u{1F4A1} ${ne}`);if(t.includes("/_npx/")||t.includes(`${v.sep}_npx${v.sep}`))throw new Error(`\u68C0\u6D4B\u5230 CLI \u5165\u53E3\u4F4D\u4E8E npx \u4E34\u65F6\u7F13\u5B58\uFF0C\u5F00\u673A\u81EA\u542F\u4F1A\u5931\u6548\uFF1A${t}
|
|
5
|
+
\u{1F4A1} ${ne}`);if(!v.isAbsolute(t))throw new Error(`CLI \u5165\u53E3\u4E0D\u662F\u7A33\u5B9A\u7684\u5168\u5C40\u7EDD\u5BF9\u8DEF\u5F84\uFF1A${t}
|
|
6
|
+
\u{1F4A1} ${ne}`)}c(ke,"assertStableCliEntry");async function yt(t){let e={};e.AITABLE_RELAY_NAME=t;let n;try{let o=await ht();n=o.agents.find(r=>r.name===t)??o.agents.find(r=>r.name===o.activeAgent)??void 0}catch{n=void 0}return n&&(n.serverUrl&&(e.AITABLE_RELAY_SERVER_URL=n.serverUrl),n.token&&(e.AITABLE_RELAY_TOKEN=n.token),n.workspaceRoot&&(e.AITABLE_RELAY_WORKSPACE_ROOT=n.workspaceRoot)),e}c(yt,"buildRelayEnv");function kt(t,e){let n=P(t),o=e?.cliEntry??ye();ke(o);let r=["relay"];e?.maxTasks&&r.push("--max-tasks",String(e.maxTasks));let i=v.join(we.homedir(),".aitable-workflow","daemons",t),s=v.join(i,"launchd.log"),a=v.join(i,"launchd.err.log"),l=[o,...r].map(w=>` <string>${$(w)}</string>`).join(`
|
|
7
|
+
`),m={PATH:process.env.PATH??"/usr/local/bin:/usr/bin:/bin",...e?.relayEnv??{}},h=Object.entries(m).map(([w,f])=>` <key>${$(w)}</key>
|
|
8
|
+
<string>${$(f)}</string>`).join(`
|
|
9
9
|
`);return`<?xml version="1.0" encoding="UTF-8"?>
|
|
10
10
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
11
11
|
<plist version="1.0">
|
|
@@ -25,45 +25,46 @@ ${h}
|
|
|
25
25
|
<key>RunAtLoad</key>
|
|
26
26
|
<true/>
|
|
27
27
|
<key>StandardOutPath</key>
|
|
28
|
-
<string>${$(
|
|
28
|
+
<string>${$(s)}</string>
|
|
29
29
|
<key>StandardErrorPath</key>
|
|
30
|
-
<string>${$(
|
|
30
|
+
<string>${$(a)}</string>
|
|
31
31
|
</dict>
|
|
32
32
|
</plist>
|
|
33
|
-
`}c(
|
|
33
|
+
`}c(kt,"generatePlist");function $(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}c($,"escapeXml");function Y(){let t=process.getuid?.();if(t===void 0)throw new Error("\u65E0\u6CD5\u83B7\u53D6\u7528\u6237 UID\uFF08\u4EC5\u652F\u6301 macOS/Linux\uFF09");return t}c(Y,"getUid");async function Ae(t,e){if(!E())throw new Error("\u5F00\u673A\u81EA\u542F\u5F53\u524D\u4EC5\u652F\u6301 macOS");let n=ye();ke(n);let o=await yt(t),r=oe(t),i=P(t);try{D(`launchctl bootout gui/${Y()}/${i} 2>/dev/null`,{encoding:"utf8"})}catch{}await pt(v.dirname(r),{recursive:!0});let s=kt(t,{maxTasks:e?.maxTasks,relayEnv:o,cliEntry:n});await mt(r,s,"utf8"),D(`launchctl bootstrap gui/${Y()} ${r}`,{encoding:"utf8"})}c(Ae,"enableAutostart");async function Re(t){if(!E())throw new Error("\u5F00\u673A\u81EA\u542F\u5F53\u524D\u4EC5\u652F\u6301 macOS");let e=P(t),n=oe(t);try{D(`launchctl bootout gui/${Y()}/${e} 2>/dev/null`,{encoding:"utf8"})}catch{}await ft(n,{force:!0})}c(Re,"disableAutostart");async function re(t){if(!E())throw new Error("\u5F00\u673A\u81EA\u542F\u5F53\u524D\u4EC5\u652F\u6301 macOS");let e=P(t),n=oe(t),o=!1;try{await ut(n),o=!0}catch{o=!1}let r=!1,i=!1,s=null;try{let a=D(`launchctl print gui/${Y()}/${e} 2>/dev/null`,{encoding:"utf8"});r=!0;let l=a.match(/pid\s*=\s*(\d+)/);l&&(s=parseInt(l[1],10),i=!0)}catch{r=!1}return{installed:o,loaded:r,running:i,pid:s}}c(re,"getAutostartStatus");import{createRequire as At}from"module";var ve="aitable-workflow-",Rt=/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;function be(){try{return At(import.meta.url)("../package.json")}catch{return{}}}c(be,"loadCliPackageManifest");function se(t){return t.version??"0.0.0"}c(se,"resolveCliVersion");function Se(t){let e=se(t),n={};t.name?.startsWith(ve)&&(n[t.name]=e);for(let[o,r]of Object.entries(t.dependencies??{}))o.startsWith(ve)&&(n[o]=Rt.test(r)?r:e);return n}c(Se,"resolveMonorepoPackageVersions");function ie(t){return t.skipConfig||t.skipConfigIfValid&&t.configValid===!0}c(ie,"canSkipSetupConfig");function Ee(t){return ie(t)?"setup":t.uiEnabled&&t.webAvailable?"web":"cli"}c(Ee,"resolveSetupConfigRoute");import y from"path";import{fileURLToPath as Yt}from"url";import{watchFile as Gt,statSync as Kt,openSync as Ht,readSync as qt,closeSync as Jt,existsSync as $e}from"fs";import{readFile as Te}from"fs/promises";import{spawn as zt}from"child_process";var O=y.dirname(Yt(import.meta.url));xe({override:!1,quiet:!0});xe({path:y.resolve(O,"..",".env"),override:!1,quiet:!0});var Le=be(),M=se(Le),De=Se(Le),k="./aitable-workflow.config.yml";function Xt(){let n=[],o=c(()=>{let r=Date.now();for(n.push(r);n.length>0&&r-n[0]>6e4;)n.shift();n.length>=10&&(g.error("cli",`60000ms \u5185\u53D1\u751F ${n.length} \u6B21\u672A\u6355\u83B7\u5F02\u5E38\uFF0C\u7591\u4F3C\u975E\u5076\u53D1\u9519\u8BEF\uFF0C\u4E3B\u52A8\u9000\u51FA`),process.exit(1))},"recordCrashAndMaybeExit");process.on("uncaughtException",r=>{g.error("cli","\u6355\u83B7\u5230\u672A\u5904\u7406\u5F02\u5E38\uFF08\u5DF2\u5B58\u6D3B\uFF0C\u4EA4\u7531\u91CD\u8FDE/\u91CD\u8BD5\u903B\u8F91\u81EA\u6108\uFF09",{error:r instanceof Error?r.message:String(r),stack:r instanceof Error?r.stack:void 0}),o()}),process.on("unhandledRejection",r=>{g.error("cli","\u6355\u83B7\u5230\u672A\u5904\u7406 Promise \u62D2\u7EDD\uFF08\u5DF2\u5B58\u6D3B\uFF09",{error:r instanceof Error?r.message:String(r),stack:r instanceof Error?r.stack:void 0}),o()})}c(Xt,"installLongRunningCrashGuards");function Qt(t){let e=process.platform,n=e==="darwin"?"open":e==="win32"?"cmd":"xdg-open",o=e==="win32"?["/c","start","",t]:[t];try{let r=zt(n,o,{stdio:"ignore",detached:!0});r.on("error",()=>{console.log(`\u8BF7\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00: ${t}`)}),r.unref()}catch{console.log(`\u8BF7\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00: ${t}`)}}c(Qt,"openBrowser");var d=new G().name("aitable-workflow").description("\u901A\u7528\u4E1A\u52A1\u6D41\u7A0B\u5F15\u64CE \u2014 \u914D\u7F6E\u9A71\u52A8\u7684 AI \u8868\u683C\u5DE5\u4F5C\u6D41\u81EA\u52A8\u5316").version(M).option("-d, --debug","\u8F93\u51FA debug \u7EA7\u522B\u65E5\u5FD7\uFF08\u542B recipe \u5185\u90E8 log\uFF09").hook("preAction",async(t,e)=>{e.name()!=="init"&&await bt()}).hook("preAction",(t,e)=>{t.opts().debug?g.setLevel("debug"):e.name()==="config"&&g.setLevel("warn")});d.command("init").description("\u4EA4\u4E92\u5F0F\u521D\u59CB\u5316 \u2014 \u9009\u62E9 Coding Agent\u3001\u914D\u7F6E\u5DE5\u4F5C\u76EE\u5F55\u3001\u63CF\u8FF0\u4E1A\u52A1\u573A\u666F\uFF1B\u6216\u7528 --template \u4ECE\u5185\u7F6E\u6A21\u677F\u811A\u624B\u67B6").option("-c, --config <path>","\u76EE\u6807\u76EE\u5F55\u6216\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84\uFF08\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF09").option("-f, --force","\u8986\u76D6\u5DF2\u6709\u914D\u7F6E\u6587\u4EF6").option("-y, --yes","\u4F7F\u7528\u9ED8\u8BA4\u503C\u8DF3\u8FC7\u4EA4\u4E92\u5F0F\u5411\u5BFC").option("-t, --template <name>","\u4ECE\u5185\u7F6E\u6A21\u677F\u811A\u624B\u67B6(\u5F53\u524D\u63A8\u8350 support-qa),\u62F7\u8D1D\u5B8C\u6574\u53EF\u8FD0\u884C\u9879\u76EE\u5230\u76EE\u6807\u76EE\u5F55").option("--profile <name>","\u9009\u5B9A\u6A21\u677F\u4E0B\u7684 profile \u4E1A\u52A1\u5305(\u5982 support-qa \u7684 aitable),\u8986\u76D6\u5F0F\u53E0\u52A0\u5230\u5B9E\u4F8B").action(async t=>{try{await It({config:t.config,force:!!t.force,yes:!!t.yes,template:t.template,profile:t.profile,templatesDir:y.resolve(O,"..","templates"),templatePackageVersions:De,cliVersion:M})}catch(e){e instanceof Error&&e.message.includes("cancelled")&&process.exit(0),g.error("cli","\u521D\u59CB\u5316\u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});d.command("migrate").description("\u5347\u7EA7\u9879\u76EE\u76EE\u5F55\u5230\u6700\u65B0\u6A21\u677F\u7248\u672C\uFF0C\u81EA\u52A8\u4FDD\u7559\u7528\u6237\u914D\u7F6E").option("-c, --config <path>","\u9879\u76EE\u76EE\u5F55\u8DEF\u5F84",k).option("--dry-run","\u4EC5\u9884\u89C8\u53D8\u66F4\uFF0C\u4E0D\u5B9E\u9645\u5199\u5165").option("--no-backup","\u8DF3\u8FC7\u81EA\u52A8\u5907\u4EFD").option("--force","\u8DF3\u8FC7\u786E\u8BA4\u63D0\u793A").option("--template <name>","\u6307\u5B9A\u76EE\u6807\u6A21\u677F").option("--profile <name>","\u6307\u5B9A\u76EE\u6807 profile").option("--rollback","\u56DE\u6EDA\u5230\u4E0A\u4E00\u6B21\u8FC1\u79FB\u524D\u7684\u72B6\u6001").option("--list","\u5217\u51FA\u6240\u6709\u5907\u4EFD").action(async t=>{try{await Bt({config:t.config,dryRun:t.dryRun,noBackup:t.backup===!1,force:t.force,template:t.template,profile:t.profile,rollback:t.rollback,list:t.list,templatesDir:y.resolve(O,"..","templates"),templatePackageVersions:De,cliVersion:M})}catch(e){e instanceof Error&&e.message.includes("cancelled")&&process.exit(0),g.error("cli","migrate \u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});d.command("design").description("\u8BA9 Coding Agent \u6839\u636E\u573A\u666F\u63CF\u8FF0\u751F\u6210\u5B8C\u6574\u7684 workflow \u548C recipes").option("-c, --config <path>","\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84",k).option("-y, --yes","\u8DF3\u8FC7\u4EA4\u4E92\u5F0F\u8865\u5145\uFF0C\u76F4\u63A5\u4F7F\u7528 config \u4E2D\u7684 scene").option("--skip-questions","\u8DF3\u8FC7\u6F84\u6E05\u95EE\u5377\uFF08\u4ECD\u4F1A\u4EA4\u4E92\u8865\u5145\u9700\u6C42\uFF09").action(async t=>{try{await $t(t)}catch(e){e instanceof Error&&e.message.includes("cancelled")&&process.exit(0),g.error("cli","design \u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});d.command("setup").description("\u6821\u9A8C\u5E76\u8865\u9F50\u9879\u76EE\u914D\u7F6E\uFF0C\u518D\u521D\u59CB\u5316 AI \u8868\u683C\u7ED3\u6784 + \u7F16\u8BD1\u77E5\u8BC6\u5E93 \u2014 \u4E00\u6B21\u5B8C\u6210 config + \u5EFA\u8868").option("-c, --config <path>","\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84",k).option("--no-clean","\u4E0D\u6E05\u7406\u7A7A\u767D\u884C").option("--no-kb","\u8DF3\u8FC7\u77E5\u8BC6\u5E93\u7F16\u8BD1").option("--kb-full","\u5F3A\u5236\u5168\u91CF\u91CD\u7F16\u8BD1\u77E5\u8BC6\u5E93").option("--skip-config","\u8DF3\u8FC7\u524D\u7F6E\u7684\u4EA4\u4E92\u5F0F\u914D\u7F6E\u6821\u9A8C\uFF08CI/\u5DF2\u786E\u8BA4\u914D\u7F6E\u65F6\u4F7F\u7528\uFF09").option("--skip-config-if-valid","\u5148\u6821\u9A8C\u914D\u7F6E\uFF1B\u65E0\u963B\u65AD\u9879\u65F6\u8DF3\u8FC7\u914D\u7F6E\u754C\u9762\u5E76\u76F4\u63A5\u5F00\u59CB\u642D\u5EFA").option("--no-ui","\u5F3A\u5236\u4F7F\u7528\u547D\u4EE4\u884C\u4EA4\u4E92\uFF08\u8DF3\u8FC7\u53EF\u89C6\u5316\u914D\u7F6E\u754C\u9762\uFF09").action(async t=>{try{if(await _e(y.dirname(y.resolve(t.config)),M),!t.skipConfig){let e=t.ui!==!1,n=!1;if(t.skipConfigIfValid){console.log("\u{1F4CB} \u6B65\u9AA4 1/2\uFF1A\u6821\u9A8C\u9879\u76EE\u914D\u7F6E");let o=await Ie({configPath:t.config});if(n=ie({skipConfig:!1,skipConfigIfValid:!0,configValid:o.ok}),n){let r=[];o.summary.warnings>0&&r.push(`${o.summary.warnings} \u9879\u8B66\u544A`),o.summary.suggestions>0&&r.push(`${o.summary.suggestions} \u9879\u5EFA\u8BAE`);let i=r.length>0?`\uFF08${r.join("\uFF0C")}\uFF0C\u5747\u4E0D\u963B\u65AD\uFF09`:"";console.log(`\u2713 \u914D\u7F6E\u65E0\u963B\u65AD\u9879${i}\uFF0C\u8DF3\u8FC7\u914D\u7F6E\u754C\u9762\u5E76\u7EE7\u7EED\u642D\u5EFA\u3002`)}else console.log(`\u2139 \u68C0\u6D4B\u5230 ${o.summary.errors} \u9879\u963B\u65AD\uFF0C\u8FDB\u5165\u914D\u7F6E\u73AF\u8282\u3002`)}if(!n&&e){let{hasWebRoot:o,runConfigUI:r}=await import("./config-ui-server-JQMJSJHD.js");if(Ee({skipConfig:!1,skipConfigIfValid:t.skipConfigIfValid===!0,uiEnabled:e,webAvailable:o(),configValid:n})==="web"){console.log("\u{1F4CB} \u6B65\u9AA4 1/2\uFF1A\u53EF\u89C6\u5316\u914D\u7F6E\u754C\u9762");try{await r({configPath:t.config}),n=(await Ie({configPath:t.config})).ok}catch(s){console.warn(`
|
|
34
34
|
\u26A0 \u53EF\u89C6\u5316\u754C\u9762\u5F02\u5E38\uFF0C\u56DE\u9000\u5230\u547D\u4EE4\u884C\u4EA4\u4E92: ${s instanceof Error?s.message:String(s)}
|
|
35
35
|
`)}}}if(!n){console.log("\u{1F4CB} \u6B65\u9AA4 1/2\uFF1A\u6821\u9A8C\u5E76\u8865\u9F50\u9879\u76EE\u914D\u7F6E");let{runConfigCommand:o}=await import("./config-command-EPY7IOF2.js");(await o({configPath:t.config,interactive:!0,offerSkipToBuild:!0})).ok||(console.error(`
|
|
36
36
|
\u2717 \u9879\u76EE\u914D\u7F6E\u4ECD\u4E0D\u5B8C\u6574\uFF0C\u5DF2\u4E2D\u6B62 setup\u3002\u8BF7\u8865\u9F50\u5FC5\u586B\u9879\u540E\u91CD\u8BD5\uFF08\u6216\u7528 --skip-config \u8DF3\u8FC7\uFF09\u3002`),process.exit(1))}console.log(`
|
|
37
37
|
\u{1F527} \u6B65\u9AA4 2/2\uFF1A\u521D\u59CB\u5316\u8868\u7ED3\u6784 + \u7F16\u8BD1\u77E5\u8BC6\u5E93
|
|
38
|
-
`)}await
|
|
39
|
-
\u6B65\u9AA4\u94FE:`);for(let[o,r]of n.orderedStepIds.entries()){let
|
|
40
|
-
\u914D\u7F6E\u6458\u8981:`),console.log(` AI \u8868\u683C: base=${e.tracker.base_id} table=${e.tracker.table_id}`),console.log(` \u5F15\u64CE: ${e.agent.engine}`),console.log(` \u8F6E\u8BE2\u95F4\u9694: ${e.polling.interval_ms}ms`)}catch(e){console.error(`\u2717 \u9A8C\u8BC1\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}});d.command("
|
|
38
|
+
`)}await Tt(t)}catch(e){g.error("cli","setup \u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});d.command("run").description("\u542F\u52A8\u7F16\u6392\u5F15\u64CE\uFF0C\u5F00\u59CB\u8F6E\u8BE2\u5904\u7406\u5DE5\u5355").option("-c, --config <path>","\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84",k).action(async t=>{try{await _e(y.dirname(y.resolve(t.config)),M),Xt(),await Et();let e=await vt(t.config),n=!1,o=c(async()=>{n&&(g.warn("cli","\u518D\u6B21\u6536\u5230\u9000\u51FA\u4FE1\u53F7\uFF0C\u5F3A\u5236\u9000\u51FA"),process.exit(1)),n=!0,g.info("cli","\u6536\u5230\u9000\u51FA\u4FE1\u53F7\uFF0C\u6B63\u5728\u7B49\u5F85\u8FDB\u884C\u4E2D\u7684\u4EFB\u52A1\u5B8C\u6210...");try{await e.stop()}finally{process.exit(0)}},"shutdown");process.on("SIGINT",o),process.on("SIGTERM",o),await e.start()}catch(e){g.error("cli","\u542F\u52A8\u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});d.command("validate").description("\u9A8C\u8BC1\u914D\u7F6E\u6587\u4EF6\u548C\u5DE5\u4F5C\u6D41\u6A21\u677F").option("-c, --config <path>","\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84",k).action(async t=>{try{let e=await K(t.config);console.log("\u2713 \u914D\u7F6E\u6587\u4EF6\u9A8C\u8BC1\u901A\u8FC7");let n=await Ce(e.workflowTemplatePath,{envSource:e.envSource});console.log(`\u2713 \u5DE5\u4F5C\u6D41\u6A21\u677F\u9A8C\u8BC1\u901A\u8FC7: ${n.meta.name} (${n.orderedStepIds.length} \u6B65)`),console.log(`
|
|
39
|
+
\u6B65\u9AA4\u94FE:`);for(let[o,r]of n.orderedStepIds.entries()){let i=n.steps.find(a=>a.id===r),s=o<n.orderedStepIds.length-1?" \u2192":" \u2713";console.log(` ${o+1}. [${r}] ${i?.name??"?"}${s}`)}console.log(`
|
|
40
|
+
\u914D\u7F6E\u6458\u8981:`),console.log(` AI \u8868\u683C: base=${e.tracker.base_id} table=${e.tracker.table_id}`),console.log(` \u5F15\u64CE: ${e.agent.engine}`),console.log(` \u8F6E\u8BE2\u95F4\u9694: ${e.polling.interval_ms}ms`)}catch(e){console.error(`\u2717 \u9A8C\u8BC1\u5931\u8D25: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}});d.command("exec").description("\u9003\u751F\u8231\uFF1A\u5728\u5B9E\u4F8B\u4E0A\u4E0B\u6587\u4E0B\u8BD5\u8DD1\u4E00\u6761\u547D\u4EE4\uFF08\u590D\u7528 shell toolkit \u7684 bins \u89E3\u6790/\u8D85\u65F6/\u8131\u654F\uFF09\uFF0C\u7528\u4E8E dws/git \u547D\u4EE4\u9762 smoke\u3002\u7528\u6CD5\uFF1Aaitable-agent exec [-c <path>] [--json] -- <bin> <args...>").option("-c, --config <path>","\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84",k).option("--timeout <ms>","\u5355\u6B21\u6267\u884C\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09").option("--json","\u4EE5 runJson \u65B9\u5F0F\u6267\u884C\uFF1A\u8981\u6C42 exit=0 \u4E14 stdout \u4E3A\u5408\u6CD5 JSON\uFF0C\u6253\u5370\u89E3\u6790\u540E\u7684\u5BF9\u8C61").allowUnknownOption().argument("<command...>","\u8981\u6267\u884C\u7684\u547D\u4EE4\u53CA\u53C2\u6570\uFF08\u5EFA\u8BAE\u7528 -- \u5206\u9694\uFF09\uFF0C\u5982\uFF1Adws chat group create --name x --format json").action(async(t,e)=>{try{let[n,...o]=t;n||(console.error("\u2717 \u672A\u63D0\u4F9B\u8981\u6267\u884C\u7684\u547D\u4EE4\uFF1B\u7528\u6CD5\uFF1Aaitable-agent exec [-c <path>] -- <bin> <args...>"),process.exit(1));let r=await K(e.config),i=St(r,n),s=e.timeout?Number.parseInt(e.timeout,10):void 0;if(s!==void 0&&(Number.isNaN(s)||s<=0)&&(console.error("\u2717 --timeout \u5FC5\u987B\u662F\u6B63\u6574\u6570\uFF08\u6BEB\u79D2\uFF09"),process.exit(1)),e.json){let l=await i.runJson(n,o,{timeoutMs:s});console.log(JSON.stringify(l??{},null,2));return}let a=await i.run(n,o,{timeoutMs:s});a.stdout&&process.stdout.write(a.stdout),a.stderr&&process.stderr.write(a.stderr),a.truncated&&console.error(`
|
|
41
|
+
\u26A0\uFE0F \u8F93\u51FA\u56E0\u8D85\u8FC7\u4E0A\u9650\u88AB\u622A\u65AD`),process.exit(a.exitCode??0)}catch(n){console.error(`\u2717 exec \u5931\u8D25: ${n instanceof Error?n.message:String(n)}`),process.exit(1)}});d.command("config").description("\u6821\u9A8C\u9879\u76EE\u914D\u7F6E\u5B8C\u6574\u6027\uFF0C\u5E76\u53EF\u4EA4\u4E92\u5F0F\u4FEE\u6539\u53C2\u6570").option("-c, --config <path>","\u9879\u76EE\u76EE\u5F55\u6216\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84",k).option("-w, --workflow <id>","\u591A\u5DE5\u4F5C\u6D41\u6A21\u5F0F\uFF1A\u6307\u5B9A\u6821\u9A8C\u7684\u5DE5\u4F5C\u6D41 ID\uFF08\u7F3A\u7701\u6821\u9A8C\u5168\u90E8\uFF09").option("--json","JSON \u683C\u5F0F\u8F93\u51FA\u6821\u9A8C\u7ED3\u679C").option("--no-interactive","\u4EC5\u6821\u9A8C\uFF0C\u8DF3\u8FC7\u4EA4\u4E92\u5F0F\u4FEE\u6539").action(async t=>{try{let{runConfigCommand:e}=await import("./config-command-EPY7IOF2.js");await e({configPath:t.config,workflowId:t.workflow,json:t.json,interactive:t.interactive})}catch(e){e instanceof Error&&e.message.includes("cancelled")&&process.exit(0),g.error("cli","config \u547D\u4EE4\u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});d.command("sync").description("\u68C0\u6D4B\u914D\u7F6E\u4E0E\u8868\u7684\u504F\u5DEE\uFF08\u5B57\u6BB5\u7C7B\u578B/\u9009\u9879 drift\uFF09\uFF0C\u8F93\u51FA\u8BCA\u65AD\u62A5\u544A\u548C\u4FEE\u590D\u5EFA\u8BAE").option("-c, --config <path>","\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84",k).option("-y, --yes","\u975E\u4EA4\u4E92\u6A21\u5F0F").action(async t=>{try{await Dt({configPath:t.config,yes:t.yes})}catch(e){g.error("cli","sync \u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});d.command("agents").description("\u5217\u51FA\u5168\u90E8\u652F\u6301\u7684 Coding Agent \u9002\u914D\u5668\uFF0C\u5E76\u6807\u6CE8\u672C\u673A\u5DF2\u63A2\u6D4B\u5230\u5B89\u88C5\u7684").option("--json","\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action(t=>{try{let e=Ot();if(t.json){process.stdout.write(JSON.stringify(e,null,2)+`
|
|
41
42
|
`);return}let n=e.filter(o=>o.installed).length;console.log(`
|
|
42
43
|
\u652F\u6301 ${e.length} \u4E2A Coding Agent\uFF0C\u672C\u673A\u5DF2\u63A2\u6D4B\u5230 ${n} \u4E2A\uFF1A
|
|
43
|
-
`);for(let o of e){let r=o.installed?"\u2713":"\u2717",
|
|
44
|
-
Agent: ${n.agentId}`),console.log(`Running: ${n.running?"\u2705 yes":"\u274C no"}`),n.pid&&console.log(`PID: ${n.pid}`),n.uptimeMs!==null){let r=Math.floor(n.uptimeMs/1e3),
|
|
45
|
-
--- Configuration ---`),console.log(`Server: ${o.serverUrl}`),console.log(`Token: ${
|
|
46
|
-
`);console.log(a.slice(-o).join(`
|
|
47
|
-
`))}let s=r.length;Ut(n,{interval:500},()=>{try{let a=jt(n);if(a.size>s){let i=Ft(n,"r"),l=Buffer.alloc(a.size-s);Bt(i,l,0,l.length,s),Wt(i),process.stdout.write(l.toString("utf8")),s=a.size}}catch{}})}else{let r=await Se(n,"utf8").catch(()=>"");if(!r){console.log(`\u6682\u65E0\u65E5\u5FD7 (${n})`);return}let s=r.trimEnd().split(`
|
|
44
|
+
`);for(let o of e){let r=o.installed?"\u2713":"\u2717",i=[o.bin,...o.fallbackBins].join(" / "),s=o.installed?`\u5DF2\u5B89\u88C5 (${o.detectedCmd}${o.detectedVersion?` ${o.detectedVersion}`:""})`:"\u672A\u63A2\u6D4B\u5230";console.log(` ${r} ${o.name.padEnd(16)} [${o.id}] bin: ${i.padEnd(24)} ${s}`)}console.log("")}catch(e){g.error("cli","\u5217\u51FA agents \u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});d.command("serve").description("\u542F\u52A8\u672C\u5730 HTTP API \u670D\u52A1\u5E76\u6258\u7BA1 Web \u524D\u7AEF\u9875\u9762\uFF08local \u6A21\u5F0F\u81EA\u52A8\u6253\u5F00\u6D4F\u89C8\u5668\uFF09").option("-p, --port <number>","\u76D1\u542C\u7AEF\u53E3\uFF08\u9ED8\u8BA4 8787\uFF0C\u53EF\u7528 AITABLE_WORKFLOW_PORT \u8986\u76D6\uFF09").option("--host <host>","\u76D1\u542C\u5730\u5740\uFF08\u9ED8\u8BA4 127.0.0.1\uFF0C\u53EF\u7528 AITABLE_WORKFLOW_HOST \u8986\u76D6\uFF09").option("--data-root <path>","\u9879\u76EE\u5DE5\u4F5C\u533A\u6570\u636E\u76EE\u5F55\uFF08\u9ED8\u8BA4 .sandbox/aitable-workflow-server\uFF0C\u53EF\u7528 AITABLE_WORKFLOW_DATA_DIR \u8986\u76D6\uFF09").option("--web-root <path>","Web \u524D\u7AEF\u9759\u6001\u8D44\u6E90\u76EE\u5F55\uFF08\u9ED8\u8BA4\u4F7F\u7528\u968F CLI \u6253\u5305\u7684 dist/web\uFF09").option("--no-open","\u542F\u52A8\u540E\u4E0D\u81EA\u52A8\u6253\u5F00\u6D4F\u89C8\u5668\uFF08\u9ED8\u8BA4\u81EA\u52A8\u6253\u5F00\uFF09").action(async t=>{try{let e={};if(t.port!==void 0){let o=Number.parseInt(t.port,10);Number.isFinite(o)&&o>0&&(e.port=o)}if(t.host!==void 0&&(e.host=t.host),t.dataRoot!==void 0&&(e.dataRoot=t.dataRoot),t.webRoot!==void 0)e.webRoot=t.webRoot;else{let o=y.join(O,"web");if($e(y.join(o,"index.html")))e.webRoot=o;else{let r=y.resolve(O,"..","..","web","dist");$e(y.join(r,"index.html"))&&(e.webRoot=r)}}let n=Vt(e);if(await Wt(n),n.webRoot&&t.open!==!1){let o=n.host==="0.0.0.0"?"127.0.0.1":n.host;Qt(`http://${o}:${n.port}`)}}catch(e){g.error("cli","HTTP \u670D\u52A1\u542F\u52A8\u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});d.command("start").description("\u4EE5\u540E\u53F0\u8FDB\u7A0B\u542F\u52A8 relay daemon").option("--agent <name>","Agent \u540D\u79F0\uFF08\u9ED8\u8BA4\u4F7F\u7528 activeAgent\uFF09").option("--server-url <url>","Gateway \u5730\u5740").option("--token <ownerToken>","owner-token").option("--key <machineKey>","per-machine key\uFF08sk_machine_*\uFF09").option("--workspace-root <dir>","\u5DE5\u4F5C\u533A\u6839\u76EE\u5F55").option("--max-tasks <n>","\u5E76\u53D1\u4E0A\u9650","1").action(async t=>{try{let e=t.agent;if(!e){let r=await A();if(!r)throw new Error("\u672A\u6307\u5B9A --agent \u4E14\u65E0\u6D3B\u8DC3 Agent\u3002\u4F7F\u7528 `aitable-workflow connect` \u6CE8\u518C\u6216 `--agent <name>` \u6307\u5B9A\u3002");e=r.name}let n=await C({serverUrl:t.serverUrl,token:t.token,key:t.key,workspaceRoot:t.workspaceRoot,name:e,maxTasks:t.maxTasks}),o=await B({agentId:e,serverUrl:n.serverUrl,token:n.token,key:n.key,name:n.name??e,workspaceRoot:n.workspaceRoot,machineId:n.machineId,maxTasks:n.maxTasks});console.log("\u2705 Daemon \u5DF2\u542F\u52A8"),console.log(` Agent: ${e}`),console.log(` PID: ${o.pid}`),console.log(` Log: ~/.aitable-workflow/daemons/${e}/daemon.log`)}catch(e){g.error("cli","start \u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});d.command("stop").description("\u505C\u6B62\u540E\u53F0 daemon").option("--agent <name>","Agent \u540D\u79F0").option("--all","\u505C\u6B62\u6240\u6709 daemon").action(async t=>{try{if(t.all){let e=await ae(),n=0;for(let o of e)await W(o.name)&&n++;console.log(`\u2705 \u5DF2\u505C\u6B62 ${n} \u4E2A daemon`)}else{let e=t.agent;if(!e){let o=await A();if(!o)throw new Error("\u672A\u6307\u5B9A --agent \u4E14\u65E0\u6D3B\u8DC3 Agent");e=o.name}let n=await W(e);console.log(n?`\u2705 Daemon "${e}" \u5DF2\u505C\u6B62`:`\u26A0\uFE0F Daemon "${e}" \u672A\u5728\u8FD0\u884C`)}}catch(e){g.error("cli","stop \u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});d.command("restart").description("\u91CD\u542F\u540E\u53F0 daemon").option("--agent <name>","Agent \u540D\u79F0").option("--max-tasks <n>","\u5E76\u53D1\u4E0A\u9650","1").action(async t=>{try{let e=t.agent;if(!e){let r=await A();if(!r)throw new Error("\u672A\u6307\u5B9A --agent \u4E14\u65E0\u6D3B\u8DC3 Agent");e=r.name}console.log(`\u{1F504} \u6B63\u5728\u91CD\u542F "${e}"...`),await W(e);let n=await C({name:e,maxTasks:t.maxTasks}),o=await B({agentId:e,serverUrl:n.serverUrl,token:n.token,key:n.key,name:n.name??e,workspaceRoot:n.workspaceRoot,machineId:n.machineId,maxTasks:n.maxTasks});console.log(`\u2705 Daemon \u5DF2\u91CD\u542F (PID: ${o.pid})`)}catch(e){g.error("cli","restart \u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});d.command("status").description("\u67E5\u770B daemon \u72B6\u6001").option("--agent <name>","Agent \u540D\u79F0").option("-v, --verbose","\u5C55\u793A\u5B8C\u6574\u914D\u7F6E").action(async t=>{try{let e=t.agent;if(!e){let r=await A();if(!r){console.log("\u65E0\u6D3B\u8DC3 Agent");return}e=r.name}let n=await V(e),o=await A();if(console.log(`
|
|
45
|
+
Agent: ${n.agentId}`),console.log(`Running: ${n.running?"\u2705 yes":"\u274C no"}`),n.pid&&console.log(`PID: ${n.pid}`),n.uptimeMs!==null){let r=Math.floor(n.uptimeMs/1e3),i=Math.floor(r/60),s=Math.floor(i/60);console.log(`Uptime: ${s}h ${i%60}m ${r%60}s`)}console.log(`Log file: ${n.logFile}`),t.verbose&&o&&(console.log(`
|
|
46
|
+
--- Configuration ---`),console.log(`Server: ${o.serverUrl}`),console.log(`Token: ${H(o.token)}`),o.machineId&&console.log(`MachineID: ${o.machineId}`),o.workspaceRoot&&console.log(`Workspace: ${o.workspaceRoot}`)),console.log("")}catch(e){g.error("cli","status \u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});d.command("logs").description("\u67E5\u770B daemon \u65E5\u5FD7").option("--agent <name>","Agent \u540D\u79F0").option("-f, --follow","\u6301\u7EED\u8DDF\u8E2A\u65E5\u5FD7\u8F93\u51FA").option("-n <lines>","\u663E\u793A\u6700\u8FD1 N \u884C","50").action(async t=>{try{let e=t.agent;if(!e){let r=await A();if(!r)throw new Error("\u672A\u6307\u5B9A --agent \u4E14\u65E0\u6D3B\u8DC3 Agent");e=r.name}let n=_(e),o=parseInt(t.n,10)||50;if(t.follow){let r=await Te(n,"utf8").catch(()=>"");if(r){let s=r.trimEnd().split(`
|
|
48
47
|
`);console.log(s.slice(-o).join(`
|
|
49
|
-
`))}
|
|
50
|
-
|
|
48
|
+
`))}let i=r.length;Gt(n,{interval:500},()=>{try{let s=Kt(n);if(s.size>i){let a=Ht(n,"r"),l=Buffer.alloc(s.size-i);qt(a,l,0,l.length,i),Jt(a),process.stdout.write(l.toString("utf8")),i=s.size}}catch{}})}else{let r=await Te(n,"utf8").catch(()=>"");if(!r){console.log(`\u6682\u65E0\u65E5\u5FD7 (${n})`);return}let i=r.trimEnd().split(`
|
|
49
|
+
`);console.log(i.slice(-o).join(`
|
|
50
|
+
`))}}catch(e){g.error("cli","logs \u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});var Zt=d.command("daemon").description("Daemon \u540E\u53F0\u5B88\u62A4\u7BA1\u7406");Zt.command("autostart").description("macOS \u5F00\u673A\u81EA\u542F\u7BA1\u7406").addCommand(new G("enable").description("\u542F\u7528 macOS \u767B\u5F55\u81EA\u542F\u52A8").requiredOption("--agent-id <id>","Agent ID / \u540D\u79F0").option("--max-tasks <n>","\u5E76\u53D1\u4E0A\u9650").action(async t=>{try{E()||(console.error("\u274C \u5F00\u673A\u81EA\u542F\u5F53\u524D\u4EC5\u652F\u6301 macOS"),process.exit(1)),await Ae(t.agentId,{maxTasks:t.maxTasks?parseInt(t.maxTasks,10):void 0}),console.log("\u2705 \u81EA\u542F\u52A8\u5DF2\u542F\u7528"),console.log(` Agent: ${t.agentId}`),console.log(` Plist: ~/Library/LaunchAgents/com.alibaba.aitable-workflow.daemon.${t.agentId}.plist`)}catch(e){g.error("cli","autostart enable \u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}})).addCommand(new G("disable").description("\u5173\u95ED macOS \u767B\u5F55\u81EA\u542F\u52A8").requiredOption("--agent-id <id>","Agent ID / \u540D\u79F0").action(async t=>{try{E()||(console.error("\u274C \u5F00\u673A\u81EA\u542F\u5F53\u524D\u4EC5\u652F\u6301 macOS"),process.exit(1)),await Re(t.agentId),console.log(`\u2705 \u81EA\u542F\u52A8\u5DF2\u5173\u95ED (${t.agentId})`)}catch(e){g.error("cli","autostart disable \u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}})).addCommand(new G("status").description("\u67E5\u770B macOS \u81EA\u542F\u52A8\u72B6\u6001").option("--agent-id <id>","Agent ID / \u540D\u79F0\uFF08\u4E0D\u6307\u5B9A\u5219\u5217\u51FA\u6240\u6709\uFF09").action(async t=>{try{if(E()||(console.error("\u274C \u5F00\u673A\u81EA\u542F\u5F53\u524D\u4EC5\u652F\u6301 macOS"),process.exit(1)),t.agentId){let e=await re(t.agentId);console.log(`
|
|
51
|
+
Agent: ${t.agentId}`),console.log(`Installed: ${e.installed?"yes":"no"}`),console.log(`Loaded: ${e.loaded?"yes":"no"}`),console.log(`Running: ${e.running?`yes (PID: ${e.pid})`:"no"}`),console.log("")}else{let e=await ae();if(e.length===0){console.log("\u65E0\u5DF2\u6CE8\u518C\u7684 Agent");return}for(let n of e){let o=await re(n.name),r=o.running?`running (PID: ${o.pid})`:o.loaded?"loaded":o.installed?"installed":"not installed";console.log(` ${n.name}: ${r}`)}}}catch(e){g.error("cli","autostart status \u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}}));d.command("connect").description("\u4E00\u952E\u6CE8\u518C + \u4FDD\u5B58\u51ED\u8BC1 + \u540E\u53F0\u542F\u52A8 daemon\uFF08\u5BF9\u6807 Harness connect\uFF09").requiredOption("--server-url <url>","Gateway / \u5E73\u53F0\u5730\u5740").option("--token <ownerToken>","owner-token\uFF08\u81EA\u6CE8\u518C\uFF0C\u4E0E --key \u4E8C\u9009\u4E00\uFF09").option("--key <machineKey>","per-machine key\uFF08sk_machine_*\uFF0C\u4E0E --token \u4E8C\u9009\u4E00\uFF09").requiredOption("--name <name>","Agent \u540D\u79F0").option("--workspace-root <dir>","\u5DE5\u4F5C\u533A\u6839\u76EE\u5F55\uFF08\u53EF\u9009\uFF09").option("--no-start","\u53EA\u6CE8\u518C\u4E0D\u542F\u52A8 daemon").option("--foreground","\u6CE8\u518C\u540E\u5728\u524D\u53F0\u8FD0\u884C relay\uFF08\u963B\u585E\u7EC8\u7AEF\uFF0C\u4FBF\u4E8E\u8C03\u8BD5\uFF09").option("--force","\u8986\u76D6\u540C\u540D\u4F46\u7F51\u5173\u4E0D\u540C\u7684\u5DF2\u6709\u51ED\u8BC1").action(async t=>{try{if(!t.token&&!t.key)throw new Error("\u5FC5\u987B\u63D0\u4F9B --token\uFF08owner-token\uFF09\u6216 --key\uFF08per-machine key\uFF0Csk_machine_*\uFF09\u5176\u4E2D\u4E4B\u4E00\u3002");if(!t.force){let s=await Ft(t.name,t.serverUrl);s&&(console.error(`\u26A0\uFE0F \u62D2\u7EDD\u8986\u76D6\uFF1AAgent \u540D\u79F0 "${t.name}" \u5DF2\u7ED1\u5B9A\u5230\u53E6\u4E00\u4E2A\u7F51\u5173\u3002`),console.error(` \u65E7\u7F51\u5173: ${s.serverUrl}`),console.error(` \u65B0\u7F51\u5173: ${t.serverUrl}`),console.error(" \u7EE7\u7EED\u5C06\u8986\u76D6\u672C\u673A\u8BE5\u51ED\u8BC1\uFF0C\u4E14\u65E7\u7F51\u5173\u7684 daemon \u65E0\u6CD5\u4E0E\u65B0\u7684\u5171\u5B58\uFF08PID \u4E92\u65A5\uFF09\u3002"),console.error(" \u8BF7\u9009\u62E9\u5176\u4E00\uFF1A"),console.error(" 1) \u6362\u4E00\u4E2A --name\uFF08\u591A\u7F51\u5173\u5EFA\u8BAE\u6BCF\u4E2A\u7F51\u5173\u7528\u4E0D\u540C\u540D\u79F0\uFF09"),console.error(" 2) \u786E\u5B9E\u8981\u6539\u7ED1\uFF0C\u52A0 --force \u5F3A\u5236\u8986\u76D6"),process.exit(1))}let e=t.workspaceRoot?Z(t.workspaceRoot,t.name):void 0,n={name:t.name,serverUrl:t.serverUrl,token:t.token??"",key:t.key,workspaceRoot:e,createdAt:new Date().toISOString()};if(await Ut(n),console.log(`\u2705 Agent "${t.name}" \u5DF2\u6CE8\u518C\u5E76\u4FDD\u5B58\u51ED\u8BC1`),console.log(` Server: ${t.serverUrl}`),console.log(` ${t.token?"Token":"Key"}: ${H(t.token??t.key??"")}`),e&&console.log(` Workspace: ${e}`),console.log(" \u51ED\u8BC1\u6587\u4EF6: ~/.aitable-workflow/credentials.json"),!t.start){t.foreground&&console.log(`
|
|
51
52
|
\u{1F4A1} --no-start \u4E0E --foreground \u540C\u65F6\u6307\u5B9A\uFF0C\u6309 --no-start \u4F18\u5148\uFF1A\u4EC5\u6CE8\u518C\u4E0D\u542F\u52A8\u3002`),console.log(`
|
|
52
53
|
\u{1F4A1} \u542F\u52A8 daemon: aitable-workflow start \u6216 aitable-workflow relay`);return}if(t.foreground){console.log(`
|
|
53
|
-
\u{1F680} \u524D\u53F0\u542F\u52A8 relay\uFF08Ctrl-C \u9000\u51FA\uFF09...`),await
|
|
54
|
-
\u{1F680} Daemon \u5DF2\u540E\u53F0\u542F\u52A8`),console.log(` Agent: ${o}`),console.log(` PID: ${
|
|
55
|
-
\u{1F4CB} \u5E38\u7528\u547D\u4EE4\uFF1A`),console.log(` \u67E5\u770B\u72B6\u6001: aitable-workflow status --agent ${o}`),console.log(` \u8DDF\u8E2A\u65E5\u5FD7: aitable-workflow logs -f --agent ${o}`),console.log(` \u505C\u6B62: aitable-workflow stop --agent ${o}`),console.log(` \u91CD\u542F: aitable-workflow restart --agent ${o}`),console.log(" \u5217\u51FA Agent: aitable-workflow agent list"),console.log(" \u5207\u6362 Agent: aitable-workflow agent use <name>"),console.log(` \u5F00\u673A\u81EA\u542F: aitable-workflow daemon autostart enable --agent-id ${o}`)}catch(e){g.error("cli","connect \u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});var
|
|
54
|
+
\u{1F680} \u524D\u53F0\u542F\u52A8 relay\uFF08Ctrl-C \u9000\u51FA\uFF09...`),await ee({serverUrl:t.serverUrl,token:t.token,key:t.key,name:t.name,workspaceRoot:e});return}let o=t.name,r=await C({serverUrl:t.serverUrl,token:t.token,key:t.key,workspaceRoot:t.workspaceRoot,name:o}),i=await B({agentId:o,serverUrl:r.serverUrl,token:r.token,key:r.key,name:r.name??o,workspaceRoot:r.workspaceRoot,machineId:r.machineId,maxTasks:r.maxTasks});console.log(`
|
|
55
|
+
\u{1F680} Daemon \u5DF2\u540E\u53F0\u542F\u52A8`),console.log(` Agent: ${o}`),console.log(` PID: ${i.pid}`),console.log(` Log: ~/.aitable-workflow/daemons/${o}/daemon.log`),console.log(`
|
|
56
|
+
\u{1F4CB} \u5E38\u7528\u547D\u4EE4\uFF1A`),console.log(` \u67E5\u770B\u72B6\u6001: aitable-workflow status --agent ${o}`),console.log(` \u8DDF\u8E2A\u65E5\u5FD7: aitable-workflow logs -f --agent ${o}`),console.log(` \u505C\u6B62: aitable-workflow stop --agent ${o}`),console.log(` \u91CD\u542F: aitable-workflow restart --agent ${o}`),console.log(" \u5217\u51FA Agent: aitable-workflow agent list"),console.log(" \u5207\u6362 Agent: aitable-workflow agent use <name>"),console.log(` \u5F00\u673A\u81EA\u542F: aitable-workflow daemon autostart enable --agent-id ${o}`)}catch(e){g.error("cli","connect \u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});var q=d.command("agent").description("Agent \u51ED\u8BC1\u7BA1\u7406");q.command("list").description("\u5217\u51FA\u6240\u6709\u5DF2\u4FDD\u5B58\u7684 Agent \u51ED\u8BC1").action(async()=>{try{let t=await ae();if(t.length===0){console.log("\u6682\u65E0\u5DF2\u4FDD\u5B58\u7684 Agent\u3002\u4F7F\u7528 `aitable-workflow connect` \u6CE8\u518C\u3002");return}let e=await Nt();console.log(`
|
|
56
57
|
\u5DF2\u4FDD\u5B58 ${t.length} \u4E2A Agent\uFF08\u6D3B\u8DC3: ${e.activeAgent??"\u65E0"}\uFF09:
|
|
57
|
-
`);for(let n of t){let o=n.name===e.activeAgent?" \u2190 active":"";console.log(` \u2022 ${n.name}${o}`),console.log(` Server: ${n.serverUrl}`),console.log(` Token: ${
|
|
58
|
-
`);return}let
|
|
59
|
-
${
|
|
60
|
-
\u{1F4C4} \u6700\u8FD1 ${
|
|
61
|
-
`)}else{if(
|
|
62
|
-
\u{1F4C4} \u5DE5\u5355 ${t} \u7684\u6267\u884C\u5386\u53F2 (${
|
|
63
|
-
`)}for(let
|
|
58
|
+
`);for(let n of t){let o=n.name===e.activeAgent?" \u2190 active":"";console.log(` \u2022 ${n.name}${o}`),console.log(` Server: ${n.serverUrl}`),console.log(` Token: ${H(n.token)}`),n.machineId&&console.log(` MachineID: ${n.machineId}`),n.workspaceRoot&&console.log(` Workspace: ${n.workspaceRoot}`),console.log(` Created: ${n.createdAt}`)}console.log("")}catch(t){g.error("cli","agent list \u5931\u8D25",{error:t instanceof Error?t.message:String(t)}),process.exit(1)}});q.command("use <name>").description("\u5207\u6362\u6D3B\u8DC3 Agent").action(async t=>{try{let e=await jt(t);console.log(`\u2705 \u5DF2\u5207\u6362\u5230 Agent "${e.activeAgent}"`)}catch(e){g.error("cli","agent use \u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});q.command("whoami").description("\u663E\u793A\u5F53\u524D\u6D3B\u8DC3 Agent").action(async()=>{try{let t=await A();if(!t){console.log("\u5F53\u524D\u65E0\u6D3B\u8DC3 Agent\u3002\u4F7F\u7528 `aitable-workflow connect` \u6CE8\u518C\u6216 `aitable-workflow agent use <name>` \u5207\u6362\u3002");return}console.log(`Agent: ${t.name}`),console.log(`Server: ${t.serverUrl}`),console.log(`Token: ${H(t.token)}`),t.machineId&&console.log(`MachineID: ${t.machineId}`),t.workspaceRoot&&console.log(`Workspace: ${t.workspaceRoot}`)}catch(t){g.error("cli","agent whoami \u5931\u8D25",{error:t instanceof Error?t.message:String(t)}),process.exit(1)}});q.command("status").description("\u67E5\u770B Agent \u8FDE\u63A5\u72B6\u6001\u4E0E\u8FD0\u884C\u65F6\u4FE1\u606F").option("--json","JSON \u683C\u5F0F\u8F93\u51FA").action(async t=>{try{let e=await A();if(!e){console.log("\u65E0\u6D3B\u8DC3 Agent\u3002\u4F7F\u7528 `aitable-workflow connect` \u6CE8\u518C\u3002");return}let n=await V(e.name),o=await te(e.name),r={agent:e.name,serverUrl:e.serverUrl,connectionState:n.running?o.connectionState:"stopped",machineId:o.machineId??e.machineId??null,lastHeartbeatAt:o.lastHeartbeatAt,runningTasks:o.runningTasks,queuedTasks:o.queuedTasks,lastDisconnectReason:o.lastDisconnectReason,pid:n.pid};if(t.json){process.stdout.write(JSON.stringify(r,null,2)+`
|
|
59
|
+
`);return}let i=r.connectionState==="connected"?"\u{1F7E2}":r.connectionState==="reconnecting"?"\u{1F7E1}":"\u{1F534}";console.log(`
|
|
60
|
+
${i} Agent: ${r.agent}`),console.log(` Server: ${r.serverUrl}`),console.log(` Connection: ${r.connectionState}`),r.machineId&&console.log(` MachineID: ${r.machineId}`),r.lastHeartbeatAt&&console.log(` Last HB: ${r.lastHeartbeatAt}`),n.running&&(console.log(` PID: ${r.pid}`),console.log(` Tasks: ${r.runningTasks} running, ${r.queuedTasks} queued`)),r.lastDisconnectReason&&console.log(` Last Error: ${r.lastDisconnectReason}`),console.log("")}catch(e){g.error("cli","agent status \u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});d.command("heartbeat").description("\u5411\u670D\u52A1\u7AEF\u53D1\u9001\u5FC3\u8DF3\u5E76\u8FD4\u56DE\u4EFB\u52A1\u5206\u914D\u60C5\u51B5").action(async()=>{try{let t=await A();if(!t)throw new Error("\u65E0\u6D3B\u8DC3 Agent");if(!(await V(t.name)).running){console.log("\u26A0\uFE0F Daemon \u672A\u5728\u8FD0\u884C\u3002\u4F7F\u7528 `aitable-workflow start` \u542F\u52A8\u3002");return}let n=await te(t.name);console.log("\u2705 Heartbeat OK"),console.log(` Agent: ${t.name}`),console.log(` Connection: ${n.connectionState}`),console.log(` Running: ${n.runningTasks} tasks`),console.log(` Queued: ${n.queuedTasks} tasks`),n.lastHeartbeatAt&&console.log(` Last HB: ${n.lastHeartbeatAt}`)}catch(t){g.error("cli","heartbeat \u5931\u8D25",{error:t instanceof Error?t.message:String(t)}),process.exit(1)}});d.command("relay").description("\u4EE5\u672C\u673A relay \u5B88\u62A4\u8EAB\u4EFD\u53CD\u8FDE RelayGateway\uFF0C\u6309\u9700\u5728\u672C\u673A\u6267\u884C Agent/dws \u4EFB\u52A1").option("--server-url <url>","Gateway \u5730\u5740\uFF08\u4E5F\u53EF\u7528 AITABLE_RELAY_SERVER_URL\uFF09").option("--key <machineKey>","per-machine key\uFF08sk_machine_*\uFF09").option("--token <ownerToken>","owner-token \u81EA\u6CE8\u518C\uFF08\u4E5F\u53EF\u7528 AITABLE_RELAY_TOKEN\uFF1B\u4F18\u5148\u4E8E --key\uFF09").option("--name <name>","\u81EA\u6CE8\u518C\u673A\u5668\u540D\uFF08\u53EF\u9009\uFF0C\u7F3A\u7701\u81EA\u52A8\u4F7F\u7528\u4E3B\u673A\u540D\uFF1Bowner+name \u5E42\u7B49\u952E\uFF0C\u591A\u673A\u573A\u666F\u5EFA\u8BAE\u663E\u5F0F\u6307\u5B9A\u4EE5\u907F\u514D hostname \u51B2\u7A81\u4E92\u76F8\u9876\u8FDE\u63A5\uFF1B\u4E5F\u53EF\u7528 AITABLE_RELAY_NAME\uFF09").option("--desc <description>","\u81EA\u6CE8\u518C\u673A\u5668\u63CF\u8FF0\uFF08\u4E5F\u53EF\u7528 AITABLE_RELAY_DESC\uFF09").option("--workspace-root <dir>","\u5DE5\u4F5C\u533A\u6839\u76EE\u5F55\uFF08\u4E5F\u53EF\u7528 AITABLE_RELAY_WORKSPACE_ROOT\uFF09").option("--machine-id <id>","\u7A33\u5B9A\u673A\u5668\u8EAB\u4EFD\uFF08\u4E5F\u53EF\u7528 AITABLE_RELAY_MACHINE_ID\uFF09").option("--max-tasks <n>","\u672C\u673A\u5E76\u53D1\u6267\u884C\u4E0A\u9650\uFF0C\u9ED8\u8BA4 1\uFF08\u4E5F\u53EF\u7528 AITABLE_RELAY_MAX_TASKS\uFF09").action(t=>{try{ee(t)}catch(e){g.error("cli","relay \u542F\u52A8\u5931\u8D25",{error:e instanceof Error?e.message:String(e)}),process.exit(1)}});d.command("inspect [recordId]").description("\u67E5\u770B\u5DE5\u5355\u6267\u884C trace\uFF1B\u6216\u7528 --recipes / --validate \u67E5\u770B\u6821\u9A8C recipe schema \u4E0E workflow \u5BF9\u9F50").option("-c, --config <path>","\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84",k).option("-s, --step <stepId>","\u67E5\u770B\u7279\u5B9A\u6B65\u9AA4").option("-l, --latest","\u67E5\u770B\u6700\u8FD1\u6267\u884C").option("-n, --limit <number>","\u6700\u591A\u663E\u793A\u6761\u6570","10").option("--recipes","\u5217\u51FA\u5404\u6B65\u9AA4 recipe \u7684 input / output / config schema\u4E0E step \u914D\u7F6E").option("--validate","\u6821\u9A8C step.config / input_mapping / output_mapping \u662F\u5426\u7B26\u5408 recipe schema").option("-w, --workflow <id>","\u591A\u5DE5\u4F5C\u6D41\u6A21\u5F0F\uFF1A\u6307\u5B9A\u5DE5\u4F5C\u6D41 ID\uFF08\u7F3A\u7701\u53D6\u7B2C\u4E00\u4E2A\uFF09").action(async(t,e)=>{try{if(e.recipes||e.validate){await en(e);return}let n=await K(e.config),o=y.resolve(n.configDir,n.agent.trace_dir),r=new Mt(o),i=parseInt(e.limit,10)||10,s;if(e.latest||!t){if(s=await r.getLatestTraces(i),s.length===0){console.log("\u26A0\uFE0F \u6682\u65E0 trace \u8BB0\u5F55\u3002\u8FD0\u884C `run` \u547D\u4EE4\u540E\u518D\u67E5\u770B\u3002");return}console.log(`
|
|
61
|
+
\u{1F4C4} \u6700\u8FD1 ${s.length} \u6761\u6267\u884C\u8BB0\u5F55:
|
|
62
|
+
`)}else{if(s=await r.getTraces(t),s.length===0){console.log(`\u26A0\uFE0F \u672A\u627E\u5230\u5DE5\u5355 ${t} \u7684 trace \u8BB0\u5F55\u3002`);return}e.step&&(s=s.filter(a=>a.step_id===e.step)),console.log(`
|
|
63
|
+
\u{1F4C4} \u5DE5\u5355 ${t} \u7684\u6267\u884C\u5386\u53F2 (${s.length} \u6B65):
|
|
64
|
+
`)}for(let a of s){let l=a.status==="success"?"\u2705":a.status==="failed"?"\u274C":"\u23F8\uFE0F",m=(a.duration_ms/1e3).toFixed(1);console.log(`${l} [${a.step_id}] ${a.step_name} \u2014 ${m}s \u2014 ${a.status.toUpperCase()}`);for(let[h,w]of a.llm_calls.entries()){let f=h===a.llm_calls.length-1,T=f?" \u2514\u2500":" \u251C\u2500",ce=f?" ":" \u2502 ",Pe=(w.duration_ms/1e3).toFixed(1);console.log(`${T} LLM: ${w.model} (${Pe}s)`),console.log(`${ce}Input: ${w.prompt_summary.slice(0,80)}${w.prompt_summary.length>80?"...":""}`),console.log(`${ce}Output: ${w.response_summary.slice(0,100)}${w.response_summary.length>100?"...":""}`)}if(Object.keys(a.outputs).length>0){let h=Object.entries(a.outputs).map(([w,f])=>`${w}=${typeof f=="string"?f.slice(0,50):JSON.stringify(f)}`).join(", ");console.log(` \u2514\u2500 Outputs: ${h}`)}a.error&&console.log(` \u2514\u2500 Error: ${a.error}`),console.log()}}catch(n){console.error(`\u2717 inspect \u5931\u8D25: ${n instanceof Error?n.message:String(n)}`),process.exit(1)}});var J=d.command("kb").description("\u77E5\u8BC6\u5E93(LLM-Wiki)\u7F16\u8BD1\u4E0E\u72B6\u6001\u7BA1\u7406");J.command("compile [collections...]").description("\u7F16\u8BD1\u77E5\u8BC6\u5E93\u4E3A wiki\uFF08\u4E0D\u6307\u5B9A collection \u65F6\u7F16\u8BD1\u89D2\u8272\u58F0\u660E\u7684\u5168\u90E8\uFF09").option("-c, --config <path>","\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84",k).option("--full","\u5F3A\u5236\u5168\u91CF\u91CD\u7F16\u8BD1").action(async(t,e)=>{try{await xt(t,e)}catch(n){console.error(`\u2717 kb compile \u5931\u8D25: ${n instanceof Error?n.message:String(n)}`),process.exit(1)}});J.command("update [collections...]").description("\u6E90\u6587\u6863\u53D8\u66F4\u540E\u89E6\u53D1\u589E\u91CF\u7F16\u8BD1\uFF08\u672A\u53D8\u66F4\u5219\u8DF3\u8FC7\uFF09").option("-c, --config <path>","\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84",k).action(async(t,e)=>{try{await Ct(t,e)}catch(n){console.error(`\u2717 kb update \u5931\u8D25: ${n instanceof Error?n.message:String(n)}`),process.exit(1)}});J.command("status [collections...]").description("\u67E5\u770B\u77E5\u8BC6\u5E93\u7F16\u8BD1\u72B6\u6001\uFF08\u7F16\u8BD1\u4E2D/\u5B8C\u6210/\u5931\u8D25 + \u6E90\u662F\u5426\u5DF2\u53D8\u66F4\uFF09").option("-c, --config <path>","\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84",k).action(async(t,e)=>{try{await _t(t,e)}catch(n){console.error(`\u2717 kb status \u5931\u8D25: ${n instanceof Error?n.message:String(n)}`),process.exit(1)}});J.command("ingest <url-or-path>").description("\u5BFC\u5165\u6587\u6863\u5230 wiki \u76EE\u5F55\uFF08\u652F\u6301\u9489\u9489 URL \u6216\u672C\u5730\u8DEF\u5F84\uFF0Cdocx/pdf \u81EA\u52A8\u8F6C md\uFF09").option("-o, --output <dir>","\u8F93\u51FA\u76EE\u5F55\uFF08\u9ED8\u8BA4 ./wiki\uFF09").option("-t, --type <type>","\u6E90\u7C7B\u578B: dingtalk-doc\uFF08\u4E0D\u4F20\u5219\u81EA\u52A8\u8BC6\u522B\uFF09").option("--flat","\u4E0D\u4FDD\u7559\u8FDC\u7AEF\u76EE\u5F55\u7ED3\u6784\uFF08\u6240\u6709\u6587\u6863\u5E73\u94FA\u5230\u8F93\u51FA\u76EE\u5F55\uFF09").option("--force","\u5F3A\u5236\u91CD\u65B0\u62C9\u53D6\u6240\u6709\u6587\u6863\uFF08\u5FFD\u7565\u589E\u91CF\u5224\u65AD\uFF09").option("--compile","\u5BFC\u5165\u540E\u81EA\u52A8\u89E6\u53D1 kb compile").option("-c, --config <path>","\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84",k).option("--timeout <ms>","\u5355\u6587\u6863\u8BF7\u6C42\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09").action(async(t,e)=>{try{await Lt(t,e)}catch(n){console.error(`\u2717 kb ingest \u5931\u8D25: ${n instanceof Error?n.message:String(n)}`),process.exit(1)}});async function en(t){let e=await K(t.config),n=t.workflow?e.workflowEntries.find(s=>s.id===t.workflow):e.workflowEntries[0];n||(console.error(`\u2717 \u672A\u627E\u5230\u5DE5\u4F5C\u6D41\u300C${t.workflow}\u300D\u3002\u53EF\u7528\uFF1A${e.workflowEntries.map(s=>s.id).join(", ")}`),process.exit(1));let o=await Ce(n.templatePath,{envSource:e.envSource}),{issues:r,rows:i}=await Pt(n.recipeDir,o);if(t.recipes){console.log(`
|
|
64
65
|
\u{1F4CB} \u5DE5\u4F5C\u6D41\u300C${o.meta.name}\u300D(${n.id}) \u7684 recipe schema:
|
|
65
|
-
`);for(let
|
|
66
|
+
`);for(let s of i){let a=s.configSummary.length>0?s.configSummary.join(", "):"(none)",l=s.loaded?"":" \u26A0 \u52A0\u8F7D\u5931\u8D25";console.log(` [${s.stepId} \u2192 ${s.recipeId}]${l}`),console.log(` config: ${a}`),console.log()}}if(t.validate)if(r.length===0)console.log(`
|
|
66
67
|
\u2705 \u5DE5\u4F5C\u6D41\u300C${o.meta.name}\u300D(${n.id}) \u5168\u90E8\u6B65\u9AA4\u6821\u9A8C\u901A\u8FC7
|
|
67
|
-
`);else{let
|
|
68
|
-
\u6821\u9A8C\u7ED3\u679C\uFF1A${
|
|
69
|
-
`);for(let l of r){let m=l.level==="error"?"\u274C":"\u26A0\uFE0F ";console.log(` ${m} [${l.stepId} \u2192 ${l.recipeId}] ${l.message}`)}console.log(),
|
|
68
|
+
`);else{let s=r.filter(l=>l.level==="error"),a=r.filter(l=>l.level==="warn");console.log(`
|
|
69
|
+
\u6821\u9A8C\u7ED3\u679C\uFF1A${s.length} \u4E2A\u9519\u8BEF\uFF0C${a.length} \u4E2A\u8B66\u544A
|
|
70
|
+
`);for(let l of r){let m=l.level==="error"?"\u274C":"\u26A0\uFE0F ";console.log(` ${m} [${l.stepId} \u2192 ${l.recipeId}] ${l.message}`)}console.log(),s.length>0&&process.exit(1)}}c(en,"runRecipeInspect");await d.parseAsync();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aitable-workflow-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14-beta.1785913842374",
|
|
4
4
|
"description": "aitable-workflow 命令行工具(cli):init/design/setup/run/serve 等命令;含 relay 本地反连客户端与 daemon 进程管理。bin: aitable-workflow。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"access": "public"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"aitable-workflow-core": "0.1.
|
|
23
|
-
"aitable-workflow-server": "0.1.
|
|
22
|
+
"aitable-workflow-core": "0.1.14-beta.1785913842374",
|
|
23
|
+
"aitable-workflow-server": "0.1.14-beta.1785913842374",
|
|
24
24
|
"@inquirer/prompts": "^8.5.2",
|
|
25
25
|
"commander": "^14.0.2",
|
|
26
26
|
"dotenv": "^17.4.2",
|
|
@@ -19,6 +19,7 @@ type Rec = { recordId: string; cells: Cells };
|
|
|
19
19
|
export type GroupConfig = {
|
|
20
20
|
groupId: string;
|
|
21
21
|
groupName?: string;
|
|
22
|
+
corpId?: string;
|
|
22
23
|
inbound?: string;
|
|
23
24
|
outbound?: string;
|
|
24
25
|
credRef?: string;
|
|
@@ -102,6 +103,7 @@ export function normalizeGroupConfigs(raw: unknown): GroupConfig[] {
|
|
|
102
103
|
const groupId = cellText(record?.groupId);
|
|
103
104
|
if (!groupId) return [];
|
|
104
105
|
const groupName = cellText(record?.groupName);
|
|
106
|
+
const corpId = cellText(record?.corpId);
|
|
105
107
|
const inbound = cellText(record?.inbound);
|
|
106
108
|
const outbound = cellText(record?.outbound);
|
|
107
109
|
const credRef = cellText(record?.credRef);
|
|
@@ -110,6 +112,7 @@ export function normalizeGroupConfigs(raw: unknown): GroupConfig[] {
|
|
|
110
112
|
|
|
111
113
|
const normalized: GroupConfig = { groupId, troubleshootingEnabled };
|
|
112
114
|
if (groupName) normalized.groupName = groupName;
|
|
115
|
+
if (corpId) normalized.corpId = corpId;
|
|
113
116
|
if (inbound) normalized.inbound = inbound;
|
|
114
117
|
if (outbound) normalized.outbound = outbound;
|
|
115
118
|
if (credRef) normalized.credRef = credRef;
|
|
@@ -302,7 +305,7 @@ const recipe: RecipeDefinition = {
|
|
|
302
305
|
config: z.object({
|
|
303
306
|
group_table: tableRefConfigSchema({
|
|
304
307
|
label: "群配置表",
|
|
305
|
-
description: "群配置表(读取群ID
|
|
308
|
+
description: "群配置表(读取群ID、群名称、群归属企业corpId、入站方式、出站方式、凭据引用、机器人昵称、是否排查问题)",
|
|
306
309
|
access: "read",
|
|
307
310
|
ownership: "owned",
|
|
308
311
|
autoCreate: true,
|
|
@@ -311,6 +314,9 @@ const recipe: RecipeDefinition = {
|
|
|
311
314
|
fields: [
|
|
312
315
|
{ name: "群ID", type: "text" },
|
|
313
316
|
{ name: "群名称", type: "text" },
|
|
317
|
+
// 可选。Webhook @人解析时作为 dws --profile 使用,确保查询到群归属企业下的 userId;
|
|
318
|
+
// 留空时继续使用 DWS 当前 profile,兼容已有配置。
|
|
319
|
+
{ name: "群归属企业corpId", type: "text" },
|
|
314
320
|
// 入站/出站双轴正交:
|
|
315
321
|
// 入站方式=dws(个人号轮询,进 pull 名单)/stream(企业机器人 WebSocket,@机器人触发);
|
|
316
322
|
// 出站方式=session(会话内 @提问者,依赖 stream 登记的 sessionWebhook)/webhook(命名 webhook 降级)。
|
|
@@ -377,6 +383,7 @@ const recipe: RecipeDefinition = {
|
|
|
377
383
|
const GROUP_FIELDS = {
|
|
378
384
|
groupId: "群ID",
|
|
379
385
|
groupName: "群名称",
|
|
386
|
+
corpId: "群归属企业corpId",
|
|
380
387
|
inbound: "入站方式",
|
|
381
388
|
outbound: "出站方式",
|
|
382
389
|
credRef: "凭据引用",
|
|
@@ -392,6 +399,7 @@ const recipe: RecipeDefinition = {
|
|
|
392
399
|
const groupFieldMap = await fetchFieldMap(gt.baseId, gt.tableId);
|
|
393
400
|
const groupIdKey = resolveFieldKey(GROUP_FIELDS.groupId, groupFieldMap);
|
|
394
401
|
const groupNameKey = resolveFieldKey(GROUP_FIELDS.groupName, groupFieldMap);
|
|
402
|
+
const corpIdKey = resolveFieldKey(GROUP_FIELDS.corpId, groupFieldMap);
|
|
395
403
|
const inboundKey = resolveFieldKey(GROUP_FIELDS.inbound, groupFieldMap);
|
|
396
404
|
const outboundKey = resolveFieldKey(GROUP_FIELDS.outbound, groupFieldMap);
|
|
397
405
|
const credRefKey = resolveFieldKey(GROUP_FIELDS.credRef, groupFieldMap);
|
|
@@ -403,6 +411,7 @@ const recipe: RecipeDefinition = {
|
|
|
403
411
|
const groups = normalizeGroupConfigs(records.map((r) => ({
|
|
404
412
|
groupId: cellText(r.cells[groupIdKey], fieldType(GROUP_FIELDS.groupId, groupFieldMap)),
|
|
405
413
|
groupName: cellText(r.cells[groupNameKey], fieldType(GROUP_FIELDS.groupName, groupFieldMap)) || undefined,
|
|
414
|
+
corpId: cellText(r.cells[corpIdKey], fieldType(GROUP_FIELDS.corpId, groupFieldMap)) || undefined,
|
|
406
415
|
inbound: cellText(r.cells[inboundKey], inboundType) || undefined,
|
|
407
416
|
outbound: cellText(r.cells[outboundKey], outboundType) || undefined,
|
|
408
417
|
credRef: cellText(r.cells[credRefKey], fieldType(GROUP_FIELDS.credRef, groupFieldMap)) || undefined,
|
|
@@ -28,7 +28,7 @@ import { isRecentOutboundEcho } from "../../../lib/echo-guard.js";
|
|
|
28
28
|
* 3. 历史消息带时间戳注入上下文:追问补位闸门(gates.standby)需要"@Owner 后已等多久"。
|
|
29
29
|
*
|
|
30
30
|
* 名单来源(均为 globalVar 引用,config-sync L2 装包后发布;未装包时为空集不影响 L0):
|
|
31
|
-
* - conversation_ids → 群配置([{groupId, botNick}]
|
|
31
|
+
* - conversation_ids → 群配置([{groupId, botNick, corpId}]):识别机器人自身消息,并为 Webhook @人指定群归属企业
|
|
32
32
|
* - admins → 真人同事名单([{nick, openDingTalkId}] 或 ["花名"])
|
|
33
33
|
*/
|
|
34
34
|
|
|
@@ -42,6 +42,7 @@ type IdentitySets = { ids: Set<string>; nicks: Set<string> };
|
|
|
42
42
|
type GroupRuntimeConfig = {
|
|
43
43
|
identity: IdentitySets;
|
|
44
44
|
outbound: string;
|
|
45
|
+
corpId: string;
|
|
45
46
|
};
|
|
46
47
|
|
|
47
48
|
function parseNicks(raw: unknown): Set<string> {
|
|
@@ -65,6 +66,7 @@ function loadGroupBots(list: unknown): Map<string, GroupRuntimeConfig> {
|
|
|
65
66
|
map.set(gid, {
|
|
66
67
|
identity: { ids: new Set(), nicks: parseNicks(rec.botNick) },
|
|
67
68
|
outbound: String(rec.outbound ?? "").trim(),
|
|
69
|
+
corpId: String(rec.corpId ?? "").trim(),
|
|
68
70
|
});
|
|
69
71
|
}
|
|
70
72
|
}
|
|
@@ -124,7 +126,7 @@ const recipe: EventHandlerRecipeDefinition = {
|
|
|
124
126
|
conversation_ids: z
|
|
125
127
|
.string()
|
|
126
128
|
.optional()
|
|
127
|
-
.describe("群配置来源(globalVar 引用,默认 ${globalVar.group_configs},形如 [{groupId, botNick}]
|
|
129
|
+
.describe("群配置来源(globalVar 引用,默认 ${globalVar.group_configs},形如 [{groupId, botNick, corpId}]):识别每群机器人身份,并为 Webhook @人指定群归属企业"),
|
|
128
130
|
admins: z
|
|
129
131
|
.string()
|
|
130
132
|
.optional()
|
|
@@ -220,6 +222,7 @@ const recipe: EventHandlerRecipeDefinition = {
|
|
|
220
222
|
conversationId,
|
|
221
223
|
senderOpenDingTalkId: senderOpenId,
|
|
222
224
|
senderName,
|
|
225
|
+
corpId: groupConfig.corpId,
|
|
223
226
|
});
|
|
224
227
|
} else if (replyCtx.credRef) {
|
|
225
228
|
// Stream 入站 + 会话内(session)出站:透传机器人身份(credRef/robotCode/openMsgId),
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import nodePath from "node:path";
|
|
1
3
|
import { z } from "zod";
|
|
2
4
|
import { resolveGlobalVarRef, type RecipeDefinition, type RecipeContext, type RecipeResult } from "aitable-workflow-core";
|
|
3
5
|
import { SUPPORT_ACTIONS, type SupportAction, KB_STATUS } from "../../../lib/contract.js";
|
|
@@ -35,7 +37,7 @@ const recipe: RecipeDefinition = {
|
|
|
35
37
|
description: "按 action 查 profile on_action 策略:留痕(track)、群行为(group)、Owner 解析与通知字段合成",
|
|
36
38
|
version: "1.0.0",
|
|
37
39
|
requiresAgent: false,
|
|
38
|
-
timeoutMs:
|
|
40
|
+
timeoutMs: 60_000,
|
|
39
41
|
config: z.object({
|
|
40
42
|
policies: z.record(z.string(), z.object({
|
|
41
43
|
track: z.boolean().optional().describe("是否建行留痕"),
|
|
@@ -140,6 +142,27 @@ const recipe: RecipeDefinition = {
|
|
|
140
142
|
...(action === "kb_gap" ? { kb_status: KB_STATUS.PENDING_SUPPLEMENT } : {}),
|
|
141
143
|
};
|
|
142
144
|
|
|
145
|
+
// kb_gap:将群消息中的图片上传到「群消息补充」附件字段,供管理员在知识收件箱中查看原始截图
|
|
146
|
+
if (action === "kb_gap") {
|
|
147
|
+
const imagePaths = extractLocalImagePaths(content);
|
|
148
|
+
if (imagePaths.length > 0) {
|
|
149
|
+
const fileTokens: Array<{ fileToken: string }> = [];
|
|
150
|
+
for (const imgPath of imagePaths) {
|
|
151
|
+
try {
|
|
152
|
+
const fileName = nodePath.basename(imgPath);
|
|
153
|
+
const result = await toolkit.table.uploadAttachmentToCurrentTable(imgPath, fileName);
|
|
154
|
+
fileTokens.push({ fileToken: result.fileToken });
|
|
155
|
+
} catch (err) {
|
|
156
|
+
log(`图片上传失败(${imgPath}),跳过: ${err}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (fileTokens.length > 0) {
|
|
160
|
+
output.image_attachments = fileTokens;
|
|
161
|
+
log(`kb_gap 图片上传完成: ${fileTokens.length}/${imagePaths.length} 张`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
143
166
|
log(`处置(${action}) → 留痕(track=true),group=${policy.group}${action === "kb_gap" ? ",进入知识收件箱" : ""}${dispatch.resolvedOwnerUserId ? `,Owner=${dispatch.resolvedOwnerName}(${dispatch.resolvedOwnerUserId})` : ""}`);
|
|
144
167
|
return {
|
|
145
168
|
status: "success",
|
|
@@ -153,4 +176,17 @@ const recipe: RecipeDefinition = {
|
|
|
153
176
|
},
|
|
154
177
|
};
|
|
155
178
|
|
|
179
|
+
const IMAGE_PATH_RE = /!\[图片\]\(([^)]+)\)/g;
|
|
180
|
+
|
|
181
|
+
function extractLocalImagePaths(content: string): string[] {
|
|
182
|
+
const paths: string[] = [];
|
|
183
|
+
for (const match of content.matchAll(IMAGE_PATH_RE)) {
|
|
184
|
+
const p = match[1]!;
|
|
185
|
+
if (p.startsWith("/") && p.includes(".aitable/media/") && !p.includes("..") && existsSync(p)) {
|
|
186
|
+
paths.push(p);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return paths;
|
|
190
|
+
}
|
|
191
|
+
|
|
156
192
|
export default recipe;
|
|
@@ -8,7 +8,7 @@ import { needsOwnerResolution } from "../../../lib/action-dispatch.js";
|
|
|
8
8
|
import type { SupportAction } from "../../../lib/contract.js";
|
|
9
9
|
|
|
10
10
|
/** 群配置(config-sync 发布到 globalVar 的形状):出站选路与凭据引用据此传给 replyToConversation。 */
|
|
11
|
-
type GroupConfig = { groupId: string; inbound?: string; outbound?: string; credRef?: string };
|
|
11
|
+
type GroupConfig = { groupId: string; corpId?: string; inbound?: string; outbound?: string; credRef?: string };
|
|
12
12
|
|
|
13
13
|
function loadGroupConfig(ctx: RecipeContext, ref: string, conversationId: string): GroupConfig | null {
|
|
14
14
|
const list = resolveGlobalVarRef(ref, ctx.getGlobalVar) as GroupConfig[] | undefined;
|
|
@@ -82,6 +82,9 @@ tracker:
|
|
|
82
82
|
type: text
|
|
83
83
|
- name: 群ID
|
|
84
84
|
type: text
|
|
85
|
+
# kb_gap 时自动上传群消息中的图片截图,供管理员在知识收件箱中查看原始上下文
|
|
86
|
+
- name: 群消息补充
|
|
87
|
+
type: attachment
|
|
85
88
|
# 知识沉淀状态机(拨单选值即流转,无代码状态机):
|
|
86
89
|
# 待确认(学习候选,human-approve) → 待沉淀(确认进轨道) → 已沉淀(kb-refine 完成)
|
|
87
90
|
# 待补充(kb_gap 缺口,等管理员填「管理员更新」) → 待沉淀 → 已沉淀;忽略=人工关闭
|
|
@@ -123,6 +126,7 @@ tracker:
|
|
|
123
126
|
- 标题
|
|
124
127
|
- 消息内容
|
|
125
128
|
- 对话上下文
|
|
129
|
+
- 群消息补充
|
|
126
130
|
- 管理员更新
|
|
127
131
|
- 知识沉淀
|
|
128
132
|
- 发送人
|
|
@@ -228,6 +232,7 @@ steps:
|
|
|
228
232
|
notification_target_user_id: "提醒人ID"
|
|
229
233
|
mention_user_id: "群内提及ID"
|
|
230
234
|
kb_status: "知识沉淀"
|
|
235
|
+
image_attachments: "群消息补充"
|
|
231
236
|
|
|
232
237
|
- id: reply_to_group
|
|
233
238
|
name: "回复群消息"
|