shennian 0.4.69 → 0.4.72
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 +150 -90
- package/dist/src/agent-env.js +4 -4
- package/dist/src/agents/adapter.d.ts +7 -3
- package/dist/src/agents/auth-status.d.ts +4 -0
- package/dist/src/agents/auth-status.js +2 -0
- package/dist/src/agents/claude.js +4 -4
- package/dist/src/agents/codex-protocol.d.ts +103 -0
- package/dist/src/agents/codex-protocol.js +3 -0
- package/dist/src/agents/codex-runtime.d.ts +91 -0
- package/dist/src/agents/codex-runtime.js +14 -0
- package/dist/src/agents/codex.d.ts +15 -91
- package/dist/src/agents/codex.js +2 -17
- package/dist/src/agents/command-spec.js +4 -3
- package/dist/src/agents/detect.js +1 -1
- package/dist/src/agents/manager.d.ts +4 -3
- package/dist/src/agents/manager.js +4 -4
- package/dist/src/agents/model-registry/discovery.js +3 -3
- package/dist/src/agents/model-registry/parsers.js +4 -4
- package/dist/src/agents/model-registry/service.d.ts +6 -2
- package/dist/src/agents/model-registry/service.js +1 -1
- package/dist/src/agents/model-registry/types.d.ts +9 -1
- package/dist/src/agents/model-registry/types.js +1 -1
- package/dist/src/agents/opencode.d.ts +4 -0
- package/dist/src/agents/opencode.js +1 -1
- package/dist/src/commands/agent-readiness.d.ts +17 -0
- package/dist/src/commands/agent-readiness.js +2 -0
- package/dist/src/commands/agent.js +2 -2
- package/dist/src/commands/daemon-instance-lock.js +1 -1
- package/dist/src/commands/daemon-process.d.ts +1 -0
- package/dist/src/commands/daemon-process.js +1 -1
- package/dist/src/commands/daemon-service-spec.d.ts +28 -0
- package/dist/src/commands/daemon-service-spec.js +1 -0
- package/dist/src/commands/daemon-start-wait.d.ts +1 -1
- package/dist/src/commands/daemon-start-wait.js +1 -1
- package/dist/src/commands/daemon-state.d.ts +57 -0
- package/dist/src/commands/daemon-state.js +1 -0
- package/dist/src/commands/daemon-windows.d.ts +8 -0
- package/dist/src/commands/daemon-windows.js +10 -10
- package/dist/src/commands/daemon.d.ts +4 -64
- package/dist/src/commands/daemon.js +13 -13
- package/dist/src/commands/manager.js +1 -1
- package/dist/src/commands/upgrade.js +1 -1
- package/dist/src/commands/worker.d.ts +11 -0
- package/dist/src/commands/worker.js +2 -0
- package/dist/src/daemon-ipc/agent-readiness.d.ts +14 -0
- package/dist/src/daemon-ipc/agent-readiness.js +1 -0
- package/dist/src/daemon-ipc/control.d.ts +1 -1
- package/dist/src/daemon-ipc/control.js +1 -1
- package/dist/src/index.js +3 -3
- package/dist/src/manager/prompt.d.ts +1 -1
- package/dist/src/manager/prompt.js +6 -6
- package/dist/src/manager/registry.d.ts +48 -2
- package/dist/src/manager/registry.js +1 -1
- package/dist/src/manager/runtime.d.ts +2 -2
- package/dist/src/manager/runtime.js +6 -6
- package/dist/src/manager/worker-ipc.d.ts +7 -0
- package/dist/src/manager/worker-ipc.js +1 -1
- package/dist/src/native-fusion/local-repository.d.ts +6 -1
- package/dist/src/native-fusion/local-repository.js +2 -2
- package/dist/src/native-fusion/service.d.ts +2 -0
- package/dist/src/native-fusion/service.js +2 -2
- package/dist/src/native-fusion/source-visibility-audit.d.ts +16 -0
- package/dist/src/native-fusion/source-visibility-audit.js +1 -0
- package/dist/src/native-fusion/source-visibility.d.ts +25 -0
- package/dist/src/native-fusion/source-visibility.js +1 -0
- package/dist/src/native-fusion/state.d.ts +1 -0
- package/dist/src/native-fusion/state.js +2 -2
- package/dist/src/native-fusion/types.d.ts +5 -0
- package/dist/src/room/managed-room-binding.d.ts +2 -0
- package/dist/src/room/managed-room-binding.js +1 -1
- package/dist/src/room/room-binding-client.d.ts +1 -0
- package/dist/src/room/room-binding-client.js +1 -1
- package/dist/src/runtime-distribution.d.ts +13 -0
- package/dist/src/runtime-distribution.js +1 -0
- package/dist/src/security/managed-agent-policy.d.ts +7 -0
- package/dist/src/security/managed-agent-policy.js +1 -1
- package/dist/src/session/handlers/agent-workspace-validation.d.ts +1 -0
- package/dist/src/session/handlers/agent-workspace-validation.js +1 -1
- package/dist/src/session/handlers/agents.d.ts +7 -1
- package/dist/src/session/handlers/agents.js +1 -1
- package/dist/src/session/handlers/chat-context.d.ts +31 -0
- package/dist/src/session/handlers/chat-context.js +1 -0
- package/dist/src/session/handlers/chat-events.d.ts +1 -1
- package/dist/src/session/handlers/chat-events.js +2 -2
- package/dist/src/session/handlers/chat-session-lifecycle.d.ts +2 -2
- package/dist/src/session/handlers/chat-session-lifecycle.js +1 -1
- package/dist/src/session/handlers/chat.d.ts +3 -29
- package/dist/src/session/handlers/chat.js +1 -1
- package/dist/src/session/manager.js +2 -2
- package/dist/src/session/queue-helpers.js +2 -2
- package/dist/src/session/queue-message.d.ts +2 -0
- package/dist/src/session/queue.d.ts +7 -1
- package/dist/src/session/queue.js +1 -1
- package/dist/src/session/queued-chat-dispatch.d.ts +4 -1
- package/dist/src/session/queued-chat-dispatch.js +1 -1
- package/dist/src/session/store.d.ts +4 -2
- package/dist/src/session/store.js +2 -2
- package/dist/src/session/types.d.ts +1 -0
- package/dist/src/upgrade/engine.js +2 -2
- package/dist/src/upgrade/scheduler.js +1 -1
- package/dist/src/upgrade/updater-launcher.js +3 -3
- package/node_modules/@shennian/wire/dist/room.d.ts +2 -0
- package/node_modules/@shennian/wire/dist/session-sync.d.ts +2 -1
- package/node_modules/@shennian/wire/dist/session.d.ts +25 -1
- package/node_modules/@shennian/wire/dist/session.js +8 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{AVAILABLE_BUILTIN_AGENT_TYPES as m}from"@shennian/wire";import{loadConfig as
|
|
1
|
+
import{probeBuiltinAuthStatus as c}from"./auth-status.js";import{AVAILABLE_BUILTIN_AGENT_TYPES as m}from"@shennian/wire";import{loadConfig as u}from"../config/index.js";import{getBuiltinAgentVersion as a,resolveBuiltinCommand as l}from"./command-spec.js";import{registerCustomRuntimeSecurity as f,resolveCustomRuntimeExecutableFingerprint as p}from"./custom-managed-runtime.js";import{resolveManagedAgentExecutableIdentity as d}from"../security/managed-agent-executable.js";import{discoverAcpAgents as g}from"./acp/discovery.js";const A=m.filter(n=>n!=="pi");function v(n,o){const i=n?.match(/(?:^|v)(\d+)\.(\d+)\.(\d+)/);if(!i)return!1;const t=i.slice(1,4).map(Number);for(let e=0;e<o.length;e+=1)if(t[e]!==o[e])return t[e]>o[e];return!0}function x(n){return v(n,[0,85,0])}function b(n){const o=n.models?.length?n.models:n.model?[n.model]:[],i=n.defaultModel??n.model;return o.map(t=>({id:t,name:t,isDefault:t===i}))}async function B(n={}){const o=[];for(const t of A){const e=l(t);if(!e)continue;const r=a(t,e);if((t==="codex"||t==="claude")&&!r||t==="pi-coding-agent"&&!x(r))continue;const s=d(e);o.push({type:t,command:e.command,args:e.args,version:r,runtimePlatform:s.platform,runtimeIsWsl:s.isWsl,executableFingerprint:s.executableFingerprint,authState:await c(t,e,r)})}const i=u();for(const[t,e]of Object.entries(i.customAgents??{})){const r=`custom:${t}`;f(r,e),o.push({type:r,command:e.command,args:[],version:e.caps.version,runtimePlatform:process.platform,runtimeIsWsl:process.platform==="linux"&&!!(process.env.WSL_DISTRO_NAME||process.env.WSL_INTEROP),executableFingerprint:p(e),models:b(e.caps)})}return o.push(...await g(n)),o}export{B as detectAgents,x as isPiCodingAgentRuntimeCompatible};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { AgentSendContext, AgentSendResult } from './adapter.js';
|
|
2
|
-
import { AgentAdapter } from './adapter.js';
|
|
3
1
|
import type { ChatAttachmentMeta, ExternalChannelSessionStatus } from '@shennian/wire';
|
|
4
2
|
import type { CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
|
|
3
|
+
import type { AgentSendContext, AgentSendResult } from './adapter.js';
|
|
4
|
+
import { AgentAdapter } from './adapter.js';
|
|
5
5
|
type ManagerSubstrate = Exclude<import('@shennian/wire').AgentType, 'manager' | 'pi' | 'openclaw'>;
|
|
6
6
|
/** Decorates a real provider Adapter with Manager session capabilities. New
|
|
7
7
|
* sessions use this class and keep their real agentType/modelId. */
|
|
@@ -45,7 +45,8 @@ export declare class ManagerAdapter extends AgentAdapter {
|
|
|
45
45
|
private workDir;
|
|
46
46
|
private agentSessionId;
|
|
47
47
|
private modelId;
|
|
48
|
-
|
|
48
|
+
private startOptions?;
|
|
49
|
+
start(sessionId: string, workDir: string, agentSessionId?: string | null, options?: import('./adapter.js').AgentStartOptions): Promise<void>;
|
|
49
50
|
send(text: string, modelId?: string): Promise<void>;
|
|
50
51
|
resume(agentSessionId: string): Promise<void>;
|
|
51
52
|
stop(): Promise<void>;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import d from"node:fs";import c from"node:path";import{MANAGER_SYSTEM_PROMPT as I,buildManagerPrompt as g}from"../manager/prompt.js";import{getManagerRuntimeService as a}from"../manager/runtime.js";import{AgentAdapter as l,registerAgent as p}from"./adapter.js";import{ClaudeAdapter as f}from"./claude.js";import{CodexAdapter as y}from"./codex.js";import{PLATFORM_OUTPUT_INSTRUCTIONS as S}from"./platform-instructions.js";function M(r){return r==="claude"?"claude":"codex"}function v(r){return r.replace(/[^a-zA-Z0-9_.-]/g,"_")}function w(r){const e=c.join(r,"AGENTS.md");try{return d.readFileSync(e,"utf8").trim()}catch{return""}}function A(r,e){return c.join(r,".shennian","runtime","manager",v(e),"instructions.md")}function h(r,e){const n=w(r),t=a(),s=t?.getManagerExternalChannelSystemPrompt(e)??"",i=typeof t?.getManagerWorkerDefaults=="function"?t.getManagerWorkerDefaults(e):void 0,o=i?.agentType?`\u9ED8\u8BA4 worker Agent\uFF1A${i.agentType}${i.modelId?`
|
|
2
2
|
\u9ED8\u8BA4 worker \u6A21\u578B\uFF1A${i.modelId}`:""}\u3002\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u6216\u4EFB\u52A1\u660E\u663E\u9700\u8981\u5176\u4ED6 Agent/\u6A21\u578B\uFF0C\u521B\u5EFA worker \u65F6\u4F18\u5148\u4F7F\u7528\u8FD9\u4E2A\u9ED8\u8BA4\u7EC4\u5408\u3002`:"";return`${["# Shennian Manager Instructions","This file is generated by Shennian for a Manager Agent substrate session. Do not edit it by hand.",n?`## Project Instructions
|
|
3
3
|
|
|
4
4
|
${n}`:"",`## Manager Instructions
|
|
5
5
|
|
|
6
|
-
${
|
|
6
|
+
${I}`,o?`## Worker Defaults
|
|
7
7
|
|
|
8
8
|
${o}`:"",s?`## External Message Channel Instructions
|
|
9
9
|
|
|
10
10
|
${s}`:"",S].filter(Boolean).join(`
|
|
11
11
|
|
|
12
12
|
`)}
|
|
13
|
-
`}function k(r,e){const n=A(r,e),t=
|
|
13
|
+
`}function k(r,e){const n=A(r,e),t=h(r,e);d.mkdirSync(c.dirname(n),{recursive:!0});try{if(d.readFileSync(n,"utf8")===t)return n}catch{}return d.writeFileSync(n,t),n}class R extends l{inner;substrate;type;get recoveryCapabilities(){return this.inner.recoveryCapabilities}sessionId="";workDir="";agentSessionId=null;configured={};constructor(e,n){super(),this.inner=e,this.substrate=n,this.type=e.type,e.on("agentEvent",t=>{t.agentSessionId&&(this.agentSessionId=t.agentSessionId),this.emit("agentEvent",{...t,source:t.source??n})}),e.on("nativeSession",(t,s)=>this.emit("nativeSession",t,s)),e.on("error",t=>this.emit("error",t))}configure(e){this.configured=e,e.sessionId&&a()?.setManagedAgentPolicy(e.sessionId,e.managedAgentPolicy??null),this.applyEnvironment()}async start(e,n,t,s){this.sessionId=e,this.workDir=n,this.agentSessionId=t??null,this.applyEnvironment(),await this.inner.start(e,n,t,s),a()?.registerManager({sessionId:e,agentSessionId:this.agentSessionId,workDir:n,modelId:this.substrate,agentType:this.type,status:"idle"}),a()?.bindManagerAdapterEvents(e,this.inner)}async send(e,n,t,s,i,o){a()?.registerManager({sessionId:this.sessionId,agentSessionId:this.agentSessionId,workDir:this.workDir,modelId:this.substrate,agentType:this.type,providerModelId:n??null,status:"running"}),await a()?.ready(),this.applyEnvironment();const u=h(this.workDir,this.sessionId),m=E(u,i);return this.inner.send(g(e),n,t,s,m,o)}async resume(e){this.agentSessionId=e,await this.inner.resume(e)}async stop(){a()?.setManagedAgentPolicy(this.sessionId,null),await this.inner.stop()}getStatus(){return this.inner.getStatus?.()??Promise.resolve({active:!1})}setTitle(e,n,t){return this.inner.setTitle?.(e,n,t)??Promise.resolve()}applyEnvironment(){const e=this.sessionId?a()?.getInjectedEnv(this.sessionId,this.agentSessionId,this.workDir,this.substrate)??{}:{};this.inner.configure?.({...this.configured,sessionId:this.configured.sessionId??this.sessionId,env:{...this.configured.env??{},...e},managedAgentPolicy:this.configured.managedAgentPolicy??null})}}function E(r,e){const n=e?.trim()??"";if(!n)return r;const s=["[\u667A\u80FD\u4F53\u79C1\u6709\u7CFB\u7EDF\u63D0\u793A\u8BCD]","[\u667A\u80FD\u4F53\u8EAB\u4EFD]"].map(i=>n.indexOf(i)).filter(i=>i>=0).sort((i,o)=>i-o)[0]??-1;return n.startsWith("[\u795E\u5FF5\u53D7\u7BA1\u4F1A\u8BDD\u89C4\u5219]")&&s>=0?`${n.slice(0,s)}[\u4F1A\u8BDD\u6A21\u5F0F]
|
|
14
14
|
${r.trim()}
|
|
15
15
|
|
|
16
16
|
${n.slice(s)}`:`${r.trim()}
|
|
17
17
|
|
|
18
|
-
${n}`}class b extends
|
|
18
|
+
${n}`}class b extends l{type="manager";get recoveryCapabilities(){return this.inner?.recoveryCapabilities??{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!0}}inner=null;sessionId="";workDir="";agentSessionId=null;modelId="codex";startOptions;async start(e,n,t,s){this.startOptions=s,this.sessionId=e,this.workDir=n,this.agentSessionId=t??null,a()?.registerManager({sessionId:e,agentSessionId:this.agentSessionId,workDir:n,modelId:this.modelId,agentType:"manager",status:"idle"})}async send(e,n){const t=M(n);if(!this.inner||t!==this.modelId){await this.inner?.stop().catch(()=>{}),this.inner?.removeAllListeners(),this.modelId=t;const s=k(this.workDir,this.sessionId),i=h(this.workDir,this.sessionId);this.inner=t==="claude"?new f({systemPrompt:i,hidden:!0}):new y({modelInstructionsFile:s,hidden:!0}),this.bindInner(this.inner),await this.inner.start(this.sessionId,this.workDir,this.agentSessionId,this.startOptions),a()?.bindManagerAdapterEvents(this.sessionId,this.inner)}a()?.registerManager({sessionId:this.sessionId,agentSessionId:this.agentSessionId,workDir:this.workDir,modelId:this.modelId,agentType:"manager",status:"running"}),await a()?.ready(),await this.withManagerEnv(()=>this.inner.send(g(e)))}async resume(e){this.agentSessionId=e,this.inner&&await this.inner.resume(e)}async stop(){await this.inner?.stop()}bindInner(e){e.on("agentEvent",n=>{n.agentSessionId&&(this.agentSessionId=n.agentSessionId),this.emit("agentEvent",this.wrapEvent(n))}),e.on("nativeSession",(n,t)=>this.emit("nativeSession",n,t)),e.on("error",n=>this.emit("error",n))}wrapEvent(e){return{...e,source:e.source??this.modelId,agentSessionId:e.agentSessionId??this.agentSessionId??void 0}}async withManagerEnv(e){const n=a()?.getInjectedEnv(this.sessionId,this.agentSessionId,this.workDir,this.modelId)??{},t=new Map;for(const[s,i]of Object.entries(n))t.set(s,process.env[s]),process.env[s]=i;try{return await e()}finally{for(const[s,i]of t)i===void 0?delete process.env[s]:process.env[s]=i}}}p("manager",()=>new b);export{b as ManagerAdapter,R as ManagerModeAdapter,E as combineManagerInstructions};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{createInterface as
|
|
2
|
-
`,p=>{p&&(clearTimeout(c),e.delete(t),a(p))})}):Promise.reject(new Error("codex app-server stdin unavailable"))}async function
|
|
3
|
-
${n.stderr}`)?s:s.filter(i=>i.id!=="fable")}async function
|
|
1
|
+
import{createInterface as k}from"node:readline";import{resolveBuiltinCommand as l,spawnResolvedCommand as w}from"../command-spec.js";import{fallbackClaudeAliasModels as E,discoverClaudeAliasModelsFromEnv as x,fallbackCodexModels as M,fallbackWorkBuddyModels as R,fallbackGeminiModels as O,parseCodexAppServerModels as A,parseCursorModels as D,parseOpenCodeModels as I}from"./parsers.js";import{runResolvedCommand as m}from"./runner.js";import{DISCOVERY_WORKDIR as b,ModelDiscoveryFallbackError as C}from"./types.js";import{buildAgentProcessEnv as _,readLatestUserEnv as S}from"../../agent-env.js";import{CODEX_APP_SERVER_CLIENT_INFO as T}from"../codex-utils.js";import{PiRpcClient as P}from"../pi-rpc-client.js";import{loadConfig as $}from"../../config/index.js";import{discoverOpenCodeModelsViaApi as L}from"./opencode-api.js";function q(r){const e=r instanceof Error?r.message:"";return/timed out/i.test(e)?"app_server_timeout":/\berror\b|invalid|unsupported|unknown (?:method|field)/i.test(e)?"app_server_rpc_error":"app_server_unavailable"}function v(r,e,t,s,n,i){return r.stdin?new Promise((o,a)=>{const c=setTimeout(()=>{e.delete(t),a(new Error(`codex app-server request timed out: ${s}`))},i);e.set(t,{resolve:o,reject:a,timer:c}),r.stdin.write(`${JSON.stringify({id:t,method:s,params:n})}
|
|
2
|
+
`,p=>{p&&(clearTimeout(c),e.delete(t),a(p))})}):Promise.reject(new Error("codex app-server stdin unavailable"))}async function j(r){const e=w(r,["app-server","--listen","stdio://"],{cwd:b,stdio:["pipe","pipe","pipe"],env:_({NO_COLOR:"1"})}),t=new Map;let s=1;k({input:e.stdout}).on("line",i=>{if(!i.trim())return;let o;try{o=JSON.parse(i)}catch{return}if(o.id==null)return;const a=t.get(o.id);a&&(clearTimeout(a.timer),t.delete(o.id),o.error?a.reject(new Error(o.error.message||`codex app-server error ${o.error.code??""}`.trim())):a.resolve(o.result))});try{await v(e,t,s++,"initialize",{clientInfo:T,capabilities:{experimentalApi:!0}},1e4);const[i,o]=await Promise.all([v(e,t,s++,"model/list",{},15e3),v(e,t,s++,"config/read",{},15e3).catch(()=>null)]),a=typeof o=="object"&&o!==null&&"config"in o?o.config:void 0;return A({...typeof i=="object"&&i!==null?i:{},config:a})}finally{for(const[i,o]of t)clearTimeout(o.timer),o.reject(new Error("codex app-server stopped")),t.delete(i);e.kill("SIGTERM"),setTimeout(()=>e.kill("SIGKILL"),1e3).unref()}}async function F(){const r=l("cursor");if(!r)return[];const e=await m(r,["--list-models"]);return D(e.stdout)}async function G(){const r=l("opencode");if(!r)return[];const e=await L().catch(()=>[]);if(e.length>0)return e;const t=await m(r,["models"]);return I(t.stdout)}async function N(){const r={id:"shennian-dashscope/deepseek-v4-flash-0731",name:"DeepSeek V4 Flash",provider:"shennian-dashscope",isDefault:!0,supportedReasoningEfforts:["low","high","max"].map(n=>({id:n,name:n})),defaultReasoningEffort:"high"},e=l("pi-coding-agent");if(!e)return[];if($().machineToken?.trim())return[r];const t=w(e,["--mode","rpc","--no-session","--offline","--no-extensions","--no-skills","--no-prompt-templates","--no-context-files","--no-tools"],{cwd:b,stdio:["pipe","pipe","pipe"],env:_({NO_COLOR:"1"})}),s=new P(t,{requestTimeoutMs:1e4});try{const[n,i]=await Promise.all([s.request("get_available_models"),s.request("get_state")]),o=n.data&&typeof n.data=="object"?n.data:{},a=i.data&&typeof i.data=="object"?i.data:{},c=typeof a.model?.id=="string"&&typeof a.model.provider=="string"?`${a.model.provider}/${a.model.id}`:null;return Array.isArray(o.models)?o.models.flatMap(f=>{if(!f||typeof f!="object")return[];const d=f;if(typeof d.id!="string"||typeof d.provider!="string")return[];const h=`${d.provider}/${d.id}`,g=d.reasoning===!0?["off","minimal","low","medium","high","xhigh","max"].map(y=>({id:y,name:y})):void 0;return[{id:h,name:typeof d.name=="string"&&d.name.trim()?d.name:d.id,provider:d.provider,isDefault:h===c,...g?{supportedReasoningEfforts:g,defaultReasoningEffort:"medium"}:{}}]}):[]}catch{return[]}finally{s.close(),t.kill("SIGTERM"),setTimeout(()=>t.kill("SIGKILL"),1e3).unref()}}async function V(){const r=l("claude");if(!r)return[];const e=S(),t=x(e);if(t.length>0)return t;const s=E(e),n=await m(r,["--help"],3e3).catch(()=>null);return!n||/\bfable\b/i.test(`${n.stdout}
|
|
3
|
+
${n.stderr}`)?s:s.filter(i=>i.id!=="fable")}async function B(){const r=l("codex");if(!r)return[];let e;try{e=await j(r)}catch(t){throw new C("codex",q(t),M())}if(e.length===0)throw new C("codex","empty_model_list",M());return e}async function K(){return l("gemini")?O():[]}const u=[{id:"qwen3.6-plus",name:"Qwen 3.6 Plus",provider:"dashscope",isDefault:!0}],U=[{id:"codex",name:"Codex",provider:"shennian",isDefault:!0},{id:"claude",name:"Claude Code",provider:"shennian"}];async function W(r){if(!r.serverUrl||!r.authToken)return u;try{const e=await fetch(`${r.serverUrl.replace(/\/$/,"")}/api/models`,{headers:{Authorization:`Bearer ${r.authToken}`}});if(!e.ok)return u;const t=await e.json(),s=(t.models??[]).map(n=>({id:n.id,name:n.name?.trim()||n.id,provider:n.provider,isDefault:n.id===t.default}));return s.length>0?s:u}catch{return u}}async function z(r,e){switch(r){case"claude":return V();case"codex":return B();case"workbuddy":return l("workbuddy")?R():[];case"gemini":return K();case"cursor":return F();case"openclaw":return[];case"opencode":return G();case"pi":return W(e);case"pi-coding-agent":return N();case"manager":return U}}async function ne(r,e){return r.type.startsWith("custom:")?r.models??[]:z(r.type,e)}export{ne as discoverModelsForAgent};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function b(e){return e.replace(/\u001b\[[0-9;?]*[a-zA-Z]/g,"").replace(/\u001b\][^\u0007]*\u0007/g,"").replace(/[\u0000-\u0008\u000b-\u001f\u007f]/g,"")}function m(e){const t=new Set,n=[];for(const o of e)!o.id||t.has(o.id)||(t.add(o.id),n.push(o));return n}function y(e){return e&&e[0].toUpperCase()+e.slice(1)}function
|
|
2
|
-
`)){const i=o.trim().match(/^(\S+)\s+-\s+(.+?)(?:\s+\(([^)]+)\))?$/);if(!i)continue;const[,s,r,d]=i,l=d?.split(",").map(u=>u.trim().toLowerCase())??[];t.push({id:s,name:r.trim()||w(s),description:d?.trim(),isDefault:l.includes("default")||l.includes("current")})}return m(t)}function
|
|
3
|
-
`).map(n=>n.trim()).filter(Boolean).filter(n=>!n.includes(" ")).map(n=>({id:n,name:n,provider:n.includes("/")?n.split("/")[0]:void 0}));return m(t)}function
|
|
4
|
-
`)){const i=o.match(/\b(gpt-[a-z0-9][a-z0-9.-]*)\b/i);if(!i)continue;const s=i[1].trim(),r=o.replace(s,"").replace(/^[^\w]+/,"").trim()||void 0;n.push({id:s,name:
|
|
1
|
+
function b(e){return e.replace(/\u001b\[[0-9;?]*[a-zA-Z]/g,"").replace(/\u001b\][^\u0007]*\u0007/g,"").replace(/[\u0000-\u0008\u000b-\u001f\u007f]/g,"")}function m(e){const t=new Set,n=[];for(const o of e)!o.id||t.has(o.id)||(t.add(o.id),n.push(o));return n}function y(e){return e&&e[0].toUpperCase()+e.slice(1)}function M(e){return e.split("-").map(t=>t==="gpt"?"GPT":t==="codex"?"Codex":/^\d/.test(t)?t:y(t)).join("-")}function A(e){switch(e){case"low":return"Low";case"medium":return"Medium";case"high":return"High";case"xhigh":return"Extra High";default:return y(e)}}const E=[{id:"low",name:"Low"},{id:"medium",name:"Medium"},{id:"high",name:"High"},{id:"xhigh",name:"Extra High"},{id:"max",name:"Max"},{id:"ultra",name:"Ultra"}],C=[{id:"gpt-6-astra",name:"GPT-6 Astra",description:"Most capable Codex model for complex, demanding work.",provider:"openai",supportedReasoningEfforts:E,defaultReasoningEffort:"medium"},{id:"gpt-5.6-sol",name:"GPT-5.6 Sol",description:"Reliable agentic workhorse for everyday tasks.",provider:"openai",supportedReasoningEfforts:E,defaultReasoningEffort:"low"},{id:"gpt-5.6-terra",name:"GPT-5.6 Terra",description:"Balanced agentic coding model for everyday work.",provider:"openai",supportedReasoningEfforts:E,defaultReasoningEffort:"medium"},{id:"gpt-5.6-luna",name:"GPT-5.6 Luna",description:"Fast and affordable agentic coding model.",provider:"openai",supportedReasoningEfforts:E.filter(e=>e.id!=="ultra"),defaultReasoningEffort:"medium"},{id:"gpt-5.5",name:"GPT-5.5",description:"Proven previous-generation model for coding and general work.",provider:"openai",supportedReasoningEfforts:E.filter(e=>e.id!=="max"&&e.id!=="ultra"),defaultReasoningEffort:"medium"}];function O(e){const t={...e};return e.supportedReasoningEfforts&&(t.supportedReasoningEfforts=e.supportedReasoningEfforts.map(n=>({...n}))),e.additionalSpeedTiers&&(t.additionalSpeedTiers=[...e.additionalSpeedTiers]),t}function L(e,t){const n=t??e.find(i=>i.isDefault)?.id,o=m([...e.map(O),...C.map(i=>O(i))]);return n?o.map(i=>({...i,isDefault:i.id===n})):o}function G(){return C.map((e,t)=>({...O(e),...t===0?{isDefault:!0}:{}}))}function H(){return[{id:"auto",name:"Auto",provider:"workbuddy",isDefault:!0,supportedReasoningEfforts:["minimal","low","medium","high","xhigh","max"].map(e=>({id:e,name:A(e)})),defaultReasoningEffort:"medium"}]}function w(e){return e.split("-").map(t=>/^\d/.test(t)?t:y(t)).join(" ")}function _(e,t,n=!1){const o=t.toLowerCase().replace(/\s+/g,"-");return`gemini-${e}-${o}${n?"-preview":""}`}function I(e){const t=[],n=b(e);for(const o of n.split(`
|
|
2
|
+
`)){const i=o.trim().match(/^(\S+)\s+-\s+(.+?)(?:\s+\(([^)]+)\))?$/);if(!i)continue;const[,s,r,d]=i,l=d?.split(",").map(u=>u.trim().toLowerCase())??[];t.push({id:s,name:r.trim()||w(s),description:d?.trim(),isDefault:l.includes("default")||l.includes("current")})}return m(t)}function U(e){try{const n=(JSON.parse(e).models??[]).filter(o=>o.available).map(o=>({id:o.key,name:o.name?.trim()||o.key,provider:o.key.split("/")[0],description:o.tags?.join(", "),isDefault:o.tags?.includes("default")??!1}));return m(n)}catch{return[]}}function B(e){const t=b(e).split(`
|
|
3
|
+
`).map(n=>n.trim()).filter(Boolean).filter(n=>!n.includes(" ")).map(n=>({id:n,name:n,provider:n.includes("/")?n.split("/")[0]:void 0}));return m(t)}function V(e,t){if(!e||typeof e!="object")return[];const n=e;if(!Array.isArray(n.all)||!Array.isArray(n.connected))return[];const o=new Set(n.connected.filter(u=>typeof u=="string")),i=t&&typeof t=="object"?t:{},s=typeof i.model=="string"?i.model:"",r=i.provider&&typeof i.provider=="object"?i.provider:{},d=new Set;for(const[u,a]of Object.entries(r)){if(!a||typeof a!="object")continue;const f=a.models;if(!(!f||typeof f!="object"))for(const p of Object.keys(f))d.add(`${u}/${p}`)}const l=[];for(const u of n.all){if(!u||typeof u!="object")continue;const a=u;if(!(typeof a.id!="string"||!o.has(a.id))&&!(!a.models||typeof a.models!="object"))for(const[f,p]of Object.entries(a.models)){if(!p||typeof p!="object")continue;const c=p;if(c.status==="deprecated"||c.status==="unavailable"||c.capabilities?.input?.text===!1||c.capabilities?.output?.text===!1||c.capabilities?.toolcall===!1)continue;const g=typeof c.id=="string"&&c.id?c.id:f,h=`${a.id}/${g}`;l.push({id:h,name:typeof c.name=="string"&&c.name.trim()?c.name:h,provider:a.id,...h===s?{isDefault:!0}:{}})}}return m(l).sort((u,a)=>{const f=p=>p.id===s?0:d.has(p.id)?1:p.provider&&Object.hasOwn(r,p.provider)?2:3;return f(u)-f(a)||u.id.localeCompare(a.id)})}function z(e){const t=b(e).replace(/\s+/g," "),n=[[/\d+\.Default\(recommended\).*?(?:Sonnet|currently\s*Sonnet)\s*([\d.]+)/i,"default","Default (recommended)"],[/\d+\.Best.*?Opus\s*([\d.]+)/i,"best","Best","Most capable available model"],[/\d+\.Fable.*?Fable\s*([\d.]+)/i,"fable","Fable","Most capable for the hardest, longest-running tasks"],[/\d+\.Opus\(1Mcontext\).*?Opus\s*([\d.]+)/i,"opus[1m]","Opus (1M context)"],[/\d+\.OpusPlan.*?Opus\s*([\d.]+)/i,"opusplan","Opus Plan","Opus for planning \xB7 Sonnet for execution"],[/\d+\.Opus(?!\(1Mcontext\)).*?Opus\s*([\d.]+)/i,"opus","Opus"],[/\d+\.Sonnet\(1Mcontext\).*?Sonnet\s*([\d.]+)/i,"sonnet[1m]","Sonnet (1M context)"],[/\d+\.Sonnet(?!\(1Mcontext\)).*?Sonnet\s*([\d.]+)/i,"sonnet","Sonnet"],[/\d+\.Haiku.*?Haiku\s*([\d.]+)/i,"haiku","Haiku"]],o=[];for(const[d,l,u,a]of n){const f=t.match(d);f&&o.push(v({id:l,name:u,description:a?`${a} \xB7 v${f[1]}`:`v${f[1]}`,provider:"anthropic",isDefault:l==="default"}))}if(o.length>0)return m(o);const i=[],s=t.match(/currently\s*(Sonnet|Opus|Haiku)\s*([34](?:\.\d)?)/i),r={sonnet:"sonnet",opus:"opus",haiku:"haiku"};for(const d of t.matchAll(/\b(Sonnet|Opus|Haiku)\s*([34](?:\.\d)?)\b/gi)){const l=(d[1]??"").toLowerCase(),u=r[l];if(!u)continue;const a=d[2]??"",f=s&&s[1]?.toLowerCase()===l&&s[2]===a;i.push(v({id:u,name:`${y(l)} ${a}`,provider:"anthropic",isDefault:!!f}))}return m(i)}function T(e,t){const n=Math.max(e.length,t.length);for(let o=0;o<n;o++){const i=e[o]??-1,s=t[o]??-1;if(i!==s)return i-s}return 0}function $(e,t){const n=e.match(new RegExp(`^claude-${t}-([0-9]+(?:-[0-9]+)*)$`,"i"));if(!n?.[1])return null;const o=n[1].split("-").map(i=>Number.parseInt(i,10)).filter(i=>Number.isFinite(i));return o.length===0||o.length>2?null:o}function F(e){if(e?.length)return`v${e.join(".")}`}const S={default:"Default (recommended)",sonnet:"Sonnet",opus:"Opus",haiku:"Haiku"},D={default:"ANTHROPIC_MODEL",sonnet:"ANTHROPIC_DEFAULT_SONNET_MODEL",opus:"ANTHROPIC_DEFAULT_OPUS_MODEL",haiku:"ANTHROPIC_DEFAULT_HAIKU_MODEL"},P=[{id:"max",name:"Max"}],j=[{id:"low",name:"Low"},{id:"medium",name:"Medium"},{id:"high",name:"High"},{id:"xhigh",name:"Extra High"},{id:"max",name:"Max"}];function v(e){return{...e,supportedReasoningEfforts:j,defaultReasoningEffort:"medium"}}function x(e,t){return e[t]?.trim()||null}function R(e,t){const n=x(t,"ANTHROPIC_BASE_URL")?.toLowerCase()??"";return n.includes("deepseek.com")||e.toLowerCase().startsWith("deepseek-")?"deepseek":n?"custom":"anthropic"}function k(e){const t=new Set;for(const o of Object.values(D)){const i=x(e,o);i&&t.add(i)}const n=Array.from(t).map((o,i)=>{const s=R(o,e),r={id:o,name:o,provider:s,isDefault:i===0};return s==="deepseek"?{...r,supportedReasoningEfforts:P,defaultReasoningEffort:x(e,"CLAUDE_CODE_EFFORT_LEVEL")??"max"}:v(r)});return m(n)}function K(e=process.env){return k(e)}function N(e,t=process.env){return e.map(n=>{if(!["default","sonnet","opus","haiku"].includes(n.id))return n;const o=n.id,i=x(t,D[o]);if(!i)return n;const r=`Claude Code alias: ${S[o]}`;return{...n,name:i,description:n.description?`${r} \xB7 ${n.description}`:r,provider:R(i,t)}})}function q(e){const t=b(e),n=new Map;for(const i of["sonnet","opus","haiku"])for(const s of t.matchAll(new RegExp(`\\bclaude-${i}-[0-9.-]+\\b`,"gi"))){const r=s[0].trim().toLowerCase().replace(/\./g,"-").replace(/-+$/g,""),d=$(r,i);if(!d)continue;const l=n.get(i);(!l||T(l,d)<0)&&n.set(i,d)}const o=[];for(const i of["default","sonnet","opus","haiku"]){if(!new RegExp(`\\b${i}\\b`,"i").test(t))continue;const s=(i==="default"?n.get("sonnet"):n.get(i))??null;o.push(v({id:i,name:S[i],description:F(s),provider:"anthropic",isDefault:i==="default"}))}return m(o)}function W(e=process.env){const t=k(e);return t.length>0?t:N([{id:"default",name:"Default (recommended)",description:"Recommended model for your account",provider:"anthropic",isDefault:!0},{id:"sonnet",name:"Sonnet",description:"Latest Sonnet \xB7 everyday coding",provider:"anthropic"},{id:"fable",name:"Fable",description:"Most capable for the hardest, longest-running tasks",provider:"anthropic"},{id:"opus",name:"Opus",description:"Latest Opus \xB7 complex reasoning",provider:"anthropic"},{id:"haiku",name:"Haiku",description:"Fast and efficient \xB7 simple tasks",provider:"anthropic"},{id:"best",name:"Best",description:"Most capable available model",provider:"anthropic"},{id:"opus[1m]",name:"Opus (1M context)",description:"Latest Opus \xB7 long sessions",provider:"anthropic"},{id:"sonnet[1m]",name:"Sonnet (1M context)",description:"Latest Sonnet \xB7 long sessions",provider:"anthropic"},{id:"opusplan",name:"Opus Plan",description:"Opus for planning \xB7 Sonnet for execution",provider:"anthropic"}],e).map(n=>n.id==="haiku"?n:v(n))}function X(e){const t=b(e),n=[];for(const o of t.split(`
|
|
4
|
+
`)){const i=o.match(/\b(gpt-[a-z0-9][a-z0-9.-]*)\b/i);if(!i)continue;const s=i[1].trim(),r=o.replace(s,"").replace(/^[^\w]+/,"").trim()||void 0;n.push({id:s,name:M(s),description:r,provider:"openai",isDefault:/\bcurrent\b/i.test(o)||/\bselected\b/i.test(o)})}return m(n)}function J(e){const t=typeof e=="object"&&e!==null?e:{},n=typeof t.config?.model=="string"?t.config.model:void 0,o=typeof t.config?.model_reasoning_effort=="string"?t.config.model_reasoning_effort:void 0,i=[];for(const s of t.data??[]){if(typeof s!="object"||s===null)continue;const r=s,d=typeof r.id=="string"?r.id:typeof r.model=="string"?r.model:"";if(!d)continue;const l=typeof r.displayName=="string"?r.displayName:typeof r.name=="string"?r.name:void 0,u=typeof r.description=="string"?r.description:void 0,a=typeof r.defaultReasoningEffort=="string"?r.defaultReasoningEffort:void 0,f=Array.isArray(r.supportedReasoningEfforts)?r.supportedReasoningEfforts.map(c=>{if(typeof c!="object"||c===null)return null;const g=c,h=typeof g.reasoningEffort=="string"?g.reasoningEffort:typeof g.id=="string"?g.id:"";return h?{id:h,name:A(h),description:typeof g.description=="string"?g.description:void 0}:null}).filter(c=>c!=null):void 0,p=Array.isArray(r.additionalSpeedTiers)?r.additionalSpeedTiers.filter(c=>typeof c=="string"):void 0;i.push({id:d,name:l||M(d),description:u,provider:"openai",isDefault:n?n===d:r.isDefault===!0,supportedReasoningEfforts:f,defaultReasoningEffort:a,additionalSpeedTiers:p})}return n&&!i.some(s=>s.id===n)&&i.unshift({id:n,name:M(n),provider:"openai",isDefault:!0,defaultReasoningEffort:o}),L(m(i),n)}function Z(e){const t=b(e),n=[];for(const o of t.matchAll(/\b(?:auto-)?gemini-[a-z0-9.-]+\b/gi)){const i=o[0].trim();n.push({id:i,name:i,provider:"google",isDefault:/\bcurrent\b/i.test(t)&&t.includes(i)})}for(const o of t.matchAll(/\bGemini\s*(\d(?:\.\d)?)\s*(Pro|Flash(?:[- ]Lite)?)\s*(Preview)?\b/gi)){const i=o[1]??"",s=(o[2]??"").replace(/\s+/g,"-"),r=!!o[3],d=_(i,s,r);n.push({id:d,name:`Gemini ${i} ${s.replace(/-/g," ")}`.trim(),provider:"google",isDefault:/\bcurrent\b/i.test(t)&&t.includes(o[0]??"")})}for(const o of t.matchAll(/\bAuto\s*Gemini\s*(\d(?:\.\d)?)\b/gi)){const i=o[1]??"",s=`auto-gemini-${i}`;n.push({id:s,name:`Auto Gemini ${i}`,provider:"google",isDefault:/\bcurrent\b/i.test(t)&&t.includes(o[0]??"")})}return m(n)}function Q(){return[{id:"gemini-2.5-pro",name:"Gemini 2.5 Pro",provider:"google",isDefault:!0},{id:"gemini-2.5-flash",name:"Gemini 2.5 Flash",provider:"google"},{id:"auto-gemini-2.5",name:"Auto Gemini 2.5",provider:"google"}]}export{N as applyClaudeModelEnvOverrides,K as discoverClaudeAliasModelsFromEnv,W as fallbackClaudeAliasModels,G as fallbackCodexModels,Q as fallbackGeminiModels,H as fallbackWorkBuddyModels,R as inferClaudeOverrideProvider,q as parseClaudeBinaryModels,z as parseClaudeModels,J as parseCodexAppServerModels,X as parseCodexModels,I as parseCursorModels,Z as parseGeminiModels,U as parseOpenClawModels,B as parseOpenCodeModels,V as parseOpenCodeProviderModels,x as readEnvValue,b as stripAnsi};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { AgentInfo } from '@shennian/wire';
|
|
2
2
|
import type { DetectedAgent } from '../detect.js';
|
|
3
|
-
import type
|
|
3
|
+
import { type DiscoveryContext } from './types.js';
|
|
4
|
+
type ResolveAgentInfoOptions = {
|
|
5
|
+
forceRefresh?: boolean;
|
|
6
|
+
};
|
|
4
7
|
export declare function getCachedAgentInfos(detectedAgents: DetectedAgent[]): AgentInfo[];
|
|
5
8
|
export declare function refreshAgentInfos(detectedAgents: DetectedAgent[], context: DiscoveryContext): Promise<AgentInfo[]>;
|
|
6
|
-
export declare function resolveAgentInfos(detectedAgents: DetectedAgent[], context: DiscoveryContext): Promise<AgentInfo[]>;
|
|
9
|
+
export declare function resolveAgentInfos(detectedAgents: DetectedAgent[], context: DiscoveryContext, options?: ResolveAgentInfoOptions): Promise<AgentInfo[]>;
|
|
10
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getAgentConfigSummary as
|
|
1
|
+
import{getAgentConfigSummary as y}from"../config-status.js";import{isCacheFresh as A,readCache as v,writeCache as S}from"./cache.js";import{discoverModelsForAgent as M}from"./discovery.js";import{ModelDiscoveryFallbackError as R}from"./types.js";import{getAgentRuntimeSummary as g}from"../runtime-compatibility.js";import{resolveManagedAgentRuntimeProtection as b}from"../../security/managed-agent-runtime.js";const x={claude:"claude-alias-v2",codex:"codex-app-server-v2",opencode:"opencode-connected-v1"};function F(o,s,e){console.warn(`[model-discovery] agent=${o} source=fallback reason=${s} usedCache=${e}`)}function E(o){const s=new Set,e=[];for(const i of o)!i.id||s.has(i.id)||(s.add(i.id),e.push(i));return e}function C(o){const s=o.type!=="manager"&&o.type!=="pi"&&o.type!=="openclaw",e=s,i=e&&!o.type.startsWith("acp:"),r=e?b({agentType:o.type,runtimeVersion:o.version,platform:o.runtimePlatform,isWsl:o.runtimeIsWsl,executableFingerprint:o.executableFingerprint,requireExecutableFingerprint:!0}):null,p=i&&(r?.level==="hard"||r?.level==="partial"),t=e,n=p||t,a=s;return{supportsStandardSession:s,supportsManagedAgent:p,supportsManagedNativeFull:t,supportsRoomTools:n,supportsWorker:a,supportsManagerMode:n&&a,supportsExternalWorkerTarget:a,...r?{managedAgentProtection:r}:{}}}function q(o){const s=v();return o.map(e=>{const i=y(e.type),r=g(e);return{type:e.type,models:s.agents[e.type]?.models??e.models??[],...e.integration?{integration:e.integration}:{},...e.display?{display:e.display}:{},...e.acpCapabilities?{acpCapabilities:e.acpCapabilities}:{},...e.authState?{authState:e.authState}:{},...e.acpAuthMethods?{acpAuthMethods:e.acpAuthMethods}:{},...e.catalogRevision?{catalogRevision:e.catalogRevision}:{},...C(e),...i?{config:i}:{},...r?{runtime:r}:{}}})}async function P(o,s){return I(o,s,{forceRefresh:!0})}async function I(o,s,e={}){const i=v();let r=!1;const p=[];for(const t of o){const n=i.agents[t.type],a=x[t.type],u=n&&(!a||n.sourceRevision===a)?n:void 0;let c;if(!e.forceRefresh&&A(n,t.version,a))c=n.models;else{let d=null;try{d=E(await M(t,s)),c=d.length>0?d:u?.models??t.models??[]}catch(l){if(l instanceof R){const m=!!u?.models.length;c=m?u.models:l.fallbackModels,F(t.type,l.failureCode,m)}else c=u?.models??t.models??[]}if(d&&d.length>0){const l={version:t.version,...a?{sourceRevision:a}:{},refreshedAt:Date.now(),models:c};JSON.stringify(n)!==JSON.stringify(l)&&(i.agents[t.type]=l,r=!0)}}const f=y(t.type),h=g(t);p.push({type:t.type,models:c,...t.integration?{integration:t.integration}:{},...t.display?{display:t.display}:{},...t.acpCapabilities?{acpCapabilities:t.acpCapabilities}:{},...t.authState?{authState:t.authState}:{},...t.acpAuthMethods?{acpAuthMethods:t.acpAuthMethods}:{},...t.catalogRevision?{catalogRevision:t.catalogRevision}:{},...C(t),...f?{config:f}:{},...h?{runtime:h}:{}})}return r&&S(i),p}export{q as getCachedAgentInfos,P as refreshAgentInfos,I as resolveAgentInfos};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ModelInfo } from '@shennian/wire';
|
|
1
|
+
import type { BuiltinAgentType, ModelInfo } from '@shennian/wire';
|
|
2
2
|
export declare const CACHE_VERSION = 1;
|
|
3
3
|
export declare const CACHE_TTL_MS: number;
|
|
4
4
|
export declare const MODEL_CACHE_PATH: string;
|
|
@@ -17,6 +17,14 @@ export type DiscoveryContext = {
|
|
|
17
17
|
serverUrl?: string;
|
|
18
18
|
authToken?: string;
|
|
19
19
|
};
|
|
20
|
+
export type ModelDiscoveryFailureCode = 'app_server_unavailable' | 'app_server_timeout' | 'app_server_rpc_error' | 'empty_model_list';
|
|
21
|
+
/** An emergency list that is safe to display but must never become authoritative cache data. */
|
|
22
|
+
export declare class ModelDiscoveryFallbackError extends Error {
|
|
23
|
+
readonly agentType: BuiltinAgentType;
|
|
24
|
+
readonly failureCode: ModelDiscoveryFailureCode;
|
|
25
|
+
readonly fallbackModels: ModelInfo[];
|
|
26
|
+
constructor(agentType: BuiltinAgentType, failureCode: ModelDiscoveryFailureCode, fallbackModels: ModelInfo[]);
|
|
27
|
+
}
|
|
20
28
|
export type PtyOptions = {
|
|
21
29
|
startupArgs?: string[];
|
|
22
30
|
readyPatterns: RegExp[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import s from"node:os";import{resolveShennianPath as t}from"../../config/index.js";const i=1,n=1440*60*1e3,d=t("model-cache.json"),p=s.tmpdir();class E extends Error{agentType;failureCode;fallbackModels;constructor(o,r,e){super(`Model discovery fell back for ${o}: ${r}`),this.agentType=o,this.failureCode=r,this.fallbackModels=e,this.name="ModelDiscoveryFallbackError"}}export{n as CACHE_TTL_MS,i as CACHE_VERSION,p as DISCOVERY_WORKDIR,d as MODEL_CACHE_PATH,E as ModelDiscoveryFallbackError};
|
|
@@ -15,6 +15,7 @@ export declare class OpenCodeAdapter extends AgentAdapter {
|
|
|
15
15
|
private seq;
|
|
16
16
|
private runId;
|
|
17
17
|
private terminalState;
|
|
18
|
+
private runUsage;
|
|
18
19
|
private externalChannel;
|
|
19
20
|
private shennianSessionId;
|
|
20
21
|
private extraEnv;
|
|
@@ -35,6 +36,9 @@ export declare class OpenCodeAdapter extends AgentAdapter {
|
|
|
35
36
|
private prepareManagedLaunch;
|
|
36
37
|
private disposeManagedConfig;
|
|
37
38
|
private handleStreamEvent;
|
|
39
|
+
private handleProcessLine;
|
|
40
|
+
private recordStepUsage;
|
|
41
|
+
private completeRunAfterProcessExit;
|
|
38
42
|
private emitEvent;
|
|
39
43
|
private emitFinalIfOpen;
|
|
40
44
|
private emitErrorIfOpen;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createInterface as
|
|
1
|
+
import{createInterface as g}from"node:readline";import{randomUUID as l}from"node:crypto";import{AgentAdapter as h,registerAgent as f}from"./adapter.js";import{resolveBuiltinCommand as m,spawnAgentCommand as E}from"./command-spec.js";import{buildAgentProcessEnv as C,buildManagedAgentProcessEnv as y,buildManagedNativeFullProcessEnv as O}from"../agent-env.js";import{buildInlineChannelPreamble as D}from"./platform-instructions.js";import{resolveManagedAgentAdapterEnforcement as I,isManagedAgentNativeFull as S}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as P}from"../security/managed-agent-runtime.js";import{compileOpenCodeManagedPermissions as w,createOpenCodeManagedConfigDirectory as _,disposeOpenCodeManagedConfigDirectory as A,OPENCODE_MANAGED_RUNTIME_SECURITY as v}from"./opencode-managed-permissions.js";P("opencode",v);function N(n){if(!n)return;const e=n.input,t=n.output;if(!(typeof e!="number"&&typeof t!="number"))return{inputTokens:typeof e=="number"?e:0,outputTokens:typeof t=="number"?t:0}}function u(n){if(n==null)return"";if(typeof n=="string")return n;try{return JSON.stringify(n)}catch{return String(n)}}function c(n){if(!n)return"opencode request failed";if(typeof n=="string")return n;if(typeof n!="object")return String(n);const e=n;return typeof e.message=="string"?e.message:typeof e.name=="string"?e.name:"data"in e?c(e.data):u(n)||"opencode request failed"}class x extends h{type="opencode";get recoveryCapabilities(){return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!0}}process=null;agentSessionId=null;workDir=null;seq=0;runId="";terminalState="closed";runUsage;externalChannel=null;shennianSessionId=null;extraEnv={};managedAgentPolicy=null;managedConfigDir=null;configure(e){this.shennianSessionId=e.sessionId??null,this.externalChannel=e.externalChannel??null,this.extraEnv=e.env??{},"managedAgentPolicy"in e&&(this.managedAgentPolicy=e.managedAgentPolicy??null),this.managedAgentPolicy&&(this.workDir=this.managedAgentPolicy.adapterPolicy.workDir)}async start(e,t,o){this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??t,o&&(this.agentSessionId=o),this.resetRunState()}async send(e,t,o,i,s,d){if(d?.source==="room_activation"&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");await this.killProcess(),this.runId=l(),this.resetRunState();const a=["run","--format","json","--dir",this.workDir??process.cwd()];this.agentSessionId&&a.push("--session",this.agentSessionId),t&&a.push("--model",t);const r=`${D(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,s)}${e}`;await this.spawnAndParse(a,r)}async resume(e){await this.killProcess(),this.agentSessionId=e,this.runId=l(),this.resetRunState(),this.emitEvent({state:"start",agentSessionId:e}),this.emitFinalIfOpen({state:"final",agentSessionId:e})}async stop(){await this.killProcess()}resetRunState(){this.seq=0,this.terminalState="open",this.runUsage=void 0}async spawnAndParse(e,t){const o=m("opencode");if(!o){this.emit("error",new Error('Command "opencode" not found. Install it with "npm i -g opencode-ai@latest".'));return}const i=await this.prepareManagedLaunch();let s;try{s=E(o,[e[0],...i.args,...e.slice(1)],{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:i.env})}catch(r){throw await this.disposeManagedConfig(i.configDir),r}this.process=s,this.managedConfigDir=i.configDir,g({input:s.stdout}).on("line",r=>{this.handleProcessLine(s,r)});let a="";s.stderr?.on("data",r=>{a+=r.toString()}),s.on("close",(r,p)=>{this.process===s&&(this.process=null,this.disposeManagedConfig(i.configDir),this.terminalState==="open"&&this.completeRunAfterProcessExit(r,p,a.trim()))}),s.on("error",r=>{this.process===s&&(this.process=null,this.disposeManagedConfig(i.configDir),r.code==="ENOENT"?this.emit("error",new Error('Command "opencode" not found. Install it with "npm i -g opencode-ai@latest".')):this.emit("error",r))}),s.stdin.on("error",r=>{this.process===s&&this.emitErrorIfOpen({state:"error",message:r.message})}),s.stdin.end(t)}async prepareManagedLaunch(){const e=this.managedAgentPolicy;if(!e)return{args:["--auto"],env:C({NO_COLOR:"1",...this.extraEnv}),configDir:null};if(S(e))return{args:["--auto"],env:O({NO_COLOR:"1",...this.extraEnv}),configDir:null};const t=w(I(e),e.adapterPolicy.workDir),o=await _(t),i=y({agentType:"opencode",providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:{NO_COLOR:"1"}});return i.OPENCODE_CONFIG=o.configPath,i.OPENCODE_CONFIG_DIR=o.directory,i.OPENCODE_CONFIG_CONTENT=JSON.stringify(t.config),i.OPENCODE_DISABLE_PROJECT_CONFIG="true",i.OPENCODE_DISABLE_CLAUDE_CODE="1",i.OPENCODE_DISABLE_CLAUDE_CODE_PROMPT="1",i.OPENCODE_DISABLE_CLAUDE_CODE_SKILLS="1",{args:t.args,env:i,configDir:o.directory}}async disposeManagedConfig(e){e&&(this.managedConfigDir===e&&(this.managedConfigDir=null),await A(e).catch(()=>{}))}handleStreamEvent(e){e.sessionID&&e.sessionID!==this.agentSessionId&&(this.agentSessionId=e.sessionID,this.emitEvent({state:"start",agentSessionId:e.sessionID}));const t=e.part;if(e.type==="text"&&t?.text){this.emitEvent({state:"delta",text:t.text});return}if(e.type==="reasoning"&&t?.text){this.emitEvent({state:"delta",text:t.text,thinking:!0});return}if(e.type==="tool_use"&&t?.type==="tool"){t.state?.status==="completed"||t.state?.status==="error"?this.emitEvent({state:"tool-result",name:t.tool??"tool",result:t.state.status==="error"?t.state.error??"tool failed":u(t.state?.output)}):this.emitEvent({state:"tool-call",name:t.tool??"tool",args:t.state?.input??{}});return}if(e.type==="error"){this.emitErrorIfOpen({state:"error",message:c(e.error)});return}e.type==="step_finish"&&this.recordStepUsage(N(t?.tokens))}handleProcessLine(e,t){if(!(this.process!==e||!t.trim()))try{this.handleStreamEvent(JSON.parse(t))}catch{this.emitEvent({state:"delta",text:t})}}recordStepUsage(e){e&&(this.runUsage={inputTokens:(this.runUsage?.inputTokens??0)+e.inputTokens,outputTokens:(this.runUsage?.outputTokens??0)+e.outputTokens})}completeRunAfterProcessExit(e=0,t=null,o=""){if(e!==0||t!==null){this.emitErrorIfOpen({state:"error",message:o||(t?`opencode exited with signal ${t}`:`opencode exited with code ${String(e)}`)});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0,usage:this.runUsage})}emitEvent(e){const t={...e,runId:this.runId,seq:this.seq++};this.emit("agentEvent",t)}emitFinalIfOpen(e){this.terminalState==="open"&&(this.terminalState="closed",this.emitEvent(e))}emitErrorIfOpen(e){this.terminalState==="open"&&(this.terminalState="closed",this.emitEvent(e))}async killProcess(){const e=this.process;if(!e){await this.disposeManagedConfig(this.managedConfigDir);return}this.process=null,e.kill("SIGTERM"),await new Promise(t=>{e.on("close",t),setTimeout(()=>{e.kill("SIGKILL"),t()},3e3)}),await this.disposeManagedConfig(this.managedConfigDir)}}f("opencode",()=>new x);export{x as OpenCodeAdapter};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import { callDaemonIpc } from '../daemon-ipc/client.js';
|
|
3
|
+
import type { JsonValue } from '../daemon-ipc/json.js';
|
|
4
|
+
export declare function executeAgentReadinessCommand(method: 'agent.discover' | 'agent.check', params?: JsonValue, call?: typeof callDaemonIpc): Promise<{
|
|
5
|
+
ok: boolean;
|
|
6
|
+
command: "agent.discover" | "agent.check";
|
|
7
|
+
result: JsonValue;
|
|
8
|
+
error?: undefined;
|
|
9
|
+
nextAction?: undefined;
|
|
10
|
+
} | {
|
|
11
|
+
ok: boolean;
|
|
12
|
+
command: "agent.discover" | "agent.check";
|
|
13
|
+
error: string;
|
|
14
|
+
nextAction: string;
|
|
15
|
+
result?: undefined;
|
|
16
|
+
}>;
|
|
17
|
+
export declare function registerAgentReadinessCommands(agent: Command): void;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{callDaemonIpc as a}from"../daemon-ipc/client.js";import{DaemonIpcError as r}from"../daemon-ipc/protocol.js";const s={method_not_found:"Update the existing Shennian installation: Desktop through Desktop updates, standalone CLI through its own update flow. Do not install a second Client.",daemon_unavailable:"Check shennian status --json on the target computer. Do not resume an intentional pause without the user requesting it.",daemon_paused:"Shennian is paused. Ask the user to resume it before checking.",ipc_timeout:"The result is unknown. Check the target machine and wait for the current check to finish; do not repeatedly send tests.",agent_check_in_progress:"Wait for the running check to finish before trying again.",agent_validation_runtime_unavailable:"Refresh discovery. If the tool is still missing, guide the user through its official installation on this computer.",agent_validation_runtime_auth_required:"Guide the user through the provider\u2019s own sign-in on this computer, then retry the selected AI and model.",agent_validation_model_unavailable:"Choose a model returned by discovery and check the provider account\u2019s model access.",agent_validation_quota_exceeded:"Check the provider account\u2019s credits, subscription or rate limit before another test.",agent_validation_network_failed:"Check the target computer\u2019s connection to the provider, then retry when it is available."};async function c(n,i,t=a){try{const e=await t({method:n,params:i,timeoutMs:9e4});if(n==="agent.check"&&(!e||typeof e!="object"||Array.isArray(e)||e.ready!==!0))throw new r("agent_check_invalid_response","The daemon did not confirm a successful hello reply.");return{ok:!0,command:n,result:e}}catch(e){const o=e instanceof r?e.code:"agent_check_failed";return{ok:!1,command:n,error:o,nextAction:s[o]??"Return to the AI preparation guide on this machine. Check the selected directory, account, model and network; do not reinstall by default."}}}function l(n){const i=async(t,e)=>{const o=await c(t,e);process.stdout.write(`${JSON.stringify(o)}
|
|
2
|
+
`),o.ok||(process.exitCode=1)};n.command("discover").description("Refresh AI discovery and sign-in summaries on the running local daemon; no model requests").option("--agent <type>","Return only one discovered Agent type").option("--compact","Return capability summary without the full model list").option("--json","Print JSON (default)").action(t=>i("agent.discover",{...t.agent?{agentType:t.agent}:{},compact:t.compact===!0})),n.command("check <agentType>").description("Send one hello test using the selected AI and model; uses provider quota, creates no managed Agent or room").requiredOption("--workdir <path>","Absolute project directory on this machine").option("--model <id>","Model returned by discovery; otherwise use the provider default").option("--json","Print JSON (default)").action((t,e)=>i("agent.check",{agentType:t,workDir:e.workdir,modelId:e.model??null}))}export{c as executeAgentReadinessCommand,l as registerAgentReadinessCommands};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
`),i||(process.exitCode=1)}export{
|
|
1
|
+
import{registerAgentReadinessCommands as p}from"./agent-readiness.js";import{createAgent as h}from"../agents/adapter.js";import{AcpAgentAdapter as u}from"../agents/acp/adapter.js";import{discoverAcpAgents as A}from"../agents/acp/discovery.js";import{callDaemonIpc as f}from"../daemon-ipc/client.js";import{readAcpManifests as w,removeAcpManifest as v,validateAcpId as s,writeAcpManifest as C}from"../agents/acp/manifests.js";function b(i){const r=i.command("agent").description("Discover and check local AI tools, or manage registered integrations");p(r),r.command("add <id>").description("Register an installed ACP v1 Agent (does not install it)").requiredOption("--name <name>","Display name").requiredOption("--command <command>","Absolute executable path or command name").option("--arg <args...>","Arguments passed to the ACP Agent").action((a,e)=>{try{const t=s(a);C({schemaVersion:1,id:t,name:e.name,protocol:"acp",command:e.command,args:e.arg??[],enabled:!0}),o(!0,"agent.add",{agentRef:`acp:${t}`,installedByShennian:!1})}catch(t){o(!1,"agent.add",void 0,t instanceof Error?t.message:String(t))}}),r.command("list").description("List locally registered ACP Agents").action(()=>{o(!0,"agent.list",{agents:w().map(a=>({agentRef:`acp:${a.id}`,name:a.name,command:a.launch.command,args:a.launch.args??[]}))})}),r.command("remove <id>").description("Remove a local ACP Agent registration (does not uninstall it)").action(a=>{try{const e=s(a),t=v(e);o(t,"agent.remove",t?{agentRef:`acp:${e}`}:void 0,t?void 0:`No local ACP Agent named "${e}" exists.`)}catch(e){o(!1,"agent.remove",void 0,e instanceof Error?e.message:String(e))}}),r.command("auth <id>").description("Authenticate an installed ACP Agent on this computer").option("--method <method>","Authentication method id").action(async(a,e)=>{let t=null;try{const n=s(a.replace(/^acp:/,""));if(!(await A()).some(d=>d.type===`acp:${n}`))throw new Error(`Installed ACP Agent "${n}" was not found.`);const m=h(`acp:${n}`);if(!(m instanceof u))throw new Error("ACP Agent adapter is unavailable");t=m,await t.start(`acp-auth-${process.pid}`,process.cwd());const c=t.getAuthMethods(),l=e.method?c.find(d=>d.id===e.method):c.length===1?c[0]:void 0;if(!l)throw new Error(c.length?`Choose an authentication method with --method: ${c.map(d=>d.id).join(", ")}`:"This ACP Agent did not advertise an authentication method.");await t.authenticate(l.id,{interactiveTerminal:!0});let g=!1;try{g=(await f({method:"agent.auth.completed",params:{agentType:`acp:${n}`}})).resumed}catch{}o(!0,"agent.auth",{agentRef:`acp:${n}`,authenticated:!0,resumed:g})}catch(n){o(!1,"agent.auth",void 0,n instanceof Error?n.message:String(n))}finally{await t?.stop().catch(()=>{})}})}function o(i,r,a,e){process.stdout.write(`${JSON.stringify(i?{ok:i,command:r,result:a}:{ok:i,command:r,error:"acp_agent_manifest_error",message:e,nextAction:null})}
|
|
2
|
+
`),i||(process.exitCode=1)}export{b as registerAgentCommand};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import r from"node:fs";import h from"node:path";import{isDaemonProcessRunning as k}from"./daemon-process.js";function d(e){let n;try{n=r.readFileSync(e,"utf8")}catch(c){if(c&&typeof c=="object"&&"code"in c&&c.code==="ENOENT")return null;throw u("read",c)}try{const c=JSON.parse(n);return Number.isInteger(c.pid)&&Number(c.pid)>0&&typeof c.instanceId=="string"?{pid:Number(c.pid),instanceId:c.instanceId}:null}catch{return null}}function u(e,n){const c=n&&typeof n=="object"&&"code"in n?String(n.code):"unknown";return Object.assign(new Error(`daemon_instance_lock_${e}_failed: ${c}`),{code:c})}function I(e){const n=e.pid??process.pid,c=e.isProcessAlive??k,m=e.now??Date.now,y=e.malformedStaleAfterMs??5e3;r.mkdirSync(h.dirname(e.lockPath),{recursive:!0});for(let f=0;f<3;f+=1)try{const i=r.openSync(e.lockPath,"wx",384);r.writeFileSync(i,JSON.stringify({pid:n,instanceId:e.instanceId})),r.fsyncSync(i);let l=!1;return{acquired:!0,release:()=>{if(l)return;l=!0,r.closeSync(i);const o=d(e.lockPath);if(!(o?.pid!==n||o.instanceId!==e.instanceId))try{r.unlinkSync(e.lockPath)}catch{}}}}catch(i){if((i&&typeof i=="object"&&"code"in i?i.code:null)!=="EEXIST")throw i;const o=d(e.lockPath);let a;try{a=r.statSync(e.lockPath)}catch(t){if(t&&typeof t=="object"&&"code"in t&&t.code==="ENOENT")continue;throw u("inspect",t)}if(o&&c(o.pid))return{acquired:!1,ownerPid:o.pid};if(!o&&Math.max(0,m()-a.mtimeMs)<y)return{acquired:!1,ownerPid:null};try{const t=d(e.lockPath),s=r.statSync(e.lockPath);if(t?.pid!==o?.pid||t?.instanceId!==o?.instanceId||a.dev!==s.dev||a.ino!==s.ino||a.mtimeMs!==s.mtimeMs)continue;r.unlinkSync(e.lockPath)}catch(t){if(t&&typeof t=="object"&&"code"in t&&t.code==="ENOENT")continue;throw u("cleanup",t)}}return{acquired:!1,ownerPid:d(e.lockPath)?.pid??null}}export{I as acquireDaemonInstanceLock};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare function isDaemonProcessRunning(pid: number): boolean;
|
|
1
2
|
export declare function isShennianRunServiceCommand(command: string): boolean;
|
|
2
3
|
export declare function inferDaemonLauncherFromCommand(command: string): 'desktop-managed' | 'global-cli' | 'unknown';
|
|
3
4
|
export declare function parseWindowsDaemonProcessJson(raw: string, excludePid?: number): number[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execFileSync as u}from"node:child_process";const d=5e3;function
|
|
1
|
+
import{execFileSync as u}from"node:child_process";import l from"node:os";const d=5e3;function m(e){try{const n=["$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue'",`try { $rows = @(Get-CimInstance Win32_Process -Filter 'ProcessId = ${e}' -ErrorAction Stop)`,`[ordered]@{ pid = ${e}; queryCompleted = $true; running = ($rows.Count -gt 0) } | ConvertTo-Json -Compress`,"} catch { exit 1 }"].join("; "),r=u("powershell.exe",["-NoProfile","-NonInteractive","-Command",n],{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:d,windowsHide:!0}),t=JSON.parse(r.trim());return t?.pid===e&&t.queryCompleted===!0&&typeof t.running=="boolean"?t.running:null}catch{return null}}function $(e){if(!Number.isSafeInteger(e)||e<=0)return!1;try{return process.kill(e,0),!0}catch(n){const r=n&&typeof n=="object"&&"code"in n?n.code:null;return r==="ESRCH"?!1:(r==="EPERM"||r==="EACCES")&&l.platform()==="win32"?m(e)??!0:!0}}function p(e){const n=e.replace(/\\/g,"/").trim();return g(n)||h(n)||S(n)||a(n,"shennian")||a(n,"npx")}function y(e){const n=e.replace(/\\/g,"/").toLowerCase();return n.includes("/cli-bridge.js")||n.includes("/resources/daemon/")?"desktop-managed":n.includes("/node_modules/shennian/")||n.includes("/bin/shennian")||n.includes(" shennian run-service")||n.includes(" shennian.js run-service")?"global-cli":"unknown"}function f(e,n=process.pid){let r;try{r=JSON.parse(C(e))}catch{return[]}return(Array.isArray(r)?r:r?[r]:[]).map(i=>{if(!i||typeof i!="object")return null;const o=i,s=Number(o.ProcessId),c=typeof o.CommandLine=="string"?o.CommandLine:"";return!Number.isInteger(s)||s<=0||s===n?null:p(c)?s:null}).filter(i=>typeof i=="number")}function C(e){const n=e.trim(),r=[...n].map((s,c)=>s==="["||s==="{"?c:-1).find(s=>s>=0)??-1;if(r<0)return n;const i=n[r]==="["?"]":"}",o=n.lastIndexOf(i);return o>=r?n.slice(r,o+1):n.slice(r)}function w(e=process.pid){try{const n=["$ProgressPreference = 'SilentlyContinue'","Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -match 'run-service' } | Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress"].join("; "),r=u("powershell.exe",["-NoProfile","-NonInteractive","-Command",n],{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:d,windowsHide:!0});return f(r,e)}catch{return[]}}function v(e){if(!Number.isSafeInteger(e)||e<=0)return null;try{const n=["$ProgressPreference = 'SilentlyContinue'",`Get-CimInstance Win32_Process -Filter "ProcessId = ${e}" | Select-Object -ExpandProperty CommandLine`].join("; ");return u("powershell.exe",["-NoProfile","-NonInteractive","-Command",n],{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:d,windowsHide:!0}).trim()||null}catch{return null}}function g(e){return/(?:^|["\s])[^"\s]*updater-worker\.js["\s]/i.test(e)&&/(?:^|["\s])--service(?:["\s]|$)/i.test(e)&&/(?:^|["\s])run-service(?:["\s]|$)/i.test(e)}function h(e){return/(?:^|["\s])[^"\s]*shennianclient\.exe["\s]/i.test(e)&&/(?:^|["\s])[^"\s]*cli-bridge\.js["\s]/i.test(e)&&/(?:^|["\s])[^"\s]*\/daemon\/dist\/bin\/shennian\.js["\s]/i.test(e)&&/(?:^|["\s])run-service(?:["\s]|$)/i.test(e)}function S(e){return/^"[^"]*\/node(?:\.exe)?"\s+"?[^"]*(?:\/node_modules\/shennian\/|\/dist\/bin\/shennian\.js|\/bin\/shennian)"?\s+run-service(?:\s|$)/i.test(e)||/^(?:\S*\/)?node(?:\.exe)?\s+\S*(?:\/node_modules\/shennian\/|\/dist\/bin\/shennian\.js|\/bin\/shennian)\s+run-service(?:\s|$)/i.test(e)}function a(e,n){const r=n==="shennian"?`${n}(?:\\.cmd|\\.exe)?`:`${n}(?:\\.cmd|\\.exe)?`,t=n==="npx"?"\\s+(?:--yes\\s+)?shennian\\s+run-service(?:\\s|$)":"\\s+run-service(?:\\s|$)";return new RegExp(`^"[^"]*\\/${r}"${t}`,"i").test(e)||new RegExp(`^(?:\\S*\\/)?${r}${t}`,"i").test(e)}export{w as findRunningWindowsDaemonProcessIds,y as inferDaemonLauncherFromCommand,$ as isDaemonProcessRunning,p as isShennianRunServiceCommand,f as parseWindowsDaemonProcessJson,v as readWindowsProcessCommand};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare function isSafeSnapshotEnvKey(key: string): boolean;
|
|
2
|
+
type ServiceLaunchMode = 'direct' | 'global-shim' | 'npx';
|
|
3
|
+
export type ServiceLaunchSpec = {
|
|
4
|
+
command: string;
|
|
5
|
+
args: string[];
|
|
6
|
+
mode: ServiceLaunchMode;
|
|
7
|
+
electronRunAsNode?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type DetachedLaunchSpec = {
|
|
10
|
+
command: string;
|
|
11
|
+
args: string[];
|
|
12
|
+
windowsVerbatimArguments?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare function isEphemeralCliPath(candidate: string): boolean;
|
|
15
|
+
export declare function resolveServiceLaunchSpec(input: {
|
|
16
|
+
nodeExec: string;
|
|
17
|
+
scriptPath: string;
|
|
18
|
+
shennianCommandPath?: string | null;
|
|
19
|
+
npxPath?: string | null;
|
|
20
|
+
desktopBridgePath?: string | null;
|
|
21
|
+
}): ServiceLaunchSpec;
|
|
22
|
+
export declare function captureEnvForService(): Record<string, string>;
|
|
23
|
+
/**
|
|
24
|
+
* Save current env vars so the daemon can load them at startup.
|
|
25
|
+
* This is critical for launchd/systemd which start with minimal env.
|
|
26
|
+
*/
|
|
27
|
+
export declare function saveEnvSnapshot(): void;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import o from"node:fs";import a from"node:path";import s from"node:os";import{resolveShennianPath as i}from"../config/index.js";import{buildAugmentedPath as N}from"../env-path.js";import{getPlatform as A,SHENNIAN_DIR as m}from"./daemon-state.js";const E=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","ELECTRON_RUN_AS_NODE","SHENNIAN_DESKTOP_CLI_SCRIPT","SHENNIAN_DESKTOP_CLI_BRIDGE","SHENNIAN_DESKTOP_SERVER_URL","SHENNIAN_WECHAT_CHANNEL_HELPER_DIR","SHENNIAN_HOME","SHENNIAN_ACCOUNT_NAMESPACE_ID","SHENNIAN_NATIVE_FUSION_DISABLED","SHENNIAN_WECHAT_DELIVERABILITY_TRACE"]);function S(e){return E.has(e)}function t(e){const r=e.replace(/\\/g,"/").toLowerCase(),n=s.tmpdir().replace(/\\/g,"/").toLowerCase();return r.includes("/_npx/")||r.includes("/npm-cache/_npx/")||r.includes("/pnpm/dlx/")||r.startsWith(n.endsWith("/")?n:`${n}/`)}function I(e){return process.env.ELECTRON_RUN_AS_NODE==="1"&&e.desktopBridgePath&&o.existsSync(e.scriptPath)&&o.existsSync(e.desktopBridgePath)?{command:e.nodeExec,args:[e.desktopBridgePath,e.scriptPath,"run-service"],mode:"direct",electronRunAsNode:!0}:o.existsSync(e.scriptPath)&&(process.env.SHENNIAN_E2E_ISOLATED_DAEMON==="1"||!t(e.scriptPath))?{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}:e.shennianCommandPath&&!t(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 T(){const e={};for(const r of E)process.env[r]&&(e[r]=process.env[r]);if(e.HOME??=s.homedir(),e.PATH=N({pathValue:e.PATH,env:e}),e.USER??=s.userInfo().username,A()==="win32"){const r=process.env.TEMP||process.env.TMP||s.tmpdir(),n=s.homedir();e.USERPROFILE??=n;const c=a.win32.parse(n);e.HOMEDRIVE??=c.root.replace(/[\\/]$/,"")||process.env.HOMEDRIVE||"",e.HOMEPATH??=n.slice((e.HOMEDRIVE||"").length)||process.env.HOMEPATH||"",e.USERNAME??=process.env.USERNAME||s.userInfo().username,e.COMPUTERNAME??=process.env.COMPUTERNAME||"",e.TMPDIR??=r,e.TEMP??=r,e.TMP??=r}else e.TMPDIR??="/tmp";return e}function O(){o.mkdirSync(m,{recursive:!0});const e={};for(const[r,n]of Object.entries(process.env))n!==void 0&&S(r)&&(e[r]=n);o.writeFileSync(i("env.json"),JSON.stringify(e,null,2))}export{T as captureEnvForService,t as isEphemeralCliPath,S as isSafeSnapshotEnvKey,I as resolveServiceLaunchSpec,O as saveEnvSnapshot};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DaemonStatus } from './daemon.js';
|
|
2
|
-
export declare function isDaemonReadyStatus(status: Pick<DaemonStatus, 'running' | 'stale'>): boolean;
|
|
2
|
+
export declare function isDaemonReadyStatus(status: Pick<DaemonStatus, 'running' | 'stale' | 'adopted'>): boolean;
|
|
3
3
|
export declare function waitForDaemonReadyStatus(readStatus: () => DaemonStatus, opts?: {
|
|
4
4
|
timeoutMs?: number;
|
|
5
5
|
intervalMs?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const c=2e4,s=500;function M(n){return n.running===!0&&n.stale!==!0&&n.adopted!==!0}function T(n){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,n)}function u(n,e={}){const r=Math.max(0,e.timeoutMs??2e4),i=Math.max(1,e.intervalMs??500),t=e.now??Date.now,A=e.sleep??T,o=t()+r;let a=n();for(;!M(a)&&t()<o;){const _=Math.max(1,o-t());A(Math.min(i,_)),a=n()}return a}export{M as isDaemonReadyStatus,u as waitForDaemonReadyStatus};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export declare const SHENNIAN_DIR: string;
|
|
2
|
+
export declare const PID_FILE: string;
|
|
3
|
+
export declare const LOG_FILE: string;
|
|
4
|
+
export declare const REMOTE_ACCESS_DISABLED_FILE: string;
|
|
5
|
+
export declare const LAUNCHER_FILE: string;
|
|
6
|
+
export type Platform = 'darwin' | 'linux' | 'win32';
|
|
7
|
+
export type DaemonLauncher = 'desktop-managed' | 'global-cli' | 'unknown';
|
|
8
|
+
export type DaemonStatus = {
|
|
9
|
+
running: boolean;
|
|
10
|
+
pid: number | null;
|
|
11
|
+
stale: boolean;
|
|
12
|
+
remoteAccessDisabled: boolean;
|
|
13
|
+
launcher: DaemonLauncher;
|
|
14
|
+
daemonVersion?: string;
|
|
15
|
+
platform: Platform;
|
|
16
|
+
shennianDir: string;
|
|
17
|
+
pidFile: string;
|
|
18
|
+
logFile: string;
|
|
19
|
+
machineId?: string;
|
|
20
|
+
paired?: boolean;
|
|
21
|
+
serverUrl?: string;
|
|
22
|
+
instanceId?: string;
|
|
23
|
+
adopted?: boolean;
|
|
24
|
+
};
|
|
25
|
+
export declare function getPlatform(): Platform;
|
|
26
|
+
type DaemonPidRecord = {
|
|
27
|
+
pid: number;
|
|
28
|
+
instanceId?: string;
|
|
29
|
+
version?: string;
|
|
30
|
+
launcher?: DaemonLauncher;
|
|
31
|
+
startedAt?: string;
|
|
32
|
+
adopted?: boolean;
|
|
33
|
+
};
|
|
34
|
+
export declare function readPidRecord(): DaemonPidRecord | null;
|
|
35
|
+
export declare function isRunning(pid: number): boolean;
|
|
36
|
+
export declare function waitForPidExitSync(pid: number, timeoutMs?: number): boolean;
|
|
37
|
+
export declare function isRemoteAccessDisabled(): boolean;
|
|
38
|
+
export declare function clearRemoteAccessDisabled(): void;
|
|
39
|
+
export declare function getCurrentProcessDaemonLauncher(): DaemonLauncher;
|
|
40
|
+
export declare function createDaemonInstanceId(): string;
|
|
41
|
+
export declare function writeDaemonPid(pid: number, instanceId: string, meta?: {
|
|
42
|
+
version?: string;
|
|
43
|
+
launcher?: DaemonLauncher;
|
|
44
|
+
adopted?: boolean;
|
|
45
|
+
}): void;
|
|
46
|
+
export declare function clearDaemonPidIfOwner(pid: number, instanceId: string): void;
|
|
47
|
+
export declare function writeDaemonLauncher(pid: number, launcher?: DaemonLauncher, instanceId?: string): void;
|
|
48
|
+
export declare function clearDaemonLauncher(instanceId?: string): void;
|
|
49
|
+
export declare function recordStartedDaemon(childPid: number | undefined): void;
|
|
50
|
+
export declare function findRunningDaemonProcessIds(excludePid?: number): number[];
|
|
51
|
+
export declare function isIsolatedDaemonE2eInstance(env?: NodeJS.ProcessEnv, shennianDir?: string, tempDir?: string, defaultDir?: string): boolean;
|
|
52
|
+
export declare function shouldDiscoverGlobalDaemonProcesses(env?: NodeJS.ProcessEnv, shennianDir?: string): boolean;
|
|
53
|
+
export declare function shouldInstallPlatformService(env?: NodeJS.ProcessEnv, shennianDir?: string): boolean;
|
|
54
|
+
export declare function getDaemonStatus(opts?: {
|
|
55
|
+
cleanupStale?: boolean;
|
|
56
|
+
}): DaemonStatus;
|
|
57
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import i from"node:fs";import c from"node:path";import f from"node:os";import{execSync as y}from"node:child_process";import{randomUUID as A}from"node:crypto";import{getShennianDir as b,loadConfig as O,resolveShennianPath as p}from"../config/index.js";import{isDesktopDistribution as P}from"../runtime-distribution.js";import{findRunningWindowsDaemonProcessIds as k,inferDaemonLauncherFromCommand as D,isDaemonProcessRunning as L,isShennianRunServiceCommand as _,readWindowsProcessCommand as F}from"./daemon-process.js";const u=b(),l=p("daemon.pid"),R=p("daemon.log"),w=p("remote-access.disabled"),m=p("daemon-launcher.json");function h(){const e=f.platform();return e==="darwin"||e==="linux"||e==="win32"?e:"linux"}function C(e){const n=e.trim();if(!n)return null;if(n.startsWith("{"))try{const t=JSON.parse(n),o=Number(t.pid);return!Number.isInteger(o)||o<=0?null:{pid:o,...typeof t.instanceId=="string"?{instanceId:t.instanceId}:{},...typeof t.version=="string"?{version:t.version}:{},...t.launcher==="desktop-managed"||t.launcher==="global-cli"||t.launcher==="unknown"?{launcher:t.launcher}:{},...typeof t.startedAt=="string"?{startedAt:t.startedAt}:{},...t.adopted===!0?{adopted:!0}:{}}}catch{return null}const r=parseInt(n,10);return isNaN(r)?null:{pid:r}}function N(){try{return C(i.readFileSync(l,"utf-8"))}catch{return null}}function S(e){return L(e)}function Q(e,n=3e3){const r=Date.now();for(;Date.now()-r<n;){if(!S(e))return!0;Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,100)}return!S(e)}function H(){try{return i.lstatSync(w),!0}catch(e){return!(e&&typeof e=="object"&&"code"in e&&e.code==="ENOENT")}}function X(){try{i.unlinkSync(w)}catch(e){if(e&&typeof e=="object"&&"code"in e&&e.code==="ENOENT")return;throw e}}function v(){return P()||process.env.SHENNIAN_DESKTOP_CLI_SCRIPT?"desktop-managed":"global-cli"}function T(){return`${process.pid}-${Date.now()}-${A()}`}function U(e,n,r={}){i.mkdirSync(u,{recursive:!0}),i.writeFileSync(l,JSON.stringify({pid:e,instanceId:n,...r.version?{version:r.version}:{},launcher:r.launcher??v(),...r.adopted?{adopted:!0}:{},startedAt:new Date().toISOString()},null,2))}function Y(e,n){const r=N();if(!(r?.pid!==e||r.instanceId!==n))try{i.unlinkSync(l)}catch{}}function x(e,n=v(),r){i.mkdirSync(u,{recursive:!0}),i.writeFileSync(m,JSON.stringify({pid:e,launcher:n,...r?{instanceId:r}:{},updatedAt:new Date().toISOString()},null,2))}function Z(e){if(e)try{if(JSON.parse(i.readFileSync(m,"utf-8")).instanceId!==e)return}catch{return}try{i.unlinkSync(m)}catch{}}function ee(e){e&&x(e)}function $(e,n){if(!e||!n)return"unknown";try{const r=JSON.parse(i.readFileSync(m,"utf-8"));if(r.pid!==e)return"unknown";if(r.launcher==="desktop-managed"||r.launcher==="global-cli")return r.launcher}catch{}return I(e)}function I(e){if(h()==="win32"){const n=F(e);return n?D(n):"unknown"}try{const n=y(`ps -p ${e} -o command=`,{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).replace(/\\/g,"/");return D(n)}catch{}return"unknown"}function J(e=process.pid){if(h()==="win32")return k(e);try{return y("ps -axo pid=,command=",{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).split(/\r?\n/).map(r=>{const t=r.trim().match(/^(\d+)\s+(.+)$/);if(!t)return null;const o=Number(t[1]),d=t[2];return!Number.isInteger(o)||o===e?null:_(d)?o:null}).filter(r=>typeof r=="number")}catch{return[]}}function E(e=process.env,n=u,r=f.tmpdir(),t=c.join(f.homedir(),".shennian")){if(e.SHENNIAN_E2E_ISOLATED_DAEMON!=="1"||!e.SHENNIAN_HOME?.trim()||c.resolve(n)===c.resolve(t))return!1;const o=c.relative(c.resolve(r),c.resolve(n));return o!==""&&o!==".."&&!o.startsWith(`..${c.sep}`)&&!c.isAbsolute(o)||c.resolve(n)===c.resolve(e.SHENNIAN_HOME)}function j(e=process.env,n=u){return!E(e,n)}function ne(e=process.env,n=u){return!E(e,n)}function re(e={}){let n=N(),r=n?.pid??null,t=r!==null&&S(r),o=r!==null&&!t,d=n?.adopted===!0;const s=O();if(o&&e.cleanupStale){try{i.unlinkSync(l)}catch{}n=null,r=null,t=!1,o=!1}if(!t&&j()){const a=J()[0];if(a&&(r=a,t=!0,o=!1,d=!0,e.cleanupStale)){const g=T();U(a,g,{launcher:I(a),adopted:!0}),x(a,I(a)),n={pid:a,instanceId:g,adopted:!0}}}return{running:t,pid:r,stale:o,remoteAccessDisabled:H(),launcher:$(r,t),...t&&n?.pid===r&&n.version?{daemonVersion:n.version}:{},platform:h(),shennianDir:u,pidFile:l,logFile:R,...n?.instanceId?{instanceId:n.instanceId}:{},...d?{adopted:!0}:{},...s.machineId?{machineId:s.machineId}:{},paired:!!(s.machineToken&&s.machineId),...s.serverUrl?{serverUrl:s.serverUrl}:{}}}export{m as LAUNCHER_FILE,R as LOG_FILE,l as PID_FILE,w as REMOTE_ACCESS_DISABLED_FILE,u as SHENNIAN_DIR,Z as clearDaemonLauncher,Y as clearDaemonPidIfOwner,X as clearRemoteAccessDisabled,T as createDaemonInstanceId,J as findRunningDaemonProcessIds,v as getCurrentProcessDaemonLauncher,re as getDaemonStatus,h as getPlatform,E as isIsolatedDaemonE2eInstance,H as isRemoteAccessDisabled,S as isRunning,N as readPidRecord,ee as recordStartedDaemon,j as shouldDiscoverGlobalDaemonProcesses,ne as shouldInstallPlatformService,Q as waitForPidExitSync,x as writeDaemonLauncher,U as writeDaemonPid};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
type WindowsLaunchSpec = {
|
|
2
2
|
command: string;
|
|
3
3
|
args: string[];
|
|
4
|
+
electronRunAsNode?: boolean;
|
|
4
5
|
};
|
|
5
6
|
export declare function escapeXml(text: string): string;
|
|
6
7
|
export declare function quoteSystemdArg(text: string): string;
|
|
@@ -12,6 +13,7 @@ export declare function buildWindowsScheduledTaskXml(input: {
|
|
|
12
13
|
userId: string;
|
|
13
14
|
command: string;
|
|
14
15
|
arguments: string[];
|
|
16
|
+
desktopManaged?: boolean;
|
|
15
17
|
}): string;
|
|
16
18
|
export declare function buildWindowsScheduledTaskCommands(input: {
|
|
17
19
|
taskName: string;
|
|
@@ -20,6 +22,12 @@ export declare function buildWindowsScheduledTaskCommands(input: {
|
|
|
20
22
|
create: string;
|
|
21
23
|
run: string;
|
|
22
24
|
};
|
|
25
|
+
/** Managed Desktop cannot promise durable autostart after a detached fallback. */
|
|
26
|
+
export declare function registerWindowsScheduledTask(input: {
|
|
27
|
+
taskName: string;
|
|
28
|
+
xmlPath: string;
|
|
29
|
+
desktopManaged: boolean;
|
|
30
|
+
}, execute: (command: string) => void): boolean;
|
|
23
31
|
export declare function buildWindowsScheduledTaskSuspendCommands(taskName: string): {
|
|
24
32
|
disable: string;
|
|
25
33
|
end: string;
|