shennian 0.3.15 → 0.3.17

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.
@@ -136,8 +136,8 @@
136
136
  },
137
137
  {
138
138
  "file": "src/agents/workbuddy.js",
139
- "beforeBytes": 11645,
140
- "afterBytes": 6365
139
+ "beforeBytes": 11914,
140
+ "afterBytes": 6467
141
141
  },
142
142
  {
143
143
  "file": "src/commands/agent.js",
@@ -156,13 +156,13 @@
156
156
  },
157
157
  {
158
158
  "file": "src/commands/daemon-windows.js",
159
- "beforeBytes": 3905,
160
- "afterBytes": 3130
159
+ "beforeBytes": 4512,
160
+ "afterBytes": 3389
161
161
  },
162
162
  {
163
163
  "file": "src/commands/daemon.js",
164
- "beforeBytes": 38493,
165
- "afterBytes": 18618
164
+ "beforeBytes": 38255,
165
+ "afterBytes": 18764
166
166
  },
167
167
  {
168
168
  "file": "src/commands/integration.js",
@@ -541,8 +541,8 @@
541
541
  },
542
542
  {
543
543
  "file": "src/room/tool-server.js",
544
- "beforeBytes": 7304,
545
- "afterBytes": 3969
544
+ "beforeBytes": 8507,
545
+ "afterBytes": 4475
546
546
  },
