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.
Files changed (106) hide show
  1. package/dist/publish-build-manifest.json +150 -90
  2. package/dist/src/agent-env.js +4 -4
  3. package/dist/src/agents/adapter.d.ts +7 -3
  4. package/dist/src/agents/auth-status.d.ts +4 -0
  5. package/dist/src/agents/auth-status.js +2 -0
  6. package/dist/src/agents/claude.js +4 -4
  7. package/dist/src/agents/codex-protocol.d.ts +103 -0
  8. package/dist/src/agents/codex-protocol.js +3 -0
  9. package/dist/src/agents/codex-runtime.d.ts +91 -0
  10. package/dist/src/agents/codex-runtime.js +14 -0
  11. package/dist/src/agents/codex.d.ts +15 -91
  12. package/dist/src/agents/codex.js +2 -17
  13. package/dist/src/agents/command-spec.js +4 -3
  14. package/dist/src/agents/detect.js +1 -1
  15. package/dist/src/agents/manager.d.ts +4 -3
  16. package/dist/src/agents/manager.js +4 -4
  17. package/dist/src/agents/model-registry/discovery.js +3 -3
  18. package/dist/src/agents/model-registry/parsers.js +4 -4
  19. package/dist/src/agents/model-registry/service.d.ts +6 -2
  20. package/dist/src/agents/model-registry/service.js +1 -1
  21. package/dist/src/agents/model-registry/types.d.ts +9 -1
  22. package/dist/src/agents/model-registry/types.js +1 -1
  23. package/dist/src/agents/opencode.d.ts +4 -0
  24. package/dist/src/agents/opencode.js +1 -1
  25. package/dist/src/commands/agent-readiness.d.ts +17 -0
  26. package/dist/src/commands/agent-readiness.js +2 -0
  27. package/dist/src/commands/agent.js +2 -2
  28. package/dist/src/commands/daemon-instance-lock.js +1 -1
  29. package/dist/src/commands/daemon-process.d.ts +1 -0
  30. package/dist/src/commands/daemon-process.js +1 -1
  31. package/dist/src/commands/daemon-service-spec.d.ts +28 -0
  32. package/dist/src/commands/daemon-service-spec.js +1 -0
  33. package/dist/src/commands/daemon-start-wait.d.ts +1 -1
  34. package/dist/src/commands/daemon-start-wait.js +1 -1
  35. package/dist/src/commands/daemon-state.d.ts +57 -0
  36. package/dist/src/commands/daemon-state.js +1 -0
  37. package/dist/src/commands/daemon-windows.d.ts +8 -0
  38. package/dist/src/commands/daemon-windows.js +10 -10
  39. package/dist/src/commands/daemon.d.ts +4 -64
  40. package/dist/src/commands/daemon.js +13 -13
  41. package/dist/src/commands/manager.js +1 -1
  42. package/dist/src/commands/upgrade.js +1 -1
  43. package/dist/src/commands/worker.d.ts +11 -0
  44. package/dist/src/commands/worker.js +2 -0
  45. package/dist/src/daemon-ipc/agent-readiness.d.ts +14 -0
  46. package/dist/src/daemon-ipc/agent-readiness.js +1 -0
  47. package/dist/src/daemon-ipc/control.d.ts +1 -1
  48. package/dist/src/daemon-ipc/control.js +1 -1
  49. package/dist/src/index.js +3 -3
  50. package/dist/src/manager/prompt.d.ts +1 -1
  51. package/dist/src/manager/prompt.js +6 -6
  52. package/dist/src/manager/registry.d.ts +48 -2
  53. package/dist/src/manager/registry.js +1 -1
  54. package/dist/src/manager/runtime.d.ts +2 -2
  55. package/dist/src/manager/runtime.js +6 -6
  56. package/dist/src/manager/worker-ipc.d.ts +7 -0
  57. package/dist/src/manager/worker-ipc.js +1 -1
  58. package/dist/src/native-fusion/local-repository.d.ts +6 -1
  59. package/dist/src/native-fusion/local-repository.js +2 -2
  60. package/dist/src/native-fusion/service.d.ts +2 -0
  61. package/dist/src/native-fusion/service.js +2 -2
  62. package/dist/src/native-fusion/source-visibility-audit.d.ts +16 -0
  63. package/dist/src/native-fusion/source-visibility-audit.js +1 -0
  64. package/dist/src/native-fusion/source-visibility.d.ts +25 -0
  65. package/dist/src/native-fusion/source-visibility.js +1 -0
  66. package/dist/src/native-fusion/state.d.ts +1 -0
  67. package/dist/src/native-fusion/state.js +2 -2
  68. package/dist/src/native-fusion/types.d.ts +5 -0
  69. package/dist/src/room/managed-room-binding.d.ts +2 -0
  70. package/dist/src/room/managed-room-binding.js +1 -1
  71. package/dist/src/room/room-binding-client.d.ts +1 -0
  72. package/dist/src/room/room-binding-client.js +1 -1
  73. package/dist/src/runtime-distribution.d.ts +13 -0
  74. package/dist/src/runtime-distribution.js +1 -0
  75. package/dist/src/security/managed-agent-policy.d.ts +7 -0
  76. package/dist/src/security/managed-agent-policy.js +1 -1
  77. package/dist/src/session/handlers/agent-workspace-validation.d.ts +1 -0
  78. package/dist/src/session/handlers/agent-workspace-validation.js +1 -1
  79. package/dist/src/session/handlers/agents.d.ts +7 -1
  80. package/dist/src/session/handlers/agents.js +1 -1
  81. package/dist/src/session/handlers/chat-context.d.ts +31 -0
  82. package/dist/src/session/handlers/chat-context.js +1 -0
  83. package/dist/src/session/handlers/chat-events.d.ts +1 -1
  84. package/dist/src/session/handlers/chat-events.js +2 -2
  85. package/dist/src/session/handlers/chat-session-lifecycle.d.ts +2 -2
  86. package/dist/src/session/handlers/chat-session-lifecycle.js +1 -1
  87. package/dist/src/session/handlers/chat.d.ts +3 -29
  88. package/dist/src/session/handlers/chat.js +1 -1
  89. package/dist/src/session/manager.js +2 -2
  90. package/dist/src/session/queue-helpers.js +2 -2
  91. package/dist/src/session/queue-message.d.ts +2 -0
  92. package/dist/src/session/queue.d.ts +7 -1
  93. package/dist/src/session/queue.js +1 -1
  94. package/dist/src/session/queued-chat-dispatch.d.ts +4 -1
  95. package/dist/src/session/queued-chat-dispatch.js +1 -1
  96. package/dist/src/session/store.d.ts +4 -2
  97. package/dist/src/session/store.js +2 -2
  98. package/dist/src/session/types.d.ts +1 -0
  99. package/dist/src/upgrade/engine.js +2 -2
  100. package/dist/src/upgrade/scheduler.js +1 -1
  101. package/dist/src/upgrade/updater-launcher.js +3 -3
  102. package/node_modules/@shennian/wire/dist/room.d.ts +2 -0
  103. package/node_modules/@shennian/wire/dist/session-sync.d.ts +2 -1
  104. package/node_modules/@shennian/wire/dist/session.d.ts +25 -1
  105. package/node_modules/@shennian/wire/dist/session.js +8 -0
  106. package/package.json +1 -1
@@ -1,20 +1,20 @@
1
- import i from"node:path";function s(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}function m(e){return`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function t(e){return`"${e.replace(/"/g,'""')}"`}function l(e){return e.replace(/"/g,'""')}function o(e){return s(e)}function d(e){const n=i.extname(e).toLowerCase();return n===".cmd"||n===".bat"}function g(e,n){const r=[e.command,...e.args].map(t).join(" ");return["@echo off","setlocal",'if not defined SHENNIAN_HOME set "SHENNIAN_HOME=%~dp0."',`${d(e.command)?`call ${r}`:r} >> ${t(n)} 2>&1`,""].join(`\r
2
- `)}function p(e){return['Set shell = CreateObject("WScript.Shell")',`shell.Run Chr(34) & "${l(e)}" & Chr(34), 0, False`,""].join(`\r
3
- `)}function S(e){const n=e.arguments.join(" ");return`<?xml version="1.0" encoding="UTF-16"?>
1
+ import l from"node:path";function d(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}function f(e){return`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function r(e){return`"${e.replace(/"/g,'""')}"`}function c(e){return e.replace(/"/g,'""')}function o(e){return d(e)}function u(e){const n=l.extname(e).toLowerCase();return n===".cmd"||n===".bat"}function S(e,n){const t=i=>r(i.replace(/%/g,"%%")),a=[e.command,...e.args].map(t).join(" "),s=u(e.command)?`call ${a}`:a;return["@echo off","chcp 65001 >nul","setlocal DisableDelayedExpansion",...e.electronRunAsNode?['set "ELECTRON_RUN_AS_NODE=1"']:[],'if not defined SHENNIAN_HOME set "SHENNIAN_HOME=%~dp0."',`${s} >> ${t(n)} 2>&1`,""].join(`\r
2
+ `)}function h(e){return['Set shell = CreateObject("WScript.Shell")',`shell.Run Chr(34) & "${c(e)}" & Chr(34), 0, False`,""].join(`\r
3
+ `)}function I(e){const n=e.arguments.join(" ");return`<?xml version="1.0" encoding="UTF-16"?>
4
4
  <Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
5
5
  <RegistrationInfo>
6
6
  <Description>Shennian Agent Daemon</Description>
7
7
  </RegistrationInfo>
8
8
  <Triggers>
9
- <BootTrigger>
9
+ ${e.desktopManaged?"":`<BootTrigger>
10
10
  <Enabled>true</Enabled>
11
11
  <Delay>PT15S</Delay>
12
- </BootTrigger>
12
+ </BootTrigger>`}
13
13
  <LogonTrigger>
14
14
  <Enabled>true</Enabled>
15
15
  <UserId>${o(e.userId)}</UserId>
16
16
  </LogonTrigger>
17
- <EventTrigger>
17
+ ${e.desktopManaged?"":`<EventTrigger>
18
18
  <Enabled>true</Enabled>
19
19
  <Subscription><![CDATA[
20
20
  <QueryList>
@@ -22,12 +22,12 @@ import i from"node:path";function s(e){return e.replace(/&/g,"&amp;").replace(/<
22
22
  <Select Path="System">*[System[Provider[@Name='Microsoft-Windows-Power-Troubleshooter'] and EventID=1]]</Select>
23
23
  </Query>
24
24
  </QueryList>]]></Subscription>
25
- </EventTrigger>
25
+ </EventTrigger>`}
26
26
  </Triggers>
27
27
  <Principals>
28
28
  <Principal id="Author">
29
29
  <UserId>${o(e.userId)}</UserId>
30
- <LogonType>S4U</LogonType>
30
+ <LogonType>${e.desktopManaged?"InteractiveToken":"S4U"}</LogonType>
31
31
  <RunLevel>LeastPrivilege</RunLevel>
32
32
  </Principal>
33
33
  </Principals>
@@ -46,7 +46,7 @@ import i from"node:path";function s(e){return e.replace(/&/g,"&amp;").replace(/<
46
46
  <Enabled>true</Enabled>
47
47
  <Hidden>true</Hidden>
48
48
  <RunOnlyIfIdle>false</RunOnlyIfIdle>
49
- <WakeToRun>true</WakeToRun>
49
+ <WakeToRun>${!e.desktopManaged}</WakeToRun>
50
50
  <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
51
51
  <Priority>7</Priority>
52
52
  <RestartOnFailure>
@@ -60,4 +60,4 @@ import i from"node:path";function s(e){return e.replace(/&/g,"&amp;").replace(/<
60
60
  <Arguments>${o(n)}</Arguments>
61
61
  </Exec>
62
62
  </Actions>
63
- </Task>`}function f(e){return{create:`schtasks /create /tn ${t(e.taskName)} /xml ${t(e.xmlPath)} /f`,run:`schtasks /run /tn ${t(e.taskName)}`}}function c(e){return{disable:`schtasks /change /tn ${t(e)} /disable`,end:`schtasks /end /tn ${t(e)}`}}function I(e,n){const r=c(e);for(const a of[r.disable,r.end])try{n(a)}catch{}}export{g as buildWindowsLauncherCommand,f as buildWindowsScheduledTaskCommands,c as buildWindowsScheduledTaskSuspendCommands,S as buildWindowsScheduledTaskXml,p as buildWindowsStartupVbs,s as escapeXml,d as isWindowsCmdScript,t as quoteCmdArg,m as quoteSystemdArg,I as suspendWindowsScheduledTask};
63
+ </Task>`}function m(e){return{create:`schtasks /create /tn ${r(e.taskName)} /xml ${r(e.xmlPath)} /f`,run:`schtasks /run /tn ${r(e.taskName)}`}}function b(e,n){const t=m(e);try{return n(t.create),n(t.run),!0}catch{if(e.desktopManaged)throw new Error("windows_autostart_registration_failed: Open Shennian Desktop in your normal Windows login and retry. An existing administrator-owned ShennianAgent task may require administrator-assisted migration; no background startup fallback was installed.");return!1}}function g(e){return{disable:`schtasks /change /tn ${r(e)} /disable`,end:`schtasks /end /tn ${r(e)}`}}function T(e,n){const t=g(e);for(const a of[t.disable,t.end])try{n(a)}catch{}}export{S as buildWindowsLauncherCommand,m as buildWindowsScheduledTaskCommands,g as buildWindowsScheduledTaskSuspendCommands,I as buildWindowsScheduledTaskXml,h as buildWindowsStartupVbs,d as escapeXml,u as isWindowsCmdScript,r as quoteCmdArg,f as quoteSystemdArg,b as registerWindowsScheduledTask,T as suspendWindowsScheduledTask};
@@ -2,46 +2,10 @@ import { spawn } from 'node:child_process';
2
2
  import type { Command } from 'commander';
3
3
  export { buildWindowsLauncherCommand, buildWindowsScheduledTaskCommands, buildWindowsScheduledTaskSuspendCommands, buildWindowsScheduledTaskXml, buildWindowsStartupVbs, suspendWindowsScheduledTask, } from './daemon-windows.js';
4
4
  export { inferDaemonLauncherFromCommand, isShennianRunServiceCommand } from './daemon-process.js';
5
- export declare function isSafeSnapshotEnvKey(key: string): boolean;
6
- type Platform = 'darwin' | 'linux' | 'win32';
7
- type ServiceLaunchMode = 'direct' | 'global-shim' | 'npx';
8
- type DaemonLauncher = 'desktop-managed' | 'global-cli' | 'unknown';
9
- export type DaemonStatus = {
10
- running: boolean;
11
- pid: number | null;
12
- stale: boolean;
13
- remoteAccessDisabled: boolean;
14
- launcher: DaemonLauncher;
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 type ServiceLaunchSpec = {
26
- command: string;
27
- args: string[];
28
- mode: ServiceLaunchMode;
29
- };
30
- export type DetachedLaunchSpec = {
31
- command: string;
32
- args: string[];
33
- windowsVerbatimArguments?: boolean;
34
- };
35
- export declare function isEphemeralCliPath(candidate: string): boolean;
36
- export declare function resolveServiceLaunchSpec(input: {
37
- nodeExec: string;
38
- scriptPath: string;
39
- shennianCommandPath?: string | null;
40
- npxPath?: string | null;
41
- desktopBridgePath?: string | null;
42
- }): ServiceLaunchSpec;
43
- export declare function isRemoteAccessDisabled(): boolean;
44
- export declare function clearRemoteAccessDisabled(): void;
5
+ import { type Platform, type DaemonStatus } from './daemon-state.js';
6
+ import { type ServiceLaunchSpec, type DetachedLaunchSpec } from './daemon-service-spec.js';
7
+ export { isRemoteAccessDisabled, clearRemoteAccessDisabled, getCurrentProcessDaemonLauncher, createDaemonInstanceId, writeDaemonPid, clearDaemonPidIfOwner, writeDaemonLauncher, clearDaemonLauncher, recordStartedDaemon, findRunningDaemonProcessIds, isIsolatedDaemonE2eInstance, shouldDiscoverGlobalDaemonProcesses, shouldInstallPlatformService, getDaemonStatus, type DaemonStatus } from './daemon-state.js';
8
+ export { isSafeSnapshotEnvKey, isEphemeralCliPath, resolveServiceLaunchSpec, captureEnvForService, saveEnvSnapshot, type ServiceLaunchSpec, type DetachedLaunchSpec } from './daemon-service-spec.js';
45
9
  export type RemoteAccessPausePreparation = {
46
10
  disabledAt: string;
47
11
  autoStartDisabled: boolean;
@@ -70,26 +34,7 @@ export declare function prepareRemoteAccessPause(options?: RemoteAccessPauseOpti
70
34
  * KeepAlive/Restart policy from relaunching the current daemon in the interim.
71
35
  */
72
36
  export declare function stopPreparedRemoteAccessService(options?: Pick<RemoteAccessPauseOptions, 'platform' | 'servicePresent' | 'execCommand'>): RemoteAccessServiceStopResult;
73
- export declare function getCurrentProcessDaemonLauncher(): DaemonLauncher;
74
- export declare function createDaemonInstanceId(): string;
75
- export declare function writeDaemonPid(pid: number, instanceId: string, meta?: {
76
- version?: string;
77
- launcher?: DaemonLauncher;
78
- adopted?: boolean;
79
- }): void;
80
- export declare function clearDaemonPidIfOwner(pid: number, instanceId: string): void;
81
- export declare function writeDaemonLauncher(pid: number, launcher?: DaemonLauncher, instanceId?: string): void;
82
- export declare function clearDaemonLauncher(instanceId?: string): void;
83
- export declare function recordStartedDaemon(childPid: number | undefined): void;
84
- export declare function findRunningDaemonProcessIds(excludePid?: number): number[];
85
- export declare function isIsolatedDaemonE2eInstance(env?: NodeJS.ProcessEnv, shennianDir?: string, tempDir?: string, defaultDir?: string): boolean;
86
- export declare function shouldDiscoverGlobalDaemonProcesses(env?: NodeJS.ProcessEnv, shennianDir?: string): boolean;
87
- export declare function shouldInstallPlatformService(env?: NodeJS.ProcessEnv, shennianDir?: string): boolean;
88
- export declare function getDaemonStatus(opts?: {
89
- cleanupStale?: boolean;
90
- }): DaemonStatus;
91
37
  export declare function buildDaemonSpawnOptions(launch: DetachedLaunchSpec, logFd: number, env?: NodeJS.ProcessEnv): Parameters<typeof spawn>[2];
92
- export declare function captureEnvForService(): Record<string, string>;
93
38
  export declare function buildMacLaunchdPlist(input?: {
94
39
  launch?: ServiceLaunchSpec;
95
40
  }): string;
@@ -99,11 +44,6 @@ export declare function buildSystemdUnit(): string;
99
44
  type LaunchdCommandExecutor = (command: string) => void;
100
45
  export declare function getLaunchdServiceTarget(uid?: number | string): string;
101
46
  export declare function reloadLaunchdAgent(execCommand?: LaunchdCommandExecutor): boolean;
102
- /**
103
- * Save current env vars so the daemon can load them at startup.
104
- * This is critical for launchd/systemd which start with minimal env.
105
- */
106
- export declare function saveEnvSnapshot(): void;
107
47
  export declare function startDaemonProcess(opts?: {
108
48
  quiet?: boolean;
109
49
  }): void;
@@ -1,13 +1,13 @@
1
- import c from"chalk";import o from"node:fs";import a from"node:path";import d from"node:os";import{execSync as u,spawn as ye}from"node:child_process";import{randomUUID as B}from"node:crypto";import{fileURLToPath as we}from"node:url";import{getShennianDir as Ee,loadConfig as V,resolveShennianPath as S,saveConfig as ve}from"../config/index.js";import{buildAugmentedPath as Pe}from"../env-path.js";import{buildWindowsLauncherCommand as Ae,buildWindowsScheduledTaskCommands as Ne,buildWindowsScheduledTaskXml as Ie,buildWindowsStartupVbs as De,escapeXml as T,isWindowsCmdScript as ke,quoteCmdArg as G,quoteSystemdArg as _e,suspendWindowsScheduledTask as be}from"./daemon-windows.js";import{waitForDaemonReadyStatus as x}from"./daemon-start-wait.js";import{findRunningWindowsDaemonProcessIds as Re,inferDaemonLauncherFromCommand as K,isShennianRunServiceCommand as Te,readWindowsProcessCommand as xe}from"./daemon-process.js";import{getCurrentVersion as J}from"../upgrade/engine.js";import{buildWindowsLauncherCommand as xn,buildWindowsScheduledTaskCommands as Ln,buildWindowsScheduledTaskSuspendCommands as On,buildWindowsScheduledTaskXml as Cn,buildWindowsStartupVbs as Hn,suspendWindowsScheduledTask as $n}from"./daemon-windows.js";import{inferDaemonLauncherFromCommand as Un,isShennianRunServiceCommand as jn}from"./daemon-process.js";const Le=a.dirname(we(import.meta.url)),h=Ee(),y=S("daemon.pid"),m=S("daemon.log"),L=S("remote-access.disabled"),k=S("daemon-launcher.json"),q=a.resolve(Le,"../../bin/shennian.js"),X=process.execPath,Y=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 Oe(e){return Y.has(e)}function p(){const e=d.platform();return e==="darwin"||e==="linux"||e==="win32"?e:"linux"}function O(e){const n=e.replace(/\\/g,"/").toLowerCase(),r=d.tmpdir().replace(/\\/g,"/").toLowerCase();return n.includes("/_npx/")||n.includes("/npm-cache/_npx/")||n.includes("/pnpm/dlx/")||n.startsWith(r.endsWith("/")?r:`${r}/`)}function Ce(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"}:o.existsSync(e.scriptPath)&&(process.env.SHENNIAN_E2E_ISOLATED_DAEMON==="1"||!O(e.scriptPath))?{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}:e.shennianCommandPath&&!O(e.shennianCommandPath)?{command:e.shennianCommandPath,args:["run-service"],mode:"global-shim"}:e.npxPath?{command:e.npxPath,args:["--yes","shennian","run-service"],mode:"npx"}:{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}}function He(e){const n=e.trim();if(!n)return null;if(n.startsWith("{"))try{const t=JSON.parse(n),s=Number(t.pid);return!Number.isInteger(s)||s<=0?null:{pid:s,...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 A(){try{return He(o.readFileSync(y,"utf-8"))}catch{return null}}function v(e){try{return process.kill(e,0),!0}catch{return!1}}function z(e,n=3e3){const r=Date.now();for(;Date.now()-r<n;){if(!v(e))return!0;Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,100)}return!v(e)}function $e(){try{return o.lstatSync(L),!0}catch(e){return!(e&&typeof e=="object"&&"code"in e&&e.code==="ENOENT")}}function Me(){try{o.unlinkSync(L)}catch(e){if(e&&typeof e=="object"&&"code"in e&&e.code==="ENOENT")return;throw e}}function Ue(e,n){const r=a.dirname(e),t=`${e}.${process.pid}.${B()}.tmp`;o.mkdirSync(r,{recursive:!0});let s=null;try{s=o.openSync(t,"w",384),o.writeSync(s,n,void 0,"utf8"),o.fsyncSync(s),o.closeSync(s),s=null,o.renameSync(t,e);try{const i=o.openSync(r,"r");try{o.fsyncSync(i)}finally{o.closeSync(i)}}catch{}}finally{s!==null&&o.closeSync(s);try{o.unlinkSync(t)}catch{}}}function je(e={}){const n=e.disabledAt??new Date().toISOString(),r=e.markerFile??L,t=e.platform??p(),s=e.servicePresent??(H()&&U(t)),i=e.execCommand??(l=>{u(l,{stdio:"pipe",windowsHide:!0})});if(Ue(r,n),!s)return{disabledAt:n,autoStartDisabled:!0};try{return i(t==="darwin"?`launchctl disable "${P()}"`:t==="linux"?"systemctl --user disable shennian":`schtasks /change /tn "${D}" /disable`),{disabledAt:n,autoStartDisabled:!0}}catch(l){return{disabledAt:n,autoStartDisabled:!1,autoStartError:l instanceof Error?l.message:String(l)}}}function We(e={}){const n=e.platform??p();if(!(e.servicePresent??(H()&&U(n))))return{serviceStopped:!0};const t=e.execCommand??(s=>{u(s,{stdio:"pipe",windowsHide:!0})});try{return t(n==="darwin"?`launchctl bootout "${P()}"`:n==="linux"?"systemctl --user stop shennian":`schtasks /end /tn "${D}"`),{serviceStopped:!0}}catch(s){return{serviceStopped:!1,error:s instanceof Error?s.message:String(s)}}}function Q(){return process.env.SHENNIAN_DESKTOP_CLI_SCRIPT?"desktop-managed":"global-cli"}function Fe(){return`${process.pid}-${Date.now()}-${B()}`}function Be(e,n,r={}){o.mkdirSync(h,{recursive:!0}),o.writeFileSync(y,JSON.stringify({pid:e,instanceId:n,...r.version?{version:r.version}:{},launcher:r.launcher??Q(),...r.adopted?{adopted:!0}:{},startedAt:new Date().toISOString()},null,2))}function _n(e,n){const r=A();if(!(r?.pid!==e||r.instanceId!==n))try{o.unlinkSync(y)}catch{}}function Z(e,n=Q(),r){o.mkdirSync(h,{recursive:!0}),o.writeFileSync(k,JSON.stringify({pid:e,launcher:n,...r?{instanceId:r}:{},updatedAt:new Date().toISOString()},null,2))}function Ve(e){if(e)try{if(JSON.parse(o.readFileSync(k,"utf-8")).instanceId!==e)return}catch{return}try{o.unlinkSync(k)}catch{}}function Ge(e){e&&Z(e)}function Ke(e,n){if(!e||!n)return"unknown";try{const r=JSON.parse(o.readFileSync(k,"utf-8"));if(r.pid!==e)return"unknown";if(r.launcher==="desktop-managed"||r.launcher==="global-cli")return r.launcher}catch{}return C(e)}function C(e){if(p()==="win32"){const n=xe(e);return n?K(n):"unknown"}try{const n=u(`ps -p ${e} -o command=`,{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).replace(/\\/g,"/");return K(n)}catch{}return"unknown"}function ee(e=process.pid){if(p()==="win32")return Re(e);try{return u("ps -axo pid=,command=",{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).split(/\r?\n/).map(r=>{const t=r.trim().match(/^(\d+)\s+(.+)$/);if(!t)return null;const s=Number(t[1]),i=t[2];return!Number.isInteger(s)||s===e?null:Te(i)?s:null}).filter(r=>typeof r=="number")}catch{return[]}}function ne(e=process.env,n=h,r=d.tmpdir(),t=a.join(d.homedir(),".shennian")){if(e.SHENNIAN_E2E_ISOLATED_DAEMON!=="1"||!e.SHENNIAN_HOME?.trim()||a.resolve(n)===a.resolve(t))return!1;const s=a.relative(a.resolve(r),a.resolve(n));return s!==""&&s!==".."&&!s.startsWith(`..${a.sep}`)&&!a.isAbsolute(s)||a.resolve(n)===a.resolve(e.SHENNIAN_HOME)}function te(e=process.env,n=h){return!ne(e,n)}function H(e=process.env,n=h){return!ne(e,n)}function f(e={}){let n=A(),r=n?.pid??null,t=r!==null&&v(r),s=r!==null&&!t,i=n?.adopted===!0;const l=V();if(s&&e.cleanupStale){try{o.unlinkSync(y)}catch{}n=null,r=null,t=!1,s=!1}if(!t&&te()){const E=ee()[0];if(E&&(r=E,t=!0,s=!1,i=!0,e.cleanupStale)){const F=Fe();Be(E,F,{launcher:C(E),adopted:!0}),Z(E,C(E)),n={pid:E,instanceId:F,adopted:!0}}}return{running:t,pid:r,stale:s,remoteAccessDisabled:$e(),launcher:Ke(r,t),platform:p(),shennianDir:h,pidFile:y,logFile:m,...n?.instanceId?{instanceId:n.instanceId}:{},...i?{adopted:!0}:{},...l.machineId?{machineId:l.machineId}:{},paired:!!(l.machineToken&&l.machineId),...l.serverUrl?{serverUrl:l.serverUrl}:{}}}function w(e){process.stdout.write(`${JSON.stringify(e,null,2)}
2
- `)}function re(e){const n=e?.trim();if(!n)return;const r=V();r.serverUrl=n,ve(r)}function oe(e){return e?.trim()||process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim()||void 0}const $="com.shennian.agent",g=a.join(d.homedir(),"Library/LaunchAgents",`${$}.plist`),se=S("Shennian.app"),Je=a.join(d.homedir(),"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),_=S("autostart.cmd"),b=S("autostart.vbs"),R=S("autostart.xml"),qe=a.join(Je,"ShennianAgent.vbs");function ie(e){const n=p()==="win32"?`where ${e}`:`command -v ${e}`;try{return u(n,{stdio:["ignore","pipe","pipe"],encoding:"utf-8",windowsHide:!0}).split(/\r?\n/).map(s=>s.trim()).find(Boolean)??null}catch{return null}}function Xe(){const e=p()==="win32"?"npx.cmd":"npx",n=a.join(a.dirname(X),e);return o.existsSync(n)?n:ie("npx")}function N(){return Ce({nodeExec:X,scriptPath:q,shennianCommandPath:ie("shennian"),npxPath:Xe(),desktopBridgePath:process.env.SHENNIAN_DESKTOP_CLI_BRIDGE})}function ce(){try{u(`schtasks /delete /tn "${D}" /f`,{stdio:"pipe",windowsHide:!0})}catch{}}function ae(){try{o.unlinkSync(qe)}catch{}}function Ye(){const e=process.env.USERDOMAIN?.trim(),n=process.env.COMPUTERNAME?.trim(),r=process.env.USERNAME?.trim()||d.userInfo().username;return e&&e.toUpperCase()!=="WORKGROUP"?`${e}\\${r}`:n?`${n}\\${r}`:r}function ze(e){if(p()==="win32"&&ke(e.command)){const n=[e.command,...e.args].map(G).join(" ");return{command:process.env.ComSpec||"cmd.exe",args:["/d","/s","/c",`"${n}"`],windowsVerbatimArguments:!0}}return{command:e.command,args:e.args}}function Qe(e,n,r=process.env){return{detached:!0,stdio:["ignore",n,n],env:r,windowsHide:!0,...e.windowsVerbatimArguments?{windowsVerbatimArguments:!0}:{}}}function Ze(){const e=N();o.writeFileSync(_,Ae(e,m)),o.writeFileSync(b,De(_)),ae(),ce();const n=Ie({userId:Ye(),command:a.join(process.env.SystemRoot||"C:\\Windows","System32","wscript.exe"),arguments:[G(b)]});o.writeFileSync(R,n,"utf8");const r=Ne({taskName:D,xmlPath:R});try{return u(r.create,{stdio:"pipe",windowsHide:!0}),u(r.run,{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}function en(){be(D,e=>{u(e,{stdio:"pipe",windowsHide:!0})})}function ue(){const e={};for(const n of Y)process.env[n]&&(e[n]=process.env[n]);if(e.HOME??=d.homedir(),e.PATH=Pe({pathValue:e.PATH,env:e}),e.USER??=d.userInfo().username,p()==="win32"){const n=process.env.TEMP||process.env.TMP||d.tmpdir(),r=d.homedir();e.USERPROFILE??=r;const t=a.win32.parse(r);e.HOMEDRIVE??=t.root.replace(/[\\/]$/,"")||process.env.HOMEDRIVE||"",e.HOMEPATH??=r.slice((e.HOMEDRIVE||"").length)||process.env.HOMEPATH||"",e.USERNAME??=process.env.USERNAME||d.userInfo().username,e.COMPUTERNAME??=process.env.COMPUTERNAME||"",e.TMPDIR??=n,e.TEMP??=n,e.TMP??=n}else e.TMPDIR??="/tmp";return e}function nn(e={}){const n=ue(),r=e.launch??N(),t=Object.entries(n).map(([i,l])=>` <key>${T(i)}</key>
3
- <string>${T(l)}</string>`).join(`
4
- `),s=[r.command,...r.args].map(i=>` <string>${T(i)}</string>`).join(`
1
+ import i from"chalk";import{isDesktopDistribution as se,readCliDistribution as ie}from"../runtime-distribution.js";import o from"node:fs";import d from"node:path";import g from"node:os";import{execSync as c,spawn as ae}from"node:child_process";import{randomUUID as ce}from"node:crypto";import{fileURLToPath as de}from"node:url";import{loadConfig as ue,resolveShennianPath as k,saveConfig as le}from"../config/index.js";import{buildWindowsLauncherCommand as me,buildWindowsScheduledTaskXml as pe,buildWindowsStartupVbs as fe,escapeXml as A,isWindowsCmdScript as Se,quoteCmdArg as T,quoteSystemdArg as ge,registerWindowsScheduledTask as he,suspendWindowsScheduledTask as ye}from"./daemon-windows.js";import{isDaemonReadyStatus as we,waitForDaemonReadyStatus as $}from"./daemon-start-wait.js";import{getCurrentVersion as O}from"../upgrade/engine.js";import{buildWindowsLauncherCommand as gn,buildWindowsScheduledTaskCommands as hn,buildWindowsScheduledTaskSuspendCommands as yn,buildWindowsScheduledTaskXml as wn,buildWindowsStartupVbs as vn,suspendWindowsScheduledTask as kn}from"./daemon-windows.js";import{inferDaemonLauncherFromCommand as Pn,isShennianRunServiceCommand as bn}from"./daemon-process.js";import{SHENNIAN_DIR as W,PID_FILE as E,LOG_FILE as u,REMOTE_ACCESS_DISABLED_FILE as U,readPidRecord as x,clearDaemonLauncher as ve,shouldDiscoverGlobalDaemonProcesses as ke,getPlatform as m,isRunning as D,waitForPidExitSync as B,isRemoteAccessDisabled as De,clearRemoteAccessDisabled as Pe,recordStartedDaemon as be,findRunningDaemonProcessIds as Ie,shouldInstallPlatformService as R,getDaemonStatus as l}from"./daemon-state.js";import{isEphemeralCliPath as Ae,resolveServiceLaunchSpec as $e,captureEnvForService as F,saveEnvSnapshot as Ee}from"./daemon-service-spec.js";import{isRemoteAccessDisabled as An,clearRemoteAccessDisabled as $n,getCurrentProcessDaemonLauncher as En,createDaemonInstanceId as xn,writeDaemonPid as Rn,clearDaemonPidIfOwner as Ln,writeDaemonLauncher as _n,clearDaemonLauncher as Nn,recordStartedDaemon as Cn,findRunningDaemonProcessIds as jn,isIsolatedDaemonE2eInstance as Tn,shouldDiscoverGlobalDaemonProcesses as On,shouldInstallPlatformService as Wn,getDaemonStatus as Un}from"./daemon-state.js";import{isSafeSnapshotEnvKey as Fn,isEphemeralCliPath as Hn,resolveServiceLaunchSpec as Mn,captureEnvForService as Gn,saveEnvSnapshot as Kn}from"./daemon-service-spec.js";const xe=d.dirname(de(import.meta.url)),H=d.resolve(xe,"../../bin/shennian.js"),M=process.execPath;function G(e,n){const r=d.dirname(e),t=`${e}.${process.pid}.${ce()}.tmp`;o.mkdirSync(r,{recursive:!0});let s=null;try{s=o.openSync(t,"w",384),o.writeSync(s,n,void 0,"utf8"),o.fsyncSync(s),o.closeSync(s),s=null,o.renameSync(t,e);try{const a=o.openSync(r,"r");try{o.fsyncSync(a)}finally{o.closeSync(a)}}catch{}}finally{s!==null&&o.closeSync(s);try{o.unlinkSync(t)}catch{}}}function Re(e={}){const n=e.disabledAt??new Date().toISOString(),r=e.markerFile??U,t=e.platform??m(),s=e.servicePresent??(R()&&N(t)),a=e.execCommand??(S=>{c(S,{stdio:"pipe",windowsHide:!0})});if(G(r,n),!s)return{disabledAt:n,autoStartDisabled:!0};try{return a(t==="darwin"?`launchctl disable "${h()}"`:t==="linux"?"systemctl --user disable shennian":`schtasks /change /tn "${v}" /disable`),{disabledAt:n,autoStartDisabled:!0}}catch(S){return{disabledAt:n,autoStartDisabled:!1,autoStartError:S instanceof Error?S.message:String(S)}}}function Le(e={}){const n=e.platform??m();if(!(e.servicePresent??(R()&&N(n))))return{serviceStopped:!0};const t=e.execCommand??(s=>{c(s,{stdio:"pipe",windowsHide:!0})});try{return t(n==="darwin"?`launchctl bootout "${h()}"`:n==="linux"?"systemctl --user stop shennian":`schtasks /end /tn "${v}"`),{serviceStopped:!0}}catch(s){return{serviceStopped:!1,error:s instanceof Error?s.message:String(s)}}}function p(e){process.stdout.write(`${JSON.stringify(e,null,2)}
2
+ `)}function K(e){const n=e?.trim();if(!n)return;const r=ue();r.serverUrl=n,le(r)}function V(e){return e?.trim()||process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim()||void 0}const L="com.shennian.agent",f=d.join(g.homedir(),"Library/LaunchAgents",`${L}.plist`),J=k("Shennian.app"),_e=d.join(g.homedir(),"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),P=k("autostart.cmd"),b=k("autostart.vbs"),I=k("autostart.xml"),Ne=d.join(_e,"ShennianAgent.vbs");function q(e){const n=m()==="win32"?`where ${e}`:`command -v ${e}`;try{return c(n,{stdio:["ignore","pipe","pipe"],encoding:"utf-8",windowsHide:!0}).split(/\r?\n/).map(s=>s.trim()).find(Boolean)??null}catch{return null}}function Ce(){const e=m()==="win32"?"npx.cmd":"npx",n=d.join(d.dirname(M),e);return o.existsSync(n)?n:q("npx")}function y(){return $e({nodeExec:M,scriptPath:H,shennianCommandPath:q("shennian"),npxPath:Ce(),desktopBridgePath:process.env.SHENNIAN_DESKTOP_CLI_BRIDGE})}function X(){try{c(`schtasks /delete /tn "${v}" /f`,{stdio:"pipe",windowsHide:!0})}catch{}}function Y(){try{o.unlinkSync(Ne)}catch{}}function je(){const e=process.env.USERDOMAIN?.trim(),n=process.env.COMPUTERNAME?.trim(),r=process.env.USERNAME?.trim()||g.userInfo().username;return e&&e.toUpperCase()!=="WORKGROUP"?`${e}\\${r}`:n?`${n}\\${r}`:r}function Te(e){if(m()==="win32"&&Se(e.command)){const n=[e.command,...e.args].map(T).join(" ");return{command:process.env.ComSpec||"cmd.exe",args:["/d","/s","/c",`"${n}"`],windowsVerbatimArguments:!0}}return{command:e.command,args:e.args}}function Oe(e,n,r=process.env){return{detached:!0,stdio:["ignore",n,n],env:r,windowsHide:!0,...e.windowsVerbatimArguments?{windowsVerbatimArguments:!0}:{}}}function We(){const e=se(),n=y();o.writeFileSync(P,me(n,u)),o.writeFileSync(b,fe(P)),Y(),e||X();const r=pe({userId:je(),command:d.join(process.env.SystemRoot||"C:\\Windows","System32","wscript.exe"),arguments:[T(b)],desktopManaged:e});return o.writeFileSync(I,`\uFEFF${r}`,"utf16le"),he({taskName:v,xmlPath:I,desktopManaged:e},t=>{c(t,{stdio:"pipe",windowsHide:!0})})}function Ue(){ye(v,e=>{c(e,{stdio:"pipe",windowsHide:!0})})}function Be(e={}){const n=F(),r=e.launch??y(),t=Object.entries(n).map(([a,S])=>` <key>${A(a)}</key>
3
+ <string>${A(S)}</string>`).join(`
4
+ `),s=[r.command,...r.args].map(a=>` <string>${A(a)}</string>`).join(`
5
5
  `);return`<?xml version="1.0" encoding="UTF-8"?>
6
6
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
7
7
  <plist version="1.0">
8
8
  <dict>
9
9
  <key>Label</key>
10
- <string>${$}</string>
10
+ <string>${L}</string>
11
11
  <key>ProgramArguments</key>
12
12
  <array>
13
13
  ${s}
@@ -23,12 +23,12 @@ ${t}
23
23
  <key>ThrottleInterval</key>
24
24
  <integer>5</integer>
25
25
  <key>StandardOutPath</key>
26
- <string>${m}</string>
26
+ <string>${u}</string>
27
27
  <key>StandardErrorPath</key>
28
- <string>${m}</string>
28
+ <string>${u}</string>
29
29
  </dict>
30
- </plist>`}function de(e=se){o.rmSync(e,{recursive:!0,force:!0})}function tn(e=p()){return e!=="darwin"?null:"macOS may show \u201CNode.js Foundation\u201D under Allow in the Background. This is the Node.js runtime used by Shennian Client."}const I=a.join(d.homedir(),".config/systemd/user/shennian.service");function rn(){const e=ue(),n=N(),r=Object.entries(e).map(([s,i])=>`Environment=${s}=${i}`).join(`
31
- `),t=[n.command,...n.args].map(_e).join(" ");return`[Unit]
30
+ </plist>`}function z(e=J){o.rmSync(e,{recursive:!0,force:!0})}function Fe(e=m()){return e!=="darwin"?null:"macOS may show \u201CNode.js Foundation\u201D under Allow in the Background. This is the Node.js runtime used by Shennian Client."}const w=d.join(g.homedir(),".config/systemd/user/shennian.service");function He(){const e=F(),n=y(),r=Object.entries(e).map(([s,a])=>`Environment=${s}=${a}`).join(`
31
+ `),t=[n.command,...n.args].map(ge).join(" ");return`[Unit]
32
32
  Description=Shennian Agent Daemon
33
33
  After=network.target
34
34
 
@@ -37,10 +37,10 @@ ${r}
37
37
  ExecStart=${t}
38
38
  Restart=on-failure
39
39
  RestartSec=10
40
- StandardOutput=append:${m}
41
- StandardError=append:${m}
40
+ StandardOutput=append:${u}
41
+ StandardError=append:${u}
42
42
 
43
43
  [Install]
44
- WantedBy=default.target`}const D="ShennianAgent";function on(e){u(e,{stdio:"pipe",windowsHide:!0})}function sn(){const e=process.getuid?.();if(typeof e=="number")return e;const n=d.userInfo();return typeof n.uid=="number"?n.uid:n.username}function P(e=sn()){return`gui/${e}/${$}`}function cn(e=on){try{return e(`launchctl enable "${P()}"; launchctl unload "${g}" 2>/dev/null; launchctl load -w "${g}"`),e(`launchctl kickstart -k "${P()}"`),!0}catch{try{return e(`launchctl enable "${P()}"`),e(`launchctl load -w "${g}"`),e(`launchctl kickstart -k "${P()}"`),!0}catch{return!1}}}function an(){o.mkdirSync(h,{recursive:!0});const e={};for(const[n,r]of Object.entries(process.env))r!==void 0&&Oe(n)&&(e[n]=r);o.writeFileSync(S("env.json"),JSON.stringify(e,null,2))}function le(e={}){o.mkdirSync(h,{recursive:!0});const n=f({cleanupStale:!0});if(n.pid!==null&&n.running&&!M(n)){e.quiet||console.log(c.green(`\u2713 Background service already running (PID ${n.pid})`));return}pe(n);const r=o.openSync(m,"a"),t=ze(N()),s=ye(t.command,t.args,Qe(t,r));s.unref(),o.closeSync(r),Ge(s.pid),e.quiet||(console.log(c.green(`\u2713 Background service started (PID ${s.pid})`)),console.log(c.gray(` Logs: ${m}`)))}function M(e,n=A(),r=J()){return e.pid===null||!e.running||e.launcher==="desktop-managed"?!1:e.adopted===!0||n?.version!==r}function U(e=p()){switch(e){case"darwin":return o.existsSync(g)&&!o.existsSync(se);case"linux":return o.existsSync(I);case"win32":return o.existsSync(_)&&o.existsSync(b)&&o.existsSync(R)}}function un(e,n,r,t=A(),s=J()){return!n.api?.trim()&&e.pid!==null&&e.running&&!e.stale&&!e.remoteAccessDisabled&&(e.launcher==="desktop-managed"||r)&&!M(e,t,s)}function pe(e=f({cleanupStale:!0})){if(!(!M(e)||e.pid===null))try{process.kill(e.pid,"SIGTERM"),z(e.pid,5e3)||(process.kill(e.pid,"SIGKILL"),z(e.pid,2e3));try{A()?.pid===e.pid&&o.unlinkSync(y)}catch{}}catch{}}function me(e={}){if(o.mkdirSync(h,{recursive:!0}),!H())return!1;const n=p();switch(N().mode==="direct"&&O(q)&&console.warn(c.yellow("\u26A0 Warning: Current CLI path is temporary (npx). Auto-start may not work after reboot.\n Run `npm install -g shennian@latest` for reliable auto-start.")),n){case"darwin":{const t=a.dirname(g);o.mkdirSync(t,{recursive:!0}),de(),o.writeFileSync(g,nn());const s=cn(),i=tn(n);return!e.quiet&&i&&console.log(c.gray(`\u2139 ${i}`)),s}case"linux":{const t=a.dirname(I);o.mkdirSync(t,{recursive:!0}),o.writeFileSync(I,rn());try{u("systemctl --user daemon-reload && systemctl --user enable shennian",{stdio:"pipe",windowsHide:!0})}catch{return!1}try{u(`loginctl enable-linger ${d.userInfo().username}`,{stdio:"pipe",windowsHide:!0})}catch{}try{return u("systemctl --user restart shennian",{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}case"win32":return Ze()}}function dn(){const n=f({cleanupStale:!0}).pid;if(n===null)return{};if(!v(n)){try{o.unlinkSync(y)}catch{}return{stalePid:n}}try{return process.kill(n,"SIGTERM"),{stoppedPid:n}}catch(r){return{error:r instanceof Error?r.message:String(r)}}}async function fe(e=5e3){const n=dn();if(!n.stoppedPid)return n;if(!await W(n.stoppedPid,e))try{process.kill(n.stoppedPid,"SIGKILL"),await W(n.stoppedPid,2e3)}catch(t){return{...n,error:t instanceof Error?t.message:String(t)}}try{o.unlinkSync(y)}catch{}if(Ve(),te()){for(const t of ee())if(t!==n.stoppedPid)try{process.kill(t,"SIGTERM"),await W(t,2e3)}catch{}}return n}function ln(e={}){re(oe(e.api)),Me(),pe(),me({quiet:e.json})||le({quiet:e.json}),e.json?w(x(()=>f({cleanupStale:!0}))):console.log(c.green("\u2713 Background service started"))}async function pn(e={}){const n=je(),r=We(),t=await fe(),s=f({cleanupStale:!0}),i=t.error??n.autoStartError??r.error;if(e.json){w(i?{...s,error:i}:s);return}i?console.error(c.red(`\u2717 Failed to stop completely: ${i}`)):console.log(c.green("\u2713 Background service stopped"))}function Se(e){const n=f({cleanupStale:!0});if(un(n,e,U())){e.json?w(n):console.log(c.green(`\u2713 Background service already running (PID ${n.pid})`));return}an(),ln(e)}async function j(e={}){await pn(e)}async function W(e,n=5e3){const r=Date.now();for(;Date.now()-r<n;){if(!v(e))return!0;await new Promise(t=>setTimeout(t,100))}return!v(e)}async function mn(e={}){const n=f({cleanupStale:!0});if(n.remoteAccessDisabled){e.json?w({...n,error:"remote_access_disabled_use_shennian_start"}):console.error(c.yellow("Remote access is paused. Run `shennian start` on this computer to resume."));return}re(oe(e.api));const r=p();r==="win32"&&en();const t=await fe();if(t.error){e.json?w({...f(),error:t.error}):console.error(c.red(`\u2717 Failed to stop: ${t.error}`));return}if(t.stoppedPid!==void 0?e.json||console.log(c.green(`\u2713 Background service stopped (PID ${t.stoppedPid})`)):t.stalePid!==void 0?e.json||console.log(c.yellow(`\u26A0 Service process no longer exists, cleaned up stale PID ${t.stalePid}`)):e.json||console.log(c.gray("\u25CF Background service not running, starting it now")),(r==="linux"||r==="win32")&&me()){e.json&&w(x(()=>f({cleanupStale:!0})));return}le({quiet:e.json}),e.json&&w(x(()=>f({cleanupStale:!0})))}function he(e={}){const n=f({cleanupStale:!0});if(e.json){w(n);return}if(n.pid===null){console.log(c.gray("\u25CF Background service not running"));return}n.running?(console.log(c.green(`\u25CF Background service running (PID ${n.pid})`)),console.log(c.gray(` Logs: ${m}`))):console.log(c.yellow(`\u25CF Background service stopped (PID ${n.pid} is stale)`))}function ge(e){if(!o.existsSync(m)){console.log(c.gray("No logs yet"));return}try{const n=u(d.platform()==="win32"?`powershell Get-Content -Tail ${e.lines} "${m}"`:`tail -n ${e.lines} "${m}"`,{encoding:"utf-8",windowsHide:!0});process.stdout.write(n)}catch{const n=o.readFileSync(m,"utf-8").split(`
44
+ WantedBy=default.target`}const v="ShennianAgent";function Me(e){c(e,{stdio:"pipe",windowsHide:!0})}function Ge(){const e=process.getuid?.();if(typeof e=="number")return e;const n=g.userInfo();return typeof n.uid=="number"?n.uid:n.username}function h(e=Ge()){return`gui/${e}/${L}`}function Ke(e=Me){try{return e(`launchctl enable "${h()}"; launchctl unload "${f}" 2>/dev/null; launchctl load -w "${f}"`),e(`launchctl kickstart -k "${h()}"`),!0}catch{try{return e(`launchctl enable "${h()}"`),e(`launchctl load -w "${f}"`),e(`launchctl kickstart -k "${h()}"`),!0}catch{return!1}}}function Q(e={}){o.mkdirSync(W,{recursive:!0});const n=l({cleanupStale:!0});if(n.pid!==null&&n.running&&!_(n)){e.quiet||console.log(i.green(`\u2713 Background service already running (PID ${n.pid})`));return}Z(n);const r=o.openSync(u,"a"),t=Te(y()),s=ae(t.command,t.args,Oe(t,r));s.unref(),o.closeSync(r),be(s.pid),e.quiet||(console.log(i.green(`\u2713 Background service started (PID ${s.pid})`)),console.log(i.gray(` Logs: ${u}`)))}function _(e,n=x(),r=O()){return e.pid===null||!e.running||e.launcher==="desktop-managed"?!1:e.adopted===!0||n?.version!==r}function N(e=m()){switch(e){case"darwin":return o.existsSync(f)&&!o.existsSync(J);case"linux":return o.existsSync(w);case"win32":return o.existsSync(P)&&o.existsSync(b)&&o.existsSync(I)}}function Ve(e,n,r,t=x(),s=O()){return!n.api?.trim()&&e.pid!==null&&e.running&&!e.stale&&!e.remoteAccessDisabled&&(e.launcher==="desktop-managed"||r)&&!_(e,t,s)}function Z(e=l({cleanupStale:!0})){if(!_(e)||e.pid===null)return;let n;try{process.kill(e.pid,"SIGTERM"),B(e.pid,5e3)||(process.kill(e.pid,"SIGKILL"),B(e.pid,2e3))}catch(r){n=r}if(D(e.pid)){const r=n instanceof Error?n.message:"daemon_stop_timeout";throw new Error(`daemon_replacement_failed: ${r}`)}try{x()?.pid===e.pid&&o.unlinkSync(E)}catch{}}function ee(e={}){if(o.mkdirSync(W,{recursive:!0}),!R())return!1;const n=m();switch(y().mode==="direct"&&Ae(H)&&console.warn(i.yellow("\u26A0 Warning: Current CLI path is temporary (npx). Auto-start may not work after reboot.\n Run `npm install -g shennian@latest` for reliable auto-start.")),n){case"darwin":{const t=d.dirname(f);o.mkdirSync(t,{recursive:!0}),z(),o.writeFileSync(f,Be());const s=Ke(),a=Fe(n);return!e.quiet&&a&&console.log(i.gray(`\u2139 ${a}`)),s}case"linux":{const t=d.dirname(w);o.mkdirSync(t,{recursive:!0}),o.writeFileSync(w,He());try{c("systemctl --user daemon-reload && systemctl --user enable shennian",{stdio:"pipe",windowsHide:!0})}catch{return!1}try{c(`loginctl enable-linger ${g.userInfo().username}`,{stdio:"pipe",windowsHide:!0})}catch{}try{return c("systemctl --user restart shennian",{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}case"win32":return We()}}function Je(){const n=l({cleanupStale:!0}).pid;if(n===null)return{};if(!D(n)){try{o.unlinkSync(E)}catch{}return{stalePid:n}}try{return process.kill(n,"SIGTERM"),{stoppedPid:n}}catch(r){return{error:r instanceof Error?r.message:String(r)}}}async function ne(e=5e3){const n=Je();if(!n.stoppedPid)return n;if(!await j(n.stoppedPid,e))try{if(process.kill(n.stoppedPid,"SIGKILL"),!await j(n.stoppedPid,2e3))return{...n,error:"daemon_stop_timeout"}}catch(t){return{...n,error:t instanceof Error?t.message:String(t)}}try{o.unlinkSync(E)}catch{}if(ve(),ke()){for(const t of Ie())if(t!==n.stoppedPid)try{process.kill(t,"SIGTERM"),await j(t,2e3)}catch{}}return n}function qe(e={}){const n=De();try{K(V(e.api)),Pe(),Z(),ee({quiet:e.json})||Q({quiet:e.json});const t=$(()=>l({cleanupStale:!0}));if(!we(t))throw new Error("daemon_start_failed");e.json?p(t):console.log(i.green("\u2713 Background service started"))}catch(r){n&&G(U,new Date().toISOString()),process.exitCode=1;const t=r instanceof Error?r.message:String(r);e.json?p({...l(),error:t}):console.error(i.red(`Background service did not start: ${t}`))}}async function Xe(e={}){const n=Re(),r=Le(),t=await ne(),s=l({cleanupStale:!0}),a=t.error??n.autoStartError??r.error??(s.running?"daemon_stop_failed":s.remoteAccessDisabled?void 0:"remote_access_pause_failed");if(a&&(process.exitCode=1),e.json){p(a?{...s,error:a}:s);return}a?console.error(i.red(`\u2717 Failed to stop completely: ${a}`)):console.log(i.green("\u2713 Background service stopped"))}function te(e){const n=l({cleanupStale:!0});if(Ve(n,e,N())){e.json?p(n):console.log(i.green(`\u2713 Background service already running (PID ${n.pid})`));return}Ee(),qe(e)}async function C(e={}){await Xe(e)}async function j(e,n=5e3){const r=Date.now();for(;Date.now()-r<n;){if(!D(e))return!0;await new Promise(t=>setTimeout(t,100))}return!D(e)}async function Ye(e={}){const n=l({cleanupStale:!0});if(n.remoteAccessDisabled){e.json?p({...n,error:"remote_access_disabled_use_shennian_start"}):console.error(i.yellow("Remote access is paused. Run `shennian start` on this computer to resume."));return}K(V(e.api));const r=m();r==="win32"&&Ue();const t=await ne();if(t.error){e.json?p({...l(),error:t.error}):console.error(i.red(`\u2717 Failed to stop: ${t.error}`));return}if(t.stoppedPid!==void 0?e.json||console.log(i.green(`\u2713 Background service stopped (PID ${t.stoppedPid})`)):t.stalePid!==void 0?e.json||console.log(i.yellow(`\u26A0 Service process no longer exists, cleaned up stale PID ${t.stalePid}`)):e.json||console.log(i.gray("\u25CF Background service not running, starting it now")),(r==="linux"||r==="win32")&&ee()){e.json&&p($(()=>l({cleanupStale:!0})));return}Q({quiet:e.json}),e.json&&p($(()=>l({cleanupStale:!0})))}function re(e={}){const n={...l(),client:ie()};if(e.json){p(n);return}if(n.pid===null){console.log(i.gray("\u25CF Background service not running"));return}n.running?(console.log(i.green(`\u25CF Background service running (PID ${n.pid})`)),console.log(i.gray(` Logs: ${u}`))):console.log(i.yellow(`\u25CF Background service stopped (PID ${n.pid} is stale)`))}function oe(e){if(!o.existsSync(u)){console.log(i.gray("No logs yet"));return}try{const n=c(g.platform()==="win32"?`powershell Get-Content -Tail ${e.lines} "${u}"`:`tail -n ${e.lines} "${u}"`,{encoding:"utf-8",windowsHide:!0});process.stdout.write(n)}catch{const n=o.readFileSync(u,"utf-8").split(`
45
45
  `).slice(-e.lines);console.log(n.join(`
46
- `))}}async function fn(){switch(await j(),p()){case"darwin":{if(o.existsSync(g)){try{u(`launchctl unload -w "${g}"`,{stdio:"pipe",windowsHide:!0})}catch{}o.unlinkSync(g)}de(),console.log(c.green("\u2713 Auto-start uninstalled (launchd)"));break}case"linux":{try{u("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}if(o.existsSync(I)){o.unlinkSync(I);try{u("systemctl --user daemon-reload",{stdio:"pipe",windowsHide:!0})}catch{}}console.log(c.green("\u2713 Auto-start uninstalled (systemd)"));break}case"win32":{ce();try{o.unlinkSync(_)}catch{}try{o.unlinkSync(b)}catch{}try{o.unlinkSync(R)}catch{}ae(),console.log(c.green("\u2713 Auto-start uninstalled (Task Scheduler)"));break}}}function bn(e){e.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(t=>Se(t)),e.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(t=>j(t)),e.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(t=>he(t)),e.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(t=>ge({lines:parseInt(t.lines,10)}));const n=e.command("daemon",{hidden:!0}).description("Deprecated: use top-level start/stop/status/logs"),r=(t,s)=>{s||console.error(c.yellow(`\u26A0 Deprecated command. Use \`shennian ${t}\` instead.`))};n.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(t=>{r("start",t.json),Se(t)}),n.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(async t=>{r("stop",t.json),await j(t)}),n.command("restart").description("Restart the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(t=>{r("stop && shennian start",t.json),mn(t)}),n.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(t=>{r("status",t.json),he(t)}),n.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(t=>{r("logs"),ge({lines:parseInt(t.lines,10)})}),n.command("uninstall").description("Uninstall auto-start service").action(fn)}export{Qe as buildDaemonSpawnOptions,nn as buildMacLaunchdPlist,rn as buildSystemdUnit,xn as buildWindowsLauncherCommand,Ln as buildWindowsScheduledTaskCommands,On as buildWindowsScheduledTaskSuspendCommands,Cn as buildWindowsScheduledTaskXml,Hn as buildWindowsStartupVbs,un as canReuseRunningDaemonForStart,ue as captureEnvForService,Ve as clearDaemonLauncher,_n as clearDaemonPidIfOwner,Me as clearRemoteAccessDisabled,Fe as createDaemonInstanceId,ee as findRunningDaemonProcessIds,Q as getCurrentProcessDaemonLauncher,f as getDaemonStatus,P as getLaunchdServiceTarget,tn as getMacBackgroundItemNotice,Un as inferDaemonLauncherFromCommand,me as installService,U as isAutoStartServicePresent,O as isEphemeralCliPath,ne as isIsolatedDaemonE2eInstance,$e as isRemoteAccessDisabled,Oe as isSafeSnapshotEnvKey,jn as isShennianRunServiceCommand,je as prepareRemoteAccessPause,Ge as recordStartedDaemon,bn as registerDaemonCommand,cn as reloadLaunchdAgent,de as removeLegacyMacBackgroundApp,Ce as resolveServiceLaunchSpec,an as saveEnvSnapshot,te as shouldDiscoverGlobalDaemonProcesses,H as shouldInstallPlatformService,M as shouldReplaceRunningDaemon,le as startDaemonProcess,We as stopPreparedRemoteAccessService,$n as suspendWindowsScheduledTask,Z as writeDaemonLauncher,Be as writeDaemonPid};
46
+ `))}}async function ze(){switch(await C(),m()){case"darwin":{if(o.existsSync(f)){try{c(`launchctl unload -w "${f}"`,{stdio:"pipe",windowsHide:!0})}catch{}o.unlinkSync(f)}z(),console.log(i.green("\u2713 Auto-start uninstalled (launchd)"));break}case"linux":{try{c("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}if(o.existsSync(w)){o.unlinkSync(w);try{c("systemctl --user daemon-reload",{stdio:"pipe",windowsHide:!0})}catch{}}console.log(i.green("\u2713 Auto-start uninstalled (systemd)"));break}case"win32":{X();try{o.unlinkSync(P)}catch{}try{o.unlinkSync(b)}catch{}try{o.unlinkSync(I)}catch{}Y(),console.log(i.green("\u2713 Auto-start uninstalled (Task Scheduler)"));break}}}function pn(e){e.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(t=>te(t)),e.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(t=>C(t)),e.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(t=>re(t)),e.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(t=>oe({lines:parseInt(t.lines,10)}));const n=e.command("daemon",{hidden:!0}).description("Deprecated: use top-level start/stop/status/logs"),r=(t,s)=>{s||console.error(i.yellow(`\u26A0 Deprecated command. Use \`shennian ${t}\` instead.`))};n.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(t=>{r("start",t.json),te(t)}),n.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(async t=>{r("stop",t.json),await C(t)}),n.command("restart").description("Restart the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(t=>{r("stop && shennian start",t.json),Ye(t)}),n.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(t=>{r("status",t.json),re(t)}),n.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(t=>{r("logs"),oe({lines:parseInt(t.lines,10)})}),n.command("uninstall").description("Uninstall auto-start service").action(ze)}export{Oe as buildDaemonSpawnOptions,Be as buildMacLaunchdPlist,He as buildSystemdUnit,gn as buildWindowsLauncherCommand,hn as buildWindowsScheduledTaskCommands,yn as buildWindowsScheduledTaskSuspendCommands,wn as buildWindowsScheduledTaskXml,vn as buildWindowsStartupVbs,Ve as canReuseRunningDaemonForStart,Gn as captureEnvForService,Nn as clearDaemonLauncher,Ln as clearDaemonPidIfOwner,$n as clearRemoteAccessDisabled,xn as createDaemonInstanceId,jn as findRunningDaemonProcessIds,En as getCurrentProcessDaemonLauncher,Un as getDaemonStatus,h as getLaunchdServiceTarget,Fe as getMacBackgroundItemNotice,Pn as inferDaemonLauncherFromCommand,ee as installService,N as isAutoStartServicePresent,Hn as isEphemeralCliPath,Tn as isIsolatedDaemonE2eInstance,An as isRemoteAccessDisabled,Fn as isSafeSnapshotEnvKey,bn as isShennianRunServiceCommand,Re as prepareRemoteAccessPause,Cn as recordStartedDaemon,pn as registerDaemonCommand,Ke as reloadLaunchdAgent,z as removeLegacyMacBackgroundApp,Mn as resolveServiceLaunchSpec,Kn as saveEnvSnapshot,On as shouldDiscoverGlobalDaemonProcesses,Wn as shouldInstallPlatformService,_ as shouldReplaceRunningDaemon,Q as startDaemonProcess,Le as stopPreparedRemoteAccessService,kn as suspendWindowsScheduledTask,_n as writeDaemonLauncher,Rn as writeDaemonPid};
@@ -1 +1 @@
1
- import l from"node:fs";import g from"node:path";import{resolveShennianPath as f}from"../config/index.js";import{callDaemonIpc as h}from"../daemon-ipc/client.js";import{DaemonIpcError as w}from"../daemon-ipc/protocol.js";import{MANAGED_ROOM_IPC_ROOT_ENV as I,resolveManagedRoomIpcPaths as k}from"../daemon-ipc/paths.js";import{MANAGED_ROOM_CONTEXT_ENV as S}from"../room/managed-context.js";function y(){const o=process.env.CODEX_SESSION_ID?.trim()||process.env.CODEX_THREAD_ID?.trim();if(o)return{host:"codex",hostSessionId:o};const t=process.env.CLAUDE_CODE_SESSION_ID?.trim()||process.env.CLAUDE_SESSION_ID?.trim();return t?{host:"claude",hostSessionId:t}:null}function O(){const o=f("runtime","manager-ipc.json"),t=l.statSync(o);if(process.platform!=="win32"){const e=typeof process.getuid=="function"?process.getuid():t.uid;if(t.uid!==e||(t.mode&63)!==0)throw new Error("Shennian daemon Manager runtime file has unsafe permissions")}const n=JSON.parse(l.readFileSync(o,"utf-8")),i=typeof n.url=="string"?n.url:"",r=typeof n.token=="string"?n.token:"";if(!/^http:\/\/127\.0\.0\.1:\d+$/.test(i)||!r)throw new Error("Shennian daemon Manager runtime is unavailable");return{url:i,token:r}}async function N(){const o=process.env.SHENNIAN_MANAGER_IPC_URL,t=process.env.SHENNIAN_MANAGER_IPC_TOKEN,n=process.env.SHENNIAN_MANAGER_SESSION_ID;if(o&&t&&n)return{url:o,token:t,managerSessionId:n};const i=y();if(!i)throw new Error("This command requires a Shennian Manager session or a current Codex/Claude Code conversation.");const r=O(),e=await fetch(`${r.url}/skill/context`,{method:"POST",headers:{authorization:`Bearer ${r.token}`,"content-type":"application/json"},body:JSON.stringify({...i,workDir:process.cwd()})}),s=await e.json().catch(()=>({ok:!1,error:e.statusText}));if(!e.ok||!s.ok||typeof s.managerSessionId!="string")throw new Error(s.error||"Unable to establish the native Agent controller context");return{...r,managerSessionId:s.managerSessionId}}async function d(o,t){const n=process.env[S]?.trim();if(n){const s=M[o];if(!s)throw new Error(`Manager IPC path is unavailable in managed Room context: ${o}`);const a=process.env[I]?.trim();if(!a||!_(a))throw new w("managed_room_context_unavailable","Managed Room IPC context is unavailable.");return await h({method:"managed-room.execute",params:{context:n,action:s,...t},paths:k(a),timeoutMs:s==="manager.sessions.start"||s==="manager.sessions.send"?18e4:15e3})}const i=await N(),r=await fetch(`${i.url}${o}`,{method:"POST",headers:{authorization:`Bearer ${i.token}`,"content-type":"application/json","x-shennian-manager-session-id":i.managerSessionId},body:JSON.stringify({...t,managerSessionId:i.managerSessionId})}),e=await r.json().catch(()=>({ok:!1,error:r.statusText}));if(!r.ok||!e.ok)throw new Error(e.error||`Manager IPC failed: ${r.status}`);return e}const M={"/sessions/list":"manager.sessions.list","/sessions/start":"manager.sessions.start","/sessions/send":"manager.sessions.send","/sessions/queue":"manager.sessions.queue","/sessions/queue/edit":"manager.sessions.queue.edit","/sessions/queue/delete":"manager.sessions.queue.delete","/sessions/stop":"manager.sessions.stop","/sessions/read":"manager.sessions.read","/memory/path":"manager.memory.path"};function _(o){return g.isAbsolute(o)}function c(o){console.log(JSON.stringify(o,null,2))}function u(o){console.log(typeof o.markdown=="string"?o.markdown:"ok")}function p(o){return o.messageFile?l.readFileSync(o.messageFile,"utf-8"):o.message??""}function D(o){const t=o.command("manager",{hidden:!0}).description("Manager Session local tools"),n=t.command("sessions").description("Manage same-project worker sessions");n.command("list").description("List same-project sessions visible to this Manager").option("--json","Print JSON").action(async e=>{const s=await d("/sessions/list",{});if(e.json)c(s);else{const a=s.sessions??[];for(const m of a)console.log(`${m.sessionId} ${m.agentType} ${m.role??""} ${m.status} ${m.title??m.summary??""}`)}}),n.command("start").description("Start a worker session in the same project").requiredOption("--agent <agent>","Worker agent type, e.g. codex, claude, cursor, opencode, pi, or custom:<name>").requiredOption("--workdir <path>","Worker workdir; must match Manager workdir").option("--title <title>","Short worker title").option("--model <model>","Worker model id").option("--message <text>","Worker prompt").option("--message-file <path>","Read worker prompt from file").option("--json","Print JSON").action(async e=>{const s=await d("/sessions/start",{agentType:e.agent,workDir:e.workdir,title:e.title,modelId:e.model,message:p(e)});e.json?c(s):u(s)}),n.command("send").description("Queue a message for a managed worker; runs immediately if idle").requiredOption("--session-id <id>","Worker session id").option("--message <text>","Message text").option("--message-file <path>","Read message from file").option("--direct","Deprecated compatibility flag; delivery still uses the durable queue").action(async e=>{const s=await d("/sessions/send",{sessionId:e.sessionId,message:p(e),enqueue:!e.direct});u(s)});const i=n.command("queue").description("Inspect and edit pending worker messages");i.command("list").description("List pending messages for a managed worker").requiredOption("--session-id <id>","Worker session id").option("--json","Print JSON").action(async e=>{const s=await d("/sessions/queue",{sessionId:e.sessionId});if(e.json)c(s);else{const a=s.queue?.pending??[];for(const m of a)console.log(`${m.id} ${m.text.replace(/\s+/g," ").slice(0,120)}`)}}),i.command("edit").description("Edit a pending worker message").requiredOption("--session-id <id>","Worker session id").requiredOption("--message-id <id>","Queued message id").option("--message <text>","Replacement message text").option("--message-file <path>","Read replacement message from file").action(async e=>{await d("/sessions/queue/edit",{sessionId:e.sessionId,queueMessageId:e.messageId,message:p(e)}),console.log("ok")}),i.command("delete").description("Delete a pending worker message").requiredOption("--session-id <id>","Worker session id").requiredOption("--message-id <id>","Queued message id").action(async e=>{await d("/sessions/queue/delete",{sessionId:e.sessionId,queueMessageId:e.messageId}),console.log("ok")});const r=async e=>{const s=await d("/sessions/stop",{sessionId:e.sessionId});u(s)};n.command("stop").aliases(["terminate","kill"]).description("Stop or terminate a running managed worker").requiredOption("--session-id <id>","Worker session id").action(r),n.command("read").description("Read a managed worker transcript").requiredOption("--session-id <id>","Worker session id").option("--limit <n>","Message limit","200").option("--json","Print JSON").action(async e=>{const s=await d("/sessions/read",{sessionId:e.sessionId,limit:Number(e.limit)});e.json?c(s):c(s.messages??[])}),n.command("wait").description("Wait up to a bounded timeout for the next worker progress or terminal event").requiredOption("--session-id <id>","Worker session id").option("--timeout <seconds>","Maximum wait in seconds (1-600)","600").option("--json","Print JSON").action(async e=>{const s=Math.max(1,Math.min(Number(e.timeout)||600,600)),a=await d("/sessions/wait",{sessionId:e.sessionId,timeoutMs:s*1e3});e.json?c(a):a.timedOut?console.log(`\u7B49\u5F85\u8D85\u65F6\uFF1BAgent \u5F53\u524D\u72B6\u6001\uFF1A${String(a.status||"unknown")}`):u(a)}),t.command("memory").description("Manager project memory helpers").command("path").description("Print project memory directory path").action(async()=>{const e=await d("/memory/path",{});console.log(e.path)})}export{D as registerManagerCommand};
1
+ import g from"node:fs";import p from"node:path";import{callDaemonIpc as f}from"../daemon-ipc/client.js";import{DaemonIpcError as w}from"../daemon-ipc/protocol.js";import{MANAGED_ROOM_IPC_ROOT_ENV as k,resolveManagedRoomIpcPaths as I}from"../daemon-ipc/paths.js";import{MANAGED_ROOM_CONTEXT_ENV as h}from"../room/managed-context.js";async function y(){const o=process.env.SHENNIAN_MANAGER_IPC_URL,r=process.env.SHENNIAN_MANAGER_IPC_TOKEN,n=process.env.SHENNIAN_MANAGER_SESSION_ID;if(o&&r&&n)return{url:o,token:r,managerSessionId:n};throw new Error("manager_context_required: this command is only available inside a Shennian managed Manager session")}async function t(o,r){const n=process.env[h]?.trim();if(n){const s=M[o];if(!s)throw new Error(`Manager IPC path is unavailable in managed Room context: ${o}`);const i=process.env[k]?.trim();if(!i||!N(i))throw new w("managed_room_context_unavailable","Managed Room IPC context is unavailable.");return await f({method:"managed-room.execute",params:{context:n,action:s,...r},paths:I(i),timeoutMs:s==="manager.sessions.start"||s==="manager.sessions.send"?18e4:15e3})}const d=await y(),m=await fetch(`${d.url}${o}`,{method:"POST",headers:{authorization:`Bearer ${d.token}`,"content-type":"application/json","x-shennian-manager-session-id":d.managerSessionId},body:JSON.stringify({...r,managerSessionId:d.managerSessionId})}),e=await m.json().catch(()=>({ok:!1,error:m.statusText}));if(!m.ok||!e.ok)throw new Error(e.error||`Manager IPC failed: ${m.status}`);return e}const M={"/sessions/list":"manager.sessions.list","/sessions/start":"manager.sessions.start","/sessions/send":"manager.sessions.send","/sessions/queue":"manager.sessions.queue","/sessions/queue/edit":"manager.sessions.queue.edit","/sessions/queue/delete":"manager.sessions.queue.delete","/sessions/stop":"manager.sessions.stop","/sessions/read":"manager.sessions.read","/memory/path":"manager.memory.path"};function N(o){return p.isAbsolute(o)}function c(o){console.log(JSON.stringify(o,null,2))}function l(o){console.log(typeof o.markdown=="string"?o.markdown:"ok")}function u(o){return o.messageFile?g.readFileSync(o.messageFile,"utf-8"):o.message??""}function E(o){const r=o.command("manager",{hidden:!0}).description("Manager Session local tools"),n=r.command("sessions").description("Manage same-project worker sessions");n.command("list").description("List same-project sessions visible to this Manager").option("--json","Print JSON").action(async e=>{const s=await t("/sessions/list",{});if(e.json)c(s);else{const i=s.sessions??[];for(const a of i)console.log(`${a.sessionId} ${a.agentType} ${a.role??""} ${a.status} ${a.title??a.summary??""}`)}}),n.command("start").description("Start a worker session in the same project").requiredOption("--agent <agent>","Worker agent type, e.g. codex, claude, cursor, opencode, pi, or custom:<name>").option("--workdir <path>","Worker workdir; defaults to the Manager-bound directory").option("--title <title>","Short worker title").option("--model <model>","Worker model id").option("--message <text>","Worker prompt").option("--message-file <path>","Read worker prompt from file").option("--json","Print JSON").action(async e=>{const s=await t("/sessions/start",{agentType:e.agent,workDir:e.workdir,title:e.title,modelId:e.model,message:u(e)});e.json?c(s):l(s)}),n.command("send").description("Queue a message for a managed worker; runs immediately if idle").requiredOption("--session-id <id>","Worker session id").option("--message <text>","Message text").option("--message-file <path>","Read message from file").option("--direct","Deprecated compatibility flag; delivery still uses the durable queue").action(async e=>{const s=await t("/sessions/send",{sessionId:e.sessionId,message:u(e),enqueue:!e.direct});l(s)});const d=n.command("queue").description("Inspect and edit pending worker messages");d.command("list").description("List pending messages for a managed worker").requiredOption("--session-id <id>","Worker session id").option("--json","Print JSON").action(async e=>{const s=await t("/sessions/queue",{sessionId:e.sessionId});if(e.json)c(s);else{const i=s.queue?.pending??[];for(const a of i)console.log(`${a.id} ${a.text.replace(/\s+/g," ").slice(0,120)}`)}}),d.command("edit").description("Edit a pending worker message").requiredOption("--session-id <id>","Worker session id").requiredOption("--message-id <id>","Queued message id").option("--message <text>","Replacement message text").option("--message-file <path>","Read replacement message from file").action(async e=>{await t("/sessions/queue/edit",{sessionId:e.sessionId,queueMessageId:e.messageId,message:u(e)}),console.log("ok")}),d.command("delete").description("Delete a pending worker message").requiredOption("--session-id <id>","Worker session id").requiredOption("--message-id <id>","Queued message id").action(async e=>{await t("/sessions/queue/delete",{sessionId:e.sessionId,queueMessageId:e.messageId}),console.log("ok")});const m=async e=>{const s=await t("/sessions/stop",{sessionId:e.sessionId});l(s)};n.command("stop").aliases(["terminate","kill"]).description("Stop or terminate a running managed worker").requiredOption("--session-id <id>","Worker session id").action(m),n.command("read").description("Read a managed worker transcript").requiredOption("--session-id <id>","Worker session id").option("--limit <n>","Message limit","200").option("--json","Print JSON").action(async e=>{const s=await t("/sessions/read",{sessionId:e.sessionId,limit:Number(e.limit)});e.json?c(s):c(s.messages??[])}),n.command("wait").description("Wait up to a bounded timeout for the next worker progress or terminal event").requiredOption("--session-id <id>","Worker session id").option("--timeout <seconds>","Maximum wait in seconds (1-600)","600").option("--json","Print JSON").action(async e=>{const s=Math.max(1,Math.min(Number(e.timeout)||600,600)),i=await t("/sessions/wait",{sessionId:e.sessionId,timeoutMs:s*1e3});e.json?c(i):i.timedOut?console.log(`\u7B49\u5F85\u8D85\u65F6\uFF1BAgent \u5F53\u524D\u72B6\u6001\uFF1A${String(i.status||"unknown")}`):l(i)}),r.command("memory").description("Manager project memory helpers").command("path").description("Print project memory directory path").action(async()=>{const e=await t("/memory/path",{});console.log(e.path)})}export{E as registerManagerCommand};
@@ -1 +1 @@
1
- import c from"chalk";import{execSync as P,spawn as V}from"node:child_process";import{randomUUID as w}from"node:crypto";import $ from"node:os";import{createInterface as A}from"node:readline/promises";import{getCurrentVersion as y,fetchLatestVersion as k,compareVersions as v,checkForUpdate as M,acquireUpgradeLock as F,recordUpgradeFailure as O}from"../upgrade/engine.js";import{callDaemonIpc as _}from"../daemon-ipc/client.js";import{getDaemonStatus as D}from"./daemon.js";import{ensureUpdaterInstalled as U}from"../upgrade/updater-bootstrap.js";import{launchUpdater as S,createUpdaterJob as R,waitForUpdaterReady as H,confirmUpdaterHandoff as L}from"../upgrade/updater-launcher.js";import{prepareRecoveryServiceEntry as j}from"../upgrade/updater-service-entry.js";import{resolveShennianPath as q}from"../config/index.js";import{assertUpdaterAvailable as N,prepareUpdaterCandidate as C}from"../upgrade/updater-worker.js";import{getUpgradeChannel as x,setUpgradeChannel as G}from"../upgrade/channel.js";import{fetchOwnedCliArtifact as T,getCliUpdateSourceOrder as b}from"../upgrade/distribution.js";const I=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];function Y(t,r){return t!=="major"||r}function ie(t={}){const r=t.platform??$.platform(),i=t.kill??process.kill,e=t.pid??process.pid,l=t.env??process.env,n=t.spawn??V,o=t.execSync??P;if(r==="win32"){const a=l.ComSpec||"cmd.exe";try{n(a,["/d","/s","/c","timeout /t 2 /nobreak >nul & schtasks /Run /TN ShennianAgent >nul"],{detached:!0,stdio:"ignore",windowsHide:!0}).unref(),i(e,"SIGTERM");return}catch{try{o('schtasks /run /tn "ShennianAgent"',{stdio:"pipe",windowsHide:!0})}catch{}}}else try{n("sh",["-c","sleep 2; shennian start >/dev/null 2>&1"],{detached:!0,stdio:"ignore",env:l}).unref()}catch{}i(e,"SIGTERM")}function ce(t){t.command("upgrade").description("Upgrade shennian CLI to the latest version").option("--channel <channel>","Persist stable (npm latest) or preview (npm next); does not install").option("--check","Only check for updates, do not install").option("--to <version>","Upgrade to a specific version").option("--yes","Confirm a major version upgrade (required in non-interactive environments)").action(async r=>{if(r.channel!==void 0){if(r.to||r.yes)throw new Error("--channel cannot be combined with --to or --yes");if(G(r.channel),console.log(`Upgrade channel: ${x()} (saved; no installation)`),!r.check)return}console.log(`Upgrade channel: ${x()}`);const i=y();console.log(c.gray(`Current version: ${i}`)),console.log();let e=0;const l=setInterval(()=>{process.stdout.write(`\r ${I[e%I.length]} Checking for updates...`),e++},80);let n;try{n=r.to??await k(),clearInterval(l),process.stdout.write("\r"+" ".repeat(40)+"\r")}catch(a){clearInterval(l),process.stdout.write("\r"+" ".repeat(40)+"\r"),console.error(c.red(`\u2717 Failed to check for updates: ${a instanceof Error?a.message:String(a)}`)),process.exit(1)}const o=v(i,n);if(o==="none"&&!r.to){console.log(c.green(`\u2713 Already up to date (${i})`));return}if(console.log(` Available: ${c.green(n)} ${o==="major"?c.red("(major)"):o==="minor"?c.yellow("(minor)"):c.green("(patch)")}`),console.log(),!r.check){if(o==="major"&&(console.log(c.yellow(" \u26A0 This is a major version update and may contain breaking changes.")),console.log(c.gray(" Please review the changelog before upgrading.")),console.log(),!r.yes)){(!process.stdin.isTTY||!process.stdout.isTTY)&&(console.error(c.red("\u2717 Major upgrades require explicit confirmation; rerun with --yes.")),process.exit(1));const a=A({input:process.stdin,output:process.stdout}),p=await a.question(` Type ${n} to confirm this major upgrade: `);if(a.close(),p.trim()!==n){console.log(c.gray(" Upgrade cancelled."));return}}try{await _({method:"upgrade.start",params:{targetVersion:n,confirmedMajor:o==="major"||r.yes===!0},timeoutMs:1e4})}catch(a){if(D({cleanupStale:!0}).running)throw a;const m=U(),u={fromVersion:i,targetVersion:n,oldDaemonPid:null,jobId:w()},g=R(u);await C(g,{fetchOwnedArtifact:T,sourceOrder:b()}),j(g,m.workerPath),S(u,{readiness:m})}console.log(),console.log(c.green(`\u2713 Upgrade scheduled ${i} \u2192 ${n}`)),console.log(c.gray(" The independent updater will verify, roll back if needed, and restart Shennian."))}})}async function de(t,r,i,e={}){const l=s=>{t.sendEvent({type:"event",event:"upgrade",payload:s})},n=e.currentVersion??y();let o;try{l({state:"checking",machineId:"self"}),o=i??await k()}catch(s){const d=`Version check failed: ${s instanceof Error?s.message:String(s)}`;return e.suppressResponse||t.sendRes({type:"res",id:r,ok:!1,error:d}),{ok:!1,error:d}}const a=v(n,o);if(a==="none")return e.suppressResponse||t.sendRes({type:"res",id:r,ok:!0,payload:{alreadyLatest:!0,version:n}}),{ok:!0,alreadyLatest:!0,version:n};if(!Y(a,e.confirmedMajor===!0))return e.suppressResponse||t.sendRes({type:"res",id:r,ok:!1,error:"Major upgrades require explicit second confirmation"}),{ok:!1,error:"Major upgrades require explicit second confirmation"};const p=w();let m;try{m=F();try{N(q("runtime","updater"))}catch(s){throw m.release(),s}}catch(s){const d=s instanceof Error?s.message:String(s);return e.suppressResponse||t.sendRes({type:"res",id:r,ok:!1,error:d}),{ok:!1,error:d}}const u={from:n,to:o,jobId:p},g=async()=>{let s=!1;try{const d=U(),f={fromVersion:n,targetVersion:o,oldDaemonPid:process.pid,jobId:p,requireHandoffAck:!0},h=R(f);if(l({state:"downloading",machineId:"self",version:o,jobId:p}),await C(h,{fetchOwnedArtifact:T,sourceOrder:b()}),j(h,d.workerPath),S(f,{readiness:d}),await H(h),e.canHandoff&&!e.canHandoff())throw new Error("upgrade_waiting_for_idle");const E=e.prepareForUpgrade?.(p)??{activeRunCount:0};return s=!0,L(h),l({state:"restarting",machineId:"self",...u,activeRunCount:E.activeRunCount}),{ok:!0,...u,resultPath:h.resultPath}}catch(d){s&&e.cancelUpgradePreparation?.();const f=d instanceof Error?d.message:String(d);return f!=="upgrade_waiting_for_idle"&&O(o,f),l({state:"failed",machineId:"self",error:f,jobId:p}),{ok:!1,error:f}}finally{m.release()}};return e.suppressResponse||t.sendRes({type:"res",id:r,ok:!0,payload:u}),e.waitForCompletion?g():(g(),{ok:!0,...u})}async function le(t,r,i={}){const e=await M(i.currentVersion);t.sendRes({type:"res",id:r,ok:!0,payload:e.hasUpdate?{hasUpdate:!0,current:e.current,latest:e.latest,changeType:e.changeType}:{hasUpdate:!1,current:i.currentVersion??y()}})}export{de as handleUpgradeStart,le as handleUpgradeStatus,Y as hasMajorUpgradeConfirmation,ce as registerUpgradeCommand,ie as restartCurrentDaemonAfterUpgrade};
1
+ import c from"chalk";import{assertStandaloneUpgrade as V,DESKTOP_UPGRADE_REQUIRED as w,isDesktopDistribution as $}from"../runtime-distribution.js";import{execSync as A,spawn as D}from"node:child_process";import{randomUUID as k}from"node:crypto";import M from"node:os";import{createInterface as _}from"node:readline/promises";import{getCurrentVersion as y,fetchLatestVersion as v,compareVersions as U,checkForUpdate as F,acquireUpgradeLock as O,recordUpgradeFailure as H}from"../upgrade/engine.js";import{callDaemonIpc as L}from"../daemon-ipc/client.js";import{getDaemonStatus as G}from"./daemon.js";import{ensureUpdaterInstalled as R}from"../upgrade/updater-bootstrap.js";import{launchUpdater as S,createUpdaterJob as j,waitForUpdaterReady as N,confirmUpdaterHandoff as q}from"../upgrade/updater-launcher.js";import{prepareRecoveryServiceEntry as C}from"../upgrade/updater-service-entry.js";import{resolveShennianPath as Y}from"../config/index.js";import{assertUpdaterAvailable as z,prepareUpdaterCandidate as x}from"../upgrade/updater-worker.js";import{getUpgradeChannel as E,setUpgradeChannel as J}from"../upgrade/channel.js";import{fetchOwnedCliArtifact as T,getCliUpdateSourceOrder as b}from"../upgrade/distribution.js";const I=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];function K(t,r){return t!=="major"||r}function pe(t={}){const r=t.platform??M.platform(),i=t.kill??process.kill,e=t.pid??process.pid,l=t.env??process.env,n=t.spawn??D,o=t.execSync??A;if(r==="win32"){const a=l.ComSpec||"cmd.exe";try{n(a,["/d","/s","/c","timeout /t 2 /nobreak >nul & schtasks /Run /TN ShennianAgent >nul"],{detached:!0,stdio:"ignore",windowsHide:!0}).unref(),i(e,"SIGTERM");return}catch{try{o('schtasks /run /tn "ShennianAgent"',{stdio:"pipe",windowsHide:!0})}catch{}}}else try{n("sh",["-c","sleep 2; shennian start >/dev/null 2>&1"],{detached:!0,stdio:"ignore",env:l}).unref()}catch{}i(e,"SIGTERM")}function ue(t){t.command("upgrade").description("Upgrade shennian CLI to the latest version").option("--channel <channel>","Persist stable (npm latest) or preview (npm next); does not install").option("--check","Only check for updates, do not install").option("--to <version>","Upgrade to a specific version").option("--yes","Confirm a major version upgrade (required in non-interactive environments)").action(async r=>{if(V(),r.channel!==void 0){if(r.to||r.yes)throw new Error("--channel cannot be combined with --to or --yes");if(J(r.channel),console.log(`Upgrade channel: ${E()} (saved; no installation)`),!r.check)return}console.log(`Upgrade channel: ${E()}`);const i=y();console.log(c.gray(`Current version: ${i}`)),console.log();let e=0;const l=setInterval(()=>{process.stdout.write(`\r ${I[e%I.length]} Checking for updates...`),e++},80);let n;try{n=r.to??await v(),clearInterval(l),process.stdout.write("\r"+" ".repeat(40)+"\r")}catch(a){clearInterval(l),process.stdout.write("\r"+" ".repeat(40)+"\r"),console.error(c.red(`\u2717 Failed to check for updates: ${a instanceof Error?a.message:String(a)}`)),process.exit(1)}const o=U(i,n);if(o==="none"&&!r.to){console.log(c.green(`\u2713 Already up to date (${i})`));return}if(console.log(` Available: ${c.green(n)} ${o==="major"?c.red("(major)"):o==="minor"?c.yellow("(minor)"):c.green("(patch)")}`),console.log(),!r.check){if(o==="major"&&(console.log(c.yellow(" \u26A0 This is a major version update and may contain breaking changes.")),console.log(c.gray(" Please review the changelog before upgrading.")),console.log(),!r.yes)){(!process.stdin.isTTY||!process.stdout.isTTY)&&(console.error(c.red("\u2717 Major upgrades require explicit confirmation; rerun with --yes.")),process.exit(1));const a=_({input:process.stdin,output:process.stdout}),p=await a.question(` Type ${n} to confirm this major upgrade: `);if(a.close(),p.trim()!==n){console.log(c.gray(" Upgrade cancelled."));return}}try{await L({method:"upgrade.start",params:{targetVersion:n,confirmedMajor:o==="major"||r.yes===!0},timeoutMs:1e4})}catch(a){if(G({cleanupStale:!0}).running)throw a;const m=R(),u={fromVersion:i,targetVersion:n,oldDaemonPid:null,jobId:k()},g=j(u);await x(g,{fetchOwnedArtifact:T,sourceOrder:b()}),C(g,m.workerPath),S(u,{readiness:m})}console.log(),console.log(c.green(`\u2713 Upgrade scheduled ${i} \u2192 ${n}`)),console.log(c.gray(" The independent updater will verify, roll back if needed, and restart Shennian."))}})}async function fe(t,r,i,e={}){if($())return e.suppressResponse||t.sendRes({type:"res",id:r,ok:!1,error:w}),{ok:!1,error:w};const l=s=>{t.sendEvent({type:"event",event:"upgrade",payload:s})},n=e.currentVersion??y();let o;try{l({state:"checking",machineId:"self"}),o=i??await v()}catch(s){const d=`Version check failed: ${s instanceof Error?s.message:String(s)}`;return e.suppressResponse||t.sendRes({type:"res",id:r,ok:!1,error:d}),{ok:!1,error:d}}const a=U(n,o);if(a==="none")return e.suppressResponse||t.sendRes({type:"res",id:r,ok:!0,payload:{alreadyLatest:!0,version:n}}),{ok:!0,alreadyLatest:!0,version:n};if(!K(a,e.confirmedMajor===!0))return e.suppressResponse||t.sendRes({type:"res",id:r,ok:!1,error:"Major upgrades require explicit second confirmation"}),{ok:!1,error:"Major upgrades require explicit second confirmation"};const p=k();let m;try{m=O();try{z(Y("runtime","updater"))}catch(s){throw m.release(),s}}catch(s){const d=s instanceof Error?s.message:String(s);return e.suppressResponse||t.sendRes({type:"res",id:r,ok:!1,error:d}),{ok:!1,error:d}}const u={from:n,to:o,jobId:p},g=async()=>{let s=!1;try{const d=R(),f={fromVersion:n,targetVersion:o,oldDaemonPid:process.pid,jobId:p,requireHandoffAck:!0},h=j(f);if(l({state:"downloading",machineId:"self",version:o,jobId:p}),await x(h,{fetchOwnedArtifact:T,sourceOrder:b()}),C(h,d.workerPath),S(f,{readiness:d}),await N(h),e.canHandoff&&!e.canHandoff())throw new Error("upgrade_waiting_for_idle");const P=e.prepareForUpgrade?.(p)??{activeRunCount:0};return s=!0,q(h),l({state:"restarting",machineId:"self",...u,activeRunCount:P.activeRunCount}),{ok:!0,...u,resultPath:h.resultPath}}catch(d){s&&e.cancelUpgradePreparation?.();const f=d instanceof Error?d.message:String(d);return f!=="upgrade_waiting_for_idle"&&H(o,f),l({state:"failed",machineId:"self",error:f,jobId:p}),{ok:!1,error:f}}finally{m.release()}};return e.suppressResponse||t.sendRes({type:"res",id:r,ok:!0,payload:u}),e.waitForCompletion?g():(g(),{ok:!0,...u})}async function me(t,r,i={}){const e=await F(i.currentVersion);t.sendRes({type:"res",id:r,ok:!0,payload:e.hasUpdate?{hasUpdate:!0,current:e.current,latest:e.latest,changeType:e.changeType}:{hasUpdate:!1,current:i.currentVersion??y()}})}export{fe as handleUpgradeStart,me as handleUpgradeStatus,K as hasMajorUpgradeConfirmation,ue as registerUpgradeCommand,pe as restartCurrentDaemonAfterUpgrade};
@@ -0,0 +1,11 @@
1
+ import type { Command } from 'commander';
2
+ type WorkerCommandResponse = {
3
+ ok: boolean;
4
+ error?: string;
5
+ message?: string;
6
+ nextAction?: string;
7
+ [key: string]: unknown;
8
+ };
9
+ export declare function executeWorkerCommand(operation: 'start' | 'read' | 'send' | 'wait' | 'stop', body: Record<string, unknown>): Promise<WorkerCommandResponse>;
10
+ export declare function registerWorkerCommand(program: Command): void;
11
+ export {};
@@ -0,0 +1,2 @@
1
+ import c from"node:fs";import{resolveShennianPath as d}from"../config/index.js";function u(){const r=d("runtime","manager-ipc.json");let t;try{t=c.statSync(r)}catch{throw new Error("daemon_unavailable")}if(process.platform!=="win32"){const o=typeof process.getuid=="function"?process.getuid():t.uid;if(t.uid!==o||(t.mode&63)!==0)throw new Error("worker_runtime_permissions_invalid")}const e=JSON.parse(c.readFileSync(r,"utf-8")),n=typeof e.url=="string"?e.url:"",a=typeof e.token=="string"?e.token:"";if(!/^http:\/\/127\.0\.0\.1:\d+$/.test(n)||!a)throw new Error("daemon_unavailable");return{url:n,token:a}}function f(r){return r==="daemon_unavailable"?"Check shennian status --json and start the existing paired daemon if it was not intentionally paused.":r==="worker_runtime_permissions_invalid"?"Repair or update the existing Shennian installation; do not bypass the local runtime file permissions.":"Check the local Shennian daemon and retry the same command once."}async function i(r,t){try{const e=u(),n=r==="wait"?Math.max(2e3,Math.min(Number(t.timeoutMs||6e5)+5e3,605e3)):r==="start"||r==="send"?18e4:15e3,a=await fetch(`${e.url}/workers/${r}`,{method:"POST",headers:{authorization:`Bearer ${e.token}`,"content-type":"application/json"},body:JSON.stringify(t),signal:AbortSignal.timeout(n)}),o=await a.json().catch(()=>({ok:!1,error:"worker_invalid_response",message:a.statusText}));return a.ok&&o.ok?o:{ok:!1,error:o.error||"worker_command_failed",...o.message?{message:o.message}:{},nextAction:o.nextAction||"Check the selected Agent, working directory, account, and Worker reference."}}catch(e){const n=e instanceof Error&&e.message==="daemon_unavailable"||e instanceof Error&&e.message==="worker_runtime_permissions_invalid"?e.message:e instanceof Error&&e.name==="TimeoutError"?"ipc_timeout":"worker_command_failed";return{ok:!1,error:n,message:e instanceof Error?e.message:String(e),nextAction:f(n)}}}function m(r){return r.messageFile?c.readFileSync(r.messageFile,"utf8"):r.message??""}function s(r){process.stdout.write(`${JSON.stringify(r)}
2
+ `),r.ok||(process.exitCode=1)}function k(r){const t=r.command("worker").description("Start and control an external temporary Worker by opaque reference");t.command("start").description("Start a new Worker; returns the only reference that can control it").requiredOption("--agent <agent>","Agent type returned by shennian agent discover").option("--workdir <path>","Working directory; defaults to the current directory").option("--title <title>","Short Worker title").option("--model <model>","Model id returned by discovery").option("--message <text>","Worker task").option("--message-file <path>","Read the Worker task from a file").option("--json","Print JSON (default)").action(async e=>s(await i("start",{agentType:e.agent,workDir:e.workdir??process.cwd(),title:e.title,modelId:e.model,message:m(e)}))),t.command("read").description("Read a Worker transcript using the reference returned by worker start").requiredOption("--ref <worker-ref>","Opaque Worker reference").option("--limit <n>","Message limit","200").option("--json","Print JSON (default)").action(async e=>s(await i("read",{workerRef:e.ref,limit:Number(e.limit)}))),t.command("send").description("Queue another message for a Worker").requiredOption("--ref <worker-ref>","Opaque Worker reference").option("--message <text>","Message text").option("--message-file <path>","Read the message from a file").option("--json","Print JSON (default)").action(async e=>s(await i("send",{workerRef:e.ref,message:m(e)}))),t.command("wait").description("Wait once for Worker progress or a terminal event").requiredOption("--ref <worker-ref>","Opaque Worker reference").option("--timeout <seconds>","Maximum wait in seconds (1-600)","600").option("--json","Print JSON (default)").action(async e=>{const n=Math.max(1,Math.min(Number(e.timeout)||600,600));s(await i("wait",{workerRef:e.ref,timeoutMs:n*1e3}))}),t.command("stop").description("Stop a Worker using its opaque reference").requiredOption("--ref <worker-ref>","Opaque Worker reference").option("--json","Print JSON (default)").action(async e=>s(await i("stop",{workerRef:e.ref})))}export{i as executeWorkerCommand,k as registerWorkerCommand};
@@ -0,0 +1,14 @@
1
+ import { type AgentInfo } from '@shennian/wire';
2
+ import type { DaemonStatus } from '../commands/daemon.js';
3
+ import type { AgentWorkspaceValidationInput, AgentWorkspaceValidationResult } from '../session/handlers/agent-workspace-validation.js';
4
+ import type { DaemonIpcHandler } from './server.js';
5
+ type DiscoveredAi = AgentInfo & {
6
+ runtimePlatform?: string;
7
+ runtimeIsWsl?: boolean;
8
+ };
9
+ export declare function createAgentReadinessHandlers(input: {
10
+ getStatus: () => DaemonStatus;
11
+ discover: () => Promise<DiscoveredAi[]>;
12
+ validate: (params: AgentWorkspaceValidationInput) => Promise<AgentWorkspaceValidationResult>;
13
+ }): Record<string, DaemonIpcHandler>;
14
+ export {};
@@ -0,0 +1 @@
1
+ import{isAvailableAgentType as a}from"@shennian/wire";import{DaemonIpcError as n}from"./protocol.js";function c(i){let s=!1;const l=()=>{const e=i.getStatus();if(e.remoteAccessDisabled)throw new n("daemon_paused","Shennian is paused; resume it only when requested.");if(!e.running)throw new n("daemon_unavailable","Shennian is not running.")};return{"agent.discover":async e=>{l();const r=e&&typeof e=="object"&&!Array.isArray(e)&&typeof e.agentType=="string"?e.agentType:null,u=!!(e&&typeof e=="object"&&!Array.isArray(e)&&e.compact===!0),d=(await i.discover()).filter(t=>!r||t.type===r);return{modelTested:!1,compact:u,agents:d.map(t=>({type:t.type,name:t.display?.name??t.type,authState:t.authState??"unknown",...t.runtime?.cliVersion?{version:t.runtime.cliVersion}:{},...t.runtime?.compatibility?{compatibility:t.runtime.compatibility}:{},...t.runtimePlatform?{runtimePlatform:t.runtimePlatform}:{},...t.runtimeIsWsl!==void 0?{runtimeIsWsl:t.runtimeIsWsl}:{},supportsStandardSession:t.supportsStandardSession===!0,supportsManagedAgent:t.supportsManagedAgent===!0,supportsManagedNativeFull:t.supportsManagedNativeFull===!0,supportsRoomTools:t.supportsRoomTools===!0,supportsWorker:t.supportsWorker===!0,supportsManagerMode:t.supportsManagerMode===!0,supportsExternalWorkerTarget:t.supportsExternalWorkerTarget===!0,...u?{modelCount:t.models.length,defaultModelId:t.models.find(o=>o.isDefault)?.id??null}:{models:t.models.map(o=>({id:o.id,name:o.name,isDefault:o.isDefault===!0}))}}))}},"agent.check":async e=>{if(l(),!e||typeof e!="object"||Array.isArray(e)||typeof e.agentType!="string"||!a(e.agentType)||typeof e.workDir!="string"||!e.workDir.trim()||e.modelId!=null&&typeof e.modelId!="string")throw new n("agent_validation_invalid_configuration","Select an AI and its absolute working directory.");if(s)throw new n("agent_check_in_progress","An AI reply check is already running.");s=!0;try{const r=await i.validate({runtimeAdapter:e.agentType,workDir:e.workDir,modelId:e.modelId??null,sessionMode:"standard"});if(!r.ok)throw new n(r.errorCode,r.errorCode);return{ready:!0,agentType:e.agentType,modelId:e.modelId??null,scope:"hello_reply"}}finally{s=!1}}}}export{c as createAgentReadinessHandlers};
@@ -2,7 +2,7 @@ import type { DetectedAgent } from '../agents/detect.js';
2
2
  import type { DaemonStatus } from '../commands/daemon.js';
3
3
  import type { JsonValue } from './json.js';
4
4
  import type { DaemonIpcHandler } from './server.js';
5
- export declare const DAEMON_CONTROL_CAPABILITIES: readonly ["protocol.info", "daemon.status", "agent.list", "agent.auth.completed", "log.read", "upgrade.start", "upgrade.prepare-external", "upgrade.cancel-external"];
5
+ export declare const DAEMON_CONTROL_CAPABILITIES: readonly ["protocol.info", "daemon.status", "agent.list", "agent.discover", "agent.check", "agent.auth.completed", "log.read", "upgrade.start", "upgrade.prepare-external", "upgrade.cancel-external"];
6
6
  export declare function createDaemonControlHandlers(input: {
7
7
  daemonVersion: string;
8
8
  protocolVersion: number;
@@ -1 +1 @@
1
- import o from"node:fs";import{getAcpLocalMetrics as d}from"../agents/acp/metrics.js";const p=["protocol.info","daemon.status","agent.list","agent.auth.completed","log.read","upgrade.start","upgrade.prepare-external","upgrade.cancel-external"],g=32*1024,m=64*1024;function _(t){return{"protocol.info":()=>{const e=t.getStatus();return{daemonVersion:t.daemonVersion,protocolVersion:t.protocolVersion,launcher:e.launcher,capabilities:[...p]}},"daemon.status":()=>t.getStatus(),"agent.list":()=>({agents:t.getAgents().map(e=>({type:e.type,...e.version?{version:e.version}:{},...e.runtimePlatform?{runtimePlatform:e.runtimePlatform}:{},...e.runtimeIsWsl!==void 0?{runtimeIsWsl:e.runtimeIsWsl}:{}})),acpMetrics:d()}),"agent.auth.completed":async e=>{const a=e&&typeof e=="object"&&!Array.isArray(e)&&typeof e.agentType=="string"?e.agentType:"";if(!/^acp:[a-z0-9][a-z0-9._-]{0,63}$/.test(a))throw new Error("invalid_acp_agent");return{resumed:await t.completeAgentAuthentication?.(a)??!1}},"log.read":e=>y(t.getStatus().logFile,e),"upgrade.start":async e=>{if(!t.startUpgrade)throw new Error("upgrade_unavailable");return t.startUpgrade(e)},"upgrade.prepare-external":async e=>{if(!t.prepareExternalUpgrade)throw new Error("external_upgrade_unavailable");return t.prepareExternalUpgrade(e)},"upgrade.cancel-external":async()=>{if(!t.cancelExternalUpgrade)throw new Error("external_upgrade_unavailable");return t.cancelExternalUpgrade()}}}function y(t,e){const a=e&&typeof e=="object"&&!Array.isArray(e)?e:{},i=typeof a.limitBytes=="number"&&Number.isFinite(a.limitBytes)?a.limitBytes:g,u=Math.max(1,Math.min(Math.floor(i),m));let s=0;try{s=o.statSync(t).size}catch{return{text:"",start:0,end:0,nextBefore:null,eof:!0}}const f=typeof a.before=="number"&&Number.isFinite(a.before)?Math.floor(a.before):s,n=Math.max(0,Math.min(f,s)),r=Math.max(0,n-u);if(n===r)return{text:"",start:r,end:n,nextBefore:r>0?r:null,eof:r===0};const l=Buffer.allocUnsafe(n-r),c=o.openSync(t,"r");try{o.readSync(c,l,0,l.length,r)}finally{o.closeSync(c)}return{text:l.toString("utf8"),start:r,end:n,nextBefore:r>0?r:null,eof:r===0}}export{p as DAEMON_CONTROL_CAPABILITIES,_ as createDaemonControlHandlers,y as readDaemonLogPage};
1
+ import o from"node:fs";import{getAcpLocalMetrics as d}from"../agents/acp/metrics.js";const g=["protocol.info","daemon.status","agent.list","agent.discover","agent.check","agent.auth.completed","log.read","upgrade.start","upgrade.prepare-external","upgrade.cancel-external"],p=32*1024,m=64*1024;function _(t){return{"protocol.info":()=>{const e=t.getStatus();return{daemonVersion:t.daemonVersion,protocolVersion:t.protocolVersion,launcher:e.launcher,capabilities:[...g]}},"daemon.status":()=>t.getStatus(),"agent.list":()=>({agents:t.getAgents().map(e=>({type:e.type,...e.version?{version:e.version}:{},...e.runtimePlatform?{runtimePlatform:e.runtimePlatform}:{},...e.runtimeIsWsl!==void 0?{runtimeIsWsl:e.runtimeIsWsl}:{}})),acpMetrics:d()}),"agent.auth.completed":async e=>{const a=e&&typeof e=="object"&&!Array.isArray(e)&&typeof e.agentType=="string"?e.agentType:"";if(!/^acp:[a-z0-9][a-z0-9._-]{0,63}$/.test(a))throw new Error("invalid_acp_agent");return{resumed:await t.completeAgentAuthentication?.(a)??!1}},"log.read":e=>y(t.getStatus().logFile,e),"upgrade.start":async e=>{if(!t.startUpgrade)throw new Error("upgrade_unavailable");return t.startUpgrade(e)},"upgrade.prepare-external":async e=>{if(!t.prepareExternalUpgrade)throw new Error("external_upgrade_unavailable");return t.prepareExternalUpgrade(e)},"upgrade.cancel-external":async()=>{if(!t.cancelExternalUpgrade)throw new Error("external_upgrade_unavailable");return t.cancelExternalUpgrade()}}}function y(t,e){const a=e&&typeof e=="object"&&!Array.isArray(e)?e:{},i=typeof a.limitBytes=="number"&&Number.isFinite(a.limitBytes)?a.limitBytes:p,u=Math.max(1,Math.min(Math.floor(i),m));let s=0;try{s=o.statSync(t).size}catch{return{text:"",start:0,end:0,nextBefore:null,eof:!0}}const f=typeof a.before=="number"&&Number.isFinite(a.before)?Math.floor(a.before):s,n=Math.max(0,Math.min(f,s)),r=Math.max(0,n-u);if(n===r)return{text:"",start:r,end:n,nextBefore:r>0?r:null,eof:r===0};const l=Buffer.allocUnsafe(n-r),c=o.openSync(t,"r");try{o.readSync(c,l,0,l.length,r)}finally{o.closeSync(c)}return{text:l.toString("utf8"),start:r,end:n,nextBefore:r>0?r:null,eof:r===0}}export{g as DAEMON_CONTROL_CAPABILITIES,_ as createDaemonControlHandlers,y as readDaemonLogPage};