shennian 0.4.35 → 0.4.37
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/publish-build-manifest.json +16 -11
- package/dist/src/agents/pi-coding-agent.js +4 -4
- package/dist/src/agents/pi-managed-model-broker.js +1 -1
- package/dist/src/session/handlers/agent-workspace-validation.d.ts +2 -0
- package/dist/src/session/handlers/agent-workspace-validation.js +1 -1
- package/dist/src/session/handlers/pi-validation-session.d.ts +7 -0
- package/dist/src/session/handlers/pi-validation-session.js +1 -0
- package/dist/src/upgrade/updater-bootstrap.js +2 -2
- package/dist/src/upgrade/updater-launcher.d.ts +6 -2
- package/dist/src/upgrade/updater-launcher.js +3 -3
- package/node_modules/@shennian/wire/dist/session.d.ts +2 -0
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"jsMinified": true,
|
|
4
4
|
"minifier": "esbuild",
|
|
5
|
-
"fileCount":
|
|
5
|
+
"fileCount": 183,
|
|
6
6
|
"files": [
|
|
7
7
|
{
|
|
8
8
|
"file": "bin/shennian.js",
|
|
@@ -241,8 +241,8 @@
|
|
|
241
241
|
},
|
|
242
242
|
{
|
|
243
243
|
"file": "src/agents/pi-coding-agent.js",
|
|
244
|
-
"beforeBytes":
|
|
245
|
-
"afterBytes":
|
|
244
|
+
"beforeBytes": 24853,
|
|
245
|
+
"afterBytes": 13461
|
|
246
246
|
},
|
|
247
247
|
{
|
|
248
248
|
"file": "src/agents/pi-managed-extension.js",
|
|
@@ -251,8 +251,8 @@
|
|
|
251
251
|
},
|
|
252
252
|
{
|
|
253
253
|
"file": "src/agents/pi-managed-model-broker.js",
|
|
254
|
-
"beforeBytes":
|
|
255
|
-
"afterBytes":
|
|
254
|
+
"beforeBytes": 8635,
|
|
255
|
+
"afterBytes": 4663
|
|
256
256
|
},
|
|
257
257
|
{
|
|
258
258
|
"file": "src/agents/pi-rpc-client.js",
|
|
@@ -731,8 +731,8 @@
|
|
|
731
731
|
},
|
|
732
732
|
{
|
|
733
733
|
"file": "src/session/handlers/agent-workspace-validation.js",
|
|
734
|
-
"beforeBytes":
|
|
735
|
-
"afterBytes":
|
|
734
|
+
"beforeBytes": 8799,
|
|
735
|
+
"afterBytes": 4597
|
|
736
736
|
},
|
|
737
737
|
{
|
|
738
738
|
"file": "src/session/handlers/agents.js",
|
|
@@ -759,6 +759,11 @@
|
|
|
759
759
|
"beforeBytes": 676,
|
|
760
760
|
"afterBytes": 392
|
|
761
761
|
},
|
|
762
|
+
{
|
|
763
|
+
"file": "src/session/handlers/pi-validation-session.js",
|
|
764
|
+
"beforeBytes": 2016,
|
|
765
|
+
"afterBytes": 1147
|
|
766
|
+
},
|
|
762
767
|
{
|
|
763
768
|
"file": "src/session/handlers/project-fs.js",
|
|
764
769
|
"beforeBytes": 3778,
|
|
@@ -891,13 +896,13 @@
|
|
|
891
896
|
},
|
|
892
897
|
{
|
|
893
898
|
"file": "src/upgrade/updater-bootstrap.js",
|
|
894
|
-
"beforeBytes":
|
|
895
|
-
"afterBytes":
|
|
899
|
+
"beforeBytes": 3595,
|
|
900
|
+
"afterBytes": 1840
|
|
896
901
|
},
|
|
897
902
|
{
|
|
898
903
|
"file": "src/upgrade/updater-launcher.js",
|
|
899
|
-
"beforeBytes":
|
|
900
|
-
"afterBytes":
|
|
904
|
+
"beforeBytes": 9356,
|
|
905
|
+
"afterBytes": 4695
|
|
901
906
|
},
|
|
902
907
|
{
|
|
903
908
|
"file": "src/upgrade/updater-protocol.js",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import h from"node:fs";import u from"node:path";import{randomUUID as P}from"node:crypto";import{AgentAdapter as E,registerAgent as
|
|
2
|
-
[truncated]`}function H(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 X(i){try{return y(JSON.stringify(i))}catch{return"[unserializable]"}}function I(i){if(!i||typeof i!="object")return;const
|
|
3
|
-
`)}`}function K(i,e){const s=[];for(const t of i??[]){if(t.kind!=="image"||!t.mimeType.startsWith("image/"))continue;let n=t.previewData?.trim()??"";const a=n.indexOf(",");if(n.startsWith("data:")&&a>=0&&(n=n.slice(a+1)),!n&&e&&t.path&&!/^https?:\/\//i.test(t.path))try{const l=h.statSync(t.path);l.isFile()&&l.size<=10*1024*1024&&(n=h.readFileSync(t.path).toString("base64"))}catch{n=""}n&&s.push({type:"image",data:n,mimeType:t.mimeType})}return s}class W extends E{type="pi-coding-agent";get recoveryCapabilities(){return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!0}}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(e){e.sessionId&&(this.shennianSessionId=e.sessionId),this.externalChannel=e.externalChannel??null,this.extraEnv=e.env??{},"managedAgentPolicy"in e&&(this.managedAgentPolicy=e.managedAgentPolicy??null)}async start(e,s,t,n){await this.stopProcess(),this.shennianSessionId=e,this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??s,this.agentSessionId=t??null;const a=f("runtime","pi-coding-agent",g(e));this.persistNativeSession=n?.persistNativeSession!==!1,this.validationRoot=this.persistNativeSession?null:u.join(a,`validation-${P()}`),this.sessionDir=this.validationRoot?u.join(this.validationRoot,"sessions"):u.join(a,"sessions"),h.mkdirSync(this.sessionDir,{recursive:!0,mode:448}),process.platform!=="win32"&&h.chmodSync(this.sessionDir,448),this.active=!1,this.terminal=!0}async send(e,s,t,n,a,l){await this.ensureProcess(a);const c=this.requireRpc(),p=await this.readState(),o=!!(p.isStreaming||p.isCompacting);(!o||!this.runId)&&(this.runId=P(),this.seq=0,this.terminal=!1,this.lastUsage=void 0,this.runPhase="thinking");const d=L(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 c.request("set_model",{provider:d.provider,modelId:d.modelId})}t&&!o&&await c.request("set_thinking_level",{level:t});const r=o?l?.source==="room_activation"?"follow_up":"steer":"prompt",m={message:Z(e,n,this.workDir,this.externalChannel,this.shennianSessionId,null)},S=K(n,!this.managedAgentPolicy);S.length&&!this.modelBroker&&(m.images=S),this.modelBroker?.setTurnActive(!0);try{await c.request(r,m)}catch(_){throw this.modelBroker?.setTurnActive(!1),_}}async resume(e){await this.stopProcess(),this.agentSessionId=e}async stop(){const e=this.rpc;e&&(await e.request("clear_queue").catch(()=>{}),await e.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 e=await this.readState().catch(()=>null),s=!!(e?.isStreaming||e?.isCompacting||this.active);return{active:s,runId:s?this.runId:null,runPhase:s?this.runPhase??"thinking":null,canStop:s}}async setTitle(e,s){this.rpc&&await this.rpc.request("set_session_name",{name:s.slice(0,200)})}async ensureProcess(e){if(this.process&&this.rpc)return;const s=v("pi-coding-agent");if(!s)throw new Error('Command "pi" not found. Install @earendil-works/pi-coding-agent from the official package.');const t=["--mode","rpc","--session-dir",this.sessionDir,"--offline"];this.agentSessionId&&t.push("--session",this.agentSessionId);const n=await this.prepareHostedModel();if(n&&t.push("--provider","shennian-dashscope","--model","deepseek-v4-flash-0731"),this.managedAgentPolicy&&!w(this.managedAgentPolicy)){t.push("--no-extensions","--no-skills","--no-prompt-templates","--no-context-files");const r=this.managedTools(),m=q({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"});t.push("--no-builtin-tools","--extension",m),r.length?t.push("--tools",r.join(",")):t.push("--no-tools")}const a=M(this.workDir,this.externalChannel,this.shennianSessionId,e),l=f("runtime","pi-coding-agent",g(this.shennianSessionId));h.mkdirSync(l,{recursive:!0,mode:448}),this.promptFile=u.join(l,"instructions.md"),h.writeFileSync(this.promptFile,a,{encoding:"utf8",mode:384}),process.platform!=="win32"&&h.chmodSync(this.promptFile,384),t.push("--append-system-prompt",this.promptFile);const c={NO_COLOR:"1",...n?{PI_CODING_AGENT_DIR:n.configDir}:{}},p=this.managedAgentPolicy&&!w(this.managedAgentPolicy)?T({agentType:this.type,providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:c}):this.managedAgentPolicy?N({...c,...this.extraEnv}):x({...c,...this.extraEnv});let o;try{o=k(s,t,{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}`,F)});const d=new C(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&&j(r.sessionId,this.sessionDir)}catch(r){throw await this.stopProcess(),r}}managedTools(){const e=this.managedAgentPolicy?.adapterPolicy;if(!e)return[];const s=["read","grep","find","ls"];(e.fileAccess==="workspace-write"||e.fileAccess==="host-full")&&s.push("edit","write");const t=!!this.extraEnv.SHENNIAN_MANAGER_SESSION_ID,n=!!this.extraEnv.SHENNIAN_MANAGED_ROOM_CONTEXT;return(t||n||e.fileAccess==="host-full")&&s.push("bash"),s}ownsSessionFile(e){const s=u.resolve(this.sessionDir),t=u.resolve(e),n=u.relative(s,t);return n!==""&&!n.startsWith("..")&&!u.isAbsolute(n)}async readState(){const e=await this.requireRpc().request("get_state");return G(e)}requireRpc(){if(!this.rpc)throw new Error("Pi RPC process is not running");return this.rpc}handleRpcEvent(e){if(e.type==="agent_start"){this.active=!0,this.runPhase="thinking",this.emitEvent({state:"start",agentSessionId:this.agentSessionId??void 0});return}if(e.type==="message_update"){this.lastUsage=I(e.usage)??this.lastUsage;const s=e.assistantMessageEvent;if(!s||typeof s!="object")return;const t=s;t.type==="text_delta"&&typeof t.delta=="string"?(this.runPhase="streaming_text",this.emitEvent({state:"delta",text:t.delta})):t.type==="thinking_delta"&&typeof t.delta=="string"&&(this.runPhase="thinking",this.emitEvent({state:"delta",text:t.delta,thinking:!0}));return}if(e.type==="tool_execution_start"){this.runPhase="tool_running",this.emitEvent({state:"tool-call",name:typeof e.toolName=="string"?e.toolName:"tool",args:e.args&&typeof e.args=="object"&&!Array.isArray(e.args)?e.args:{}});return}if(e.type==="tool_execution_end"){this.runPhase="thinking",this.emitEvent({state:"tool-result",name:typeof e.toolName=="string"?e.toolName:"tool",result:z(e.result)||X(e.result)});return}if(e.type==="tool_execution_update"){this.emitEvent({state:"notify",runPhase:"tool_running",message:"Pi tool is running"});return}if(e.type==="message_end"){const s=e.message;if(s&&typeof s=="object"){const t=s;this.lastUsage=I(t.usage)??this.lastUsage,t.stopReason==="error"&&(this.modelBroker?.setTurnActive(!1),this.emitError(typeof t.errorMessage=="string"?t.errorMessage:"Pi model request failed"))}return}if(e.type==="compaction_start"){this.runPhase="thinking",this.emitEvent({state:"notify",runPhase:"thinking",message:"Pi is compacting context"});return}if(e.type==="auto_retry_start"||e.type==="summarization_retry_scheduled"){this.runPhase="thinking",this.emitEvent({state:"notify",runPhase:"thinking",message:"Pi is retrying"});return}if(e.type==="extension_ui_request"){typeof e.id=="string"&&this.rpc?.notify("extension_ui_response",{id:e.id,cancelled:!0});return}if(e.type==="extension_error"){this.modelBroker?.setTurnActive(!1),this.emitError(typeof e.error=="string"?e.error:"Pi extension failed");return}e.type==="agent_settled"&&(this.active=!1,this.modelBroker?.setTurnActive(!1),this.emitTerminal({state:"final",agentSessionId:this.agentSessionId??void 0,usage:this.lastUsage}))}emitEvent(e){this.runId&&this.emit("agentEvent",{...e,runId:this.runId,seq:this.seq++})}emitTerminal(e){this.terminal||(this.terminal=!0,this.active=!1,this.runPhase=null,this.emitEvent(e))}emitError(e){this.modelBroker?.setTurnActive(!1),this.emitTerminal({state:"error",message:H(e)})}async prepareHostedModel(){const e=R(),s=e.machineToken?.trim();if(!s)return null;const t=new O({serverUrl:e.serverUrl?.trim()||"https://shennian.net",machineToken:s,sessionId:this.shennianSessionId}),n=await t.start(),a=f("runtime","pi-coding-agent",g(this.shennianSessionId),"agent-config");h.mkdirSync(a,{recursive:!0,mode:448}),process.platform!=="win32"&&h.chmodSync(a,448);const l=u.join(a,"models.json"),c={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 h.writeFileSync(l,`${JSON.stringify(c,null,2)}
|
|
4
|
-
`,{encoding:"utf8",mode:384}),process.platform!=="win32"&&h.chmodSync(l,384),this.modelBroker=
|
|
1
|
+
import h from"node:fs";import u from"node:path";import{randomUUID as P}from"node:crypto";import{AgentAdapter as E,registerAgent as v}from"./adapter.js";import{resolveBuiltinCommand as A,spawnAgentCommand as k}from"./command-spec.js";import{buildAgentProcessEnv as x,buildManagedAgentProcessEnv as R,buildManagedNativeFullProcessEnv as T}from"../agent-env.js";import{loadConfig as N,resolveShennianPath as f}from"../config/index.js";import{buildInlineChannelPreamble as b,buildPlatformInstructions as M}from"./platform-instructions.js";import{isManagedAgentNativeFull as w}from"../security/managed-agent-policy.js";import{PiRpcClient as C}from"./pi-rpc-client.js";import{registerManagedAgentRuntimeSecurity as D}from"../security/managed-agent-runtime.js";import{PI_CODING_AGENT_MANAGED_RUNTIME_SECURITY as B}from"./pi-coding-agent-managed-permissions.js";import{PiManagedModelBroker as O}from"./pi-managed-model-broker.js";import{writePiManagedExtension as j}from"./pi-managed-extension.js";import{recordPiSessionOwnership as q}from"./pi-session-ownership.js";D("pi-coding-agent",B);const F=32*1024,U=256*1024,$=3e3;function g(i){return i.replace(/[^a-zA-Z0-9_.-]/g,"_").slice(0,180)||"session"}function G(i){return i.data&&typeof i.data=="object"&&!Array.isArray(i.data)?i.data:{}}function z(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=U){return i.length<=t?i:`${i.slice(0,t)}
|
|
2
|
+
[truncated]`}function H(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 X(i){try{return y(JSON.stringify(i))}catch{return"[unserializable]"}}function I(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 L(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 Z(i,t,s,e,n,a){const l=b(s,e,n,a),c=t?.filter(o=>o.kind!=="image")??[];if(!c.length)return`${l}${i}`;const p=c.map(o=>`[${o.name}](<${o.path}>)`);return`${l}${[i,"",...p].join(`
|
|
3
|
+
`)}`}function K(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=h.statSync(e.path);l.isFile()&&l.size<=10*1024*1024&&(n=h.readFileSync(e.path).toString("base64"))}catch{n=""}n&&s.push({type:"image",data:n,mimeType:e.mimeType})}return s}class W extends E{type="pi-coding-agent";get recoveryCapabilities(){return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!0}}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-${P()}`),this.sessionDir=this.validationRoot?u.join(this.validationRoot,"sessions"):u.join(a,"sessions"),h.mkdirSync(this.sessionDir,{recursive:!0,mode:448}),process.platform!=="win32"&&h.chmodSync(this.sessionDir,448),this.active=!1,this.terminal=!0}async send(t,s,e,n,a,l){await this.ensureProcess(a);const c=this.requireRpc(),p=await this.readState(),o=!!(p.isStreaming||p.isCompacting);(!o||!this.runId)&&(this.runId=P(),this.seq=0,this.terminal=!1,this.lastUsage=void 0,this.runPhase="thinking");const d=L(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 c.request("set_model",{provider:d.provider,modelId:d.modelId})}e&&!o&&await c.request("set_thinking_level",{level:e});const r=o?l?.source==="room_activation"?"follow_up":"steer":"prompt",m={message:Z(t,n,this.workDir,this.externalChannel,this.shennianSessionId,null)},S=K(n,!this.managedAgentPolicy);S.length&&!this.modelBroker&&(m.images=S),this.modelBroker?.setTurnActive(!0);try{await c.request(r,m)}catch(_){throw this.modelBroker?.setTurnActive(!1),_}}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&&!w(this.managedAgentPolicy)){e.push("--no-extensions","--no-skills","--no-prompt-templates","--no-context-files");const r=this.managedTools(),m=j({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=M(this.workDir,this.externalChannel,this.shennianSessionId,t),l=this.validationRoot??f("runtime","pi-coding-agent",g(this.shennianSessionId));h.mkdirSync(l,{recursive:!0,mode:448}),this.promptFile=u.join(l,"instructions.md"),h.writeFileSync(this.promptFile,a,{encoding:"utf8",mode:384}),process.platform!=="win32"&&h.chmodSync(this.promptFile,384),e.push("--append-system-prompt",this.promptFile);const c={NO_COLOR:"1",...n?{PI_CODING_AGENT_DIR:n.configDir}:{}},p=this.managedAgentPolicy&&!w(this.managedAgentPolicy)?R({agentType:this.type,providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:c}):this.managedAgentPolicy?T({...c,...this.extraEnv}):x({...c,...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}`,F)});const d=new C(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&&q(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 G(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=I(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:z(t.result)||X(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=I(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:H(t)})}async prepareHostedModel(){const t=N(),s=t.machineToken?.trim();if(!s)return null;const e=new O({serverUrl:t.serverUrl?.trim()||"https://shennian.net",machineToken:s,sessionId:this.shennianSessionId}),n=await e.start(),a=this.validationRoot?u.join(this.validationRoot,"agent-config"):f("runtime","pi-coding-agent",g(this.shennianSessionId),"agent-config");h.mkdirSync(a,{recursive:!0,mode:448}),process.platform!=="win32"&&h.chmodSync(a,448);const l=u.join(a,"models.json"),c={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 h.writeFileSync(l,`${JSON.stringify(c,null,2)}
|
|
4
|
+
`,{encoding:"utf8",mode:384}),process.platform!=="win32"&&h.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()},$).unref()}),await this.stopModelBroker()):await this.stopModelBroker(),this.validationRoot&&(h.rmSync(this.validationRoot,{recursive:!0,force:!0}),this.validationRoot=null)}}v("pi-coding-agent",()=>new W);export{W as PiCodingAgentAdapter};
|
|
@@ -1 +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
|
|
1
|
+
import f from"node:http";import{randomBytes as c,timingSafeEqual as y}from"node:crypto";const b=20*1024*1024,p=18e4;function g(a){return a?.startsWith("Bearer ")?a.slice(7):""}function _(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 C{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=g(t.headers.authorization);if(!r||!_(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();if(e.destroyed||!this.turnActive){await this.revokeUpstreamCapability();return}const n=new AbortController;t.once("aborted",()=>n.abort()),e.once("close",()=>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}`},signal:AbortSignal.timeout(5e3),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}`},signal:AbortSignal.timeout(5e3)}).catch(()=>{})}}export{C as PiManagedModelBroker};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type PiValidationSession } from './pi-validation-session.js';
|
|
1
2
|
import { type AgentType, type ReqFrame, type SessionMode } from '@shennian/wire';
|
|
2
3
|
import { type AgentAdapter } from '../../agents/adapter.js';
|
|
3
4
|
import type { SessionManagerRuntime } from '../types.js';
|
|
@@ -14,6 +15,7 @@ export type AgentWorkspaceValidationResult = {
|
|
|
14
15
|
errorCode: string;
|
|
15
16
|
};
|
|
16
17
|
type ValidationDependencies = {
|
|
18
|
+
registerPiSession?: () => Promise<PiValidationSession>;
|
|
17
19
|
createAdapter?: (type: AgentType) => AgentAdapter | null;
|
|
18
20
|
resolveWorkDir?: (workDir: string) => Promise<string>;
|
|
19
21
|
timeoutMs?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import{registerPiValidationSession as I}from"./pi-validation-session.js";import y from"node:fs/promises";import{randomUUID as C}from"node:crypto";import{isAvailableAgentType as A}from"@shennian/wire";import{createAgent as b}from"../../agents/adapter.js";import{buildManagedAgentEnv as D}from"../../agents/config-status.js";import{ManagerModeAdapter as S}from"../../agents/manager.js";import{NativeSessionLifecycleRegistry as M}from"../native-session-lifecycle.js";const T="\u8BF7\u56DE\u590D hello\uFF0C\u7528\u4E8E\u786E\u8BA4\u4F60\u5DF2\u51C6\u5907\u597D\u3002",x=6e4,N=new M;async function O(e,r={}){try{return await L(e,r)}catch(t){const i=t instanceof Error?t.message:"";return{ok:!1,errorCode:i==="agent_validation_cleanup_failed"?i:p(t,"start")}}}async function L(e,r){if(!A(e.runtimeAdapter)||e.runtimeAdapter==="manager"||e.runtimeAdapter==="pi"||e.runtimeAdapter==="openclaw")return{ok:!1,errorCode:"agent_validation_runtime_unavailable"};if(e.sessionMode==="manager"&&!["codex","claude","pi-coding-agent"].includes(e.runtimeAdapter))return{ok:!1,errorCode:"agent_validation_manager_unavailable"};let t;try{t=await(r.resolveWorkDir??P)(e.workDir)}catch(o){return{ok:!1,errorCode:R(o)}}const i=(r.createAdapter??b)(e.runtimeAdapter);if(!i)return{ok:!1,errorCode:"agent_validation_runtime_unavailable"};const n=e.sessionMode==="manager"?new S(i,e.runtimeAdapter):i;let u=`agent-validation-${C()}`,d,c="",f="",m=null,l=null;const E=new Promise(o=>{l=o}),k=o=>{o.agentSessionId&&(c=o.agentSessionId),o.state==="delta"&&!o.thinking&&o.text&&(f+=o.text),o.state==="final"?(o.text&&(f+=o.text),l?.({kind:"final"})):(o.state==="error"||o.state==="aborted")&&l?.({kind:"error",error:o.message??o.state})},v=o=>l?.({kind:"error",error:o});n.on("agentEvent",k),n.on("error",v);const h=async()=>{e.runtimeAdapter==="pi-coding-agent"&&(d=await(r.registerPiSession??I)(),u=d.sessionId),n.configure?.({sessionId:u,env:D(e.runtimeAdapter)});try{await n.start(u,t,null,{persistNativeSession:!1})}catch(a){return{ok:!1,errorCode:p(a,"start")}}const o=new Promise(a=>{m=setTimeout(()=>a({kind:"timeout"}),r.timeoutMs??x)}),_=n.send(T,e.modelId??void 0).then(()=>E).catch(a=>({kind:"error",error:a})),s=await Promise.race([_,o]);return s.kind==="timeout"?{ok:!1,errorCode:"agent_validation_response_timeout"}:s.kind==="error"?{ok:!1,errorCode:p(s.error,"response")}:f.toLowerCase().includes("hello")?{ok:!0}:{ok:!1,errorCode:"agent_validation_response_mismatch"}};let w,g=!1;try{w=await h()}finally{m&&clearTimeout(m),n.removeListener("agentEvent",k),n.removeListener("error",v);try{await n.stop()}catch{d&&(g=!0)}finally{await d?.dispose().catch(()=>{g=!0})}c&&(r.cleanupNativeSession??((_,s,a)=>{N.execute(_,s,{workDir:a}).catch(()=>{})}))(e.runtimeAdapter,c,t)}return g?{ok:!1,errorCode:"agent_validation_cleanup_failed"}:w}async function H(e,r){const t=V(r.params);if(!t){e.client.sendRes({type:"res",id:r.id,ok:!1,error:"agent_validation_invalid_configuration"});return}const i=await O(t);e.client.sendRes(i.ok?{type:"res",id:r.id,ok:!0,payload:{ready:!0}}:{type:"res",id:r.id,ok:!1,error:i.errorCode})}async function P(e){const r=e.trim();if(!r)throw Object.assign(new Error("workdir_required"),{code:"EINVAL"});const t=await y.realpath(r);if(!(await y.stat(t)).isDirectory())throw Object.assign(new Error("workdir_not_directory"),{code:"ENOTDIR"});return t}function R(e){const r=W(e);return r==="ENOENT"?"agent_validation_workdir_not_found":r==="ENOTDIR"?"agent_validation_workdir_not_directory":r==="EACCES"||r==="EPERM"?"agent_validation_workdir_unavailable":"agent_validation_workdir_invalid"}function p(e,r){const t=e instanceof Error?e.message:String(e);return t==="agent_validation_server_upgrade_required"?t:/model.*(?:not found|unavailable|unsupported|invalid)|unknown model/i.test(t)?"agent_validation_model_unavailable":/not found|enoent|command .* missing|is .* installed/i.test(t)?"agent_validation_runtime_unavailable":/unauth|authentication|not logged|login required|api key|token.*missing|forbidden|401|403/i.test(t)?"agent_validation_runtime_auth_required":r==="start"?"agent_validation_start_failed":"agent_validation_response_failed"}function W(e){return typeof e=="object"&&e&&"code"in e?String(e.code):""}function V(e){const r=typeof e.runtimeAdapter=="string"?e.runtimeAdapter:"",t=typeof e.workDir=="string"?e.workDir:"",i=e.sessionMode==="manager"?"manager":"standard";return!A(r)||!t.trim()?null:{runtimeAdapter:r,workDir:t,sessionMode:i,modelId:typeof e.modelId=="string"?e.modelId:null}}export{H as handleAgentWorkspaceValidation,O as validateAgentWorkspaceConfiguration};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type PiValidationSession = {
|
|
2
|
+
sessionId: string;
|
|
3
|
+
dispose: () => Promise<void>;
|
|
4
|
+
};
|
|
5
|
+
/** Register a short-lived, non-listable session using the daemon's normal machine auth. */
|
|
6
|
+
export declare function registerPiValidationSession(): Promise<PiValidationSession>;
|
|
7
|
+
export declare function createPiValidationSession(serverUrl: string, machineToken: string, request?: typeof fetch): Promise<PiValidationSession>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{loadConfig as s}from"../../config/index.js";import{regionToUrl as d}from"../../region.js";async function h(){const i=s();if(!i.machineToken)throw new Error("401 machine authentication required");return l(i.serverUrl||d("cn"),i.machineToken)}async function l(i,o,t=fetch){const n=`${i.replace(/\/$/,"")}/api/managed-model/v1/validation-sessions`,r=await t(n,{method:"POST",headers:{authorization:`Bearer ${o}`},signal:AbortSignal.timeout(5e3)});if(r.status===404)throw new Error("agent_validation_server_upgrade_required");if(!r.ok)throw new Error(`Pi validation registration failed (${r.status})`);const e=await r.json();if(typeof e.sessionId!="string"||!/^pi-validation-[0-9a-f-]{36}$/.test(e.sessionId))throw new Error("Pi validation registration returned an invalid session");const a=e.sessionId;return{sessionId:a,async dispose(){try{if(!(await t(`${n}/${a}`,{method:"DELETE",headers:{authorization:`Bearer ${o}`},signal:AbortSignal.timeout(5e3)})).ok)throw new Error("agent_validation_cleanup_failed")}catch{throw new Error("agent_validation_cleanup_failed")}}}}export{l as createPiValidationSession,h as registerPiValidationSession};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import r from"node:fs";import e from"node:path";import{fileURLToPath as S}from"node:url";import{randomUUID as
|
|
2
|
-
`,{mode:384,flag:"wx"});const i=r.openSync(n,process.platform==="win32"?"r+":"r");try{r.fsyncSync(i)}finally{r.closeSync(i)}r.renameSync(n,
|
|
1
|
+
import r from"node:fs";import e from"node:path";import{fileURLToPath as S}from"node:url";import{randomUUID as d}from"node:crypto";import{resolveShennianPath as f}from"../config/index.js";import{UPDATER_PROTOCOL_VERSION as l}from"./updater-protocol.js";function m(t,o){r.mkdirSync(e.dirname(t),{recursive:!0,mode:448});const n=`${t}.tmp-${process.pid}-${d()}`;try{r.writeFileSync(n,`${JSON.stringify(o,null,2)}
|
|
2
|
+
`,{mode:384,flag:"wx"});const i=r.openSync(n,process.platform==="win32"?"r+":"r");try{r.fsyncSync(i)}finally{r.closeSync(i)}r.renameSync(n,t)}finally{try{r.unlinkSync(n)}catch{}}}function U(t={}){const o=t.root??f("runtime","updater"),n=e.join(o,"versions",String(l));r.mkdirSync(n,{recursive:!0,mode:448}),process.platform!=="win32"&&r.chmodSync(n,448),m(e.join(n,"package.json"),{type:"module"});const i=t.assetPath??S(new URL("./updater-worker.js",import.meta.url)),s=e.join(n,"updater-worker.js"),p=r.readFileSync(i),u=(()=>{try{return r.readFileSync(s)}catch{return null}})();if(!u||!u.equals(p)){const c=`${s}.tmp-${process.pid}-${d()}`;try{r.writeFileSync(c,p,{mode:448,flag:"wx"}),process.platform!=="win32"&&r.chmodSync(c,448);const y=r.openSync(c,process.platform==="win32"?"r+":"r");try{r.fsyncSync(y)}finally{r.closeSync(y)}r.renameSync(c,s)}finally{try{r.unlinkSync(c)}catch{}}}const a={protocolVersion:l,workerPath:s,installedAt:(t.now??new Date).toISOString()};return m(e.join(o,"current.json"),a),m(e.join(o,"readiness.json"),{protocolVersion:a.protocolVersion,installedAt:a.installedAt}),a}function $(t=f("runtime","updater")){try{const o=JSON.parse(r.readFileSync(e.join(t,"current.json"),"utf8"));if(o.protocolVersion!==l||!o.workerPath)return null;const n=r.lstatSync(o.workerPath);return n.isSymbolicLink()||!n.isFile()?null:o}catch{return null}}export{U as ensureUpdaterInstalled,$ as readUpdaterReadiness};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn, type ChildProcess } from 'node:child_process';
|
|
1
|
+
import { spawn, spawnSync, type ChildProcess } from 'node:child_process';
|
|
2
2
|
import { ensureUpdaterInstalled } from './updater-bootstrap.js';
|
|
3
3
|
import { type UpdaterJob } from './updater-protocol.js';
|
|
4
4
|
export declare function createUpdaterJob(input: {
|
|
@@ -15,14 +15,18 @@ export declare function createUpdaterJob(input: {
|
|
|
15
15
|
isolatedDaemonStart?: boolean;
|
|
16
16
|
}): UpdaterJob;
|
|
17
17
|
export declare function writeUpdaterJob(job: UpdaterJob, root?: string): string;
|
|
18
|
+
export declare function needsSystemdUpdater(platform: string, cgroup: string): boolean;
|
|
18
19
|
export declare function launchUpdater(input: Parameters<typeof createUpdaterJob>[0], deps?: {
|
|
19
20
|
root?: string;
|
|
20
21
|
spawn?: typeof spawn;
|
|
21
22
|
readiness?: ReturnType<typeof ensureUpdaterInstalled>;
|
|
22
23
|
now?: () => number;
|
|
23
24
|
isAlive?: (pid: number) => boolean;
|
|
25
|
+
serviceCgroup?: string;
|
|
26
|
+
platform?: NodeJS.Platform;
|
|
27
|
+
systemdRun?: typeof spawnSync;
|
|
24
28
|
}): {
|
|
25
29
|
job: UpdaterJob;
|
|
26
|
-
process: ChildProcess;
|
|
30
|
+
process: ChildProcess | null;
|
|
27
31
|
};
|
|
28
32
|
export declare function markUpdaterHealthy(jobId: string, effectiveVersion: string, root?: string): boolean;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
`,{mode:384,flag:"wx"});const c=
|
|
3
|
-
`,{mode:384,flag:"wx"});const
|
|
1
|
+
import e from"node:fs";import u from"node:path";import{randomUUID as S}from"node:crypto";import{spawn as j,spawnSync as P}from"node:child_process";import{resolveShennianPath as p}from"../config/index.js";import{findPackageJson as I,resolveNpmGlobalPrefixFromPackageJson as E}from"./engine.js";import{ensureUpdaterInstalled as _}from"./updater-bootstrap.js";import{UPDATER_PROTOCOL_VERSION as w}from"./updater-protocol.js";function b(r,o){e.mkdirSync(u.dirname(r),{recursive:!0,mode:448});const n=`${r}.tmp-${process.pid}-${S()}`;try{e.writeFileSync(n,`${JSON.stringify(o,null,2)}
|
|
2
|
+
`,{mode:384,flag:"wx"});const c=e.openSync(n,process.platform==="win32"?"r+":"r");try{e.fsyncSync(c)}finally{e.closeSync(c)}if(e.renameSync(n,r),process.platform!=="win32"){const t=e.openSync(u.dirname(r),"r");try{e.fsyncSync(t)}finally{e.closeSync(t)}}}finally{try{e.unlinkSync(n)}catch{}}}function $(r){const o=r.root??p("runtime","updater"),n=I(),c=r.npmPrefix??(n?E(n):null);if(!c)throw new Error("Cannot determine the active npm prefix");const t=r.jobId??S();return{protocolVersion:w,jobId:t,fromVersion:r.fromVersion,targetVersion:r.targetVersion,npmPrefix:c,npmExecutable:r.npmExecutable??(process.platform==="win32"?"npm.cmd":"npm"),nodeExecutable:r.nodeExecutable??process.execPath,packageName:"shennian",operation:r.operation??"upgrade",oldDaemonPid:r.oldDaemonPid,platform:process.platform,createdAt:(r.now??new Date).toISOString(),daemonExitTimeoutMs:15e3,commandTimeoutMs:12e4,resultPath:u.join(o,"results",`${t}.json`),attemptPath:p("upgrade-attempt.json"),...r.isolatedDaemonStart?{isolatedDaemonStart:!0}:{}}}function O(r,o=p("runtime","updater")){const n=u.join(o,"jobs");e.mkdirSync(n,{recursive:!0,mode:448});const c=u.join(n,`${r.jobId}.json`),t=`${c}.tmp-${process.pid}-${S()}`;try{e.writeFileSync(t,`${JSON.stringify(r,null,2)}
|
|
3
|
+
`,{mode:384,flag:"wx"});const s=e.openSync(t,process.platform==="win32"?"r+":"r");try{e.fsyncSync(s)}finally{e.closeSync(s)}if(e.renameSync(t,c),process.platform!=="win32"){const a=e.openSync(n,"r");try{e.fsyncSync(a)}finally{e.closeSync(a)}}return c}finally{try{e.unlinkSync(t)}catch{}}}function v(r,o){return r==="linux"&&/(?:^|\/)shennian\.service(?:\n|$)/m.test(o.trim())}function C(r,o={}){const n=o.root??r.root??p("runtime","updater"),c=o.readiness??_({root:n}),t=$({...r,root:n}),s=O(t,n),a=u.join(n,"updater.lock");e.mkdirSync(n,{recursive:!0,mode:448});let l=null,m=!1;const h=()=>{if(m){try{JSON.parse(e.readFileSync(a,"utf8")).jobId===t.jobId&&e.unlinkSync(a)}catch{}m=!1}};try{try{l=e.openSync(a,"wx",384),m=!0}catch(i){if(i.code!=="EEXIST")throw i;let g=!1;try{const y=JSON.parse(e.readFileSync(a,"utf8")),k=o.isAlive??(x=>{try{return process.kill(x,0),!0}catch{return!1}});g=!!(y.pid&&k(y.pid)&&(o.now??Date.now)()-Number(y.createdAt)<30*6e4)}catch{}if(g)throw new Error("upgrade_locked");e.rmSync(a,{force:!0}),l=e.openSync(a,"wx",384),m=!0}e.writeFileSync(l,JSON.stringify({jobId:t.jobId,pid:process.pid,createdAt:(o.now??Date.now)()})),e.closeSync(l),l=null;let d=o.serviceCgroup??"";if(process.platform==="linux"&&!o.spawn&&o.serviceCgroup===void 0)try{d=e.readFileSync("/proc/self/cgroup","utf8")}catch{}if(v(o.platform??process.platform,d)){const i=(o.systemdRun??P)("systemd-run",["--user","--quiet","--collect",`--unit=shennian-updater-${t.jobId}`,"--property=Type=exec","--property=KillMode=process",`--setenv=PATH=${process.env.PATH??"/usr/local/bin:/usr/bin:/bin"}`,"--",t.nodeExecutable,c.workerPath,s],{encoding:"utf8",stdio:"pipe",timeout:1e4});if(i.error||i.status!==0)throw new Error("updater_service_launch_failed");return{job:t,process:null}}const f=(o.spawn??j)(t.nodeExecutable,[c.workerPath,s],{detached:!0,stdio:"ignore",windowsHide:!0});return f.once?.("error",i=>{b(t.resultPath,{protocolVersion:w,jobId:t.jobId,state:"failed",fromVersion:t.fromVersion,targetVersion:t.targetVersion,errorCode:"internal_error",errorSummary:i instanceof Error?i.message.slice(0,500):"updater_spawn_failed",updatedAt:new Date().toISOString()}),h()}),f.unref(),{job:t,process:f}}catch(d){l!==null&&e.closeSync(l),h();try{e.unlinkSync(s)}catch{}throw d}}function H(r,o,n=p("runtime","updater")){if(!/^[A-Za-z0-9_-]{1,128}$/.test(r))return!1;const c=u.join(n,"results",`${r}.json`);try{const t=e.lstatSync(c);if(t.isSymbolicLink()||!t.isFile())return!1;const s=JSON.parse(e.readFileSync(c,"utf8"));return s.protocolVersion!==w||s.jobId!==r||s.state!=="starting"||s.targetVersion!==o?!1:(b(c,{...s,state:s.operation==="rollback"?"rolled_back":"healthy",effectiveVersion:o,updatedAt:new Date().toISOString()}),!0)}catch{return!1}}export{$ as createUpdaterJob,C as launchUpdater,H as markUpdaterHealthy,v as needsSystemdUpdater,O as writeUpdaterJob};
|
|
@@ -269,6 +269,8 @@ export type WeChatRpaPreflightCheck = {
|
|
|
269
269
|
};
|
|
270
270
|
export type NianSession = {
|
|
271
271
|
id: string;
|
|
272
|
+
/** Device-local lifecycle only; never sent as canonical session metadata. */
|
|
273
|
+
localCreationState?: 'draft' | 'created';
|
|
272
274
|
agentIdentityId?: string | null;
|
|
273
275
|
machineId: string;
|
|
274
276
|
userId: string;
|