547
547
  {
548
548
  "file": "src/session/archive-zip.js",
@@ -1,6 +1,6 @@
1
1
  import type { ChatAttachmentMeta, ExternalChannelSessionStatus, SessionRunPhase } from '@shennian/wire';
2
2
  import { AgentAdapter, type AgentSendContext } from './adapter.js';
3
- export declare function normalizeWorkBuddyModelId(modelId?: string | null): string;
3
+ export declare function normalizeWorkBuddyModelId(modelId?: string | null): string | undefined;
4
4
  export declare function normalizeWorkBuddyReasoningEffort(value?: string | null): string | undefined;
5
5
  export declare class WorkBuddyAdapter extends AgentAdapter {
6
6
  readonly type: "workbuddy";
@@ -1,3 +1,3 @@
1
- import{createInterface as c}from"node:readline";import{randomUUID as g}from"node:crypto";import{AgentAdapter as y,registerAgent as I}from"./adapter.js";import{resolveBuiltinCommand as k,spawnAgentCommand as S}from"./command-spec.js";import{buildAgentProcessEnv as _}from"../agent-env.js";import{buildPlatformInstructions as x}from"./platform-instructions.js";import{ensureWorkBuddyRoomPluginBundle as E}from"../integrations/workbuddy-plugin-bundle.js";import{roomMcpHostToolName as w}from"../integrations/room-plugin-shared.js";const P=new Set(["minimal","low","medium","high","xhigh","max"]);function B(i){return i?.trim()||"auto"}function T(i){const t=i?.trim();if(t){if(P.has(t))return t;throw new Error(`Unsupported WorkBuddy reasoning effort "${t}". Supported values: minimal, low, medium, high, xhigh, max.`)}}class W extends y{type="workbuddy";process=null;agentSessionId=null;workDir=null;seq=0;runId="";terminalState="open";hasEmittedText=!1;runPhase=null;externalChannel=null;shennianSessionId=null;extraEnv={};configure(t){this.shennianSessionId=t.sessionId??null,this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{}}async start(t,s,e){this.workDir=s,this.seq=0,e&&(this.agentSessionId=e)}async send(t,s,e,r,a,n){await this.killProcess(),this.runId=g(),this.resetRunState();const u=n?.source==="room_activation",o=["-p",t,"--output-format","stream-json","--verbose","--permission-mode",u?"dontAsk":"auto","--setting-sources","user,project,local","--model",B(s)],h=E();if(u){const f=w("workbuddy");o.push("--allowedTools",f,"--tools","","--mcp-config",h.mcpFile,"--strict-mcp-config","--disallowedTools","mcp__shennian_room__shennian_agent","--max-turns","2")}o.push("--plugin-dir",h.pluginRoot);const d=x(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,a);d&&o.push("--append-system-prompt",d);const m=T(e);m&&o.push("--effort",m),this.agentSessionId&&o.push("--resume",this.agentSessionId),this.spawnAndParse(o)}async resume(t){await this.killProcess(),this.agentSessionId=t,this.resetRunState()}async stop(){await this.killProcess()}async getStatus(){return{active:this.process!=null&&this.terminalState==="open",runId:this.runId||null,runPhase:this.runPhase,canStop:this.process!=null&&this.terminalState==="open"}}spawnAndParse(t){const s=k("workbuddy");if(!s){this.emit("error",new Error("WorkBuddy was not found. Install WorkBuddy Desktop or the CodeBuddy CLI, then retry."));return}const e=S(s,t,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:_(this.extraEnv)});this.process=e,c({input:e.stdout}).on("line",n=>{if(n.trim())try{this.handleStreamEvent(JSON.parse(n))}catch{}});let a="";e.stderr?.on("data",n=>{a=`${a}${n.toString()}`.slice(-8e3)}),e.on("close",n=>{this.process===e&&(this.process=null,this.runPhase=null,this.handleProcessClose(n,a))}),e.on("error",n=>{if(this.process!==e)return;this.process=null,this.runPhase=null;const u=n.code==="ENOENT";this.emitErrorIfOpen({state:"error",message:u?"WorkBuddy was not found. Install WorkBuddy Desktop or the CodeBuddy CLI, then retry.":l(n.message)})})}handleStreamEvent(t){if(t.type==="system"&&t.subtype==="init"&&t.session_id){this.agentSessionId=t.session_id,this.runPhase="thinking",this.emitEvent({state:"start",agentSessionId:t.session_id});return}if(t.type==="control_request"&&t.request?.subtype==="can_use_tool"){this.runPhase="waiting_approval",this.emitEvent({state:"approval-pending",name:t.request.tool_name,args:t.request.input,approval:{title:`WorkBuddy \u8BF7\u6C42\u4F7F\u7528 ${t.request.tool_name||"\u5DE5\u5177"}`,source:"workbuddy",actionHint:"\u8BF7\u5728 WorkBuddy \u6216\u795E\u5FF5\u4E2D\u786E\u8BA4\u540E\u7EE7\u7EED\u3002"}});return}if(t.type==="assistant"){const e=t.message?.content;if(e?.length)for(const r of e)this.handleContentBlock(r);else t.text&&this.emitText(t.text,!!t.thinking);return}if(t.type!=="result")return;if(t.subtype==="tool_result"){this.runPhase="tool_running",this.emitEvent({state:"tool-result",name:t.name,result:p(t.content)});return}if(t.subtype==="error"||t.error){this.emitErrorIfOpen({state:"error",message:l(t.error||t.result||"WorkBuddy failed.")});return}!this.hasEmittedText&&t.result&&this.emitText(t.result,!1),t.session_id&&(this.agentSessionId=t.session_id);const s=t.usage??t.message?.usage;this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0,usage:s?{inputTokens:s.input_tokens??0,outputTokens:s.output_tokens??0}:void 0})}handleContentBlock(t){t.type==="text"&&t.text?this.emitText(t.text,!1):t.type==="thinking"&&t.thinking?this.emitText(t.thinking,!0):t.type==="tool_use"?(this.runPhase="tool_running",this.emitEvent({state:"tool-call",name:t.name,args:t.input})):t.type==="tool_result"&&this.emitEvent({state:"tool-result",name:t.name,result:p(t.content)})}emitText(t,s){this.runPhase=s?"thinking":"streaming_text";const e=!s&&this.hasEmittedText?`
1
+ import{createInterface as g}from"node:readline";import{randomUUID as y}from"node:crypto";import{AgentAdapter as I,registerAgent as k}from"./adapter.js";import{resolveBuiltinCommand as S,spawnAgentCommand as _}from"./command-spec.js";import{buildAgentProcessEnv as x}from"../agent-env.js";import{buildPlatformInstructions as w}from"./platform-instructions.js";import{ensureWorkBuddyRoomPluginBundle as E}from"../integrations/workbuddy-plugin-bundle.js";import{roomMcpHostToolName as P}from"../integrations/room-plugin-shared.js";const B=new Set(["minimal","low","medium","high","xhigh","max"]);function T(i){const t=i?.trim();return t&&t!=="auto"?t:void 0}function W(i){const t=i?.trim();if(t){if(B.has(t))return t;throw new Error(`Unsupported WorkBuddy reasoning effort "${t}". Supported values: minimal, low, medium, high, xhigh, max.`)}}class C extends I{type="workbuddy";process=null;agentSessionId=null;workDir=null;seq=0;runId="";terminalState="open";hasEmittedText=!1;runPhase=null;externalChannel=null;shennianSessionId=null;extraEnv={};configure(t){this.shennianSessionId=t.sessionId??null,this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{}}async start(t,s,e){this.workDir=s,this.seq=0,e&&(this.agentSessionId=e)}async send(t,s,e,o,a,n){await this.killProcess(),this.runId=y(),this.resetRunState();const u=n?.source==="room_activation",r=["-p",t,"--output-format","stream-json","--verbose","--permission-mode",u?"dontAsk":"auto","--setting-sources","user,project,local"],h=T(s);h&&r.push("--model",h);const m=E(),l=P("workbuddy");u?r.push("--allowedTools",l,"--tools",`NoDefer(${l})`,"--mcp-config",m.mcpFile,"--strict-mcp-config","--disallowedTools","mcp__shennian_room__shennian_agent","--max-turns","2"):r.push("--allowedTools",l,"--tools",`*,NoDefer(${l})`),r.push("--plugin-dir",m.pluginRoot);const p=w(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,a);p&&r.push("--append-system-prompt",p);const f=W(e);f&&r.push("--effort",f),this.agentSessionId&&r.push("--resume",this.agentSessionId),this.spawnAndParse(r)}async resume(t){await this.killProcess(),this.agentSessionId=t,this.resetRunState()}async stop(){await this.killProcess()}async getStatus(){return{active:this.process!=null&&this.terminalState==="open",runId:this.runId||null,runPhase:this.runPhase,canStop:this.process!=null&&this.terminalState==="open"}}spawnAndParse(t){const s=S("workbuddy");if(!s){this.emit("error",new Error("WorkBuddy was not found. Install WorkBuddy Desktop or the CodeBuddy CLI, then retry."));return}const e=_(s,t,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:x(this.extraEnv)});this.process=e,g({input:e.stdout}).on("line",n=>{if(n.trim())try{this.handleStreamEvent(JSON.parse(n))}catch{}});let a="";e.stderr?.on("data",n=>{a=`${a}${n.toString()}`.slice(-8e3)}),e.on("close",n=>{this.process===e&&(this.process=null,this.runPhase=null,this.handleProcessClose(n,a))}),e.on("error",n=>{if(this.process!==e)return;this.process=null,this.runPhase=null;const u=n.code==="ENOENT";this.emitErrorIfOpen({state:"error",message:u?"WorkBuddy was not found. Install WorkBuddy Desktop or the CodeBuddy CLI, then retry.":d(n.message)})})}handleStreamEvent(t){if(t.type==="system"&&t.subtype==="init"&&t.session_id){this.agentSessionId=t.session_id,this.runPhase="thinking",this.emitEvent({state:"start",agentSessionId:t.session_id});return}if(t.type==="control_request"&&t.request?.subtype==="can_use_tool"){this.runPhase="waiting_approval",this.emitEvent({state:"approval-pending",name:t.request.tool_name,args:t.request.input,approval:{title:`WorkBuddy \u8BF7\u6C42\u4F7F\u7528 ${t.request.tool_name||"\u5DE5\u5177"}`,source:"workbuddy",actionHint:"\u8BF7\u5728 WorkBuddy \u6216\u795E\u5FF5\u4E2D\u786E\u8BA4\u540E\u7EE7\u7EED\u3002"}});return}if(t.type==="assistant"){const e=t.message?.content;if(e?.length)for(const o of e)this.handleContentBlock(o);else t.text&&this.emitText(t.text,!!t.thinking);return}if(t.type!=="result")return;if(t.subtype==="tool_result"){this.runPhase="tool_running",this.emitEvent({state:"tool-result",name:t.name,result:c(t.content)});return}if(t.subtype==="error"||t.error){this.emitErrorIfOpen({state:"error",message:d(t.error||t.result||"WorkBuddy failed.")});return}!this.hasEmittedText&&t.result&&this.emitText(t.result,!1),t.session_id&&(this.agentSessionId=t.session_id);const s=t.usage??t.message?.usage;this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0,usage:s?{inputTokens:s.input_tokens??0,outputTokens:s.output_tokens??0}:void 0})}handleContentBlock(t){t.type==="text"&&t.text?this.emitText(t.text,!1):t.type==="thinking"&&t.thinking?this.emitText(t.thinking,!0):t.type==="tool_use"?(this.runPhase="tool_running",this.emitEvent({state:"tool-call",name:t.name,args:t.input})):t.type==="tool_result"&&this.emitEvent({state:"tool-result",name:t.name,result:c(t.content)})}emitText(t,s){this.runPhase=s?"thinking":"streaming_text";const e=!s&&this.hasEmittedText?`
2
2
 
3
- `:"";this.emitEvent({state:"delta",text:e+t,thinking:s||void 0}),s||(this.hasEmittedText=!0)}handleProcessClose(t,s){if(t!==0&&t!==null){this.emitErrorIfOpen({state:"error",message:l(s)||`WorkBuddy exited with code ${t}.`});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0})}emitFinalIfOpen(t){this.terminalState==="open"&&(this.terminalState="final",this.runPhase=null,this.emitEvent(t))}emitErrorIfOpen(t){this.terminalState==="open"&&(this.terminalState="error",this.runPhase=null,this.emitEvent(t))}emitEvent(t){this.emit("agentEvent",{...t,runId:this.runId,seq:this.seq++})}resetRunState(){this.seq=0,this.terminalState="open",this.hasEmittedText=!1,this.runPhase=null}async killProcess(){const t=this.process;t&&(this.process=null,this.runPhase=null,t.kill("SIGTERM"),await new Promise(s=>{let e=!1;const r=()=>{e||(e=!0,s())};t.once("close",r),setTimeout(()=>{t.kill("SIGKILL"),r()},3e3).unref?.()}))}}function p(i){if(typeof i=="string")return i;try{return JSON.stringify(i)}catch{return"[unserializable WorkBuddy tool result]"}}function l(i){return i.replace(/(Authorization\s*:\s*)Bearer\s+[A-Za-z0-9._~+\/-]+/gi,"$1Bearer [redacted]").replace(/(api[-_ ]?key|access[-_ ]?token|refresh[-_ ]?token|connector[-_ ]?token)(\s*[:=]\s*)[^\s,;]+/gi,"$1$2[redacted]").trim().slice(0,2e3)}I("workbuddy",()=>new W);export{W as WorkBuddyAdapter,B as normalizeWorkBuddyModelId,T as normalizeWorkBuddyReasoningEffort,l as sanitizeWorkBuddyError};
3
+ `:"";this.emitEvent({state:"delta",text:e+t,thinking:s||void 0}),s||(this.hasEmittedText=!0)}handleProcessClose(t,s){if(t!==0&&t!==null){this.emitErrorIfOpen({state:"error",message:d(s)||`WorkBuddy exited with code ${t}.`});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0})}emitFinalIfOpen(t){this.terminalState==="open"&&(this.terminalState="final",this.runPhase=null,this.emitEvent(t))}emitErrorIfOpen(t){this.terminalState==="open"&&(this.terminalState="error",this.runPhase=null,this.emitEvent(t))}emitEvent(t){this.emit("agentEvent",{...t,runId:this.runId,seq:this.seq++})}resetRunState(){this.seq=0,this.terminalState="open",this.hasEmittedText=!1,this.runPhase=null}async killProcess(){const t=this.process;t&&(this.process=null,this.runPhase=null,t.kill("SIGTERM"),await new Promise(s=>{let e=!1;const o=()=>{e||(e=!0,s())};t.once("close",o),setTimeout(()=>{t.kill("SIGKILL"),o()},3e3).unref?.()}))}}function c(i){if(typeof i=="string")return i;try{return JSON.stringify(i)}catch{return"[unserializable WorkBuddy tool result]"}}function d(i){return i.replace(/(Authorization\s*:\s*)Bearer\s+[A-Za-z0-9._~+\/-]+/gi,"$1Bearer [redacted]").replace(/(api[-_ ]?key|access[-_ ]?token|refresh[-_ ]?token|connector[-_ ]?token)(\s*[:=]\s*)[^\s,;]+/gi,"$1$2[redacted]").trim().slice(0,2e3)}k("workbuddy",()=>new C);export{C as WorkBuddyAdapter,T as normalizeWorkBuddyModelId,W as normalizeWorkBuddyReasoningEffort,d as sanitizeWorkBuddyError};
@@ -20,4 +20,9 @@ export declare function buildWindowsScheduledTaskCommands(input: {
20
20
  create: string;
21
21
  run: string;
22
22
  };
23
+ export declare function buildWindowsScheduledTaskSuspendCommands(taskName: string): {
24
+ disable: string;
25
+ end: string;
26
+ };
27
+ export declare function suspendWindowsScheduledTask(taskName: string, execute: (command: string) => void): void;
23
28
  export {};
@@ -1,6 +1,6 @@
1
- import a from"node:path";function i(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}function d(e){return`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function n(e){return`"${e.replace(/"/g,'""')}"`}function l(e){return e.replace(/"/g,'""')}function r(e){return i(e)}function s(e){const t=a.extname(e).toLowerCase();return t===".cmd"||t===".bat"}function m(e,t){const o=[e.command,...e.args].map(n).join(" ");return["@echo off","setlocal",'if not defined SHENNIAN_HOME set "SHENNIAN_HOME=%~dp0."',`${s(e.command)?`call ${o}`:o} >> ${n(t)} 2>&1`,""].join(`\r
2
- `)}function g(e){return['Set shell = CreateObject("WScript.Shell")',`shell.Run Chr(34) & "${l(e)}" & Chr(34), 0, False`,""].join(`\r
3
- `)}function p(e){const t=e.arguments.join(" ");return`<?xml version="1.0" encoding="UTF-16"?>
1
+ import i from"node:path";function s(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}function m(e){return`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function t(e){return`"${e.replace(/"/g,'""')}"`}function l(e){return e.replace(/"/g,'""')}function o(e){return s(e)}function d(e){const n=i.extname(e).toLowerCase();return n===".cmd"||n===".bat"}function g(e,n){const r=[e.command,...e.args].map(t).join(" ");return["@echo off","setlocal",'if not defined SHENNIAN_HOME set "SHENNIAN_HOME=%~dp0."',`${d(e.command)?`call ${r}`:r} >> ${t(n)} 2>&1`,""].join(`\r
2
+ `)}function p(e){return['Set shell = CreateObject("WScript.Shell")',`shell.Run Chr(34) & "${l(e)}" & Chr(34), 0, False`,""].join(`\r
3
+ `)}function S(e){const n=e.arguments.join(" ");return`<?xml version="1.0" encoding="UTF-16"?>
4
4
  <Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
5
5
  <RegistrationInfo>
6
6
  <Description>Shennian Agent Daemon</Description>
@@ -12,7 +12,7 @@ import a from"node:path";function i(e){return e.replace(/&/g,"&amp;").replace(/<
12
12
  </BootTrigger>
13
13
  <LogonTrigger>
14
14
  <Enabled>true</Enabled>
15
- <UserId>${r(e.userId)}</UserId>
15
+ <UserId>${o(e.userId)}</UserId>
16
16
  </LogonTrigger>
17
17
  <EventTrigger>
18
18
  <Enabled>true</Enabled>
@@ -26,7 +26,7 @@ import a from"node:path";function i(e){return e.replace(/&/g,"&amp;").replace(/<
26
26
  </Triggers>
27
27
  <Principals>
28
28
  <Principal id="Author">
29
- <UserId>${r(e.userId)}</UserId>
29
+ <UserId>${o(e.userId)}</UserId>
30
30
  <LogonType>S4U</LogonType>
31
31
  <RunLevel>LeastPrivilege</RunLevel>
32
32
  </Principal>
@@ -56,8 +56,8 @@ import a from"node:path";function i(e){return e.replace(/&/g,"&amp;").replace(/<
56
56
  </Settings>
57
57
  <Actions Context="Author">
58
58
  <Exec>
59
- <Command>${r(e.command)}</Command>
60
- <Arguments>${r(t)}</Arguments>
59
+ <Command>${o(e.command)}</Command>
60
+ <Arguments>${o(n)}</Arguments>
61
61
  </Exec>
62
62
  </Actions>
63
- </Task>`}function S(e){return{create:`schtasks /create /tn ${n(e.taskName)} /xml ${n(e.xmlPath)} /f`,run:`schtasks /run /tn ${n(e.taskName)}`}}export{m as buildWindowsLauncherCommand,S as buildWindowsScheduledTaskCommands,p as buildWindowsScheduledTaskXml,g as buildWindowsStartupVbs,i as escapeXml,s as isWindowsCmdScript,n as quoteCmdArg,d as quoteSystemdArg};
63
+ </Task>`}function f(e){return{create:`schtasks /create /tn ${t(e.taskName)} /xml ${t(e.xmlPath)} /f`,run:`schtasks /run /tn ${t(e.taskName)}`}}function c(e){return{disable:`schtasks /change /tn ${t(e)} /disable`,end:`schtasks /end /tn ${t(e)}`}}function I(e,n){const r=c(e);for(const a of[r.disable,r.end])try{n(a)}catch{}}export{g as buildWindowsLauncherCommand,f as buildWindowsScheduledTaskCommands,c as buildWindowsScheduledTaskSuspendCommands,S as buildWindowsScheduledTaskXml,p as buildWindowsStartupVbs,s as escapeXml,d as isWindowsCmdScript,t as quoteCmdArg,m as quoteSystemdArg,I as suspendWindowsScheduledTask};
@@ -1,6 +1,6 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import type { Command } from 'commander';
3
- export { buildWindowsLauncherCommand, buildWindowsScheduledTaskCommands, buildWindowsScheduledTaskXml, buildWindowsStartupVbs, } from './daemon-windows.js';
3
+ export { buildWindowsLauncherCommand, buildWindowsScheduledTaskCommands, buildWindowsScheduledTaskSuspendCommands, buildWindowsScheduledTaskXml, buildWindowsStartupVbs, suspendWindowsScheduledTask, } from './daemon-windows.js';
4
4
  export { isShennianRunServiceCommand } from './daemon-process.js';
5
5
  export declare function isSafeSnapshotEnvKey(key: string): boolean;
6
6
  type Platform = 'darwin' | 'linux' | 'win32';
@@ -1,13 +1,13 @@
1
- import s from"chalk";import i from"node:fs";import c from"node:path";import u from"node:os";import{execSync as a,spawn as ue}from"node:child_process";import{randomUUID as de}from"node:crypto";import{fileURLToPath as le}from"node:url";import{getShennianDir as pe,loadConfig as j,resolveShennianPath as h,saveConfig as me}from"../config/index.js";import{buildAugmentedPath as fe}from"../env-path.js";import{buildWindowsLauncherCommand as Se,buildWindowsScheduledTaskCommands as he,buildWindowsScheduledTaskXml as ge,buildWindowsStartupVbs as ye,escapeXml as b,isWindowsCmdScript as we,quoteCmdArg as W,quoteSystemdArg as Ee}from"./daemon-windows.js";import{waitForDaemonReadyStatus as L}from"./daemon-start-wait.js";import{findRunningWindowsDaemonProcessIds as ve,isShennianRunServiceCommand as Ie}from"./daemon-process.js";import{getCurrentVersion as ke}from"../upgrade/engine.js";import{buildWindowsLauncherCommand as wn,buildWindowsScheduledTaskCommands as En,buildWindowsScheduledTaskXml as vn,buildWindowsStartupVbs as In}from"./daemon-windows.js";import{isShennianRunServiceCommand as An}from"./daemon-process.js";const Ae=c.dirname(le(import.meta.url)),g=pe(),p=h("daemon.pid"),d=h("daemon.log"),R=h("remote-access.disabled"),P=h("daemon-launcher.json"),F=c.resolve(Ae,"../../bin/shennian.js"),B=process.execPath,V=new Set(["PATH","HOME","USERPROFILE","HOMEDRIVE","HOMEPATH","USERNAME","USERDOMAIN","COMPUTERNAME","USER","LOGNAME","SHELL","TMPDIR","LANG","LC_ALL","LC_CTYPE","SSH_AUTH_SOCK","XDG_CONFIG_HOME","XDG_DATA_HOME","TEMP","TMP","APPDATA","LOCALAPPDATA","SHENNIAN_DESKTOP_SERVER_URL","SHENNIAN_WECHAT_CHANNEL_HELPER_DIR","SHENNIAN_HOME","SHENNIAN_NATIVE_FUSION_DISABLED","SHENNIAN_WECHAT_DELIVERABILITY_TRACE"]);function Pe(e){return V.has(e)}function l(){const e=u.platform();return e==="darwin"||e==="linux"||e==="win32"?e:"linux"}function O(e){const n=e.replace(/\\/g,"/").toLowerCase(),t=u.tmpdir().replace(/\\/g,"/").toLowerCase();return n.includes("/_npx/")||n.includes("/npm-cache/_npx/")||n.includes("/pnpm/dlx/")||n.startsWith(t.endsWith("/")?t:`${t}/`)}function De(e){return i.existsSync(e.scriptPath)&&!O(e.scriptPath)?{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}:e.shennianCommandPath&&!O(e.shennianCommandPath)?{command:e.shennianCommandPath,args:["run-service"],mode:"global-shim"}:e.npxPath?{command:e.npxPath,args:["--yes","shennian","run-service"],mode:"npx"}:{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}}function Ne(e){const n=e.trim();if(!n)return null;if(n.startsWith("{"))try{const r=JSON.parse(n),o=Number(r.pid);return!Number.isInteger(o)||o<=0?null:{pid:o,...typeof r.instanceId=="string"?{instanceId:r.instanceId}:{},...typeof r.version=="string"?{version:r.version}:{},...r.launcher==="desktop-managed"||r.launcher==="global-cli"||r.launcher==="unknown"?{launcher:r.launcher}:{},...typeof r.startedAt=="string"?{startedAt:r.startedAt}:{},...r.adopted===!0?{adopted:!0}:{}}}catch{return null}const t=parseInt(n,10);return isNaN(t)?null:{pid:t}}function I(){try{return Ne(i.readFileSync(p,"utf-8"))}catch{return null}}function be(){return I()?.pid??null}function w(e){try{return process.kill(e,0),!0}catch{return!1}}function G(e,n=3e3){const t=Date.now();for(;Date.now()-t<n;){if(!w(e))return!0;Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,100)}return!w(e)}function Le(){return i.existsSync(R)}function Re(){try{i.unlinkSync(R)}catch{}}function J(){return"global-cli"}function Oe(){return`${process.pid}-${Date.now()}-${de()}`}function Te(e,n,t={}){i.mkdirSync(g,{recursive:!0}),i.writeFileSync(p,JSON.stringify({pid:e,instanceId:n,...t.version?{version:t.version}:{},launcher:t.launcher??J(),...t.adopted?{adopted:!0}:{},startedAt:new Date().toISOString()},null,2))}function fn(e,n){const t=I();if(!(t?.pid!==e||t.instanceId!==n))try{i.unlinkSync(p)}catch{}}function K(e,n=J(),t){i.mkdirSync(g,{recursive:!0}),i.writeFileSync(P,JSON.stringify({pid:e,launcher:n,...t?{instanceId:t}:{},updatedAt:new Date().toISOString()},null,2))}function T(e){if(e)try{if(JSON.parse(i.readFileSync(P,"utf-8")).instanceId!==e)return}catch{return}try{i.unlinkSync(P)}catch{}}function xe(e){e&&K(e)}function He(e,n){if(!e||!n)return"unknown";try{const t=JSON.parse(i.readFileSync(P,"utf-8"));if(t.pid!==e)return"unknown";if(t.launcher==="desktop-managed"||t.launcher==="global-cli")return t.launcher}catch{}return x(e)}function x(e){if(l()==="win32")return"unknown";try{const n=a(`ps -p ${e} -o command=`,{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).replace(/\\/g,"/");if(n.includes("/node_modules/shennian/")||n.includes("/bin/shennian")||n.includes(" shennian run-service")||n.includes(" shennian.js run-service"))return"global-cli"}catch{}return"unknown"}function X(e=process.pid){if(l()==="win32")return ve(e);try{return a("ps -axo pid=,command=",{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).split(/\r?\n/).map(t=>{const r=t.trim().match(/^(\d+)\s+(.+)$/);if(!r)return null;const o=Number(r[1]),S=r[2];return!Number.isInteger(o)||o===e?null:Ie(S)?o:null}).filter(t=>typeof t=="number")}catch{return[]}}function Sn(e=process.env,n=g,t=u.tmpdir()){if(e.SHENNIAN_E2E_ISOLATED_DAEMON!=="1"||!e.SHENNIAN_HOME?.trim())return!1;const r=c.relative(c.resolve(t),c.resolve(n));return r!==""&&r!==".."&&!r.startsWith(`..${c.sep}`)&&!c.isAbsolute(r)}function f(e={}){let n=I(),t=n?.pid??null,r=t!==null&&w(t),o=t!==null&&!r,S=n?.adopted===!0;const E=j();if(o&&e.cleanupStale){try{i.unlinkSync(p)}catch{}n=null,t=null,r=!1,o=!1}if(!r){const y=X()[0];if(y&&(t=y,r=!0,o=!1,S=!0,e.cleanupStale)){const U=Oe();Te(y,U,{launcher:x(y),adopted:!0}),K(y,x(y)),n={pid:y,instanceId:U,adopted:!0}}}return{running:r,pid:t,stale:o,remoteAccessDisabled:Le(),launcher:He(t,r),platform:l(),shennianDir:g,pidFile:p,logFile:d,...n?.instanceId?{instanceId:n.instanceId}:{},...S?{adopted:!0}:{},...E.machineId?{machineId:E.machineId}:{},paired:!!(E.machineToken&&E.machineId),...E.serverUrl?{serverUrl:E.serverUrl}:{}}}function v(e){process.stdout.write(`${JSON.stringify(e,null,2)}
2
- `)}function q(e){const n=e?.trim();if(!n)return;const t=j();t.serverUrl=n,me(t)}function Y(e){return e?.trim()||process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim()||void 0}const H="com.shennian.agent",m=c.join(u.homedir(),"Library/LaunchAgents",`${H}.plist`),_e=c.join(u.homedir(),"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),_=h("autostart.cmd"),$=h("autostart.vbs"),C=h("autostart.xml"),$e=c.join(_e,"ShennianAgent.vbs");function z(e){const n=l()==="win32"?`where ${e}`:`command -v ${e}`;try{return a(n,{stdio:["ignore","pipe","pipe"],encoding:"utf-8",windowsHide:!0}).split(/\r?\n/).map(o=>o.trim()).find(Boolean)??null}catch{return null}}function Ce(){const e=l()==="win32"?"npx.cmd":"npx",n=c.join(c.dirname(B),e);return i.existsSync(n)?n:z("npx")}function k(){return De({nodeExec:B,scriptPath:F,shennianCommandPath:z("shennian"),npxPath:Ce()})}function Q(){try{a(`schtasks /delete /tn "${N}" /f`,{stdio:"pipe",windowsHide:!0})}catch{}}function Z(){try{i.unlinkSync($e)}catch{}}function Me(){const e=process.env.USERDOMAIN?.trim(),n=process.env.COMPUTERNAME?.trim(),t=process.env.USERNAME?.trim()||u.userInfo().username;return e&&e.toUpperCase()!=="WORKGROUP"?`${e}\\${t}`:n?`${n}\\${t}`:t}function Ue(e){if(l()==="win32"&&we(e.command)){const n=[e.command,...e.args].map(W).join(" ");return{command:process.env.ComSpec||"cmd.exe",args:["/d","/s","/c",`"${n}"`],windowsVerbatimArguments:!0}}return{command:e.command,args:e.args}}function je(e,n,t=process.env){return{detached:!0,stdio:["ignore",n,n],env:t,windowsHide:!0,...e.windowsVerbatimArguments?{windowsVerbatimArguments:!0}:{}}}function We(){const e=k();i.writeFileSync(_,Se(e,d)),i.writeFileSync($,ye(_)),Z(),Q();const n=ge({userId:Me(),command:c.join(process.env.SystemRoot||"C:\\Windows","System32","wscript.exe"),arguments:[W($)]});i.writeFileSync(C,n,"utf8");const t=he({taskName:N,xmlPath:C});try{return a(t.create,{stdio:"pipe",windowsHide:!0}),a(t.run,{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}function ee(){const e={};for(const n of V)process.env[n]&&(e[n]=process.env[n]);if(e.HOME??=u.homedir(),e.PATH=fe({pathValue:e.PATH,env:e}),e.USER??=u.userInfo().username,l()==="win32"){const n=process.env.TEMP||process.env.TMP||u.tmpdir(),t=u.homedir();e.USERPROFILE??=t;const r=c.win32.parse(t);e.HOMEDRIVE??=r.root.replace(/[\\/]$/,"")||process.env.HOMEDRIVE||"",e.HOMEPATH??=t.slice((e.HOMEDRIVE||"").length)||process.env.HOMEPATH||"",e.USERNAME??=process.env.USERNAME||u.userInfo().username,e.COMPUTERNAME??=process.env.COMPUTERNAME||"",e.TMPDIR??=n,e.TEMP??=n,e.TMP??=n}else e.TMPDIR??="/tmp";return e}function Fe(){const e=ee(),n=k(),t=Object.entries(e).map(([o,S])=>` <key>${b(o)}</key>
3
- <string>${b(S)}</string>`).join(`
4
- `),r=[n.command,...n.args].map(o=>` <string>${b(o)}</string>`).join(`
1
+ import s from"chalk";import i from"node:fs";import c from"node:path";import u from"node:os";import{execSync as a,spawn as ue}from"node:child_process";import{randomUUID as de}from"node:crypto";import{fileURLToPath as le}from"node:url";import{getShennianDir as pe,loadConfig as M,resolveShennianPath as S,saveConfig as me}from"../config/index.js";import{buildAugmentedPath as fe}from"../env-path.js";import{buildWindowsLauncherCommand as Se,buildWindowsScheduledTaskCommands as he,buildWindowsScheduledTaskXml as ge,buildWindowsStartupVbs as we,escapeXml as N,isWindowsCmdScript as ye,quoteCmdArg as W,quoteSystemdArg as Ee,suspendWindowsScheduledTask as ve}from"./daemon-windows.js";import{waitForDaemonReadyStatus as b}from"./daemon-start-wait.js";import{findRunningWindowsDaemonProcessIds as Pe,isShennianRunServiceCommand as ke}from"./daemon-process.js";import{getCurrentVersion as Ie}from"../upgrade/engine.js";import{buildWindowsLauncherCommand as vn,buildWindowsScheduledTaskCommands as Pn,buildWindowsScheduledTaskSuspendCommands as kn,buildWindowsScheduledTaskXml as In,buildWindowsStartupVbs as An,suspendWindowsScheduledTask as Dn}from"./daemon-windows.js";import{isShennianRunServiceCommand as bn}from"./daemon-process.js";const Ae=c.dirname(le(import.meta.url)),h=pe(),g=S("daemon.pid"),d=S("daemon.log"),R=S("remote-access.disabled"),A=S("daemon-launcher.json"),j=c.resolve(Ae,"../../bin/shennian.js"),F=process.execPath,B=new Set(["PATH","HOME","USERPROFILE","HOMEDRIVE","HOMEPATH","USERNAME","USERDOMAIN","COMPUTERNAME","USER","LOGNAME","SHELL","TMPDIR","LANG","LC_ALL","LC_CTYPE","SSH_AUTH_SOCK","XDG_CONFIG_HOME","XDG_DATA_HOME","TEMP","TMP","APPDATA","LOCALAPPDATA","SHENNIAN_DESKTOP_SERVER_URL","SHENNIAN_WECHAT_CHANNEL_HELPER_DIR","SHENNIAN_HOME","SHENNIAN_NATIVE_FUSION_DISABLED","SHENNIAN_WECHAT_DELIVERABILITY_TRACE"]);function De(e){return B.has(e)}function l(){const e=u.platform();return e==="darwin"||e==="linux"||e==="win32"?e:"linux"}function L(e){const n=e.replace(/\\/g,"/").toLowerCase(),t=u.tmpdir().replace(/\\/g,"/").toLowerCase();return n.includes("/_npx/")||n.includes("/npm-cache/_npx/")||n.includes("/pnpm/dlx/")||n.startsWith(t.endsWith("/")?t:`${t}/`)}function Ne(e){return i.existsSync(e.scriptPath)&&!L(e.scriptPath)?{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}:e.shennianCommandPath&&!L(e.shennianCommandPath)?{command:e.shennianCommandPath,args:["run-service"],mode:"global-shim"}:e.npxPath?{command:e.npxPath,args:["--yes","shennian","run-service"],mode:"npx"}:{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}}function be(e){const n=e.trim();if(!n)return null;if(n.startsWith("{"))try{const r=JSON.parse(n),o=Number(r.pid);return!Number.isInteger(o)||o<=0?null:{pid:o,...typeof r.instanceId=="string"?{instanceId:r.instanceId}:{},...typeof r.version=="string"?{version:r.version}:{},...r.launcher==="desktop-managed"||r.launcher==="global-cli"||r.launcher==="unknown"?{launcher:r.launcher}:{},...typeof r.startedAt=="string"?{startedAt:r.startedAt}:{},...r.adopted===!0?{adopted:!0}:{}}}catch{return null}const t=parseInt(n,10);return isNaN(t)?null:{pid:t}}function P(){try{return be(i.readFileSync(g,"utf-8"))}catch{return null}}function Sn(){return P()?.pid??null}function E(e){try{return process.kill(e,0),!0}catch{return!1}}function V(e,n=3e3){const t=Date.now();for(;Date.now()-t<n;){if(!E(e))return!0;Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,100)}return!E(e)}function Re(){return i.existsSync(R)}function Le(){try{i.unlinkSync(R)}catch{}}function J(){return"global-cli"}function Te(){return`${process.pid}-${Date.now()}-${de()}`}function Oe(e,n,t={}){i.mkdirSync(h,{recursive:!0}),i.writeFileSync(g,JSON.stringify({pid:e,instanceId:n,...t.version?{version:t.version}:{},launcher:t.launcher??J(),...t.adopted?{adopted:!0}:{},startedAt:new Date().toISOString()},null,2))}function hn(e,n){const t=P();if(!(t?.pid!==e||t.instanceId!==n))try{i.unlinkSync(g)}catch{}}function G(e,n=J(),t){i.mkdirSync(h,{recursive:!0}),i.writeFileSync(A,JSON.stringify({pid:e,launcher:n,...t?{instanceId:t}:{},updatedAt:new Date().toISOString()},null,2))}function xe(e){if(e)try{if(JSON.parse(i.readFileSync(A,"utf-8")).instanceId!==e)return}catch{return}try{i.unlinkSync(A)}catch{}}function He(e){e&&G(e)}function _e(e,n){if(!e||!n)return"unknown";try{const t=JSON.parse(i.readFileSync(A,"utf-8"));if(t.pid!==e)return"unknown";if(t.launcher==="desktop-managed"||t.launcher==="global-cli")return t.launcher}catch{}return T(e)}function T(e){if(l()==="win32")return"unknown";try{const n=a(`ps -p ${e} -o command=`,{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).replace(/\\/g,"/");if(n.includes("/node_modules/shennian/")||n.includes("/bin/shennian")||n.includes(" shennian run-service")||n.includes(" shennian.js run-service"))return"global-cli"}catch{}return"unknown"}function K(e=process.pid){if(l()==="win32")return Pe(e);try{return a("ps -axo pid=,command=",{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).split(/\r?\n/).map(t=>{const r=t.trim().match(/^(\d+)\s+(.+)$/);if(!r)return null;const o=Number(r[1]),f=r[2];return!Number.isInteger(o)||o===e?null:ke(f)?o:null}).filter(t=>typeof t=="number")}catch{return[]}}function gn(e=process.env,n=h,t=u.tmpdir()){if(e.SHENNIAN_E2E_ISOLATED_DAEMON!=="1"||!e.SHENNIAN_HOME?.trim())return!1;const r=c.relative(c.resolve(t),c.resolve(n));return r!==""&&r!==".."&&!r.startsWith(`..${c.sep}`)&&!c.isAbsolute(r)}function m(e={}){let n=P(),t=n?.pid??null,r=t!==null&&E(t),o=t!==null&&!r,f=n?.adopted===!0;const y=M();if(o&&e.cleanupStale){try{i.unlinkSync(g)}catch{}n=null,t=null,r=!1,o=!1}if(!r){const w=K()[0];if(w&&(t=w,r=!0,o=!1,f=!0,e.cleanupStale)){const U=Te();Oe(w,U,{launcher:T(w),adopted:!0}),G(w,T(w)),n={pid:w,instanceId:U,adopted:!0}}}return{running:r,pid:t,stale:o,remoteAccessDisabled:Re(),launcher:_e(t,r),platform:l(),shennianDir:h,pidFile:g,logFile:d,...n?.instanceId?{instanceId:n.instanceId}:{},...f?{adopted:!0}:{},...y.machineId?{machineId:y.machineId}:{},paired:!!(y.machineToken&&y.machineId),...y.serverUrl?{serverUrl:y.serverUrl}:{}}}function v(e){process.stdout.write(`${JSON.stringify(e,null,2)}
2
+ `)}function X(e){const n=e?.trim();if(!n)return;const t=M();t.serverUrl=n,me(t)}function q(e){return e?.trim()||process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim()||void 0}const O="com.shennian.agent",p=c.join(u.homedir(),"Library/LaunchAgents",`${O}.plist`),$e=c.join(u.homedir(),"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),x=S("autostart.cmd"),H=S("autostart.vbs"),_=S("autostart.xml"),Ce=c.join($e,"ShennianAgent.vbs");function Y(e){const n=l()==="win32"?`where ${e}`:`command -v ${e}`;try{return a(n,{stdio:["ignore","pipe","pipe"],encoding:"utf-8",windowsHide:!0}).split(/\r?\n/).map(o=>o.trim()).find(Boolean)??null}catch{return null}}function Ue(){const e=l()==="win32"?"npx.cmd":"npx",n=c.join(c.dirname(F),e);return i.existsSync(n)?n:Y("npx")}function k(){return Ne({nodeExec:F,scriptPath:j,shennianCommandPath:Y("shennian"),npxPath:Ue()})}function z(){try{a(`schtasks /delete /tn "${I}" /f`,{stdio:"pipe",windowsHide:!0})}catch{}}function Q(){try{i.unlinkSync(Ce)}catch{}}function Me(){const e=process.env.USERDOMAIN?.trim(),n=process.env.COMPUTERNAME?.trim(),t=process.env.USERNAME?.trim()||u.userInfo().username;return e&&e.toUpperCase()!=="WORKGROUP"?`${e}\\${t}`:n?`${n}\\${t}`:t}function We(e){if(l()==="win32"&&ye(e.command)){const n=[e.command,...e.args].map(W).join(" ");return{command:process.env.ComSpec||"cmd.exe",args:["/d","/s","/c",`"${n}"`],windowsVerbatimArguments:!0}}return{command:e.command,args:e.args}}function je(e,n,t=process.env){return{detached:!0,stdio:["ignore",n,n],env:t,windowsHide:!0,...e.windowsVerbatimArguments?{windowsVerbatimArguments:!0}:{}}}function Fe(){const e=k();i.writeFileSync(x,Se(e,d)),i.writeFileSync(H,we(x)),Q(),z();const n=ge({userId:Me(),command:c.join(process.env.SystemRoot||"C:\\Windows","System32","wscript.exe"),arguments:[W(H)]});i.writeFileSync(_,n,"utf8");const t=he({taskName:I,xmlPath:_});try{return a(t.create,{stdio:"pipe",windowsHide:!0}),a(t.run,{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}function Be(){ve(I,e=>{a(e,{stdio:"pipe",windowsHide:!0})})}function Z(){const e={};for(const n of B)process.env[n]&&(e[n]=process.env[n]);if(e.HOME??=u.homedir(),e.PATH=fe({pathValue:e.PATH,env:e}),e.USER??=u.userInfo().username,l()==="win32"){const n=process.env.TEMP||process.env.TMP||u.tmpdir(),t=u.homedir();e.USERPROFILE??=t;const r=c.win32.parse(t);e.HOMEDRIVE??=r.root.replace(/[\\/]$/,"")||process.env.HOMEDRIVE||"",e.HOMEPATH??=t.slice((e.HOMEDRIVE||"").length)||process.env.HOMEPATH||"",e.USERNAME??=process.env.USERNAME||u.userInfo().username,e.COMPUTERNAME??=process.env.COMPUTERNAME||"",e.TMPDIR??=n,e.TEMP??=n,e.TMP??=n}else e.TMPDIR??="/tmp";return e}function Ve(){const e=Z(),n=k(),t=Object.entries(e).map(([o,f])=>` <key>${N(o)}</key>
3
+ <string>${N(f)}</string>`).join(`
4
+ `),r=[n.command,...n.args].map(o=>` <string>${N(o)}</string>`).join(`
5
5
  `);return`<?xml version="1.0" encoding="UTF-8"?>
6
6
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
7
7
  <plist version="1.0">
8
8
  <dict>
9
9
  <key>Label</key>
10
- <string>${H}</string>
10
+ <string>${O}</string>
11
11
  <key>ProgramArguments</key>
12
12
  <array>
13
13
  ${r}
@@ -27,7 +27,7 @@ ${t}
27
27
  <key>StandardErrorPath</key>
28
28
  <string>${d}</string>
29
29
  </dict>
30
- </plist>`}const D=c.join(u.homedir(),".config/systemd/user/shennian.service");function Be(){const e=ee(),n=k(),t=Object.entries(e).map(([o,S])=>`Environment=${o}=${S}`).join(`
30
+ </plist>`}const D=c.join(u.homedir(),".config/systemd/user/shennian.service");function Je(){const e=Z(),n=k(),t=Object.entries(e).map(([o,f])=>`Environment=${o}=${f}`).join(`
31
31
  `),r=[n.command,...n.args].map(Ee).join(" ");return`[Unit]
32
32
  Description=Shennian Agent Daemon
33
33
  After=network.target
@@ -41,6 +41,6 @@ StandardOutput=append:${d}
41
41
  StandardError=append:${d}
42
42
 
43
43
  [Install]
44
- WantedBy=default.target`}const N="ShennianAgent";function Ve(e){a(e,{stdio:"pipe",windowsHide:!0})}function Ge(){const e=process.getuid?.();if(typeof e=="number")return e;const n=u.userInfo();return typeof n.uid=="number"?n.uid:n.username}function ne(e=Ge()){return`gui/${e}/${H}`}function Je(e=Ve){try{return e(`launchctl unload "${m}" 2>/dev/null; launchctl load -w "${m}"`),e(`launchctl kickstart -k "${ne()}"`),!0}catch{try{return e(`launchctl load -w "${m}"`),e(`launchctl kickstart -k "${ne()}"`),!0}catch{return!1}}}function Ke(){i.mkdirSync(g,{recursive:!0});const e={};for(const[n,t]of Object.entries(process.env))t!==void 0&&Pe(n)&&(e[n]=t);i.writeFileSync(h("env.json"),JSON.stringify(e,null,2))}function te(e={}){i.mkdirSync(g,{recursive:!0});const n=f({cleanupStale:!0});if(n.pid!==null&&n.running&&!re(n)){e.quiet||console.log(s.green(`\u2713 Background service already running (PID ${n.pid})`));return}ie(n);const t=i.openSync(d,"a"),r=Ue(k()),o=ue(r.command,r.args,je(r,t));o.unref(),i.closeSync(t),xe(o.pid),e.quiet||(console.log(s.green(`\u2713 Background service started (PID ${o.pid})`)),console.log(s.gray(` Logs: ${d}`)))}function re(e,n=I(),t=ke()){return e.pid===null||!e.running?!1:e.adopted===!0||n?.version!==t}function ie(e=f({cleanupStale:!0})){if(!(!re(e)||e.pid===null))try{process.kill(e.pid,"SIGTERM"),G(e.pid,5e3)||(process.kill(e.pid,"SIGKILL"),G(e.pid,2e3));try{I()?.pid===e.pid&&i.unlinkSync(p)}catch{}}catch{}}function oe(){i.mkdirSync(g,{recursive:!0});const e=l();switch(k().mode==="direct"&&O(F)&&console.warn(s.yellow("\u26A0 Warning: Current CLI path is temporary (npx). Auto-start may not work after reboot.\n Run `npm install -g shennian@latest` for reliable auto-start.")),e){case"darwin":{const t=c.dirname(m);return i.mkdirSync(t,{recursive:!0}),i.writeFileSync(m,Fe()),Je()}case"linux":{const t=c.dirname(D);i.mkdirSync(t,{recursive:!0}),i.writeFileSync(D,Be());try{a("systemctl --user daemon-reload && systemctl --user enable shennian",{stdio:"pipe",windowsHide:!0})}catch{return!1}try{a(`loginctl enable-linger ${u.userInfo().username}`,{stdio:"pipe",windowsHide:!0})}catch{}try{return a("systemctl --user restart shennian",{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}case"win32":return We()}}function Xe(){const n=f({cleanupStale:!0}).pid;if(n===null)return{};if(!w(n)){try{i.unlinkSync(p)}catch{}return{stalePid:n}}try{return process.kill(n,"SIGTERM"),{stoppedPid:n}}catch(t){return{error:t instanceof Error?t.message:String(t)}}}async function qe(e=5e3){const n=Xe();if(!n.stoppedPid)return n;if(!await A(n.stoppedPid,e))try{process.kill(n.stoppedPid,"SIGKILL"),await A(n.stoppedPid,2e3)}catch(r){return{...n,error:r instanceof Error?r.message:String(r)}}try{i.unlinkSync(p)}catch{}T();for(const r of X())if(r!==n.stoppedPid)try{process.kill(r,"SIGTERM"),await A(r,2e3)}catch{}return n}function Ye(e={}){q(Y(e.api)),Re(),ie(),oe()||te({quiet:e.json}),e.json?v(L(()=>f({cleanupStale:!0}))):console.log(s.green("\u2713 Background service started"))}async function ze(e={}){switch(i.mkdirSync(g,{recursive:!0}),i.writeFileSync(R,new Date().toISOString()),l()){case"darwin":{if(i.existsSync(m))try{a(`launchctl unload -w "${m}"`,{stdio:"pipe",windowsHide:!0})}catch{}break}case"linux":{try{a("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}break}case"win32":{try{a(`schtasks /change /tn "${N}" /disable`,{stdio:"pipe",windowsHide:!0})}catch{}try{a(`schtasks /end /tn "${N}"`,{stdio:"pipe",windowsHide:!0})}catch{}break}}const t=await qe(),r=f({cleanupStale:!0});if(e.json){v(t.error?{...r,error:t.error}:r);return}t.error?console.error(s.red(`\u2717 Failed to stop: ${t.error}`)):console.log(s.green("\u2713 Background service stopped"))}function se(e){Ke(),Ye(e)}async function M(e={}){await ze(e)}async function A(e,n=5e3){const t=Date.now();for(;Date.now()-t<n;){if(!w(e))return!0;await new Promise(r=>setTimeout(r,100))}return!w(e)}async function Qe(e={}){q(Y(e.api));const n=be();if(n!==null&&w(n))try{process.kill(n,"SIGTERM"),await A(n)||(process.kill(n,"SIGKILL"),await A(n,2e3));try{i.unlinkSync(p)}catch{}T(),e.json||console.log(s.green(`\u2713 Background service stopped (PID ${n})`))}catch(t){const r=t instanceof Error?t.message:String(t);e.json?v({...f(),error:r}):console.error(s.red(`\u2717 Failed to stop: ${r}`));return}else if(n!==null){try{i.unlinkSync(p)}catch{}T(),e.json||console.log(s.yellow(`\u26A0 Service process no longer exists, cleaned up stale PID ${n}`))}else e.json||console.log(s.gray("\u25CF Background service not running, starting it now"));if(l()==="linux"&&oe()){e.json&&v(L(()=>f({cleanupStale:!0})));return}te({quiet:e.json}),e.json&&v(L(()=>f({cleanupStale:!0})))}function ae(e={}){const n=f({cleanupStale:!0});if(e.json){v(n);return}if(n.pid===null){console.log(s.gray("\u25CF Background service not running"));return}n.running?(console.log(s.green(`\u25CF Background service running (PID ${n.pid})`)),console.log(s.gray(` Logs: ${d}`))):console.log(s.yellow(`\u25CF Background service stopped (PID ${n.pid} is stale)`))}function ce(e){if(!i.existsSync(d)){console.log(s.gray("No logs yet"));return}try{const n=a(u.platform()==="win32"?`powershell Get-Content -Tail ${e.lines} "${d}"`:`tail -n ${e.lines} "${d}"`,{encoding:"utf-8",windowsHide:!0});process.stdout.write(n)}catch{const n=i.readFileSync(d,"utf-8").split(`
44
+ WantedBy=default.target`}const I="ShennianAgent";function Ge(e){a(e,{stdio:"pipe",windowsHide:!0})}function Ke(){const e=process.getuid?.();if(typeof e=="number")return e;const n=u.userInfo();return typeof n.uid=="number"?n.uid:n.username}function ee(e=Ke()){return`gui/${e}/${O}`}function Xe(e=Ge){try{return e(`launchctl unload "${p}" 2>/dev/null; launchctl load -w "${p}"`),e(`launchctl kickstart -k "${ee()}"`),!0}catch{try{return e(`launchctl load -w "${p}"`),e(`launchctl kickstart -k "${ee()}"`),!0}catch{return!1}}}function qe(){i.mkdirSync(h,{recursive:!0});const e={};for(const[n,t]of Object.entries(process.env))t!==void 0&&De(n)&&(e[n]=t);i.writeFileSync(S("env.json"),JSON.stringify(e,null,2))}function ne(e={}){i.mkdirSync(h,{recursive:!0});const n=m({cleanupStale:!0});if(n.pid!==null&&n.running&&!te(n)){e.quiet||console.log(s.green(`\u2713 Background service already running (PID ${n.pid})`));return}re(n);const t=i.openSync(d,"a"),r=We(k()),o=ue(r.command,r.args,je(r,t));o.unref(),i.closeSync(t),He(o.pid),e.quiet||(console.log(s.green(`\u2713 Background service started (PID ${o.pid})`)),console.log(s.gray(` Logs: ${d}`)))}function te(e,n=P(),t=Ie()){return e.pid===null||!e.running?!1:e.adopted===!0||n?.version!==t}function re(e=m({cleanupStale:!0})){if(!(!te(e)||e.pid===null))try{process.kill(e.pid,"SIGTERM"),V(e.pid,5e3)||(process.kill(e.pid,"SIGKILL"),V(e.pid,2e3));try{P()?.pid===e.pid&&i.unlinkSync(g)}catch{}}catch{}}function ie(){i.mkdirSync(h,{recursive:!0});const e=l();switch(k().mode==="direct"&&L(j)&&console.warn(s.yellow("\u26A0 Warning: Current CLI path is temporary (npx). Auto-start may not work after reboot.\n Run `npm install -g shennian@latest` for reliable auto-start.")),e){case"darwin":{const t=c.dirname(p);return i.mkdirSync(t,{recursive:!0}),i.writeFileSync(p,Ve()),Xe()}case"linux":{const t=c.dirname(D);i.mkdirSync(t,{recursive:!0}),i.writeFileSync(D,Je());try{a("systemctl --user daemon-reload && systemctl --user enable shennian",{stdio:"pipe",windowsHide:!0})}catch{return!1}try{a(`loginctl enable-linger ${u.userInfo().username}`,{stdio:"pipe",windowsHide:!0})}catch{}try{return a("systemctl --user restart shennian",{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}case"win32":return Fe()}}function Ye(){const n=m({cleanupStale:!0}).pid;if(n===null)return{};if(!E(n)){try{i.unlinkSync(g)}catch{}return{stalePid:n}}try{return process.kill(n,"SIGTERM"),{stoppedPid:n}}catch(t){return{error:t instanceof Error?t.message:String(t)}}}async function oe(e=5e3){const n=Ye();if(!n.stoppedPid)return n;if(!await C(n.stoppedPid,e))try{process.kill(n.stoppedPid,"SIGKILL"),await C(n.stoppedPid,2e3)}catch(r){return{...n,error:r instanceof Error?r.message:String(r)}}try{i.unlinkSync(g)}catch{}xe();for(const r of K())if(r!==n.stoppedPid)try{process.kill(r,"SIGTERM"),await C(r,2e3)}catch{}return n}function ze(e={}){X(q(e.api)),Le(),re(),ie()||ne({quiet:e.json}),e.json?v(b(()=>m({cleanupStale:!0}))):console.log(s.green("\u2713 Background service started"))}async function Qe(e={}){switch(i.mkdirSync(h,{recursive:!0}),i.writeFileSync(R,new Date().toISOString()),l()){case"darwin":{if(i.existsSync(p))try{a(`launchctl unload -w "${p}"`,{stdio:"pipe",windowsHide:!0})}catch{}break}case"linux":{try{a("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}break}case"win32":{try{a(`schtasks /change /tn "${I}" /disable`,{stdio:"pipe",windowsHide:!0})}catch{}try{a(`schtasks /end /tn "${I}"`,{stdio:"pipe",windowsHide:!0})}catch{}break}}const t=await oe(),r=m({cleanupStale:!0});if(e.json){v(t.error?{...r,error:t.error}:r);return}t.error?console.error(s.red(`\u2717 Failed to stop: ${t.error}`)):console.log(s.green("\u2713 Background service stopped"))}function se(e){qe(),ze(e)}async function $(e={}){await Qe(e)}async function C(e,n=5e3){const t=Date.now();for(;Date.now()-t<n;){if(!E(e))return!0;await new Promise(r=>setTimeout(r,100))}return!E(e)}async function Ze(e={}){X(q(e.api));const n=l();n==="win32"&&Be();const t=await oe();if(t.error){e.json?v({...m(),error:t.error}):console.error(s.red(`\u2717 Failed to stop: ${t.error}`));return}if(t.stoppedPid!==void 0?e.json||console.log(s.green(`\u2713 Background service stopped (PID ${t.stoppedPid})`)):t.stalePid!==void 0?e.json||console.log(s.yellow(`\u26A0 Service process no longer exists, cleaned up stale PID ${t.stalePid}`)):e.json||console.log(s.gray("\u25CF Background service not running, starting it now")),(n==="linux"||n==="win32")&&ie()){e.json&&v(b(()=>m({cleanupStale:!0})));return}ne({quiet:e.json}),e.json&&v(b(()=>m({cleanupStale:!0})))}function ae(e={}){const n=m({cleanupStale:!0});if(e.json){v(n);return}if(n.pid===null){console.log(s.gray("\u25CF Background service not running"));return}n.running?(console.log(s.green(`\u25CF Background service running (PID ${n.pid})`)),console.log(s.gray(` Logs: ${d}`))):console.log(s.yellow(`\u25CF Background service stopped (PID ${n.pid} is stale)`))}function ce(e){if(!i.existsSync(d)){console.log(s.gray("No logs yet"));return}try{const n=a(u.platform()==="win32"?`powershell Get-Content -Tail ${e.lines} "${d}"`:`tail -n ${e.lines} "${d}"`,{encoding:"utf-8",windowsHide:!0});process.stdout.write(n)}catch{const n=i.readFileSync(d,"utf-8").split(`
45
45
  `).slice(-e.lines);console.log(n.join(`
46
- `))}}async function Ze(){switch(await M(),l()){case"darwin":{if(i.existsSync(m)){try{a(`launchctl unload -w "${m}"`,{stdio:"pipe",windowsHide:!0})}catch{}i.unlinkSync(m),console.log(s.green("\u2713 Auto-start uninstalled (launchd)"))}break}case"linux":{try{a("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}if(i.existsSync(D)){i.unlinkSync(D);try{a("systemctl --user daemon-reload",{stdio:"pipe",windowsHide:!0})}catch{}}console.log(s.green("\u2713 Auto-start uninstalled (systemd)"));break}case"win32":{Q();try{i.unlinkSync(_)}catch{}try{i.unlinkSync($)}catch{}try{i.unlinkSync(C)}catch{}Z(),console.log(s.green("\u2713 Auto-start uninstalled (Task Scheduler)"));break}}}function hn(e){e.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>se(r)),e.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(r=>M(r)),e.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>ae(r)),e.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>ce({lines:parseInt(r.lines,10)}));const n=e.command("daemon",{hidden:!0}).description("Deprecated: use top-level start/stop/status/logs"),t=(r,o)=>{o||console.error(s.yellow(`\u26A0 Deprecated command. Use \`shennian ${r}\` instead.`))};n.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("start",r.json),se(r)}),n.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(async r=>{t("stop",r.json),await M(r)}),n.command("restart").description("Restart the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("stop && shennian start",r.json),Qe(r)}),n.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>{t("status",r.json),ae(r)}),n.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>{t("logs"),ce({lines:parseInt(r.lines,10)})}),n.command("uninstall").description("Uninstall auto-start service").action(Ze)}export{je as buildDaemonSpawnOptions,wn as buildWindowsLauncherCommand,En as buildWindowsScheduledTaskCommands,vn as buildWindowsScheduledTaskXml,In as buildWindowsStartupVbs,ee as captureEnvForService,T as clearDaemonLauncher,fn as clearDaemonPidIfOwner,Re as clearRemoteAccessDisabled,Oe as createDaemonInstanceId,X as findRunningDaemonProcessIds,J as getCurrentProcessDaemonLauncher,f as getDaemonStatus,ne as getLaunchdServiceTarget,oe as installService,O as isEphemeralCliPath,Sn as isIsolatedDaemonE2eInstance,Le as isRemoteAccessDisabled,Pe as isSafeSnapshotEnvKey,An as isShennianRunServiceCommand,xe as recordStartedDaemon,hn as registerDaemonCommand,Je as reloadLaunchdAgent,De as resolveServiceLaunchSpec,Ke as saveEnvSnapshot,re as shouldReplaceRunningDaemon,te as startDaemonProcess,K as writeDaemonLauncher,Te as writeDaemonPid};
46
+ `))}}async function en(){switch(await $(),l()){case"darwin":{if(i.existsSync(p)){try{a(`launchctl unload -w "${p}"`,{stdio:"pipe",windowsHide:!0})}catch{}i.unlinkSync(p),console.log(s.green("\u2713 Auto-start uninstalled (launchd)"))}break}case"linux":{try{a("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}if(i.existsSync(D)){i.unlinkSync(D);try{a("systemctl --user daemon-reload",{stdio:"pipe",windowsHide:!0})}catch{}}console.log(s.green("\u2713 Auto-start uninstalled (systemd)"));break}case"win32":{z();try{i.unlinkSync(x)}catch{}try{i.unlinkSync(H)}catch{}try{i.unlinkSync(_)}catch{}Q(),console.log(s.green("\u2713 Auto-start uninstalled (Task Scheduler)"));break}}}function wn(e){e.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>se(r)),e.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(r=>$(r)),e.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>ae(r)),e.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>ce({lines:parseInt(r.lines,10)}));const n=e.command("daemon",{hidden:!0}).description("Deprecated: use top-level start/stop/status/logs"),t=(r,o)=>{o||console.error(s.yellow(`\u26A0 Deprecated command. Use \`shennian ${r}\` instead.`))};n.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("start",r.json),se(r)}),n.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(async r=>{t("stop",r.json),await $(r)}),n.command("restart").description("Restart the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("stop && shennian start",r.json),Ze(r)}),n.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>{t("status",r.json),ae(r)}),n.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>{t("logs"),ce({lines:parseInt(r.lines,10)})}),n.command("uninstall").description("Uninstall auto-start service").action(en)}export{je as buildDaemonSpawnOptions,vn as buildWindowsLauncherCommand,Pn as buildWindowsScheduledTaskCommands,kn as buildWindowsScheduledTaskSuspendCommands,In as buildWindowsScheduledTaskXml,An as buildWindowsStartupVbs,Z as captureEnvForService,xe as clearDaemonLauncher,hn as clearDaemonPidIfOwner,Le as clearRemoteAccessDisabled,Te as createDaemonInstanceId,K as findRunningDaemonProcessIds,J as getCurrentProcessDaemonLauncher,m as getDaemonStatus,ee as getLaunchdServiceTarget,ie as installService,L as isEphemeralCliPath,gn as isIsolatedDaemonE2eInstance,Re as isRemoteAccessDisabled,De as isSafeSnapshotEnvKey,bn as isShennianRunServiceCommand,He as recordStartedDaemon,wn as registerDaemonCommand,Xe as reloadLaunchdAgent,Ne as resolveServiceLaunchSpec,qe as saveEnvSnapshot,te as shouldReplaceRunningDaemon,ne as startDaemonProcess,Dn as suspendWindowsScheduledTask,G as writeDaemonLauncher,Oe as writeDaemonPid};
@@ -2,4 +2,10 @@ import { callDaemonIpc } from '../daemon-ipc/client.js';
2
2
  import type { JsonValue } from './pending-authorized-actions.js';
3
3
  export declare function executeRoomTool(rawInput: unknown, invoke?: typeof callDaemonIpc, env?: NodeJS.ProcessEnv): Promise<JsonValue>;
4
4
  export declare function handleRoomToolRpc(value: unknown, invoke?: typeof callDaemonIpc, env?: NodeJS.ProcessEnv): Promise<Record<string, unknown> | null>;
5
+ /**
6
+ * The daemon keeps a canonical Room URL for validation and persistence. At the
7
+ * MCP host boundary, expose that same Room route in the focused workspace mode
8
+ * so Codex, Claude Code, and WorkBuddy never land on the generic app shell.
9
+ */
10
+ export declare function focusRoomWebUrlForHost(result: JsonValue): JsonValue;
5
11
  export declare function runRoomToolServer(input?: NodeJS.ReadableStream, output?: NodeJS.WritableStream): Promise<void>;
@@ -1,3 +1,3 @@
1
- import _ from"node:readline";import{callDaemonIpc as d}from"../daemon-ipc/client.js";import{DaemonIpcError as N}from"../daemon-ipc/protocol.js";import{normalizeRoomActionParameters as O}from"./room-action-schema.js";import{normalizeRoomToolInput as T,RoomToolInputError as y,SHENNIAN_ROOM_TOOL as A,SHENNIAN_ROOM_TOOL_NAME as p}from"./tool-schema.js";import{AgentToolError as E,executeAgentTool as R,SHENNIAN_AGENT_TOOL as w,SHENNIAN_AGENT_TOOL_NAME as h}from"./agent-tool.js";async function I(t,e=d,i=process.env){const o=await b(t,e,i);return o.authorizedActionId?e({method:"authorized-action.wait",params:{id:o.authorizedActionId,timeoutMs:12e4},timeoutMs:135e3}):e({method:"room-action.execute",params:{registryKey:o.registryKey,invocationToken:o.invocationToken,action:o.action,actionParameters:o.actionParameters},timeoutMs:o.action==="room.read"?65e3:15e3})}async function j(t,e=d,i=process.env){if(!t||typeof t!="object"||Array.isArray(t))return m(null,-32600,"Invalid Request");const o=t,r=S(o.id);if(o.jsonrpc!=="2.0"||typeof o.method!="string")return m(r,-32600,"Invalid Request");if(o.method.startsWith("notifications/"))return null;if(o.method==="initialize")return f(r,{protocolVersion:x(o.params),capabilities:{tools:{listChanged:!1}},serverInfo:{name:"shennian-room",version:"1.0.0"}});if(o.method==="ping")return f(r,{});if(o.method==="tools/list")return f(r,{tools:[A,w]});if(o.method!=="tools/call")return m(r,-32601,"Method not found");const n=M(o.params);if(!n||n.name!==p&&n.name!==h)return m(r,-32602,"Invalid params");try{const c=n.name===h?await R(n.arguments):await I(n.arguments,e,i);return f(r,{content:[{type:"text",text:JSON.stringify(c)}],structuredContent:c,isError:!1})}catch(c){const s=v(c);return f(r,{content:[{type:"text",text:JSON.stringify(s)}],structuredContent:s,isError:!0})}}async function b(t,e,i){try{return T(t)}catch(o){let r=i.SHENNIAN_ROOM_REGISTRY_KEY?.trim();if(!(o instanceof y)||o.code!=="session_context_required"||r&&!/^[a-f0-9]{64}$/.test(r)||!t||typeof t!="object"||Array.isArray(t))throw o;const n=t;if(Object.keys(n).some(a=>a.startsWith("_shennian")))throw o;const c={create:"room.create",join:"room.join",read:"room.read",send:"room.send",mode:"room.mode",status:"room.status"},s=typeof n.action=="string"?c[n.action]:void 0;if(!s)throw o;const g=Object.fromEntries(Object.entries(n).filter(([a])=>a!=="action")),l=O(s,g);if(!r){const a=await e({method:"room-session.resolve-active-workspace",params:{workspacePath:process.cwd()},timeoutMs:2e3});if(typeof a?.registryKey!="string"||!/^[a-f0-9]{64}$/.test(a.registryKey))throw o;r=a.registryKey}const u=(await e({method:"room-invocation.issue",params:{registryKey:r,action:s,targetTool:p,actionParameters:l},timeoutMs:2e3}))?.invocationToken;if(typeof u!="string"||!/^sni_v1_[A-Za-z0-9_-]{40,80}$/.test(u))throw o;return{action:s,actionParameters:l,registryKey:r,invocationToken:u}}}async function H(t=process.stdin,e=process.stdout){const i=_.createInterface({input:t,crlfDelay:1/0});for await(const o of i){if(!o.trim())continue;let r;try{r=JSON.parse(o)}catch{e.write(`${JSON.stringify(m(null,-32700,"Parse error"))}
2
- `);continue}const n=await j(r);n&&e.write(`${JSON.stringify(n)}
3
- `)}}function S(t){return typeof t=="string"||typeof t=="number"||t===null?t:null}function x(t){if(t&&typeof t=="object"){const e=t.protocolVersion;if(typeof e=="string"&&/^\d{4}-\d{2}-\d{2}$/.test(e))return e}return"2025-06-18"}function M(t){if(!t||typeof t!="object"||Array.isArray(t))return null;const e=t;return typeof e.name=="string"?{name:e.name,arguments:e.arguments??{}}:null}function v(t){return t instanceof y||t instanceof E||t instanceof N?{ok:!1,error:{code:t.code,message:t.message}}:{ok:!1,error:{code:"room_tool_failed",message:"Shennian Room action failed."}}}function f(t,e){return{jsonrpc:"2.0",id:t,result:e}}function m(t,e,i){return{jsonrpc:"2.0",id:t,error:{code:e,message:i}}}export{I as executeRoomTool,j as handleRoomToolRpc,H as runRoomToolServer};
1
+ import g from"node:readline";import{callDaemonIpc as p}from"../daemon-ipc/client.js";import{DaemonIpcError as _}from"../daemon-ipc/protocol.js";import{normalizeRoomActionParameters as A}from"./room-action-schema.js";import{normalizeRoomToolInput as N,RoomToolInputError as y,SHENNIAN_ROOM_TOOL as O,SHENNIAN_ROOM_TOOL_NAME as l}from"./tool-schema.js";import{AgentToolError as T,executeAgentTool as w,SHENNIAN_AGENT_TOOL as E,SHENNIAN_AGENT_TOOL_NAME as R}from"./agent-tool.js";async function b(t,o=p,i=process.env){const r=await x(t,o,i);return r.authorizedActionId?o({method:"authorized-action.wait",params:{id:r.authorizedActionId,timeoutMs:12e4},timeoutMs:135e3}):o({method:"room-action.execute",params:{registryKey:r.registryKey,invocationToken:r.invocationToken,action:r.action,actionParameters:r.actionParameters},timeoutMs:r.action==="room.read"?65e3:15e3})}async function j(t,o=p,i=process.env){if(!t||typeof t!="object"||Array.isArray(t))return m(null,-32600,"Invalid Request");const r=t,e=M(r.id);if(r.jsonrpc!=="2.0"||typeof r.method!="string")return m(e,-32600,"Invalid Request");if(r.method.startsWith("notifications/"))return null;if(r.method==="initialize")return f(e,{protocolVersion:z(r.params),capabilities:{tools:{listChanged:!1}},serverInfo:{name:"shennian-room",version:"1.0.0"}});if(r.method==="ping")return f(e,{});if(r.method==="tools/list")return f(e,{tools:[O,E]});if(r.method!=="tools/call")return m(e,-32601,"Method not found");const n=P(r.params);if(!n||n.name!==l&&n.name!==R)return m(e,-32602,"Invalid params");try{const c=n.name===l?I(await b(n.arguments,o,i)):await w(n.arguments);return f(e,{content:[{type:"text",text:JSON.stringify(c)}],structuredContent:c,isError:!1})}catch(c){const s=v(c);return f(e,{content:[{type:"text",text:JSON.stringify(s)}],structuredContent:s,isError:!0})}}function I(t){if(!t||typeof t!="object"||Array.isArray(t))return t;const o=t.room;if(!o||typeof o!="object"||Array.isArray(o))return t;const i=S(o.webUrl);return i?{...t,room:{...o,webUrl:i}}:t}function S(t){if(typeof t!="string")return null;let o;try{o=new URL(t)}catch{return null}return!["http:","https:"].includes(o.protocol)||o.username||o.password||o.search||o.hash||!/^\/spaces\/[A-Za-z0-9_-]+$/.test(o.pathname)?null:(o.searchParams.set("focus","room"),o.toString())}async function x(t,o,i){try{return N(t)}catch(r){let e=i.SHENNIAN_ROOM_REGISTRY_KEY?.trim();if(!(r instanceof y)||r.code!=="session_context_required"||e&&!/^[a-f0-9]{64}$/.test(e)||!t||typeof t!="object"||Array.isArray(t))throw r;const n=t;if(Object.keys(n).some(a=>a.startsWith("_shennian")))throw r;const c={create:"room.create",join:"room.join",read:"room.read",send:"room.send",mode:"room.mode",status:"room.status"},s=typeof n.action=="string"?c[n.action]:void 0;if(!s)throw r;const h=Object.fromEntries(Object.entries(n).filter(([a])=>a!=="action")),d=A(s,h);if(!e){const a=await o({method:"room-session.resolve-active-workspace",params:{workspacePath:process.cwd()},timeoutMs:2e3});if(typeof a?.registryKey!="string"||!/^[a-f0-9]{64}$/.test(a.registryKey))throw r;e=a.registryKey}const u=(await o({method:"room-invocation.issue",params:{registryKey:e,action:s,targetTool:l,actionParameters:d},timeoutMs:2e3}))?.invocationToken;if(typeof u!="string"||!/^sni_v1_[A-Za-z0-9_-]{40,80}$/.test(u))throw r;return{action:s,actionParameters:d,registryKey:e,invocationToken:u}}}async function J(t=process.stdin,o=process.stdout){const i=g.createInterface({input:t,crlfDelay:1/0});for await(const r of i){if(!r.trim())continue;let e;try{e=JSON.parse(r)}catch{o.write(`${JSON.stringify(m(null,-32700,"Parse error"))}
2
+ `);continue}const n=await j(e);n&&o.write(`${JSON.stringify(n)}
3
+ `)}}function M(t){return typeof t=="string"||typeof t=="number"||t===null?t:null}function z(t){if(t&&typeof t=="object"){const o=t.protocolVersion;if(typeof o=="string"&&/^\d{4}-\d{2}-\d{2}$/.test(o))return o}return"2025-06-18"}function P(t){if(!t||typeof t!="object"||Array.isArray(t))return null;const o=t;return typeof o.name=="string"?{name:o.name,arguments:o.arguments??{}}:null}function v(t){return t instanceof y||t instanceof T||t instanceof _?{ok:!1,error:{code:t.code,message:t.message}}:{ok:!1,error:{code:"room_tool_failed",message:"Shennian Room action failed."}}}function f(t,o){return{jsonrpc:"2.0",id:t,result:o}}function m(t,o,i){return{jsonrpc:"2.0",id:t,error:{code:o,message:i}}}export{b as executeRoomTool,I as focusRoomWebUrlForHost,j as handleRoomToolRpc,J as runRoomToolServer};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.3.15",
3
+ "version": "0.3.17",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {