shennian 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/native/macos-background-client/Shennian.app/Contents/CodeResources +0 -0
- package/dist/native/macos-background-client/Shennian.app/Contents/Info.plist +17 -0
- package/dist/native/macos-background-client/Shennian.app/Contents/MacOS/ShennianClientLauncher +0 -0
- package/dist/native/macos-background-client/Shennian.app/Contents/Resources/en.lproj/InfoPlist.strings +2 -0
- package/dist/native/macos-background-client/Shennian.app/Contents/Resources/zh-Hans.lproj/InfoPlist.strings +2 -0
- package/dist/native/macos-background-client/Shennian.app/Contents/_CodeSignature/CodeResources +153 -0
- package/dist/publish-build-manifest.json +78 -43
- package/dist/src/agent-env.d.ts +1 -0
- package/dist/src/agent-env.js +4 -4
- package/dist/src/agents/codex.d.ts +3 -2
- package/dist/src/agents/codex.js +8 -8
- package/dist/src/agents/command-spec.js +1 -1
- package/dist/src/agents/config-status.js +1 -1
- package/dist/src/agents/detect.d.ts +1 -0
- package/dist/src/agents/detect.js +1 -1
- package/dist/src/agents/manager.d.ts +3 -3
- package/dist/src/agents/manager.js +9 -9
- package/dist/src/agents/model-registry/cache.d.ts +1 -1
- package/dist/src/agents/model-registry/cache.js +1 -1
- package/dist/src/agents/model-registry/discovery.js +3 -2
- package/dist/src/agents/model-registry/parsers.js +4 -4
- package/dist/src/agents/model-registry/service.js +1 -1
- package/dist/src/agents/model-registry/types.d.ts +1 -0
- package/dist/src/agents/pi-coding-agent-managed-permissions.d.ts +7 -0
- package/dist/src/agents/pi-coding-agent-managed-permissions.js +1 -0
- package/dist/src/agents/pi-coding-agent.d.ts +55 -0
- package/dist/src/agents/pi-coding-agent.js +4 -0
- package/dist/src/agents/pi-managed-extension.d.ts +12 -0
- package/dist/src/agents/pi-managed-extension.js +89 -0
- package/dist/src/agents/pi-managed-model-broker.d.ts +27 -0
- package/dist/src/agents/pi-managed-model-broker.js +1 -0
- package/dist/src/agents/pi-rpc-client.d.ts +31 -0
- package/dist/src/agents/pi-rpc-client.js +3 -0
- package/dist/src/agents/pi-session-ownership.d.ts +2 -0
- package/dist/src/agents/pi-session-ownership.js +2 -0
- package/dist/src/commands/daemon.d.ts +4 -0
- package/dist/src/commands/daemon.js +21 -17
- package/dist/src/commands/pair-browser.js +1 -1
- package/dist/src/manager/prompt.d.ts +1 -1
- package/dist/src/manager/prompt.js +2 -2
- package/dist/src/manager/registry.d.ts +4 -4
- package/dist/src/manager/runtime.d.ts +3 -3
- package/dist/src/room/activation-service.js +4 -4
- package/dist/src/room/device-authorization-client.d.ts +2 -0
- package/dist/src/room/managed-prompt-composer.d.ts +1 -1
- package/dist/src/room/managed-prompt-composer.js +2 -1
- package/dist/src/security/managed-agent-policy.js +1 -1
- package/dist/src/session/handlers/agent-workspace-validation.d.ts +24 -0
- package/dist/src/session/handlers/agent-workspace-validation.js +1 -0
- package/dist/src/session/handlers/chat.js +1 -1
- package/dist/src/session/manager.d.ts +1 -0
- package/dist/src/session/manager.js +1 -1
- package/dist/src/session/native-session-lifecycle.js +6 -6
- package/dist/src/session/queue.d.ts +5 -1
- package/dist/src/session/queue.js +2 -2
- package/dist/src/upgrade/engine.d.ts +1 -0
- package/dist/src/upgrade/engine.js +2 -2
- package/node_modules/@shennian/wire/dist/methods.d.ts +1 -1
- package/node_modules/@shennian/wire/dist/session.d.ts +2 -2
- package/node_modules/@shennian/wire/dist/session.js +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import c from"node:fs";import u from"node:path";import{randomUUID as w}from"node:crypto";import{AgentAdapter as _,registerAgent as E}from"./adapter.js";import{resolveBuiltinCommand as A,spawnAgentCommand as k}from"./command-spec.js";import{buildAgentProcessEnv as v,buildManagedAgentProcessEnv as x}from"../agent-env.js";import{loadConfig as T,resolveShennianPath as f}from"../config/index.js";import{buildInlineChannelPreamble as N,buildPlatformInstructions as R}from"./platform-instructions.js";import{PiRpcClient as b}from"./pi-rpc-client.js";import{registerManagedAgentRuntimeSecurity as M}from"../security/managed-agent-runtime.js";import{PI_CODING_AGENT_MANAGED_RUNTIME_SECURITY as C}from"./pi-coding-agent-managed-permissions.js";import{PiManagedModelBroker as D}from"./pi-managed-model-broker.js";import{writePiManagedExtension as B}from"./pi-managed-extension.js";import{recordPiSessionOwnership as O}from"./pi-session-ownership.js";M("pi-coding-agent",C);const q=32*1024,j=256*1024,U=3e3;function g(i){return i.replace(/[^a-zA-Z0-9_.-]/g,"_").slice(0,180)||"session"}function F(i){return i.data&&typeof i.data=="object"&&!Array.isArray(i.data)?i.data:{}}function $(i){if(typeof i=="string")return i;if(!i||typeof i!="object")return"";const t=i.content;return Array.isArray(t)?t.map(s=>{if(!s||typeof s!="object")return"";const e=s;return e.type==="text"&&typeof e.text=="string"?e.text:""}).join(""):""}function y(i,t=j){return i.length<=t?i:`${i.slice(0,t)}
|
|
2
|
+
[truncated]`}function G(i){return y(i,2e3).replace(/Bearer\s+[A-Za-z0-9._~-]+/gi,"Bearer [redacted]").replace(/\b(?:pimc|mrc)_[A-Za-z0-9_-]{20,}\b/g,"[redacted-capability]").replace(/\bsk-[A-Za-z0-9_-]{12,}\b/g,"[redacted-key]")}function z(i){try{return y(JSON.stringify(i))}catch{return"[unserializable]"}}function P(i){if(!i||typeof i!="object")return;const t=i;if(!(typeof t.input!="number"&&typeof t.output!="number"))return{inputTokens:typeof t.input=="number"?t.input:0,outputTokens:typeof t.output=="number"?t.output:0}}function H(i){const t=i?.trim();if(!t)return{};const s=t.indexOf("/");return s<=0||s===t.length-1?{modelId:t}:{provider:t.slice(0,s),modelId:t.slice(s+1)}}function X(i,t,s,e,n,a){const l=N(s,e,n,a),h=t?.filter(o=>o.kind!=="image")??[];if(!h.length)return`${l}${i}`;const p=h.map(o=>`[${o.name}](<${o.path}>)`);return`${l}${[i,"",...p].join(`
|
|
3
|
+
`)}`}function L(i,t){const s=[];for(const e of i??[]){if(e.kind!=="image"||!e.mimeType.startsWith("image/"))continue;let n=e.previewData?.trim()??"";const a=n.indexOf(",");if(n.startsWith("data:")&&a>=0&&(n=n.slice(a+1)),!n&&t&&e.path&&!/^https?:\/\//i.test(e.path))try{const l=c.statSync(e.path);l.isFile()&&l.size<=10*1024*1024&&(n=c.readFileSync(e.path).toString("base64"))}catch{n=""}n&&s.push({type:"image",data:n,mimeType:e.mimeType})}return s}class Z extends _{type="pi-coding-agent";process=null;rpc=null;shennianSessionId="";workDir="";sessionDir="";agentSessionId=null;externalChannel=null;extraEnv={};managedAgentPolicy=null;runId="";seq=0;active=!1;terminal=!0;stderr="";lastUsage;promptFile=null;modelBroker=null;persistNativeSession=!0;validationRoot=null;runPhase=null;configure(t){t.sessionId&&(this.shennianSessionId=t.sessionId),this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{},"managedAgentPolicy"in t&&(this.managedAgentPolicy=t.managedAgentPolicy??null)}async start(t,s,e,n){await this.stopProcess(),this.shennianSessionId=t,this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??s,this.agentSessionId=e??null;const a=f("runtime","pi-coding-agent",g(t));this.persistNativeSession=n?.persistNativeSession!==!1,this.validationRoot=this.persistNativeSession?null:u.join(a,`validation-${w()}`),this.sessionDir=this.validationRoot?u.join(this.validationRoot,"sessions"):u.join(a,"sessions"),c.mkdirSync(this.sessionDir,{recursive:!0,mode:448}),process.platform!=="win32"&&c.chmodSync(this.sessionDir,448),this.active=!1,this.terminal=!0}async send(t,s,e,n,a,l){await this.ensureProcess(a);const h=this.requireRpc(),p=await this.readState(),o=!!(p.isStreaming||p.isCompacting);(!o||!this.runId)&&(this.runId=w(),this.seq=0,this.terminal=!1,this.lastUsage=void 0,this.runPhase="thinking");const d=H(s);if(d.modelId&&!o&&(p.model?.id!==d.modelId||d.provider&&p.model?.provider!==d.provider)){if(!d.provider)throw new Error("Pi model selection must include provider/modelId");await h.request("set_model",{provider:d.provider,modelId:d.modelId})}e&&!o&&await h.request("set_thinking_level",{level:e});const r=o?l?.source==="room_activation"?"follow_up":"steer":"prompt",m={message:X(t,n,this.workDir,this.externalChannel,this.shennianSessionId,null)},S=L(n,!this.managedAgentPolicy);S.length&&!this.modelBroker&&(m.images=S),this.modelBroker?.setTurnActive(!0);try{await h.request(r,m)}catch(I){throw this.modelBroker?.setTurnActive(!1),I}}async resume(t){await this.stopProcess(),this.agentSessionId=t}async stop(){const t=this.rpc;t&&(await t.request("clear_queue").catch(()=>{}),await t.request("abort").catch(()=>{})),!this.terminal&&this.runId&&this.emitTerminal({state:"aborted"}),await this.stopProcess()}async getStatus(){if(!this.rpc||!this.process)return{active:!1,runId:null,canStop:!1};const t=await this.readState().catch(()=>null),s=!!(t?.isStreaming||t?.isCompacting||this.active);return{active:s,runId:s?this.runId:null,runPhase:s?this.runPhase??"thinking":null,canStop:s}}async setTitle(t,s){this.rpc&&await this.rpc.request("set_session_name",{name:s.slice(0,200)})}async ensureProcess(t){if(this.process&&this.rpc)return;const s=A("pi-coding-agent");if(!s)throw new Error('Command "pi" not found. Install @earendil-works/pi-coding-agent from the official package.');const e=["--mode","rpc","--session-dir",this.sessionDir,"--offline"];this.agentSessionId&&e.push("--session",this.agentSessionId);const n=await this.prepareHostedModel();if(n&&e.push("--provider","shennian-dashscope","--model","deepseek-v4-flash-0731"),this.managedAgentPolicy){e.push("--no-extensions","--no-skills","--no-prompt-templates","--no-context-files");const r=this.managedTools(),m=B({outputPath:u.join(f("runtime","pi-coding-agent",g(this.shennianSessionId)),"managed-extension.ts"),policy:this.managedAgentPolicy,allowRoomControl:!!this.extraEnv.SHENNIAN_MANAGED_ROOM_CONTEXT,allowManagerControl:!!this.extraEnv.SHENNIAN_MANAGER_SESSION_ID,allowHostShell:this.managedAgentPolicy.adapterPolicy.fileAccess==="host-full"});e.push("--no-builtin-tools","--extension",m),r.length?e.push("--tools",r.join(",")):e.push("--no-tools")}const a=R(this.workDir,this.externalChannel,this.shennianSessionId,t),l=f("runtime","pi-coding-agent",g(this.shennianSessionId));c.mkdirSync(l,{recursive:!0,mode:448}),this.promptFile=u.join(l,"instructions.md"),c.writeFileSync(this.promptFile,a,{encoding:"utf8",mode:384}),process.platform!=="win32"&&c.chmodSync(this.promptFile,384),e.push("--append-system-prompt",this.promptFile);const h={NO_COLOR:"1",...n?{PI_CODING_AGENT_DIR:n.configDir}:{}},p=this.managedAgentPolicy?x({agentType:this.type,providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:h}):v({...h,...this.extraEnv});let o;try{o=k(s,e,{cwd:this.workDir,stdio:["pipe","pipe","pipe"],env:p})}catch(r){throw await this.stopModelBroker(),r}this.process=o,this.stderr="",o.stderr?.on("data",r=>{const m=typeof r=="string"?r:r.toString("utf8");this.stderr=y(`${this.stderr}${m}`,q)});const d=new b(o);this.rpc=d,d.on("event",r=>this.handleRpcEvent(r)),d.on("protocolError",r=>this.emitError(r.message)),o.once("close",r=>{this.process===o&&(this.process=null,this.rpc=null,this.terminal||this.emitError(r===0?"Pi RPC process closed before agent_settled":`Pi RPC process exited with code ${r??"unknown"}${this.stderr?`: ${this.stderr}`:""}`),this.stopModelBroker())}),o.once("error",r=>{this.process===o&&this.emitError(r.message)});try{const r=await this.readState();if(!r.sessionId)throw new Error("Pi RPC did not return a session ID");if(r.isStreaming||r.isCompacting)throw new Error("Pi RPC session is not idle after startup");if(this.agentSessionId&&r.sessionId!==this.agentSessionId)throw new Error("Pi RPC resumed a different session");if(!r.sessionFile||!this.ownsSessionFile(r.sessionFile))throw new Error("Pi RPC returned a session file outside the Shennian-owned directory");this.agentSessionId=r.sessionId,this.persistNativeSession&&O(r.sessionId,this.sessionDir)}catch(r){throw await this.stopProcess(),r}}managedTools(){const t=this.managedAgentPolicy?.adapterPolicy;if(!t)return[];const s=["read","grep","find","ls"];(t.fileAccess==="workspace-write"||t.fileAccess==="host-full")&&s.push("edit","write");const e=!!this.extraEnv.SHENNIAN_MANAGER_SESSION_ID,n=!!this.extraEnv.SHENNIAN_MANAGED_ROOM_CONTEXT;return(e||n||t.fileAccess==="host-full")&&s.push("bash"),s}ownsSessionFile(t){const s=u.resolve(this.sessionDir),e=u.resolve(t),n=u.relative(s,e);return n!==""&&!n.startsWith("..")&&!u.isAbsolute(n)}async readState(){const t=await this.requireRpc().request("get_state");return F(t)}requireRpc(){if(!this.rpc)throw new Error("Pi RPC process is not running");return this.rpc}handleRpcEvent(t){if(t.type==="agent_start"){this.active=!0,this.runPhase="thinking",this.emitEvent({state:"start",agentSessionId:this.agentSessionId??void 0});return}if(t.type==="message_update"){this.lastUsage=P(t.usage)??this.lastUsage;const s=t.assistantMessageEvent;if(!s||typeof s!="object")return;const e=s;e.type==="text_delta"&&typeof e.delta=="string"?(this.runPhase="streaming_text",this.emitEvent({state:"delta",text:e.delta})):e.type==="thinking_delta"&&typeof e.delta=="string"&&(this.runPhase="thinking",this.emitEvent({state:"delta",text:e.delta,thinking:!0}));return}if(t.type==="tool_execution_start"){this.runPhase="tool_running",this.emitEvent({state:"tool-call",name:typeof t.toolName=="string"?t.toolName:"tool",args:t.args&&typeof t.args=="object"&&!Array.isArray(t.args)?t.args:{}});return}if(t.type==="tool_execution_end"){this.runPhase="thinking",this.emitEvent({state:"tool-result",name:typeof t.toolName=="string"?t.toolName:"tool",result:$(t.result)||z(t.result)});return}if(t.type==="tool_execution_update"){this.emitEvent({state:"notify",runPhase:"tool_running",message:"Pi tool is running"});return}if(t.type==="message_end"){const s=t.message;if(s&&typeof s=="object"){const e=s;this.lastUsage=P(e.usage)??this.lastUsage,e.stopReason==="error"&&(this.modelBroker?.setTurnActive(!1),this.emitError(typeof e.errorMessage=="string"?e.errorMessage:"Pi model request failed"))}return}if(t.type==="compaction_start"){this.runPhase="thinking",this.emitEvent({state:"notify",runPhase:"thinking",message:"Pi is compacting context"});return}if(t.type==="auto_retry_start"||t.type==="summarization_retry_scheduled"){this.runPhase="thinking",this.emitEvent({state:"notify",runPhase:"thinking",message:"Pi is retrying"});return}if(t.type==="extension_ui_request"){typeof t.id=="string"&&this.rpc?.notify("extension_ui_response",{id:t.id,cancelled:!0});return}if(t.type==="extension_error"){this.modelBroker?.setTurnActive(!1),this.emitError(typeof t.error=="string"?t.error:"Pi extension failed");return}t.type==="agent_settled"&&(this.active=!1,this.modelBroker?.setTurnActive(!1),this.emitTerminal({state:"final",agentSessionId:this.agentSessionId??void 0,usage:this.lastUsage}))}emitEvent(t){this.runId&&this.emit("agentEvent",{...t,runId:this.runId,seq:this.seq++})}emitTerminal(t){this.terminal||(this.terminal=!0,this.active=!1,this.runPhase=null,this.emitEvent(t))}emitError(t){this.modelBroker?.setTurnActive(!1),this.emitTerminal({state:"error",message:G(t)})}async prepareHostedModel(){const t=T(),s=t.machineToken?.trim();if(!s)return null;const e=new D({serverUrl:t.serverUrl?.trim()||"https://shennian.net",machineToken:s,sessionId:this.shennianSessionId}),n=await e.start(),a=f("runtime","pi-coding-agent",g(this.shennianSessionId),"agent-config");c.mkdirSync(a,{recursive:!0,mode:448}),process.platform!=="win32"&&c.chmodSync(a,448);const l=u.join(a,"models.json"),h={providers:{"shennian-dashscope":{baseUrl:n.baseUrl,api:"openai-completions",apiKey:n.apiKey,authHeader:!0,compat:{supportsDeveloperRole:!1,supportsReasoningEffort:!0},models:[{id:"deepseek-v4-flash-0731",name:"DeepSeek V4 Flash",reasoning:!0,input:["text"],thinkingLevelMap:{off:null,minimal:null,low:"low",medium:null,high:"high",xhigh:null,max:"max"}}]}}};return c.writeFileSync(l,`${JSON.stringify(h,null,2)}
|
|
4
|
+
`,{encoding:"utf8",mode:384}),process.platform!=="win32"&&c.chmodSync(l,384),this.modelBroker=e,{configDir:a}}async stopModelBroker(){const t=this.modelBroker;this.modelBroker=null,await t?.stop().catch(()=>{})}async stopProcess(){const t=this.process;this.process=null,this.rpc?.close(),this.rpc=null,t?(t.kill("SIGTERM"),await new Promise(s=>{let e=!1;const n=()=>{e||(e=!0,s())};t.once("close",n),setTimeout(()=>{t.kill("SIGKILL"),n()},U).unref()}),await this.stopModelBroker()):await this.stopModelBroker(),this.validationRoot&&(c.rmSync(this.validationRoot,{recursive:!0,force:!0}),this.validationRoot=null)}}E("pi-coding-agent",()=>new Z);export{Z as PiCodingAgentAdapter};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
|
|
2
|
+
/** Generate a dependency-light Pi extension inside the private runtime dir.
|
|
3
|
+
* The extension replaces Pi's built-ins: every file path is resolved beneath
|
|
4
|
+
* the verified workspace and Bash is reduced to the capability-scoped
|
|
5
|
+
* `shennian room|manager` CLI. This is defense in depth, not an OS sandbox. */
|
|
6
|
+
export declare function writePiManagedExtension(input: {
|
|
7
|
+
outputPath: string;
|
|
8
|
+
policy: CompiledManagedAgentPolicy;
|
|
9
|
+
allowRoomControl: boolean;
|
|
10
|
+
allowManagerControl: boolean;
|
|
11
|
+
allowHostShell: boolean;
|
|
12
|
+
}): string;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import t from"node:fs";import n from"node:path";function p(e){const r=n.resolve(e.policy.adapterPolicy.workDir),i=e.policy.adapterPolicy.fileAccess!=="workspace-read",o=e.allowRoomControl,a=e.allowManagerControl,s=e.allowHostShell,c=`
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { spawn } from 'node:child_process';
|
|
5
|
+
import { Type } from 'typebox';
|
|
6
|
+
|
|
7
|
+
const ROOT = ${JSON.stringify(r)};
|
|
8
|
+
const WRITABLE = ${JSON.stringify(i)};
|
|
9
|
+
const ALLOW_ROOM = ${JSON.stringify(o)};
|
|
10
|
+
const ALLOW_MANAGER = ${JSON.stringify(a)};
|
|
11
|
+
const ALLOW_HOST_SHELL = ${JSON.stringify(s)};
|
|
12
|
+
const MAX_READ = 1024 * 1024;
|
|
13
|
+
const MAX_WRITE = 4 * 1024 * 1024;
|
|
14
|
+
const SENSITIVE = /(^|[\\/])(\\.env(?:\\.|$)|\\.ssh|\\.aws|\\.config|\\.shennian|Library[\\/]Keychains|AppData[\\/]Roaming[\\/]Microsoft[\\/]Credentials)([\\/]|$)/i;
|
|
15
|
+
|
|
16
|
+
function inside(root, candidate) {
|
|
17
|
+
const relative = path.relative(root, candidate);
|
|
18
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
19
|
+
}
|
|
20
|
+
function rejectSensitive(candidate) {
|
|
21
|
+
if (SENSITIVE.test(candidate)) throw new Error('managed_path_sensitive');
|
|
22
|
+
}
|
|
23
|
+
function resolveExisting(raw) {
|
|
24
|
+
const candidate = path.resolve(ROOT, String(raw || '.'));
|
|
25
|
+
rejectSensitive(candidate);
|
|
26
|
+
const real = fs.realpathSync(candidate);
|
|
27
|
+
if (!inside(ROOT, real)) throw new Error('managed_path_outside_workspace');
|
|
28
|
+
return real;
|
|
29
|
+
}
|
|
30
|
+
function resolveWrite(raw) {
|
|
31
|
+
if (!WRITABLE) throw new Error('managed_workspace_read_only');
|
|
32
|
+
const candidate = path.resolve(ROOT, String(raw || ''));
|
|
33
|
+
rejectSensitive(candidate);
|
|
34
|
+
const parent = fs.realpathSync(path.dirname(candidate));
|
|
35
|
+
if (!inside(ROOT, parent)) throw new Error('managed_path_outside_workspace');
|
|
36
|
+
if (fs.existsSync(candidate)) {
|
|
37
|
+
const stat = fs.lstatSync(candidate);
|
|
38
|
+
if (stat.isSymbolicLink() || (stat.isFile() && stat.nlink > 1)) throw new Error('managed_path_link_rejected');
|
|
39
|
+
const real = fs.realpathSync(candidate);
|
|
40
|
+
if (!inside(ROOT, real)) throw new Error('managed_path_outside_workspace');
|
|
41
|
+
}
|
|
42
|
+
return candidate;
|
|
43
|
+
}
|
|
44
|
+
function textResult(text) { return { content: [{ type: 'text', text }], details: {} }; }
|
|
45
|
+
function tokenize(command) {
|
|
46
|
+
const result = []; let current = ''; let quote = '';
|
|
47
|
+
for (const ch of String(command || '')) {
|
|
48
|
+
if (quote) { if (ch === quote) quote = ''; else current += ch; continue; }
|
|
49
|
+
if (ch === '"' || ch === "'") { quote = ch; continue; }
|
|
50
|
+
if (/\\s/.test(ch)) { if (current) { result.push(current); current = ''; } continue; }
|
|
51
|
+
if (';&|><$()\\n\\r'.includes(ch) || ch.charCodeAt(0) === 96) throw new Error('managed_shell_syntax_rejected');
|
|
52
|
+
current += ch;
|
|
53
|
+
}
|
|
54
|
+
if (quote) throw new Error('managed_shell_quote_unterminated');
|
|
55
|
+
if (current) result.push(current);
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export default function (pi) {
|
|
60
|
+
pi.registerTool({ name: 'read', label: 'Read', description: 'Read a file inside the authorized workspace.', parameters: Type.Object({ path: Type.String(), offset: Type.Optional(Type.Number()), limit: Type.Optional(Type.Number()) }), async execute(_id, p) {
|
|
61
|
+
const file = resolveExisting(p.path); const stat = fs.statSync(file); if (!stat.isFile() || stat.size > MAX_READ) throw new Error('managed_read_size_rejected');
|
|
62
|
+
const lines = fs.readFileSync(file, 'utf8').split(/\\r?\\n/); const offset = Math.max(1, Math.floor(p.offset || 1)); const limit = Math.min(2000, Math.max(1, Math.floor(p.limit || 2000)));
|
|
63
|
+
return textResult(lines.slice(offset - 1, offset - 1 + limit).join('\\n'));
|
|
64
|
+
}});
|
|
65
|
+
pi.registerTool({ name: 'ls', label: 'List', description: 'List a directory inside the authorized workspace.', parameters: Type.Object({ path: Type.Optional(Type.String()) }), async execute(_id, p) {
|
|
66
|
+
const dir = resolveExisting(p.path || '.'); return textResult(fs.readdirSync(dir, { withFileTypes: true }).slice(0, 1000).map(e => e.name + (e.isDirectory() ? '/' : '')).join('\\n'));
|
|
67
|
+
}});
|
|
68
|
+
pi.registerTool({ name: 'find', label: 'Find', description: 'Find file names inside the authorized workspace.', parameters: Type.Object({ pattern: Type.String(), path: Type.Optional(Type.String()) }), async execute(_id, p) {
|
|
69
|
+
const base = resolveExisting(p.path || '.'); const needle = String(p.pattern).toLowerCase(); const out = []; const queue = [base];
|
|
70
|
+
while (queue.length && out.length < 1000) { const dir = queue.shift(); for (const e of fs.readdirSync(dir, { withFileTypes: true })) { const full = path.join(dir, e.name); if (SENSITIVE.test(full) || e.isSymbolicLink()) continue; if (e.name.toLowerCase().includes(needle)) out.push(path.relative(ROOT, full)); if (e.isDirectory()) queue.push(full); } }
|
|
71
|
+
return textResult(out.join('\\n'));
|
|
72
|
+
}});
|
|
73
|
+
pi.registerTool({ name: 'grep', label: 'Grep', description: 'Search text files inside the authorized workspace.', parameters: Type.Object({ pattern: Type.String(), path: Type.Optional(Type.String()) }), async execute(_id, p) {
|
|
74
|
+
const base = resolveExisting(p.path || '.'); const regex = new RegExp(String(p.pattern), 'i'); const out = []; const queue = [base];
|
|
75
|
+
while (queue.length && out.length < 1000) { const next = queue.shift(); const stat = fs.statSync(next); if (stat.isDirectory()) { for (const e of fs.readdirSync(next, { withFileTypes: true })) { const full = path.join(next, e.name); if (!SENSITIVE.test(full) && !e.isSymbolicLink()) queue.push(full); } continue; } if (!stat.isFile() || stat.size > MAX_READ) continue; const lines = fs.readFileSync(next, 'utf8').split(/\\r?\\n/); lines.forEach((line, i) => { if (out.length < 1000 && regex.test(line)) out.push(path.relative(ROOT, next) + ':' + (i + 1) + ':' + line); }); }
|
|
76
|
+
return textResult(out.join('\\n'));
|
|
77
|
+
}});
|
|
78
|
+
pi.registerTool({ name: 'write', label: 'Write', description: 'Write a file inside the authorized workspace.', parameters: Type.Object({ path: Type.String(), content: Type.String() }), async execute(_id, p) {
|
|
79
|
+
if (Buffer.byteLength(p.content, 'utf8') > MAX_WRITE) throw new Error('managed_write_size_rejected'); const file = resolveWrite(p.path); fs.writeFileSync(file, p.content, { encoding: 'utf8', flag: 'w' }); return textResult('Wrote ' + path.relative(ROOT, file));
|
|
80
|
+
}});
|
|
81
|
+
pi.registerTool({ name: 'edit', label: 'Edit', description: 'Replace exact text inside an authorized workspace file.', parameters: Type.Object({ path: Type.String(), oldText: Type.String(), newText: Type.String() }), async execute(_id, p) {
|
|
82
|
+
const file = resolveWrite(p.path); const current = fs.readFileSync(file, 'utf8'); if (!p.oldText || !current.includes(p.oldText)) throw new Error('managed_edit_target_not_found'); const next = current.replace(p.oldText, p.newText); if (Buffer.byteLength(next, 'utf8') > MAX_WRITE) throw new Error('managed_write_size_rejected'); fs.writeFileSync(file, next, { encoding: 'utf8', flag: 'w' }); return textResult('Edited ' + path.relative(ROOT, file));
|
|
83
|
+
}});
|
|
84
|
+
pi.registerTool({ name: 'bash', label: 'Shennian control', description: 'Run only capability-scoped shennian room or manager commands. General shell commands are unavailable.', parameters: Type.Object({ command: Type.String() }), async execute(_id, p, signal) {
|
|
85
|
+
const argv = tokenize(p.command); const scoped = argv[0] === 'shennian' && ((ALLOW_ROOM && argv[1] === 'room') || (ALLOW_MANAGER && argv[1] === 'manager')); if (!ALLOW_HOST_SHELL && !scoped) throw new Error('managed_shell_command_rejected');
|
|
86
|
+
return await new Promise((resolve, reject) => { const child = ALLOW_HOST_SHELL ? spawn(String(p.command), [], { cwd: ROOT, shell: true, env: process.env, windowsHide: true }) : spawn('shennian', argv.slice(1), { cwd: ROOT, shell: false, env: process.env, windowsHide: true }); let output = ''; const add = c => { output = (output + String(c)).slice(0, 256 * 1024); }; child.stdout.on('data', add); child.stderr.on('data', add); signal?.addEventListener('abort', () => child.kill('SIGTERM'), { once: true }); child.once('error', reject); child.once('close', code => resolve(textResult(output + '\\n[exit ' + code + ']'))); });
|
|
87
|
+
}});
|
|
88
|
+
}
|
|
89
|
+
`.trimStart();return t.mkdirSync(n.dirname(e.outputPath),{recursive:!0,mode:448}),t.writeFileSync(e.outputPath,c,{encoding:"utf8",mode:384}),process.platform!=="win32"&&t.chmodSync(e.outputPath,384),e.outputPath}export{p as writePiManagedExtension};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type PiManagedModelBrokerOptions = {
|
|
2
|
+
serverUrl: string;
|
|
3
|
+
machineToken: string;
|
|
4
|
+
sessionId: string;
|
|
5
|
+
fetchImpl?: typeof fetch;
|
|
6
|
+
};
|
|
7
|
+
export declare class PiManagedModelBroker {
|
|
8
|
+
private readonly options;
|
|
9
|
+
private server;
|
|
10
|
+
private capability;
|
|
11
|
+
private turnActive;
|
|
12
|
+
private port;
|
|
13
|
+
private upstreamCapability;
|
|
14
|
+
private upstreamCapabilityExpiresAt;
|
|
15
|
+
constructor(options: PiManagedModelBrokerOptions);
|
|
16
|
+
start(): Promise<{
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
apiKey: string;
|
|
19
|
+
}>;
|
|
20
|
+
setTurnActive(active: boolean): void;
|
|
21
|
+
stop(): Promise<void>;
|
|
22
|
+
private connection;
|
|
23
|
+
private handle;
|
|
24
|
+
private json;
|
|
25
|
+
private ensureUpstreamCapability;
|
|
26
|
+
private revokeUpstreamCapability;
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import f from"node:http";import{randomBytes as c,timingSafeEqual as y}from"node:crypto";const b=20*1024*1024,p=18e4;function _(a){return a?.startsWith("Bearer ")?a.slice(7):""}function g(a,t){const e=Buffer.from(a),r=Buffer.from(t);return e.length===r.length&&y(e,r)}async function v(a){const t=[];let e=0;for await(const r of a){const i=Buffer.isBuffer(r)?r:Buffer.from(r);if(e+=i.length,e>b)throw new Error("request_too_large");t.push(i)}return Buffer.concat(t)}class k{options;server=null;capability=c(32).toString("base64url");turnActive=!1;port=0;upstreamCapability=null;upstreamCapabilityExpiresAt=0;constructor(t){this.options=t}async start(){if(this.server)return this.connection();const t=f.createServer((r,i)=>{this.handle(r,i)});t.requestTimeout=p,t.headersTimeout=1e4,await new Promise((r,i)=>{t.once("error",i),t.listen(0,"127.0.0.1",()=>{t.removeListener("error",i),r()})});const e=t.address();if(!e||typeof e=="string")throw t.close(),new Error("pi_model_broker_address_unavailable");return this.server=t,this.port=e.port,this.connection()}setTurnActive(t){this.turnActive=t,t||this.revokeUpstreamCapability()}async stop(){this.turnActive=!1,await this.revokeUpstreamCapability(),this.capability=c(32).toString("base64url");const t=this.server;this.server=null,this.port=0,t&&await new Promise(e=>t.close(()=>e()))}connection(){if(!this.port)throw new Error("pi_model_broker_not_started");return{baseUrl:`http://127.0.0.1:${this.port}/v1`,apiKey:this.capability}}async handle(t,e){e.setHeader("Cache-Control","no-store");const r=_(t.headers.authorization);if(!r||!g(r,this.capability)){this.json(e,401,{error:{message:"Unauthorized",type:"authentication_error"}});return}if(t.method==="GET"&&t.url==="/v1/models"){this.json(e,200,{object:"list",data:[{id:"deepseek-v4-flash-0731",object:"model",owned_by:"shennian"}]});return}if(t.method!=="POST"||t.url!=="/v1/chat/completions"){this.json(e,404,{error:{message:"Not found",type:"invalid_request_error"}});return}if(!this.turnActive){this.json(e,403,{error:{message:"Managed model capability is not active",type:"permission_error"}});return}try{const i=await v(t),o=await this.ensureUpstreamCapability(),n=new AbortController;t.once("aborted",()=>n.abort());const h=setTimeout(()=>n.abort(),p);let s;try{s=await(this.options.fetchImpl??fetch)(`${this.options.serverUrl.replace(/\/$/,"")}/api/managed-model/v1/chat/completions`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${o}`},body:i.toString("utf8"),signal:n.signal})}finally{clearTimeout(h)}if(e.statusCode=s.status,e.setHeader("content-type",s.headers.get("content-type")||"application/json; charset=utf-8"),!s.body){e.end();return}const l=s.body.getReader();try{for(;;){const{done:u,value:d}=await l.read();if(u)break;e.write(Buffer.from(d))||await new Promise(m=>e.once("drain",m))}}finally{l.releaseLock()}e.end()}catch(i){if(e.headersSent){e.destroy();return}const o=i instanceof Error&&i.message==="request_too_large"?413:502;this.json(e,o,{error:{message:o===413?"Request too large":"Managed model request failed",type:o===413?"invalid_request_error":"upstream_error"}})}}json(t,e,r){t.statusCode=e,t.setHeader("content-type","application/json; charset=utf-8"),t.end(JSON.stringify(r))}async ensureUpstreamCapability(){if(this.upstreamCapability&&this.upstreamCapabilityExpiresAt>Date.now()+1e4)return this.upstreamCapability;const t=await(this.options.fetchImpl??fetch)(`${this.options.serverUrl.replace(/\/$/,"")}/api/managed-model/v1/capabilities`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${this.options.machineToken}`},body:JSON.stringify({sessionId:this.options.sessionId,agentType:"pi-coding-agent"})});if(!t.ok)throw new Error("managed_model_capability_issue_failed");const e=await t.json(),r=typeof e.expiresAt=="string"?Date.parse(e.expiresAt):NaN;if(typeof e.token!="string"||!/^pimc_[A-Za-z0-9_-]{43}$/.test(e.token)||e.model!=="deepseek-v4-flash-0731"||!Number.isFinite(r))throw new Error("managed_model_capability_response_invalid");return this.upstreamCapability=e.token,this.upstreamCapabilityExpiresAt=r,e.token}async revokeUpstreamCapability(){const t=this.upstreamCapability;this.upstreamCapability=null,this.upstreamCapabilityExpiresAt=0,t&&await(this.options.fetchImpl??fetch)(`${this.options.serverUrl.replace(/\/$/,"")}/api/managed-model/v1/capabilities`,{method:"DELETE",headers:{authorization:`Bearer ${t}`}}).catch(()=>{})}}export{k as PiManagedModelBroker};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import type { ChildProcess } from 'node:child_process';
|
|
3
|
+
export type PiRpcRecord = Record<string, unknown> & {
|
|
4
|
+
type?: string;
|
|
5
|
+
};
|
|
6
|
+
export type PiRpcClientEvents = {
|
|
7
|
+
event: [record: PiRpcRecord];
|
|
8
|
+
protocolError: [error: Error];
|
|
9
|
+
};
|
|
10
|
+
/** Strict LF-only JSONL transport. Node readline is intentionally not used:
|
|
11
|
+
* Pi allows U+2028/U+2029 inside JSON strings and documents that generic line
|
|
12
|
+
* readers can split those characters incorrectly. */
|
|
13
|
+
export declare class PiRpcClient extends EventEmitter<PiRpcClientEvents> {
|
|
14
|
+
private readonly process;
|
|
15
|
+
private readonly options;
|
|
16
|
+
private readonly pending;
|
|
17
|
+
private buffer;
|
|
18
|
+
private nextId;
|
|
19
|
+
private closed;
|
|
20
|
+
constructor(process: ChildProcess, options?: {
|
|
21
|
+
requestTimeoutMs?: number;
|
|
22
|
+
maxFrameBytes?: number;
|
|
23
|
+
});
|
|
24
|
+
request(command: string, fields?: Record<string, unknown>): Promise<PiRpcRecord>;
|
|
25
|
+
notify(command: string, fields?: Record<string, unknown>): void;
|
|
26
|
+
close(error?: Error): void;
|
|
27
|
+
private consume;
|
|
28
|
+
private consumeEnd;
|
|
29
|
+
private parseFrame;
|
|
30
|
+
private protocolFailure;
|
|
31
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{EventEmitter as l}from"node:events";const d=15e3,a=4*1024*1024;function h(n,t){return(typeof n.error=="string"?n.error:"").trim().slice(0,1e3)||`Pi RPC command failed: ${t}`}class m extends l{process;options;pending=new Map;buffer=Buffer.alloc(0);nextId=1;closed=!1;constructor(t,r={}){super(),this.process=t,this.options=r,t.stdout?.on("data",e=>this.consume(e)),t.stdout?.on("end",()=>this.consumeEnd()),t.once("close",()=>this.close(new Error("Pi RPC process closed"))),t.once("error",e=>this.close(e))}request(t,r={}){if(this.closed)return Promise.reject(new Error("Pi RPC process is closed"));if(!this.process.stdin?.writable)return Promise.reject(new Error("Pi RPC stdin unavailable"));const e=`shennian-${this.nextId++}`;return new Promise((s,i)=>{const c=setTimeout(()=>{this.pending.delete(e),i(new Error(`Pi RPC request timed out: ${t}`))},this.options.requestTimeoutMs??d);this.pending.set(e,{command:t,resolve:s,reject:i,timeout:c});const u=`${JSON.stringify({id:e,type:t,...r})}
|
|
2
|
+
`;this.process.stdin.write(u,f=>{if(!f)return;const o=this.pending.get(e);o&&(clearTimeout(o.timeout),this.pending.delete(e),o.reject(f))})})}notify(t,r={}){this.closed||!this.process.stdin?.writable||this.process.stdin.write(`${JSON.stringify({type:t,...r})}
|
|
3
|
+
`)}close(t=new Error("Pi RPC client closed")){if(!this.closed){this.closed=!0;for(const[r,e]of this.pending)clearTimeout(e.timeout),e.reject(t),this.pending.delete(r)}}consume(t){const r=Buffer.isBuffer(t)?t:Buffer.from(t);this.buffer=this.buffer.length?Buffer.concat([this.buffer,r]):r;const e=this.options.maxFrameBytes??a;if(this.buffer.length>e&&this.buffer.indexOf(10)===-1){this.protocolFailure(new Error("Pi RPC frame exceeds size limit"));return}let s=this.buffer.indexOf(10);for(;s!==-1;){let i=this.buffer.subarray(0,s);if(this.buffer=this.buffer.subarray(s+1),i.length&&i[i.length-1]===13&&(i=i.subarray(0,-1)),i.length&&this.parseFrame(i),this.closed)return;if(this.buffer.length>e&&this.buffer.indexOf(10)===-1){this.protocolFailure(new Error("Pi RPC frame exceeds size limit"));return}s=this.buffer.indexOf(10)}}consumeEnd(){!this.buffer.length||this.closed||(this.buffer=Buffer.alloc(0),this.protocolFailure(new Error("Pi RPC stream ended with an unterminated JSONL record")))}parseFrame(t){let r;try{const e=JSON.parse(t.toString("utf8"));if(!e||typeof e!="object"||Array.isArray(e))throw new Error("record must be a JSON object");r=e}catch{this.protocolFailure(new Error("Invalid Pi RPC JSON record"));return}if(r.type==="response"&&typeof r.id=="string"){const e=this.pending.get(r.id);if(!e)return;clearTimeout(e.timeout),this.pending.delete(r.id),r.success===!1?e.reject(new Error(h(r,e.command))):e.resolve(r);return}this.emit("event",r)}protocolFailure(t){this.emit("protocolError",t),this.close(t)}}export{m as PiRpcClient};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createHash as p}from"node:crypto";import i from"node:fs";import o from"node:path";import{resolveShennianPath as c}from"../config/index.js";function a(){return c("runtime","pi-coding-agent","session-owners")}function d(e){const t=p("sha256").update(e).digest("hex");return o.join(a(),`${t}.json`)}function u(e){const t=o.resolve(c("runtime","pi-coding-agent")),r=o.resolve(e),n=o.relative(t,r);return n!==""&&!n.startsWith("..")&&!o.isAbsolute(n)&&o.basename(r)==="sessions"}function y(e,t){if(!e||!u(t))throw new Error("pi_session_ownership_invalid");const r=a();i.mkdirSync(r,{recursive:!0,mode:448});const n=d(e),s=`${n}.${process.pid}.tmp`,m={version:1,agentSessionId:e,sessionDir:o.resolve(t)};i.writeFileSync(s,`${JSON.stringify(m)}
|
|
2
|
+
`,{encoding:"utf8",mode:384}),i.renameSync(s,n),process.platform!=="win32"&&i.chmodSync(n,384)}function w(e){if(!e||e.length>255||/[\0\r\n]/.test(e))return"invalid_identity";const t=d(e);let r;try{r=JSON.parse(i.readFileSync(t,"utf8"))}catch(n){return n.code==="ENOENT"?"already_absent":"invalid_identity"}return r.version!==1||r.agentSessionId!==e||!u(r.sessionDir)?"invalid_identity":(i.rmSync(r.sessionDir,{recursive:!0,force:!0}),i.rmSync(t,{force:!0}),"deleted")}export{w as deleteOwnedPiSession,y as recordPiSessionOwnership};
|
|
@@ -88,6 +88,10 @@ export declare function getDaemonStatus(opts?: {
|
|
|
88
88
|
}): DaemonStatus;
|
|
89
89
|
export declare function buildDaemonSpawnOptions(launch: DetachedLaunchSpec, logFd: number, env?: NodeJS.ProcessEnv): Parameters<typeof spawn>[2];
|
|
90
90
|
export declare function captureEnvForService(): Record<string, string>;
|
|
91
|
+
export declare function buildMacLaunchdPlist(input?: {
|
|
92
|
+
launch?: ServiceLaunchSpec;
|
|
93
|
+
brandedLauncherPath?: string | null;
|
|
94
|
+
}): string;
|
|
91
95
|
export declare function buildSystemdUnit(): string;
|
|
92
96
|
type LaunchdCommandExecutor = (command: string) => void;
|
|
93
97
|
export declare function getLaunchdServiceTarget(uid?: number | string): string;
|
|
@@ -1,20 +1,24 @@
|
|
|
1
|
-
import c from"chalk";import
|
|
2
|
-
`)}function
|
|
3
|
-
<string>${T(
|
|
4
|
-
`),
|
|
5
|
-
`)
|
|
1
|
+
import c from"chalk";import i from"node:fs";import s from"node:path";import d from"node:os";import{execSync as u,spawn as he,spawnSync as ye}from"node:child_process";import{randomUUID as V}from"node:crypto";import{fileURLToPath as ge}from"node:url";import{getShennianDir as we,loadConfig as G,resolveShennianPath as y,saveConfig as ve}from"../config/index.js";import{buildAugmentedPath as Ee}from"../env-path.js";import{buildWindowsLauncherCommand as Ae,buildWindowsScheduledTaskCommands as Pe,buildWindowsScheduledTaskXml as ke,buildWindowsStartupVbs as De,escapeXml as T,isWindowsCmdScript as Ie,quoteCmdArg as J,quoteSystemdArg as Ne,suspendWindowsScheduledTask as be}from"./daemon-windows.js";import{waitForDaemonReadyStatus as $}from"./daemon-start-wait.js";import{findRunningWindowsDaemonProcessIds as Re,isShennianRunServiceCommand as xe}from"./daemon-process.js";import{getCurrentVersion as K}from"../upgrade/engine.js";import{buildWindowsLauncherCommand as _n,buildWindowsScheduledTaskCommands as On,buildWindowsScheduledTaskSuspendCommands as Tn,buildWindowsScheduledTaskXml as $n,buildWindowsStartupVbs as Cn,suspendWindowsScheduledTask as Hn}from"./daemon-windows.js";import{isShennianRunServiceCommand as Un}from"./daemon-process.js";const X=s.dirname(ge(import.meta.url)),w=we(),v=y("daemon.pid"),p=y("daemon.log"),C=y("remote-access.disabled"),x=y("daemon-launcher.json"),q=s.resolve(X,"../../bin/shennian.js"),Y=process.execPath,z=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_ACCOUNT_NAMESPACE_ID","SHENNIAN_NATIVE_FUSION_DISABLED","SHENNIAN_WECHAT_DELIVERABILITY_TRACE"]);function Le(e){return z.has(e)}function m(){const e=d.platform();return e==="darwin"||e==="linux"||e==="win32"?e:"linux"}function H(e){const n=e.replace(/\\/g,"/").toLowerCase(),t=d.tmpdir().replace(/\\/g,"/").toLowerCase();return n.includes("/_npx/")||n.includes("/npm-cache/_npx/")||n.includes("/pnpm/dlx/")||n.startsWith(t.endsWith("/")?t:`${t}/`)}function _e(e){return i.existsSync(e.scriptPath)&&!H(e.scriptPath)?{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}:e.shennianCommandPath&&!H(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 Oe(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 D(){try{return Oe(i.readFileSync(v,"utf-8"))}catch{return null}}function A(e){try{return process.kill(e,0),!0}catch{return!1}}function Q(e,n=3e3){const t=Date.now();for(;Date.now()-t<n;){if(!A(e))return!0;Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,100)}return!A(e)}function Te(){return i.existsSync(C)}function $e(){try{i.unlinkSync(C)}catch{}}function Ce(e,n){const t=s.dirname(e),r=`${e}.${process.pid}.${V()}.tmp`;i.mkdirSync(t,{recursive:!0});let o=null;try{o=i.openSync(r,"w",384),i.writeSync(o,n,void 0,"utf8"),i.fsyncSync(o),i.closeSync(o),o=null,i.renameSync(r,e);try{const a=i.openSync(t,"r");try{i.fsyncSync(a)}finally{i.closeSync(a)}}catch{}}finally{o!==null&&i.closeSync(o);try{i.unlinkSync(r)}catch{}}}function He(e={}){const n=e.disabledAt??new Date().toISOString(),t=e.markerFile??C,r=e.platform??m(),o=e.servicePresent??W(r),a=e.execCommand??(l=>{u(l,{stdio:"pipe",windowsHide:!0})});if(Ce(t,n),!o)return{disabledAt:n,autoStartDisabled:!0};try{return a(r==="darwin"?`launchctl disable "${k()}"`:r==="linux"?"systemctl --user disable shennian":`schtasks /change /tn "${b}" /disable`),{disabledAt:n,autoStartDisabled:!0}}catch(l){return{disabledAt:n,autoStartDisabled:!1,autoStartError:l instanceof Error?l.message:String(l)}}}function Me(e={}){const n=e.platform??m();if(!(e.servicePresent??W(n)))return{serviceStopped:!0};const r=e.execCommand??(o=>{u(o,{stdio:"pipe",windowsHide:!0})});try{return r(n==="darwin"?`launchctl bootout "${k()}"`:n==="linux"?"systemctl --user stop shennian":`schtasks /end /tn "${b}"`),{serviceStopped:!0}}catch(o){return{serviceStopped:!1,error:o instanceof Error?o.message:String(o)}}}function Z(){return"global-cli"}function Ue(){return`${process.pid}-${Date.now()}-${V()}`}function je(e,n,t={}){i.mkdirSync(w,{recursive:!0}),i.writeFileSync(v,JSON.stringify({pid:e,instanceId:n,...t.version?{version:t.version}:{},launcher:t.launcher??Z(),...t.adopted?{adopted:!0}:{},startedAt:new Date().toISOString()},null,2))}function bn(e,n){const t=D();if(!(t?.pid!==e||t.instanceId!==n))try{i.unlinkSync(v)}catch{}}function ee(e,n=Z(),t){i.mkdirSync(w,{recursive:!0}),i.writeFileSync(x,JSON.stringify({pid:e,launcher:n,...t?{instanceId:t}:{},updatedAt:new Date().toISOString()},null,2))}function We(e){if(e)try{if(JSON.parse(i.readFileSync(x,"utf-8")).instanceId!==e)return}catch{return}try{i.unlinkSync(x)}catch{}}function Fe(e){e&&ee(e)}function Be(e,n){if(!e||!n)return"unknown";try{const t=JSON.parse(i.readFileSync(x,"utf-8"));if(t.pid!==e)return"unknown";if(t.launcher==="desktop-managed"||t.launcher==="global-cli")return t.launcher}catch{}return M(e)}function M(e){if(m()==="win32")return"unknown";try{const n=u(`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 ne(e=process.pid){if(m()==="win32")return Re(e);try{return u("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]),a=r[2];return!Number.isInteger(o)||o===e?null:xe(a)?o:null}).filter(t=>typeof t=="number")}catch{return[]}}function Ve(e=process.env,n=w,t=d.tmpdir(),r=s.join(d.homedir(),".shennian")){if(e.SHENNIAN_E2E_ISOLATED_DAEMON!=="1"||!e.SHENNIAN_HOME?.trim()||s.resolve(n)===s.resolve(r))return!1;const o=s.relative(s.resolve(t),s.resolve(n));return o!==""&&o!==".."&&!o.startsWith(`..${s.sep}`)&&!s.isAbsolute(o)||s.resolve(n)===s.resolve(e.SHENNIAN_HOME)}function Ge(e=process.env,n=w){return!Ve(e,n)}function S(e={}){let n=D(),t=n?.pid??null,r=t!==null&&A(t),o=t!==null&&!r,a=n?.adopted===!0;const l=G();if(o&&e.cleanupStale){try{i.unlinkSync(v)}catch{}n=null,t=null,r=!1,o=!1}if(!r&&Ge()){const f=ne()[0];if(f&&(t=f,r=!0,o=!1,a=!0,e.cleanupStale)){const R=Ue();je(f,R,{launcher:M(f),adopted:!0}),ee(f,M(f)),n={pid:f,instanceId:R,adopted:!0}}}return{running:r,pid:t,stale:o,remoteAccessDisabled:Te(),launcher:Be(t,r),platform:m(),shennianDir:w,pidFile:v,logFile:p,...n?.instanceId?{instanceId:n.instanceId}:{},...a?{adopted:!0}:{},...l.machineId?{machineId:l.machineId}:{},paired:!!(l.machineToken&&l.machineId),...l.serverUrl?{serverUrl:l.serverUrl}:{}}}function E(e){process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
2
|
+
`)}function te(e){const n=e?.trim();if(!n)return;const t=G();t.serverUrl=n,ve(t)}function re(e){return e?.trim()||process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim()||void 0}const U="com.shennian.agent",g=s.join(d.homedir(),"Library/LaunchAgents",`${U}.plist`),Je="net.shennian.client.background-service",ie=s.resolve(X,"../../native/macos-background-client/Shennian.app"),h=y("Shennian.app"),P=s.join(h,"Contents","MacOS","ShennianClientLauncher"),Ke=s.join(d.homedir(),"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),L=y("autostart.cmd"),_=y("autostart.vbs"),O=y("autostart.xml"),Xe=s.join(Ke,"ShennianAgent.vbs");function oe(e){const n=m()==="win32"?`where ${e}`:`command -v ${e}`;try{return u(n,{stdio:["ignore","pipe","pipe"],encoding:"utf-8",windowsHide:!0}).split(/\r?\n/).map(o=>o.trim()).find(Boolean)??null}catch{return null}}function qe(){const e=m()==="win32"?"npx.cmd":"npx",n=s.join(s.dirname(Y),e);return i.existsSync(n)?n:oe("npx")}function I(){return _e({nodeExec:Y,scriptPath:q,shennianCommandPath:oe("shennian"),npxPath:qe()})}function se(){try{u(`schtasks /delete /tn "${b}" /f`,{stdio:"pipe",windowsHide:!0})}catch{}}function ce(){try{i.unlinkSync(Xe)}catch{}}function Ye(){const e=process.env.USERDOMAIN?.trim(),n=process.env.COMPUTERNAME?.trim(),t=process.env.USERNAME?.trim()||d.userInfo().username;return e&&e.toUpperCase()!=="WORKGROUP"?`${e}\\${t}`:n?`${n}\\${t}`:t}function ze(e){if(m()==="win32"&&Ie(e.command)){const n=[e.command,...e.args].map(J).join(" ");return{command:process.env.ComSpec||"cmd.exe",args:["/d","/s","/c",`"${n}"`],windowsVerbatimArguments:!0}}return{command:e.command,args:e.args}}function Qe(e,n,t=process.env){return{detached:!0,stdio:["ignore",n,n],env:t,windowsHide:!0,...e.windowsVerbatimArguments?{windowsVerbatimArguments:!0}:{}}}function Ze(){const e=I();i.writeFileSync(L,Ae(e,p)),i.writeFileSync(_,De(L)),ce(),se();const n=ke({userId:Ye(),command:s.join(process.env.SystemRoot||"C:\\Windows","System32","wscript.exe"),arguments:[J(_)]});i.writeFileSync(O,n,"utf8");const t=Pe({taskName:b,xmlPath:O});try{return u(t.create,{stdio:"pipe",windowsHide:!0}),u(t.run,{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}function en(){be(b,e=>{u(e,{stdio:"pipe",windowsHide:!0})})}function ae(){const e={};for(const n of z)process.env[n]&&(e[n]=process.env[n]);if(e.HOME??=d.homedir(),e.PATH=Ee({pathValue:e.PATH,env:e}),e.USER??=d.userInfo().username,m()==="win32"){const n=process.env.TEMP||process.env.TMP||d.tmpdir(),t=d.homedir();e.USERPROFILE??=t;const r=s.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||d.userInfo().username,e.COMPUTERNAME??=process.env.COMPUTERNAME||"",e.TMPDIR??=n,e.TEMP??=n,e.TMP??=n}else e.TMPDIR??="/tmp";return e}function nn(e={}){const n=ae(),t=e.launch??I(),r=e.brandedLauncherPath===void 0?i.existsSync(P)?P:null:e.brandedLauncherPath,o=Object.entries(n).map(([f,R])=>` <key>${T(f)}</key>
|
|
3
|
+
<string>${T(R)}</string>`).join(`
|
|
4
|
+
`),a=[...r?[r,"--exec"]:[],t.command,...t.args].map(f=>` <string>${T(f)}</string>`).join(`
|
|
5
|
+
`),l=r?` <key>AssociatedBundleIdentifiers</key>
|
|
6
|
+
<array>
|
|
7
|
+
<string>${Je}</string>
|
|
8
|
+
</array>
|
|
9
|
+
`:"";return`<?xml version="1.0" encoding="UTF-8"?>
|
|
6
10
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
7
11
|
<plist version="1.0">
|
|
8
12
|
<dict>
|
|
9
13
|
<key>Label</key>
|
|
10
|
-
<string>${
|
|
11
|
-
<key>ProgramArguments</key>
|
|
14
|
+
<string>${U}</string>
|
|
15
|
+
${l} <key>ProgramArguments</key>
|
|
12
16
|
<array>
|
|
13
|
-
${
|
|
17
|
+
${a}
|
|
14
18
|
</array>
|
|
15
19
|
<key>EnvironmentVariables</key>
|
|
16
20
|
<dict>
|
|
17
|
-
${
|
|
21
|
+
${o}
|
|
18
22
|
</dict>
|
|
19
23
|
<key>RunAtLoad</key>
|
|
20
24
|
<true/>
|
|
@@ -23,12 +27,12 @@ ${t}
|
|
|
23
27
|
<key>ThrottleInterval</key>
|
|
24
28
|
<integer>5</integer>
|
|
25
29
|
<key>StandardOutPath</key>
|
|
26
|
-
<string>${
|
|
30
|
+
<string>${p}</string>
|
|
27
31
|
<key>StandardErrorPath</key>
|
|
28
|
-
<string>${
|
|
32
|
+
<string>${p}</string>
|
|
29
33
|
</dict>
|
|
30
|
-
</plist>`}const
|
|
31
|
-
`),r=[n.command,...n.args].map(
|
|
34
|
+
</plist>`}function tn(){if(!i.existsSync(ie))return console.warn(c.yellow("\u26A0 Shennian background identity bundle is missing; using the legacy launcher")),null;const e=`${h}.installing-${process.pid}`,n=`${h}.previous-${process.pid}`;try{if(i.rmSync(e,{recursive:!0,force:!0}),i.rmSync(n,{recursive:!0,force:!0}),i.cpSync(ie,e,{recursive:!0,preserveTimestamps:!0,verbatimSymlinks:!0}),i.existsSync(h)&&i.renameSync(h,n),i.renameSync(e,h),i.rmSync(n,{recursive:!0,force:!0}),i.chmodSync(P,493),ye(P,["--register-bundle",h],{stdio:"ignore",windowsHide:!0}).status!==0)throw new Error("Launch Services registration failed");return P}catch(t){try{i.rmSync(e,{recursive:!0,force:!0}),!i.existsSync(h)&&i.existsSync(n)&&i.renameSync(n,h)}catch{}return console.warn(c.yellow(`\u26A0 Unable to install the Shennian background identity; using the legacy launcher: ${t instanceof Error?t.message:String(t)}`)),null}}const N=s.join(d.homedir(),".config/systemd/user/shennian.service");function rn(){const e=ae(),n=I(),t=Object.entries(e).map(([o,a])=>`Environment=${o}=${a}`).join(`
|
|
35
|
+
`),r=[n.command,...n.args].map(Ne).join(" ");return`[Unit]
|
|
32
36
|
Description=Shennian Agent Daemon
|
|
33
37
|
After=network.target
|
|
34
38
|
|
|
@@ -37,10 +41,10 @@ ${t}
|
|
|
37
41
|
ExecStart=${r}
|
|
38
42
|
Restart=on-failure
|
|
39
43
|
RestartSec=10
|
|
40
|
-
StandardOutput=append:${
|
|
41
|
-
StandardError=append:${
|
|
44
|
+
StandardOutput=append:${p}
|
|
45
|
+
StandardError=append:${p}
|
|
42
46
|
|
|
43
47
|
[Install]
|
|
44
|
-
WantedBy=default.target`}const
|
|
48
|
+
WantedBy=default.target`}const b="ShennianAgent";function on(e){u(e,{stdio:"pipe",windowsHide:!0})}function sn(){const e=process.getuid?.();if(typeof e=="number")return e;const n=d.userInfo();return typeof n.uid=="number"?n.uid:n.username}function k(e=sn()){return`gui/${e}/${U}`}function cn(e=on){try{return e(`launchctl enable "${k()}"; launchctl unload "${g}" 2>/dev/null; launchctl load -w "${g}"`),e(`launchctl kickstart -k "${k()}"`),!0}catch{try{return e(`launchctl enable "${k()}"`),e(`launchctl load -w "${g}"`),e(`launchctl kickstart -k "${k()}"`),!0}catch{return!1}}}function an(){i.mkdirSync(w,{recursive:!0});const e={};for(const[n,t]of Object.entries(process.env))t!==void 0&&Le(n)&&(e[n]=t);i.writeFileSync(y("env.json"),JSON.stringify(e,null,2))}function ue(e={}){i.mkdirSync(w,{recursive:!0});const n=S({cleanupStale:!0});if(n.pid!==null&&n.running&&!j(n)){e.quiet||console.log(c.green(`\u2713 Background service already running (PID ${n.pid})`));return}de(n);const t=i.openSync(p,"a"),r=ze(I()),o=he(r.command,r.args,Qe(r,t));o.unref(),i.closeSync(t),Fe(o.pid),e.quiet||(console.log(c.green(`\u2713 Background service started (PID ${o.pid})`)),console.log(c.gray(` Logs: ${p}`)))}function j(e,n=D(),t=K()){return e.pid===null||!e.running?!1:e.adopted===!0||n?.version!==t}function W(e=m()){switch(e){case"darwin":return i.existsSync(g)&&i.existsSync(P);case"linux":return i.existsSync(N);case"win32":return i.existsSync(L)&&i.existsSync(_)&&i.existsSync(O)}}function un(e,n,t,r=D(),o=K()){return!n.api?.trim()&&e.pid!==null&&e.running&&!e.stale&&!e.remoteAccessDisabled&&t&&!j(e,r,o)}function de(e=S({cleanupStale:!0})){if(!(!j(e)||e.pid===null))try{process.kill(e.pid,"SIGTERM"),Q(e.pid,5e3)||(process.kill(e.pid,"SIGKILL"),Q(e.pid,2e3));try{D()?.pid===e.pid&&i.unlinkSync(v)}catch{}}catch{}}function le(){i.mkdirSync(w,{recursive:!0});const e=m();switch(I().mode==="direct"&&H(q)&&console.warn(c.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=s.dirname(g);i.mkdirSync(t,{recursive:!0});const r=tn();return i.writeFileSync(g,nn({brandedLauncherPath:r})),cn()}case"linux":{const t=s.dirname(N);i.mkdirSync(t,{recursive:!0}),i.writeFileSync(N,rn());try{u("systemctl --user daemon-reload && systemctl --user enable shennian",{stdio:"pipe",windowsHide:!0})}catch{return!1}try{u(`loginctl enable-linger ${d.userInfo().username}`,{stdio:"pipe",windowsHide:!0})}catch{}try{return u("systemctl --user restart shennian",{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}case"win32":return Ze()}}function dn(){const n=S({cleanupStale:!0}).pid;if(n===null)return{};if(!A(n)){try{i.unlinkSync(v)}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 pe(e=5e3){const n=dn();if(!n.stoppedPid)return n;if(!await B(n.stoppedPid,e))try{process.kill(n.stoppedPid,"SIGKILL"),await B(n.stoppedPid,2e3)}catch(r){return{...n,error:r instanceof Error?r.message:String(r)}}try{i.unlinkSync(v)}catch{}We();for(const r of ne())if(r!==n.stoppedPid)try{process.kill(r,"SIGTERM"),await B(r,2e3)}catch{}return n}function ln(e={}){te(re(e.api)),$e(),de(),le()||ue({quiet:e.json}),e.json?E($(()=>S({cleanupStale:!0}))):console.log(c.green("\u2713 Background service started"))}async function pn(e={}){const n=He(),t=Me(),r=await pe(),o=S({cleanupStale:!0}),a=r.error??n.autoStartError??t.error;if(e.json){E(a?{...o,error:a}:o);return}a?console.error(c.red(`\u2717 Failed to stop completely: ${a}`)):console.log(c.green("\u2713 Background service stopped"))}function me(e){const n=S({cleanupStale:!0});if(un(n,e,W())){e.json?E(n):console.log(c.green(`\u2713 Background service already running (PID ${n.pid})`));return}an(),ln(e)}async function F(e={}){await pn(e)}async function B(e,n=5e3){const t=Date.now();for(;Date.now()-t<n;){if(!A(e))return!0;await new Promise(r=>setTimeout(r,100))}return!A(e)}async function mn(e={}){const n=S({cleanupStale:!0});if(n.remoteAccessDisabled){e.json?E({...n,error:"remote_access_disabled_use_shennian_start"}):console.error(c.yellow("Remote access is paused. Run `shennian start` on this computer to resume."));return}te(re(e.api));const t=m();t==="win32"&&en();const r=await pe();if(r.error){e.json?E({...S(),error:r.error}):console.error(c.red(`\u2717 Failed to stop: ${r.error}`));return}if(r.stoppedPid!==void 0?e.json||console.log(c.green(`\u2713 Background service stopped (PID ${r.stoppedPid})`)):r.stalePid!==void 0?e.json||console.log(c.yellow(`\u26A0 Service process no longer exists, cleaned up stale PID ${r.stalePid}`)):e.json||console.log(c.gray("\u25CF Background service not running, starting it now")),(t==="linux"||t==="win32")&&le()){e.json&&E($(()=>S({cleanupStale:!0})));return}ue({quiet:e.json}),e.json&&E($(()=>S({cleanupStale:!0})))}function fe(e={}){const n=S({cleanupStale:!0});if(e.json){E(n);return}if(n.pid===null){console.log(c.gray("\u25CF Background service not running"));return}n.running?(console.log(c.green(`\u25CF Background service running (PID ${n.pid})`)),console.log(c.gray(` Logs: ${p}`))):console.log(c.yellow(`\u25CF Background service stopped (PID ${n.pid} is stale)`))}function Se(e){if(!i.existsSync(p)){console.log(c.gray("No logs yet"));return}try{const n=u(d.platform()==="win32"?`powershell Get-Content -Tail ${e.lines} "${p}"`:`tail -n ${e.lines} "${p}"`,{encoding:"utf-8",windowsHide:!0});process.stdout.write(n)}catch{const n=i.readFileSync(p,"utf-8").split(`
|
|
45
49
|
`).slice(-e.lines);console.log(n.join(`
|
|
46
|
-
`))}}async function
|
|
50
|
+
`))}}async function fn(){switch(await F(),m()){case"darwin":{if(i.existsSync(g)){try{u(`launchctl unload -w "${g}"`,{stdio:"pipe",windowsHide:!0})}catch{}i.unlinkSync(g)}i.rmSync(h,{recursive:!0,force:!0}),console.log(c.green("\u2713 Auto-start uninstalled (launchd)"));break}case"linux":{try{u("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}if(i.existsSync(N)){i.unlinkSync(N);try{u("systemctl --user daemon-reload",{stdio:"pipe",windowsHide:!0})}catch{}}console.log(c.green("\u2713 Auto-start uninstalled (systemd)"));break}case"win32":{se();try{i.unlinkSync(L)}catch{}try{i.unlinkSync(_)}catch{}try{i.unlinkSync(O)}catch{}ce(),console.log(c.green("\u2713 Auto-start uninstalled (Task Scheduler)"));break}}}function Rn(e){e.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>me(r)),e.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(r=>F(r)),e.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>fe(r)),e.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>Se({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(c.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),me(r)}),n.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(async r=>{t("stop",r.json),await F(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),mn(r)}),n.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>{t("status",r.json),fe(r)}),n.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>{t("logs"),Se({lines:parseInt(r.lines,10)})}),n.command("uninstall").description("Uninstall auto-start service").action(fn)}export{Qe as buildDaemonSpawnOptions,nn as buildMacLaunchdPlist,rn as buildSystemdUnit,_n as buildWindowsLauncherCommand,On as buildWindowsScheduledTaskCommands,Tn as buildWindowsScheduledTaskSuspendCommands,$n as buildWindowsScheduledTaskXml,Cn as buildWindowsStartupVbs,un as canReuseRunningDaemonForStart,ae as captureEnvForService,We as clearDaemonLauncher,bn as clearDaemonPidIfOwner,$e as clearRemoteAccessDisabled,Ue as createDaemonInstanceId,ne as findRunningDaemonProcessIds,Z as getCurrentProcessDaemonLauncher,S as getDaemonStatus,k as getLaunchdServiceTarget,le as installService,W as isAutoStartServicePresent,H as isEphemeralCliPath,Ve as isIsolatedDaemonE2eInstance,Te as isRemoteAccessDisabled,Le as isSafeSnapshotEnvKey,Un as isShennianRunServiceCommand,He as prepareRemoteAccessPause,Fe as recordStartedDaemon,Rn as registerDaemonCommand,cn as reloadLaunchdAgent,_e as resolveServiceLaunchSpec,an as saveEnvSnapshot,Ge as shouldDiscoverGlobalDaemonProcesses,j as shouldReplaceRunningDaemon,ue as startDaemonProcess,Me as stopPreparedRemoteAccessService,Hn as suspendWindowsScheduledTask,ee as writeDaemonLauncher,je as writeDaemonPid};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import u from"node:crypto";import{spawn as f}from"node:child_process";import{HttpDeviceAuthorizationClient as p}from"../room/device-authorization-client.js";import{getDaemonInstallationId as A}from"../personal-sync/identity.js";import{hashActionPayload as g,hashPendingDeviceSecret as P}from"./pair-authorization.js";const L=3e3;class a extends Error{code;constructor(r,t){super(t),this.code=r,this.name="BrowserPairError"}}async function O(e){const r=new p(e.serverUrl),t=u.randomBytes(32).toString("base64url"),n=A(),c={action:"pair_machine",agentList:[...e.agentList].sort(),daemonInstallationId:n,machineName:e.machineName,machineOs:e.machineOs},d=g(c),o=await r.create({deviceSecretHash:P(t),daemonInstallationId:n,machineName:e.machineName,machineOs:e.machineOs,agentList:e.agentList,actionType:"machine.pair",actionSummary:"Pair this machine with your Shennian account",actionPayloadHash:d,returnTo:"/spaces"}),m=y(o.publicLoginUrl,e.serverUrl);e.emit?.({type:"pair.browser.created",intentId:o.loginIntentId,url:m,expiresAt:o.expiresAt}),await(e.openUrl??_)(m).catch(()=>!1);const I=new Date(o.expiresAt).getTime(),h=e.pollIntervalMs??L;let l=!1;for(;Date.now()<I;){if(e.signal?.aborted)throw await r.cancel({loginIntentId:o.loginIntentId,deviceSecret:t}).catch(()=>{}),new a("PAIR_CANCELLED","Pairing was cancelled");let s;try{s=await r.getStatus({loginIntentId:o.loginIntentId,deviceSecret:t})}catch{await w(h,e.signal);continue}if(s.actionPayloadHash!==d)throw new a("PAIR_PAYLOAD_MISMATCH","Authorization request changed unexpectedly");if(s.status==="denied")throw new a("PAIR_DENIED","Pairing request was denied");if(s.status==="expired")throw new a("PAIR_EXPIRED","Pairing request expired");if(s.status==="claimed")throw new a("PAIR_ALREADY_CLAIMED","Pairing credentials were already claimed");if(s.status==="approved"){const i=await r.claim({loginIntentId:o.loginIntentId,deviceSecret:t});if(i.actionPayloadHash!==d||!i.machineId||!i.machineToken||!i.accountNamespaceId)throw new a("PAIR_INVALID_CLAIM","Authorization did not return machine credentials");return e.emit?.({type:"pair.claimed"}),{machineId:i.machineId,machineToken:i.machineToken,accountNamespaceId:i.accountNamespaceId,machineName:e.machineName,publicLoginUrl:m}}l||(l=!0,e.emit?.({type:"pair.waiting"})),await w(h,e.signal)}throw new a("PAIR_TIMEOUT","Timed out waiting for browser authorization")}function S(e){if(e.browser&&e.token)throw new a("PAIR_MODE_CONFLICT","Choose either --browser or --token, not both");return e.browser?"browser":e.token||e.json||!e.stdinIsTty||!e.stdoutIsTty?"token":"prompt"}function U(e){return e.trim()==="2"?"token":"browser"}async function _(e){if(process.env.SSH_CONNECTION||process.env.SSH_TTY)return!1;const{command:r,args:t}=T(process.platform,e);return new Promise(n=>{const c=f(r,t,{detached:!0,stdio:"ignore",windowsHide:!0});c.once("error",()=>n(!1)),c.once("spawn",()=>{c.unref(),n(!0)})})}function T(e,r){return e==="darwin"?{command:"open",args:[r]}:e==="win32"?{command:"explorer.exe",args:[r]}:{command:"xdg-open",args:[r]}}function y(e,r){let t,n;try{t=new URL(e),n=new URL(r)}catch{throw new a("PAIR_INVALID_AUTHORIZATION_URL","Server returned an invalid authorization URL")}if(!["http:","https:"].includes(t.protocol)||t.hostname!==n.hostname||t.username||t.password||t.search||t.hash||!/^\/authorize\/[A-Za-z0-9_-]{8,128}$/.test(t.pathname))throw new a("PAIR_INVALID_AUTHORIZATION_URL","Server returned an unsafe authorization URL");return t.toString()}function w(e,r){return new Promise(t=>{if(r?.aborted)return t();const n=setTimeout(t,e);r?.addEventListener("abort",()=>{clearTimeout(n),t()},{once:!0})})}export{a as BrowserPairError,T as browserOpenCommand,y as normalizePairAuthorizationUrl,_ as openExternalUrl,U as parsePairModeChoice,S as resolvePairMode,O as runBrowserPairFlow};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const MANAGER_SYSTEM_PROMPT = "\u4F60\u662F\u9879\u76EE\u7ECF\u7406\uFF0C\u662F\u5F53\u524D\u9879\u76EE\u7684\u7BA1\u7406\u8005\u3002\n\n\u4F60\u7684\u804C\u8D23\uFF1A\n- \u7406\u89E3\u7528\u6237\u76EE\u6807\u3002\n- \u62C6\u89E3\u4EFB\u52A1\u3002\n- \u521B\u5EFA\u3001\u6307\u6D3E\u3001\u89C2\u5BDF\u548C\u505C\u6B62\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u4E0B\u7684 worker Agent session\u3002\n- \u6C47\u603B worker \u7ED3\u679C\u3002\n- \u5224\u65AD\u662F\u5426\u9700\u8981\u7EE7\u7EED\u7B49\u5F85\u3001\u8C03\u6574\u5B89\u6392\u3001\u8BE2\u95EE\u7528\u6237\u6216\u9A8C\u6536\u3002\n- \u5728\u9879\u76EE .shennian/ \u76EE\u5F55\u4E0B\u7EF4\u62A4\u5FC5\u8981\u7684\u8BA1\u5212\u3001\u8BB0\u5F55\u548C\u9879\u76EE\u8BB0\u5FC6\u3002\n\n\u4F60\u7684\u8FB9\u754C\uFF1A\n- \u4E0D\u8981\u628A\u81EA\u5DF1\u5F53\u4F5C\u4E3B\u8981\u6267\u884C\u8005\u3002\n- \u4E0D\u8981\u76F4\u63A5\u7F16\u8F91\u4E1A\u52A1\u4EE3\u7801\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u4EB2\u81EA\u6267\u884C\u3002\n- \u53EF\u4EE5\u8BFB\u53D6\u6587\u4EF6\u3001\u641C\u7D22\u9879\u76EE\u548C\u68C0\u67E5\u4E0A\u4E0B\u6587\uFF0C\u4EE5\u4FBF\u505A\u5224\u65AD\u3002\n- \u9700\u8981\u4FEE\u6539\u4EE3\u7801\u3001\u8FD0\u884C\u6D4B\u8BD5\u3001\u8C03\u7814\u65B9\u6848\u65F6\uFF0C\u4F18\u5148\u521B\u5EFA\u6216\u6307\u6D3E worker\u3002\n- \u6BCF\u6B21\u6536\u5230\u65B0\u4EFB\u52A1\u3001\u8865\u5145\u8981\u6C42\u3001\u7EA0\u504F\u6216\u5916\u90E8\u6D88\u606F\u65F6\uFF0C\u5148\u7528 sessions list \u67E5\u770B\u5F53\u524D\u540C\u9879\u76EE worker\uFF1B\u5982\u679C\u53EF\u80FD\u76F8\u5173\uFF0C\u518D\u7528 sessions read \u8BFB\u53D6\u5FC5\u8981\u6458\u8981\u540E\u5224\u65AD\u662F\u5426\u590D\u7528\u3002\n- \u5982\u679C\u5DF2\u6709 worker \u6B63\u5728\u5904\u7406\u540C\u4E00\u76EE\u6807\u3001\u540C\u4E00\u529F\u80FD\u533A\u3001\u540C\u4E00\u6587\u4EF6\u8303\u56F4\u6216\u540C\u4E00\u95EE\u9898\u94FE\u8DEF\uFF0C\u4F18\u5148\u7528 sessions send \u628A\u65B0\u8981\u6C42\u53D1\u7ED9\u8FD9\u4E2A worker\uFF1B\u5373\u4F7F worker \u6B63\u5FD9\u4E5F\u53EF\u4EE5\u53D1\u9001\uFF0C\u9ED8\u8BA4\u4F1A\u8FDB\u5165\u672C\u673A\u961F\u5217\uFF0C\u4E0D\u8981\u56E0\u4E3A\u5B83\u5FD9\u5C31\u65B0\u5EFA worker\u3002\n- \u53EA\u6709\u6CA1\u6709\u76F8\u5173 worker\u3001\u73B0\u6709 worker \u5DF2\u660E\u663E\u4E0D\u9002\u5408\u7EE7\u7EED\u63A8\u8FDB\u3001\u6216\u4EFB\u52A1\u9700\u8981\u5E76\u884C\u62C6\u5206\u7ED9\u4E0D\u540C\u4E13\u957F\u65F6\uFF0C\u624D\u521B\u5EFA\u65B0\u7684 worker\u3002\n- \u521B\u5EFA\u6216\u6307\u6D3E worker \u540E\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u5F53\u573A\u7EE7\u7EED\u8C03\u5EA6\uFF0C\u5426\u5219\u56DE\u590D\u7528\u6237\u5DF2\u5B89\u6392\u5E76\u7ED3\u675F\u5F53\u524D turn\uFF1B\u4E0D\u8981\u4E3B\u52A8\u8F6E\u8BE2 worker \u72B6\u6001\uFF0C\u795E\u5FF5\u4F1A\u5728 worker \u7EC8\u6001\u6216\u5065\u5EB7\u6458\u8981\u5230\u6765\u65F6\u91CD\u65B0\u5524\u9192\u4F60\u3002\n- sessions read \u8FD4\u56DE\u7684\u662F\u7ED9\u7BA1\u7406\u8005\u770B\u7684\u7B80\u6D01\u8FDB\u5C55\u3001\u5DE5\u5177\u6458\u8981\u548C\u6700\u7EC8\u7ED3\u679C\uFF0C\u4E0D\u662F\u539F\u59CB\u6D41\u5F0F token\uFF1B\u4E0D\u8981\u8981\u6C42\u8BFB\u53D6\u6216\u8F6C\u8FF0\u5B8C\u6574\u6D41\u5F0F\u65E5\u5FD7\u3002\n- \u53EA\u80FD\u7BA1\u7406\u4E0E\u4F60\u5904\u4E8E\u540C\u4E00\u53F0\u673A\u5668\u3001\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u7684\u4F1A\u8BDD\uFF1B\u4E0D\u8981\u8DE8\u673A\u5668\u6216\u8DE8\u9879\u76EE\u8C03\u5EA6\u3002\n- \u4E0D\u8981\u65E0\u9650\u5FAA\u73AF\uFF1B\u6CA1\u6709\u660E\u786E\u4E0B\u4E00\u6B65\u65F6\u8BE2\u95EE\u7528\u6237\u6216\u7ED3\u675F\u5F53\u524D turn \u7B49\u5F85\u7CFB\u7EDF\u4E8B\u4EF6\u3002\n- \u4E0D\u8981\u81EA\u5DF1\u8BBE\u7F6E\u5B9A\u65F6\u5524\u9192\uFF1B\u795E\u5FF5\u4F1A\u5728\u7528\u6237\u6D88\u606F\u3001worker \u7EC8\u6001\u6216 worker \u957F\u8FD0\u884C\u5065\u5EB7\u6458\u8981\u5230\u6765\u65F6\u5524\u9192\u4F60\u3002\n- \u5916\u90E8\u6D88\u606F\u901A\u9053\u4E8B\u4EF6\u4F1A\u50CF\u666E\u901A\u7528\u6237\u6D88\u606F\u4E00\u6837\u9001\u8FBE\uFF0C\u683C\u5F0F\u4E3A\u201C\u5916\u90E8\u5BF9\u8BDD: <\u5BF9\u8BDD\u540D\u79F0>\u201D\u540E\u8DDF\u201C\u5BF9\u65B9: <\u5185\u5BB9\u6216\u9644\u4EF6\u94FE\u63A5>\u201D\uFF0C\u53EF\u80FD\u662F\u5408\u5E76\u6D88\u606F\uFF0C\u4E5F\u53EF\u80FD\u5305\u542B\u56FE\u7247\u3001\u89C6\u9891\u6216\u6587\u4EF6 URL\u3002\n- \u5916\u90E8\u6D88\u606F\u662F\u5426\u9700\u8981\u56DE\u590D\u3001\u8FFD\u95EE\u3001\u5FFD\u7565\u3001\u8F6C\u4EA4\u5185\u90E8\u8D1F\u8D23\u4EBA\uFF0C\u6216\u521B\u5EFA/\u6307\u6D3E worker\uFF0C\u7531\u4F60\u6839\u636E\u9879\u76EE\u4E0A\u4E0B\u6587\u663E\u5F0F\u5224\u65AD\u3002\n- \u5BF9\u5916\u4F60\u662F\u5F53\u524D\u9879\u76EE\u7684\u9879\u76EE\u7ECF\u7406\uFF0C\u4E0D\u8981\u81EA\u79F0\u795E\u5FF5\u3001Manager Agent \u6216 worker\uFF0C\u4E5F\u4E0D\u8981\u89E3\u91CA\u5185\u90E8\u8C03\u5EA6\u673A\u5236\uFF1B\u53EA\u5728\u9700\u8981\u65F6\u7528\u201C\u6211\u8FD9\u8FB9/\u6211\u4EEC\u8FD9\u8FB9\u201D\u6C9F\u901A\u3002\n- \u4E0D\u8981\u628A\u6240\u6709\u7EC6\u8282\u585E\u8FDB\u5BF9\u8BDD\u4E0A\u4E0B\u6587\uFF1B\u9700\u8981\u957F\u671F\u4FDD\u5B58\u7684\u4FE1\u606F\u5199\u5230\u9879\u76EE .shennian/ \u4E0B\u3002\n\n\u9700\u8981\u7BA1\u7406 worker \u6216\u5916\u90E8\u901A\u9053\u65F6\uFF0C\u4F7F\u7528\u672C\u5730\u547D\u4EE4\uFF1A\n- shennian manager sessions list --json\n- shennian manager sessions start --agent <codex|claude|
|
|
1
|
+
export declare const MANAGER_SYSTEM_PROMPT = "\u4F60\u662F\u9879\u76EE\u7ECF\u7406\uFF0C\u662F\u5F53\u524D\u9879\u76EE\u7684\u7BA1\u7406\u8005\u3002\n\n\u4F60\u7684\u804C\u8D23\uFF1A\n- \u7406\u89E3\u7528\u6237\u76EE\u6807\u3002\n- \u62C6\u89E3\u4EFB\u52A1\u3002\n- \u521B\u5EFA\u3001\u6307\u6D3E\u3001\u89C2\u5BDF\u548C\u505C\u6B62\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u4E0B\u7684 worker Agent session\u3002\n- \u6C47\u603B worker \u7ED3\u679C\u3002\n- \u5224\u65AD\u662F\u5426\u9700\u8981\u7EE7\u7EED\u7B49\u5F85\u3001\u8C03\u6574\u5B89\u6392\u3001\u8BE2\u95EE\u7528\u6237\u6216\u9A8C\u6536\u3002\n- \u5728\u9879\u76EE .shennian/ \u76EE\u5F55\u4E0B\u7EF4\u62A4\u5FC5\u8981\u7684\u8BA1\u5212\u3001\u8BB0\u5F55\u548C\u9879\u76EE\u8BB0\u5FC6\u3002\n\n\u4F60\u7684\u8FB9\u754C\uFF1A\n- \u4E0D\u8981\u628A\u81EA\u5DF1\u5F53\u4F5C\u4E3B\u8981\u6267\u884C\u8005\u3002\n- \u4E0D\u8981\u76F4\u63A5\u7F16\u8F91\u4E1A\u52A1\u4EE3\u7801\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u4EB2\u81EA\u6267\u884C\u3002\n- \u53EF\u4EE5\u8BFB\u53D6\u6587\u4EF6\u3001\u641C\u7D22\u9879\u76EE\u548C\u68C0\u67E5\u4E0A\u4E0B\u6587\uFF0C\u4EE5\u4FBF\u505A\u5224\u65AD\u3002\n- \u9700\u8981\u4FEE\u6539\u4EE3\u7801\u3001\u8FD0\u884C\u6D4B\u8BD5\u3001\u8C03\u7814\u65B9\u6848\u65F6\uFF0C\u4F18\u5148\u521B\u5EFA\u6216\u6307\u6D3E worker\u3002\n- \u6BCF\u6B21\u6536\u5230\u65B0\u4EFB\u52A1\u3001\u8865\u5145\u8981\u6C42\u3001\u7EA0\u504F\u6216\u5916\u90E8\u6D88\u606F\u65F6\uFF0C\u5148\u7528 sessions list \u67E5\u770B\u5F53\u524D\u540C\u9879\u76EE worker\uFF1B\u5982\u679C\u53EF\u80FD\u76F8\u5173\uFF0C\u518D\u7528 sessions read \u8BFB\u53D6\u5FC5\u8981\u6458\u8981\u540E\u5224\u65AD\u662F\u5426\u590D\u7528\u3002\n- \u5982\u679C\u5DF2\u6709 worker \u6B63\u5728\u5904\u7406\u540C\u4E00\u76EE\u6807\u3001\u540C\u4E00\u529F\u80FD\u533A\u3001\u540C\u4E00\u6587\u4EF6\u8303\u56F4\u6216\u540C\u4E00\u95EE\u9898\u94FE\u8DEF\uFF0C\u4F18\u5148\u7528 sessions send \u628A\u65B0\u8981\u6C42\u53D1\u7ED9\u8FD9\u4E2A worker\uFF1B\u5373\u4F7F worker \u6B63\u5FD9\u4E5F\u53EF\u4EE5\u53D1\u9001\uFF0C\u9ED8\u8BA4\u4F1A\u8FDB\u5165\u672C\u673A\u961F\u5217\uFF0C\u4E0D\u8981\u56E0\u4E3A\u5B83\u5FD9\u5C31\u65B0\u5EFA worker\u3002\n- \u53EA\u6709\u6CA1\u6709\u76F8\u5173 worker\u3001\u73B0\u6709 worker \u5DF2\u660E\u663E\u4E0D\u9002\u5408\u7EE7\u7EED\u63A8\u8FDB\u3001\u6216\u4EFB\u52A1\u9700\u8981\u5E76\u884C\u62C6\u5206\u7ED9\u4E0D\u540C\u4E13\u957F\u65F6\uFF0C\u624D\u521B\u5EFA\u65B0\u7684 worker\u3002\n- \u521B\u5EFA\u6216\u6307\u6D3E worker \u540E\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u5F53\u573A\u7EE7\u7EED\u8C03\u5EA6\uFF0C\u5426\u5219\u56DE\u590D\u7528\u6237\u5DF2\u5B89\u6392\u5E76\u7ED3\u675F\u5F53\u524D turn\uFF1B\u4E0D\u8981\u4E3B\u52A8\u8F6E\u8BE2 worker \u72B6\u6001\uFF0C\u795E\u5FF5\u4F1A\u5728 worker \u7EC8\u6001\u6216\u5065\u5EB7\u6458\u8981\u5230\u6765\u65F6\u91CD\u65B0\u5524\u9192\u4F60\u3002\n- sessions read \u8FD4\u56DE\u7684\u662F\u7ED9\u7BA1\u7406\u8005\u770B\u7684\u7B80\u6D01\u8FDB\u5C55\u3001\u5DE5\u5177\u6458\u8981\u548C\u6700\u7EC8\u7ED3\u679C\uFF0C\u4E0D\u662F\u539F\u59CB\u6D41\u5F0F token\uFF1B\u4E0D\u8981\u8981\u6C42\u8BFB\u53D6\u6216\u8F6C\u8FF0\u5B8C\u6574\u6D41\u5F0F\u65E5\u5FD7\u3002\n- \u53EA\u80FD\u7BA1\u7406\u4E0E\u4F60\u5904\u4E8E\u540C\u4E00\u53F0\u673A\u5668\u3001\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u7684\u4F1A\u8BDD\uFF1B\u4E0D\u8981\u8DE8\u673A\u5668\u6216\u8DE8\u9879\u76EE\u8C03\u5EA6\u3002\n- \u4E0D\u8981\u65E0\u9650\u5FAA\u73AF\uFF1B\u6CA1\u6709\u660E\u786E\u4E0B\u4E00\u6B65\u65F6\u8BE2\u95EE\u7528\u6237\u6216\u7ED3\u675F\u5F53\u524D turn \u7B49\u5F85\u7CFB\u7EDF\u4E8B\u4EF6\u3002\n- \u4E0D\u8981\u81EA\u5DF1\u8BBE\u7F6E\u5B9A\u65F6\u5524\u9192\uFF1B\u795E\u5FF5\u4F1A\u5728\u7528\u6237\u6D88\u606F\u3001worker \u7EC8\u6001\u6216 worker \u957F\u8FD0\u884C\u5065\u5EB7\u6458\u8981\u5230\u6765\u65F6\u5524\u9192\u4F60\u3002\n- \u5916\u90E8\u6D88\u606F\u901A\u9053\u4E8B\u4EF6\u4F1A\u50CF\u666E\u901A\u7528\u6237\u6D88\u606F\u4E00\u6837\u9001\u8FBE\uFF0C\u683C\u5F0F\u4E3A\u201C\u5916\u90E8\u5BF9\u8BDD: <\u5BF9\u8BDD\u540D\u79F0>\u201D\u540E\u8DDF\u201C\u5BF9\u65B9: <\u5185\u5BB9\u6216\u9644\u4EF6\u94FE\u63A5>\u201D\uFF0C\u53EF\u80FD\u662F\u5408\u5E76\u6D88\u606F\uFF0C\u4E5F\u53EF\u80FD\u5305\u542B\u56FE\u7247\u3001\u89C6\u9891\u6216\u6587\u4EF6 URL\u3002\n- \u5916\u90E8\u6D88\u606F\u662F\u5426\u9700\u8981\u56DE\u590D\u3001\u8FFD\u95EE\u3001\u5FFD\u7565\u3001\u8F6C\u4EA4\u5185\u90E8\u8D1F\u8D23\u4EBA\uFF0C\u6216\u521B\u5EFA/\u6307\u6D3E worker\uFF0C\u7531\u4F60\u6839\u636E\u9879\u76EE\u4E0A\u4E0B\u6587\u663E\u5F0F\u5224\u65AD\u3002\n- \u5BF9\u5916\u4F60\u662F\u5F53\u524D\u9879\u76EE\u7684\u9879\u76EE\u7ECF\u7406\uFF0C\u4E0D\u8981\u81EA\u79F0\u795E\u5FF5\u3001Manager Agent \u6216 worker\uFF0C\u4E5F\u4E0D\u8981\u89E3\u91CA\u5185\u90E8\u8C03\u5EA6\u673A\u5236\uFF1B\u53EA\u5728\u9700\u8981\u65F6\u7528\u201C\u6211\u8FD9\u8FB9/\u6211\u4EEC\u8FD9\u8FB9\u201D\u6C9F\u901A\u3002\n- \u4E0D\u8981\u628A\u6240\u6709\u7EC6\u8282\u585E\u8FDB\u5BF9\u8BDD\u4E0A\u4E0B\u6587\uFF1B\u9700\u8981\u957F\u671F\u4FDD\u5B58\u7684\u4FE1\u606F\u5199\u5230\u9879\u76EE .shennian/ \u4E0B\u3002\n\n\u9700\u8981\u7BA1\u7406 worker \u6216\u5916\u90E8\u901A\u9053\u65F6\uFF0C\u4F7F\u7528\u672C\u5730\u547D\u4EE4\uFF1A\n- shennian manager sessions list --json\n- shennian manager sessions start --agent <codex|claude|workbuddy|cursor|opencode|pi-coding-agent|custom:name> --workdir <path> --message <text>\n- shennian manager sessions send --session-id <id> --message <text>\n- shennian manager sessions send --session-id <id> --message <text> --direct\n- shennian manager sessions queue list --session-id <id> --json\n- shennian manager sessions queue edit --session-id <id> --message-id <queueMessageId> --message <text>\n- shennian manager sessions queue delete --session-id <id> --message-id <queueMessageId>\n- shennian manager sessions stop --session-id <id>\uFF08\u7EC8\u6B62\u6B63\u5728\u8FD0\u884C\u7684 worker\uFF1B\u4E5F\u53EF\u7528 terminate/kill \u522B\u540D\uFF09\n- shennian manager sessions read --session-id <id> --limit 200 --json\n- shennian manager memory path\n\n\u53EF\u521B\u5EFA\u7684 worker Agent \u5305\u62EC Codex\u3001Claude Code\u3001WorkBuddy\u3001Cursor\u3001opencode\u3001Pi\uFF0C\u4EE5\u53CA\u672C\u673A custom agent\uFF08custom:<name>\uFF09\u3002\u9ED8\u8BA4\u7528 sessions send \u6392\u961F\u53D1\u9001 worker \u6D88\u606F\uFF1Aworker \u6B63\u5FD9\u65F6\u6D88\u606F\u4F1A\u5728\u672C\u673A daemon \u961F\u5217\u91CC\u7B49\u5F85\uFF0Cworker \u7A7A\u95F2\u65F6\u81EA\u52A8\u6267\u884C\u3002\u961F\u5217\u91CC\u7684\u672A\u6267\u884C\u6D88\u606F\u53EF\u4EE5 list/edit/delete\uFF1B\u5DF2\u7ECF\u5F00\u59CB\u6267\u884C\u7684\u6D88\u606F\u4E0D\u80FD\u7F16\u8F91\u6216\u5220\u9664\uFF0C\u53EA\u80FD stop \u540E\u91CD\u65B0\u53D1\u9001\u3002\u53EA\u6709\u660E\u786E\u9700\u8981\u6253\u65AD\u987A\u5E8F\u65F6\u624D\u4F7F\u7528 --direct\u3002\n\n\u8FD9\u4E9B\u547D\u4EE4\u5DF2\u7ECF\u7531\u795E\u5FF5\u6CE8\u5165\u5F53\u524D Manager \u8EAB\u4EFD\u548C\u540C\u9879\u76EE\u6743\u9650\u8FB9\u754C\u3002\u4E0D\u8981\u5C1D\u8BD5\u4F2A\u9020 Manager session id\u3002";
|
|
2
2
|
export declare function buildManagerPrompt(userText: string): string;
|
|
@@ -28,7 +28,7 @@ const s=`\u4F60\u662F\u9879\u76EE\u7ECF\u7406\uFF0C\u662F\u5F53\u524D\u9879\u76E
|
|
|
28
28
|
|
|
29
29
|
\u9700\u8981\u7BA1\u7406 worker \u6216\u5916\u90E8\u901A\u9053\u65F6\uFF0C\u4F7F\u7528\u672C\u5730\u547D\u4EE4\uFF1A
|
|
30
30
|
- shennian manager sessions list --json
|
|
31
|
-
- shennian manager sessions start --agent <codex|claude|
|
|
31
|
+
- shennian manager sessions start --agent <codex|claude|workbuddy|cursor|opencode|pi-coding-agent|custom:name> --workdir <path> --message <text>
|
|
32
32
|
- shennian manager sessions send --session-id <id> --message <text>
|
|
33
33
|
- shennian manager sessions send --session-id <id> --message <text> --direct
|
|
34
34
|
- shennian manager sessions queue list --session-id <id> --json
|
|
@@ -38,6 +38,6 @@ const s=`\u4F60\u662F\u9879\u76EE\u7ECF\u7406\uFF0C\u662F\u5F53\u524D\u9879\u76E
|
|
|
38
38
|
- shennian manager sessions read --session-id <id> --limit 200 --json
|
|
39
39
|
- shennian manager memory path
|
|
40
40
|
|
|
41
|
-
\u53EF\u521B\u5EFA\u7684 worker Agent \u5305\u62EC Codex\u3001Claude Code\
|
|
41
|
+
\u53EF\u521B\u5EFA\u7684 worker Agent \u5305\u62EC Codex\u3001Claude Code\u3001WorkBuddy\u3001Cursor\u3001opencode\u3001Pi\uFF0C\u4EE5\u53CA\u672C\u673A custom agent\uFF08custom:<name>\uFF09\u3002\u9ED8\u8BA4\u7528 sessions send \u6392\u961F\u53D1\u9001 worker \u6D88\u606F\uFF1Aworker \u6B63\u5FD9\u65F6\u6D88\u606F\u4F1A\u5728\u672C\u673A daemon \u961F\u5217\u91CC\u7B49\u5F85\uFF0Cworker \u7A7A\u95F2\u65F6\u81EA\u52A8\u6267\u884C\u3002\u961F\u5217\u91CC\u7684\u672A\u6267\u884C\u6D88\u606F\u53EF\u4EE5 list/edit/delete\uFF1B\u5DF2\u7ECF\u5F00\u59CB\u6267\u884C\u7684\u6D88\u606F\u4E0D\u80FD\u7F16\u8F91\u6216\u5220\u9664\uFF0C\u53EA\u80FD stop \u540E\u91CD\u65B0\u53D1\u9001\u3002\u53EA\u6709\u660E\u786E\u9700\u8981\u6253\u65AD\u987A\u5E8F\u65F6\u624D\u4F7F\u7528 --direct\u3002
|
|
42
42
|
|
|
43
43
|
\u8FD9\u4E9B\u547D\u4EE4\u5DF2\u7ECF\u7531\u795E\u5FF5\u6CE8\u5165\u5F53\u524D Manager \u8EAB\u4EFD\u548C\u540C\u9879\u76EE\u6743\u9650\u8FB9\u754C\u3002\u4E0D\u8981\u5C1D\u8BD5\u4F2A\u9020 Manager session id\u3002`;function n(e){return e}export{s as MANAGER_SYSTEM_PROMPT,n as buildManagerPrompt};
|
|
@@ -27,9 +27,9 @@ export type ManagerRecord = {
|
|
|
27
27
|
agentSessionId: string | null;
|
|
28
28
|
workDir: string;
|
|
29
29
|
machineId?: string | null;
|
|
30
|
-
modelId:
|
|
30
|
+
modelId: AgentType;
|
|
31
31
|
/** Real provider for new Manager-mode sessions; legacy sessions use manager. */
|
|
32
|
-
agentType?:
|
|
32
|
+
agentType?: AgentType;
|
|
33
33
|
/** Real provider model for new Manager-mode sessions. */
|
|
34
34
|
providerModelId?: string | null;
|
|
35
35
|
defaultWorkerAgentType?: AgentType | null;
|
|
@@ -76,8 +76,8 @@ export declare class ManagerRegistry {
|
|
|
76
76
|
agentSessionId?: string | null;
|
|
77
77
|
workDir: string;
|
|
78
78
|
machineId?: string | null;
|
|
79
|
-
modelId:
|
|
80
|
-
agentType?:
|
|
79
|
+
modelId: AgentType;
|
|
80
|
+
agentType?: AgentType;
|
|
81
81
|
providerModelId?: string | null;
|
|
82
82
|
defaultWorkerAgentType?: AgentType | null;
|
|
83
83
|
defaultWorkerModelId?: string | null;
|
|
@@ -44,8 +44,8 @@ export declare class ManagerRuntimeService {
|
|
|
44
44
|
sessionId: string;
|
|
45
45
|
agentSessionId?: string | null;
|
|
46
46
|
workDir: string;
|
|
47
|
-
modelId:
|
|
48
|
-
agentType?:
|
|
47
|
+
modelId: AgentType;
|
|
48
|
+
agentType?: AgentType;
|
|
49
49
|
providerModelId?: string | null;
|
|
50
50
|
defaultWorkerAgentType?: AgentType | null;
|
|
51
51
|
defaultWorkerModelId?: string | null;
|
|
@@ -58,7 +58,7 @@ export declare class ManagerRuntimeService {
|
|
|
58
58
|
agentType: AgentType | null;
|
|
59
59
|
modelId: string | null;
|
|
60
60
|
};
|
|
61
|
-
noteManagerAgentSession(sessionId: string, agentSessionId: string | null, workDir: string, modelId:
|
|
61
|
+
noteManagerAgentSession(sessionId: string, agentSessionId: string | null, workDir: string, modelId: AgentType): void;
|
|
62
62
|
noteAgentEvent(sessionId: string, event: AgentEvent): void;
|
|
63
63
|
private findWorker;
|
|
64
64
|
handleAppReq(req: ReqFrame): Promise<void>;
|