shennian 0.3.41 → 0.3.43

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 (75) hide show
  1. package/dist/publish-build-manifest.json +93 -43
  2. package/dist/src/agents/adapter.d.ts +1 -0
  3. package/dist/src/commands/daemon.d.ts +6 -0
  4. package/dist/src/commands/daemon.js +9 -9
  5. package/dist/src/index.js +3 -3
  6. package/dist/src/native-fusion/codex-parser.d.ts +1 -0
  7. package/dist/src/native-fusion/codex-parser.js +4 -4
  8. package/dist/src/native-fusion/file-watcher.d.ts +18 -0
  9. package/dist/src/native-fusion/file-watcher.js +1 -0
  10. package/dist/src/native-fusion/local-repository.d.ts +52 -0
  11. package/dist/src/native-fusion/local-repository.js +2 -0
  12. package/dist/src/native-fusion/opencode-parser.js +2 -2
  13. package/dist/src/native-fusion/parser-common.d.ts +7 -1
  14. package/dist/src/native-fusion/parser-common.js +4 -4
  15. package/dist/src/native-fusion/parsers.d.ts +2 -1
  16. package/dist/src/native-fusion/parsers.js +2 -2
  17. package/dist/src/native-fusion/service.d.ts +10 -2
  18. package/dist/src/native-fusion/service.js +2 -2
  19. package/dist/src/native-fusion/session-header.d.ts +9 -0
  20. package/dist/src/native-fusion/session-header.js +2 -0
  21. package/dist/src/native-fusion/state.d.ts +1 -1
  22. package/dist/src/native-fusion/state.js +2 -1
  23. package/dist/src/native-fusion/types.d.ts +29 -5
  24. package/dist/src/native-fusion/types.js +1 -1
  25. package/dist/src/personal-sync/paths.d.ts +2 -0
  26. package/dist/src/personal-sync/paths.js +1 -0
  27. package/dist/src/relay/client.d.ts +4 -1
  28. package/dist/src/relay/client.js +1 -1
  29. package/dist/src/session/attachment-reference.d.ts +2 -0
  30. package/dist/src/session/attachment-reference.js +1 -0
  31. package/dist/src/session/attachment-transfer-store.d.ts +38 -0
  32. package/dist/src/session/attachment-transfer-store.js +2 -0
  33. package/dist/src/session/canonical-store.d.ts +101 -0
  34. package/dist/src/session/canonical-store.js +5 -0
  35. package/dist/src/session/handlers/chat.js +2 -2
  36. package/dist/src/session/handlers/fs.d.ts +0 -1
  37. package/dist/src/session/handlers/fs.js +1 -1
  38. package/dist/src/session/handlers/message-status.d.ts +3 -0
  39. package/dist/src/session/handlers/message-status.js +1 -0
  40. package/dist/src/session/handlers/title.js +1 -1
  41. package/dist/src/session/handlers/tool-detail.js +1 -1
  42. package/dist/src/session/handlers/voice-window.d.ts +3 -0
  43. package/dist/src/session/handlers/voice-window.js +1 -0
  44. package/dist/src/session/history-subscriptions.d.ts +17 -0
  45. package/dist/src/session/history-subscriptions.js +1 -0
  46. package/dist/src/session/index-publisher.d.ts +3 -0
  47. package/dist/src/session/index-publisher.js +1 -0
  48. package/dist/src/session/manager.d.ts +2 -0
  49. package/dist/src/session/manager.js +1 -1
  50. package/dist/src/session/queue.d.ts +1 -1
  51. package/dist/src/session/queue.js +4 -3
  52. package/dist/src/session/store.d.ts +33 -3
  53. package/dist/src/session/store.js +2 -3
  54. package/dist/src/session/tool-detail-store.d.ts +16 -0
  55. package/dist/src/session/tool-detail-store.js +2 -0
  56. package/dist/src/session/types.d.ts +3 -13
  57. package/node_modules/@shennian/wire/dist/events.d.ts +2 -0
  58. package/node_modules/@shennian/wire/dist/fs.d.ts +45 -0
  59. package/node_modules/@shennian/wire/dist/fs.js +7 -1
  60. package/node_modules/@shennian/wire/dist/index.d.ts +1 -0
  61. package/node_modules/@shennian/wire/dist/index.js +1 -0
  62. package/node_modules/@shennian/wire/dist/machine.d.ts +6 -0
  63. package/node_modules/@shennian/wire/dist/message-payload.d.ts +2 -0
  64. package/node_modules/@shennian/wire/dist/message-payload.js +15 -1
  65. package/node_modules/@shennian/wire/dist/methods.d.ts +1 -1
  66. package/node_modules/@shennian/wire/dist/session-sync.d.ts +197 -0
  67. package/node_modules/@shennian/wire/dist/session-sync.js +49 -0
  68. package/node_modules/@shennian/wire/dist/session.d.ts +24 -13
  69. package/node_modules/@shennian/wire/dist/tool-detail.d.ts +18 -11
  70. package/node_modules/@shennian/wire/dist/tool-detail.js +7 -1
  71. package/package.json +1 -1
  72. package/dist/src/session/remote-attachments.d.ts +0 -15
  73. package/dist/src/session/remote-attachments.js +0 -1
  74. package/dist/src/session/tool-summary.d.ts +0 -1
  75. package/dist/src/session/tool-summary.js +0 -1
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "jsMinified": true,
4
4
  "minifier": "esbuild",
5
- "fileCount": 111,
5
+ "fileCount": 121,
6
6
  "files": [
7
7
  {
8
8
  "file": "bin/shennian.js",
@@ -156,8 +156,8 @@
156
156
  },
157
157
  {
158
158
  "file": "src/commands/daemon.js",
159
- "beforeBytes": 38631,
160
- "afterBytes": 18928
159
+ "beforeBytes": 39808,
160
+ "afterBytes": 19439
161
161
  },
162
162
  {
163
163
  "file": "src/commands/integration.js",
@@ -276,8 +276,8 @@
276
276
  },
277
277
  {
278
278
  "file": "src/index.js",
279
- "beforeBytes": 21040,
280
- "afterBytes": 11051
279
+ "beforeBytes": 21396,
280
+ "afterBytes": 11301
281
281
  },
282
282
  {
283
283
  "file": "src/log-reporter.js",
@@ -306,43 +306,63 @@
306
306
  },
307
307
  {
308
308
  "file": "src/native-fusion/codex-parser.js",
309
- "beforeBytes": 27675,
310
- "afterBytes": 11480
309
+ "beforeBytes": 28990,
310
+ "afterBytes": 12133
311
311
  },
312
312
  {
313
313
  "file": "src/native-fusion/config.js",
314
314
  "beforeBytes": 225,
315
315
  "afterBytes": 109
316
316
  },
317
+ {
318
+ "file": "src/native-fusion/file-watcher.js",
319
+ "beforeBytes": 2954,
320
+ "afterBytes": 1278
321
+ },
322
+ {
323
+ "file": "src/native-fusion/local-repository.js",
324
+ "beforeBytes": 11197,
325
+ "afterBytes": 5755
326
+ },
317
327
  {
318
328
  "file": "src/native-fusion/opencode-parser.js",
319
- "beforeBytes": 4849,
320
- "afterBytes": 2575
329
+ "beforeBytes": 5208,
330
+ "afterBytes": 2743
321
331
  },
322
332
  {
323
333
  "file": "src/native-fusion/parser-common.js",
324
- "beforeBytes": 7754,
325
- "afterBytes": 3606
334
+ "beforeBytes": 7739,
335
+ "afterBytes": 3552
326
336
  },
327
337
  {
328
338
  "file": "src/native-fusion/parsers.js",
329
- "beforeBytes": 5325,
330
- "afterBytes": 2730
339
+ "beforeBytes": 5543,
340
+ "afterBytes": 3005
331
341
  },
332
342
  {
333
343
  "file": "src/native-fusion/service.js",
334
- "beforeBytes": 12900,
335
- "afterBytes": 6147
344
+ "beforeBytes": 20039,
345
+ "afterBytes": 9441
346
+ },
347
+ {
348
+ "file": "src/native-fusion/session-header.js",
349
+ "beforeBytes": 3498,
350
+ "afterBytes": 1657
336
351
  },
337
352
  {
338
353
  "file": "src/native-fusion/state.js",
339
- "beforeBytes": 808,
340
- "afterBytes": 467
354
+ "beforeBytes": 3258,
355
+ "afterBytes": 1598
341
356
  },
342
357
  {
343
358
  "file": "src/native-fusion/types.js",
344
- "beforeBytes": 11,
345
- "afterBytes": 1
359
+ "beforeBytes": 54,
360
+ "afterBytes": 60
361
+ },
362
+ {
363
+ "file": "src/personal-sync/paths.js",
364
+ "beforeBytes": 364,
365
+ "afterBytes": 223
346
366
  },
347
367
  {
348
368
  "file": "src/region.js",
@@ -351,8 +371,8 @@
351
371
  },
352
372
  {
353
373
  "file": "src/relay/client.js",
354
- "beforeBytes": 11985,
355
- "afterBytes": 5934
374
+ "beforeBytes": 12612,
375
+ "afterBytes": 6310
356
376
  },
357
377
  {
358
378
  "file": "src/room/activation-audit.js",
@@ -454,6 +474,21 @@
454
474
  "beforeBytes": 8037,
455
475
  "afterBytes": 3438
456
476
  },
477
+ {
478
+ "file": "src/session/attachment-reference.js",
479
+ "beforeBytes": 1717,
480
+ "afterBytes": 914
481
+ },
482
+ {
483
+ "file": "src/session/attachment-transfer-store.js",
484
+ "beforeBytes": 18717,
485
+ "afterBytes": 9372
486
+ },
487
+ {
488
+ "file": "src/session/canonical-store.js",
489
+ "beforeBytes": 28665,
490
+ "afterBytes": 14062
491
+ },
457
492
  {
458
493
  "file": "src/session/handlers/agent-capabilities.js",
459
494
  "beforeBytes": 767,
@@ -471,8 +506,8 @@
471
506
  },
472
507
  {
473
508
  "file": "src/session/handlers/chat.js",
474
- "beforeBytes": 31311,
475
- "afterBytes": 13268
509
+ "beforeBytes": 33758,
510
+ "afterBytes": 14377
476
511
  },
477
512
  {
478
513
  "file": "src/session/handlers/control.js",
@@ -481,8 +516,13 @@
481
516
  },
482
517
  {
483
518
  "file": "src/session/handlers/fs.js",
484
- "beforeBytes": 29785,
485
- "afterBytes": 13579
519
+ "beforeBytes": 20840,
520
+ "afterBytes": 10026
521
+ },
522
+ {
523
+ "file": "src/session/handlers/message-status.js",
524
+ "beforeBytes": 561,
525
+ "afterBytes": 313
486
526
  },
487
527
  {
488
528
  "file": "src/session/handlers/room-managed.js",
@@ -496,18 +536,33 @@
496
536
  },
497
537
  {
498
538
  "file": "src/session/handlers/title.js",
499
- "beforeBytes": 2225,
500
- "afterBytes": 1113
539
+ "beforeBytes": 3013,
540
+ "afterBytes": 1583
501
541
  },
502
542
  {
503
543
  "file": "src/session/handlers/tool-detail.js",
504
- "beforeBytes": 10763,
505
- "afterBytes": 4812
544
+ "beforeBytes": 6010,
545
+ "afterBytes": 3159
546
+ },
547
+ {
548
+ "file": "src/session/handlers/voice-window.js",
549
+ "beforeBytes": 2324,
550
+ "afterBytes": 1105
551
+ },
552
+ {
553
+ "file": "src/session/history-subscriptions.js",
554
+ "beforeBytes": 6272,
555
+ "afterBytes": 3181
556
+ },
557
+ {
558
+ "file": "src/session/index-publisher.js",
559
+ "beforeBytes": 1469,
560
+ "afterBytes": 731
506
561
  },
507
562
  {
508
563
  "file": "src/session/manager.js",
509
- "beforeBytes": 18103,
510
- "afterBytes": 9204
564
+ "beforeBytes": 20168,
565
+ "afterBytes": 10430
511
566
  },
512
567
  {
513
568
  "file": "src/session/projection.js",
@@ -516,23 +571,18 @@
516
571
  },
517
572
  {
518
573
  "file": "src/session/queue.js",
519
- "beforeBytes": 15015,
520
- "afterBytes": 7165
521
- },
522
- {
523
- "file": "src/session/remote-attachments.js",
524
- "beforeBytes": 2999,
525
- "afterBytes": 1492
574
+ "beforeBytes": 17298,
575
+ "afterBytes": 8175
526
576
  },
527
577
  {
528
578
  "file": "src/session/store.js",
529
- "beforeBytes": 4333,
530
- "afterBytes": 2212
579
+ "beforeBytes": 13169,
580
+ "afterBytes": 7349
531
581
  },
532
582
  {
533
- "file": "src/session/tool-summary.js",
534
- "beforeBytes": 1401,
535
- "afterBytes": 731
583
+ "file": "src/session/tool-detail-store.js",
584
+ "beforeBytes": 2690,
585
+ "afterBytes": 1307
536
586
  },
537
587
  {
538
588
  "file": "src/session/types.js",
@@ -10,6 +10,7 @@ export type AgentEvent = {
10
10
  name?: string;
11
11
  args?: Record<string, unknown>;
12
12
  result?: string;
13
+ durationMs?: number;
13
14
  message?: string;
14
15
  approval?: {
15
16
  title?: string;
@@ -74,6 +74,12 @@ export declare function startDaemonProcess(opts?: {
74
74
  export declare function shouldReplaceRunningDaemon(status: Pick<DaemonStatus, 'pid' | 'running' | 'adopted'>, record?: {
75
75
  version?: string;
76
76
  } | null, currentVersion?: string): boolean;
77
+ export declare function isAutoStartServicePresent(platform?: Platform): boolean;
78
+ export declare function canReuseRunningDaemonForStart(status: Pick<DaemonStatus, 'pid' | 'running' | 'stale' | 'remoteAccessDisabled' | 'adopted'>, opts: {
79
+ api?: string;
80
+ }, servicePresent: boolean, record?: {
81
+ version?: string;
82
+ } | null, currentVersion?: string): boolean;
77
83
  /**
78
84
  * Install the platform-native auto-start service.
79
85
  * Returns true if the service was immediately started (so caller can skip startDaemonProcess).
@@ -1,13 +1,13 @@
1
- import a from"chalk";import o from"node:fs";import s from"node:path";import u from"node:os";import{execSync as c,spawn as ue}from"node:child_process";import{randomUUID as de}from"node:crypto";import{fileURLToPath as le}from"node:url";import{getShennianDir as pe,loadConfig as U,resolveShennianPath as h,saveConfig as me}from"../config/index.js";import{buildAugmentedPath as fe}from"../env-path.js";import{buildWindowsLauncherCommand as Se,buildWindowsScheduledTaskCommands as he,buildWindowsScheduledTaskXml as ge,buildWindowsStartupVbs as we,escapeXml as N,isWindowsCmdScript as ye,quoteCmdArg as j,quoteSystemdArg as ve,suspendWindowsScheduledTask as Ee}from"./daemon-windows.js";import{waitForDaemonReadyStatus as b}from"./daemon-start-wait.js";import{findRunningWindowsDaemonProcessIds as Pe,isShennianRunServiceCommand as Ie}from"./daemon-process.js";import{getCurrentVersion as ke}from"../upgrade/engine.js";import{buildWindowsLauncherCommand as En,buildWindowsScheduledTaskCommands as Pn,buildWindowsScheduledTaskSuspendCommands as In,buildWindowsScheduledTaskXml as kn,buildWindowsStartupVbs as An,suspendWindowsScheduledTask as Dn}from"./daemon-windows.js";import{isShennianRunServiceCommand as bn}from"./daemon-process.js";const Ae=s.dirname(le(import.meta.url)),f=pe(),g=h("daemon.pid"),d=h("daemon.log"),R=h("remote-access.disabled"),k=h("daemon-launcher.json"),W=s.resolve(Ae,"../../bin/shennian.js"),F=process.execPath,B=new Set(["PATH","HOME","USERPROFILE","HOMEDRIVE","HOMEPATH","USERNAME","USERDOMAIN","COMPUTERNAME","USER","LOGNAME","SHELL","TMPDIR","LANG","LC_ALL","LC_CTYPE","SSH_AUTH_SOCK","XDG_CONFIG_HOME","XDG_DATA_HOME","TEMP","TMP","APPDATA","LOCALAPPDATA","SHENNIAN_DESKTOP_SERVER_URL","SHENNIAN_WECHAT_CHANNEL_HELPER_DIR","SHENNIAN_HOME","SHENNIAN_NATIVE_FUSION_DISABLED","SHENNIAN_WECHAT_DELIVERABILITY_TRACE"]);function De(e){return B.has(e)}function l(){const e=u.platform();return e==="darwin"||e==="linux"||e==="win32"?e:"linux"}function T(e){const n=e.replace(/\\/g,"/").toLowerCase(),t=u.tmpdir().replace(/\\/g,"/").toLowerCase();return n.includes("/_npx/")||n.includes("/npm-cache/_npx/")||n.includes("/pnpm/dlx/")||n.startsWith(t.endsWith("/")?t:`${t}/`)}function Ne(e){return o.existsSync(e.scriptPath)&&!T(e.scriptPath)?{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}:e.shennianCommandPath&&!T(e.shennianCommandPath)?{command:e.shennianCommandPath,args:["run-service"],mode:"global-shim"}:e.npxPath?{command:e.npxPath,args:["--yes","shennian","run-service"],mode:"npx"}:{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}}function be(e){const n=e.trim();if(!n)return null;if(n.startsWith("{"))try{const r=JSON.parse(n),i=Number(r.pid);return!Number.isInteger(i)||i<=0?null:{pid:i,...typeof r.instanceId=="string"?{instanceId:r.instanceId}:{},...typeof r.version=="string"?{version:r.version}:{},...r.launcher==="desktop-managed"||r.launcher==="global-cli"||r.launcher==="unknown"?{launcher:r.launcher}:{},...typeof r.startedAt=="string"?{startedAt:r.startedAt}:{},...r.adopted===!0?{adopted:!0}:{}}}catch{return null}const t=parseInt(n,10);return isNaN(t)?null:{pid:t}}function A(){try{return be(o.readFileSync(g,"utf-8"))}catch{return null}}function v(e){try{return process.kill(e,0),!0}catch{return!1}}function V(e,n=3e3){const t=Date.now();for(;Date.now()-t<n;){if(!v(e))return!0;Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,100)}return!v(e)}function Re(){return o.existsSync(R)}function Te(){try{o.unlinkSync(R)}catch{}}function J(){return"global-cli"}function Le(){return`${process.pid}-${Date.now()}-${de()}`}function Oe(e,n,t={}){o.mkdirSync(f,{recursive:!0}),o.writeFileSync(g,JSON.stringify({pid:e,instanceId:n,...t.version?{version:t.version}:{},launcher:t.launcher??J(),...t.adopted?{adopted:!0}:{},startedAt:new Date().toISOString()},null,2))}function gn(e,n){const t=A();if(!(t?.pid!==e||t.instanceId!==n))try{o.unlinkSync(g)}catch{}}function G(e,n=J(),t){o.mkdirSync(f,{recursive:!0}),o.writeFileSync(k,JSON.stringify({pid:e,launcher:n,...t?{instanceId:t}:{},updatedAt:new Date().toISOString()},null,2))}function xe(e){if(e)try{if(JSON.parse(o.readFileSync(k,"utf-8")).instanceId!==e)return}catch{return}try{o.unlinkSync(k)}catch{}}function He(e){e&&G(e)}function _e(e,n){if(!e||!n)return"unknown";try{const t=JSON.parse(o.readFileSync(k,"utf-8"));if(t.pid!==e)return"unknown";if(t.launcher==="desktop-managed"||t.launcher==="global-cli")return t.launcher}catch{}return L(e)}function L(e){if(l()==="win32")return"unknown";try{const n=c(`ps -p ${e} -o command=`,{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).replace(/\\/g,"/");if(n.includes("/node_modules/shennian/")||n.includes("/bin/shennian")||n.includes(" shennian run-service")||n.includes(" shennian.js run-service"))return"global-cli"}catch{}return"unknown"}function K(e=process.pid){if(l()==="win32")return Pe(e);try{return c("ps -axo pid=,command=",{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).split(/\r?\n/).map(t=>{const r=t.trim().match(/^(\d+)\s+(.+)$/);if(!r)return null;const i=Number(r[1]),m=r[2];return!Number.isInteger(i)||i===e?null:Ie(m)?i:null}).filter(t=>typeof t=="number")}catch{return[]}}function $e(e=process.env,n=f,t=u.tmpdir(),r=s.join(u.homedir(),".shennian")){if(e.SHENNIAN_E2E_ISOLATED_DAEMON!=="1"||!e.SHENNIAN_HOME?.trim()||s.resolve(n)===s.resolve(r))return!1;const i=s.relative(s.resolve(t),s.resolve(n));return i!==""&&i!==".."&&!i.startsWith(`..${s.sep}`)&&!s.isAbsolute(i)||s.resolve(n)===s.resolve(e.SHENNIAN_HOME)}function Ce(e=process.env,n=f){return!$e(e,n)}function S(e={}){let n=A(),t=n?.pid??null,r=t!==null&&v(t),i=t!==null&&!r,m=n?.adopted===!0;const y=U();if(i&&e.cleanupStale){try{o.unlinkSync(g)}catch{}n=null,t=null,r=!1,i=!1}if(!r&&Ce()){const w=K()[0];if(w&&(t=w,r=!0,i=!1,m=!0,e.cleanupStale)){const M=Le();Oe(w,M,{launcher:L(w),adopted:!0}),G(w,L(w)),n={pid:w,instanceId:M,adopted:!0}}}return{running:r,pid:t,stale:i,remoteAccessDisabled:Re(),launcher:_e(t,r),platform:l(),shennianDir:f,pidFile:g,logFile:d,...n?.instanceId?{instanceId:n.instanceId}:{},...m?{adopted:!0}:{},...y.machineId?{machineId:y.machineId}:{},paired:!!(y.machineToken&&y.machineId),...y.serverUrl?{serverUrl:y.serverUrl}:{}}}function E(e){process.stdout.write(`${JSON.stringify(e,null,2)}
2
- `)}function X(e){const n=e?.trim();if(!n)return;const t=U();t.serverUrl=n,me(t)}function q(e){return e?.trim()||process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim()||void 0}const O="com.shennian.agent",p=s.join(u.homedir(),"Library/LaunchAgents",`${O}.plist`),Me=s.join(u.homedir(),"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),x=h("autostart.cmd"),H=h("autostart.vbs"),_=h("autostart.xml"),Ue=s.join(Me,"ShennianAgent.vbs");function Y(e){const n=l()==="win32"?`where ${e}`:`command -v ${e}`;try{return c(n,{stdio:["ignore","pipe","pipe"],encoding:"utf-8",windowsHide:!0}).split(/\r?\n/).map(i=>i.trim()).find(Boolean)??null}catch{return null}}function je(){const e=l()==="win32"?"npx.cmd":"npx",n=s.join(s.dirname(F),e);return o.existsSync(n)?n:Y("npx")}function P(){return Ne({nodeExec:F,scriptPath:W,shennianCommandPath:Y("shennian"),npxPath:je()})}function z(){try{c(`schtasks /delete /tn "${I}" /f`,{stdio:"pipe",windowsHide:!0})}catch{}}function Q(){try{o.unlinkSync(Ue)}catch{}}function We(){const e=process.env.USERDOMAIN?.trim(),n=process.env.COMPUTERNAME?.trim(),t=process.env.USERNAME?.trim()||u.userInfo().username;return e&&e.toUpperCase()!=="WORKGROUP"?`${e}\\${t}`:n?`${n}\\${t}`:t}function Fe(e){if(l()==="win32"&&ye(e.command)){const n=[e.command,...e.args].map(j).join(" ");return{command:process.env.ComSpec||"cmd.exe",args:["/d","/s","/c",`"${n}"`],windowsVerbatimArguments:!0}}return{command:e.command,args:e.args}}function Be(e,n,t=process.env){return{detached:!0,stdio:["ignore",n,n],env:t,windowsHide:!0,...e.windowsVerbatimArguments?{windowsVerbatimArguments:!0}:{}}}function Ve(){const e=P();o.writeFileSync(x,Se(e,d)),o.writeFileSync(H,we(x)),Q(),z();const n=ge({userId:We(),command:s.join(process.env.SystemRoot||"C:\\Windows","System32","wscript.exe"),arguments:[j(H)]});o.writeFileSync(_,n,"utf8");const t=he({taskName:I,xmlPath:_});try{return c(t.create,{stdio:"pipe",windowsHide:!0}),c(t.run,{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}function Je(){Ee(I,e=>{c(e,{stdio:"pipe",windowsHide:!0})})}function Z(){const e={};for(const n of B)process.env[n]&&(e[n]=process.env[n]);if(e.HOME??=u.homedir(),e.PATH=fe({pathValue:e.PATH,env:e}),e.USER??=u.userInfo().username,l()==="win32"){const n=process.env.TEMP||process.env.TMP||u.tmpdir(),t=u.homedir();e.USERPROFILE??=t;const r=s.win32.parse(t);e.HOMEDRIVE??=r.root.replace(/[\\/]$/,"")||process.env.HOMEDRIVE||"",e.HOMEPATH??=t.slice((e.HOMEDRIVE||"").length)||process.env.HOMEPATH||"",e.USERNAME??=process.env.USERNAME||u.userInfo().username,e.COMPUTERNAME??=process.env.COMPUTERNAME||"",e.TMPDIR??=n,e.TEMP??=n,e.TMP??=n}else e.TMPDIR??="/tmp";return e}function Ge(){const e=Z(),n=P(),t=Object.entries(e).map(([i,m])=>` <key>${N(i)}</key>
3
- <string>${N(m)}</string>`).join(`
4
- `),r=[n.command,...n.args].map(i=>` <string>${N(i)}</string>`).join(`
1
+ import c from"chalk";import i from"node:fs";import s from"node:path";import u from"node:os";import{execSync as a,spawn as de}from"node:child_process";import{randomUUID as le}from"node:crypto";import{fileURLToPath as pe}from"node:url";import{getShennianDir as me,loadConfig as j,resolveShennianPath as h,saveConfig as fe}from"../config/index.js";import{buildAugmentedPath as Se}from"../env-path.js";import{buildWindowsLauncherCommand as he,buildWindowsScheduledTaskCommands as ge,buildWindowsScheduledTaskXml as we,buildWindowsStartupVbs as ye,escapeXml as T,isWindowsCmdScript as ve,quoteCmdArg as W,quoteSystemdArg as Ee,suspendWindowsScheduledTask as Pe}from"./daemon-windows.js";import{waitForDaemonReadyStatus as L}from"./daemon-start-wait.js";import{findRunningWindowsDaemonProcessIds as Ae,isShennianRunServiceCommand as Ie}from"./daemon-process.js";import{getCurrentVersion as F}from"../upgrade/engine.js";import{buildWindowsLauncherCommand as An,buildWindowsScheduledTaskCommands as In,buildWindowsScheduledTaskSuspendCommands as kn,buildWindowsScheduledTaskXml as Dn,buildWindowsStartupVbs as Nn,suspendWindowsScheduledTask as bn}from"./daemon-windows.js";import{isShennianRunServiceCommand as Tn}from"./daemon-process.js";const ke=s.dirname(pe(import.meta.url)),S=me(),g=h("daemon.pid"),d=h("daemon.log"),x=h("remote-access.disabled"),D=h("daemon-launcher.json"),B=s.resolve(ke,"../../bin/shennian.js"),V=process.execPath,J=new Set(["PATH","HOME","USERPROFILE","HOMEDRIVE","HOMEPATH","USERNAME","USERDOMAIN","COMPUTERNAME","USER","LOGNAME","SHELL","TMPDIR","LANG","LC_ALL","LC_CTYPE","SSH_AUTH_SOCK","XDG_CONFIG_HOME","XDG_DATA_HOME","TEMP","TMP","APPDATA","LOCALAPPDATA","SHENNIAN_DESKTOP_SERVER_URL","SHENNIAN_WECHAT_CHANNEL_HELPER_DIR","SHENNIAN_HOME","SHENNIAN_NATIVE_FUSION_DISABLED","SHENNIAN_WECHAT_DELIVERABILITY_TRACE"]);function De(e){return J.has(e)}function l(){const e=u.platform();return e==="darwin"||e==="linux"||e==="win32"?e:"linux"}function O(e){const n=e.replace(/\\/g,"/").toLowerCase(),t=u.tmpdir().replace(/\\/g,"/").toLowerCase();return n.includes("/_npx/")||n.includes("/npm-cache/_npx/")||n.includes("/pnpm/dlx/")||n.startsWith(t.endsWith("/")?t:`${t}/`)}function Ne(e){return i.existsSync(e.scriptPath)&&!O(e.scriptPath)?{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}:e.shennianCommandPath&&!O(e.shennianCommandPath)?{command:e.shennianCommandPath,args:["run-service"],mode:"global-shim"}:e.npxPath?{command:e.npxPath,args:["--yes","shennian","run-service"],mode:"npx"}:{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}}function be(e){const n=e.trim();if(!n)return null;if(n.startsWith("{"))try{const r=JSON.parse(n),o=Number(r.pid);return!Number.isInteger(o)||o<=0?null:{pid:o,...typeof r.instanceId=="string"?{instanceId:r.instanceId}:{},...typeof r.version=="string"?{version:r.version}:{},...r.launcher==="desktop-managed"||r.launcher==="global-cli"||r.launcher==="unknown"?{launcher:r.launcher}:{},...typeof r.startedAt=="string"?{startedAt:r.startedAt}:{},...r.adopted===!0?{adopted:!0}:{}}}catch{return null}const t=parseInt(n,10);return isNaN(t)?null:{pid:t}}function P(){try{return be(i.readFileSync(g,"utf-8"))}catch{return null}}function E(e){try{return process.kill(e,0),!0}catch{return!1}}function G(e,n=3e3){const t=Date.now();for(;Date.now()-t<n;){if(!E(e))return!0;Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,100)}return!E(e)}function Re(){return i.existsSync(x)}function Te(){try{i.unlinkSync(x)}catch{}}function K(){return"global-cli"}function Le(){return`${process.pid}-${Date.now()}-${le()}`}function xe(e,n,t={}){i.mkdirSync(S,{recursive:!0}),i.writeFileSync(g,JSON.stringify({pid:e,instanceId:n,...t.version?{version:t.version}:{},launcher:t.launcher??K(),...t.adopted?{adopted:!0}:{},startedAt:new Date().toISOString()},null,2))}function yn(e,n){const t=P();if(!(t?.pid!==e||t.instanceId!==n))try{i.unlinkSync(g)}catch{}}function X(e,n=K(),t){i.mkdirSync(S,{recursive:!0}),i.writeFileSync(D,JSON.stringify({pid:e,launcher:n,...t?{instanceId:t}:{},updatedAt:new Date().toISOString()},null,2))}function Oe(e){if(e)try{if(JSON.parse(i.readFileSync(D,"utf-8")).instanceId!==e)return}catch{return}try{i.unlinkSync(D)}catch{}}function He(e){e&&X(e)}function _e(e,n){if(!e||!n)return"unknown";try{const t=JSON.parse(i.readFileSync(D,"utf-8"));if(t.pid!==e)return"unknown";if(t.launcher==="desktop-managed"||t.launcher==="global-cli")return t.launcher}catch{}return H(e)}function H(e){if(l()==="win32")return"unknown";try{const n=a(`ps -p ${e} -o command=`,{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).replace(/\\/g,"/");if(n.includes("/node_modules/shennian/")||n.includes("/bin/shennian")||n.includes(" shennian run-service")||n.includes(" shennian.js run-service"))return"global-cli"}catch{}return"unknown"}function q(e=process.pid){if(l()==="win32")return Ae(e);try{return a("ps -axo pid=,command=",{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).split(/\r?\n/).map(t=>{const r=t.trim().match(/^(\d+)\s+(.+)$/);if(!r)return null;const o=Number(r[1]),f=r[2];return!Number.isInteger(o)||o===e?null:Ie(f)?o:null}).filter(t=>typeof t=="number")}catch{return[]}}function $e(e=process.env,n=S,t=u.tmpdir(),r=s.join(u.homedir(),".shennian")){if(e.SHENNIAN_E2E_ISOLATED_DAEMON!=="1"||!e.SHENNIAN_HOME?.trim()||s.resolve(n)===s.resolve(r))return!1;const o=s.relative(s.resolve(t),s.resolve(n));return o!==""&&o!==".."&&!o.startsWith(`..${s.sep}`)&&!s.isAbsolute(o)||s.resolve(n)===s.resolve(e.SHENNIAN_HOME)}function Ce(e=process.env,n=S){return!$e(e,n)}function m(e={}){let n=P(),t=n?.pid??null,r=t!==null&&E(t),o=t!==null&&!r,f=n?.adopted===!0;const v=j();if(o&&e.cleanupStale){try{i.unlinkSync(g)}catch{}n=null,t=null,r=!1,o=!1}if(!r&&Ce()){const w=q()[0];if(w&&(t=w,r=!0,o=!1,f=!0,e.cleanupStale)){const U=Le();xe(w,U,{launcher:H(w),adopted:!0}),X(w,H(w)),n={pid:w,instanceId:U,adopted:!0}}}return{running:r,pid:t,stale:o,remoteAccessDisabled:Re(),launcher:_e(t,r),platform:l(),shennianDir:S,pidFile:g,logFile:d,...n?.instanceId?{instanceId:n.instanceId}:{},...f?{adopted:!0}:{},...v.machineId?{machineId:v.machineId}:{},paired:!!(v.machineToken&&v.machineId),...v.serverUrl?{serverUrl:v.serverUrl}:{}}}function y(e){process.stdout.write(`${JSON.stringify(e,null,2)}
2
+ `)}function Y(e){const n=e?.trim();if(!n)return;const t=j();t.serverUrl=n,fe(t)}function z(e){return e?.trim()||process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim()||void 0}const _="com.shennian.agent",p=s.join(u.homedir(),"Library/LaunchAgents",`${_}.plist`),Me=s.join(u.homedir(),"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),N=h("autostart.cmd"),b=h("autostart.vbs"),R=h("autostart.xml"),Ue=s.join(Me,"ShennianAgent.vbs");function Q(e){const n=l()==="win32"?`where ${e}`:`command -v ${e}`;try{return a(n,{stdio:["ignore","pipe","pipe"],encoding:"utf-8",windowsHide:!0}).split(/\r?\n/).map(o=>o.trim()).find(Boolean)??null}catch{return null}}function je(){const e=l()==="win32"?"npx.cmd":"npx",n=s.join(s.dirname(V),e);return i.existsSync(n)?n:Q("npx")}function A(){return Ne({nodeExec:V,scriptPath:B,shennianCommandPath:Q("shennian"),npxPath:je()})}function Z(){try{a(`schtasks /delete /tn "${k}" /f`,{stdio:"pipe",windowsHide:!0})}catch{}}function ee(){try{i.unlinkSync(Ue)}catch{}}function We(){const e=process.env.USERDOMAIN?.trim(),n=process.env.COMPUTERNAME?.trim(),t=process.env.USERNAME?.trim()||u.userInfo().username;return e&&e.toUpperCase()!=="WORKGROUP"?`${e}\\${t}`:n?`${n}\\${t}`:t}function Fe(e){if(l()==="win32"&&ve(e.command)){const n=[e.command,...e.args].map(W).join(" ");return{command:process.env.ComSpec||"cmd.exe",args:["/d","/s","/c",`"${n}"`],windowsVerbatimArguments:!0}}return{command:e.command,args:e.args}}function Be(e,n,t=process.env){return{detached:!0,stdio:["ignore",n,n],env:t,windowsHide:!0,...e.windowsVerbatimArguments?{windowsVerbatimArguments:!0}:{}}}function Ve(){const e=A();i.writeFileSync(N,he(e,d)),i.writeFileSync(b,ye(N)),ee(),Z();const n=we({userId:We(),command:s.join(process.env.SystemRoot||"C:\\Windows","System32","wscript.exe"),arguments:[W(b)]});i.writeFileSync(R,n,"utf8");const t=ge({taskName:k,xmlPath:R});try{return a(t.create,{stdio:"pipe",windowsHide:!0}),a(t.run,{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}function Je(){Pe(k,e=>{a(e,{stdio:"pipe",windowsHide:!0})})}function ne(){const e={};for(const n of J)process.env[n]&&(e[n]=process.env[n]);if(e.HOME??=u.homedir(),e.PATH=Se({pathValue:e.PATH,env:e}),e.USER??=u.userInfo().username,l()==="win32"){const n=process.env.TEMP||process.env.TMP||u.tmpdir(),t=u.homedir();e.USERPROFILE??=t;const r=s.win32.parse(t);e.HOMEDRIVE??=r.root.replace(/[\\/]$/,"")||process.env.HOMEDRIVE||"",e.HOMEPATH??=t.slice((e.HOMEDRIVE||"").length)||process.env.HOMEPATH||"",e.USERNAME??=process.env.USERNAME||u.userInfo().username,e.COMPUTERNAME??=process.env.COMPUTERNAME||"",e.TMPDIR??=n,e.TEMP??=n,e.TMP??=n}else e.TMPDIR??="/tmp";return e}function Ge(){const e=ne(),n=A(),t=Object.entries(e).map(([o,f])=>` <key>${T(o)}</key>
3
+ <string>${T(f)}</string>`).join(`
4
+ `),r=[n.command,...n.args].map(o=>` <string>${T(o)}</string>`).join(`
5
5
  `);return`<?xml version="1.0" encoding="UTF-8"?>
6
6
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
7
7
  <plist version="1.0">
8
8
  <dict>
9
9
  <key>Label</key>
10
- <string>${O}</string>
10
+ <string>${_}</string>
11
11
  <key>ProgramArguments</key>
12
12
  <array>
13
13
  ${r}
@@ -27,8 +27,8 @@ ${t}
27
27
  <key>StandardErrorPath</key>
28
28
  <string>${d}</string>
29
29
  </dict>
30
- </plist>`}const D=s.join(u.homedir(),".config/systemd/user/shennian.service");function Ke(){const e=Z(),n=P(),t=Object.entries(e).map(([i,m])=>`Environment=${i}=${m}`).join(`
31
- `),r=[n.command,...n.args].map(ve).join(" ");return`[Unit]
30
+ </plist>`}const I=s.join(u.homedir(),".config/systemd/user/shennian.service");function Ke(){const e=ne(),n=A(),t=Object.entries(e).map(([o,f])=>`Environment=${o}=${f}`).join(`
31
+ `),r=[n.command,...n.args].map(Ee).join(" ");return`[Unit]
32
32
  Description=Shennian Agent Daemon
33
33
  After=network.target
34
34
 
@@ -41,6 +41,6 @@ StandardOutput=append:${d}
41
41
  StandardError=append:${d}
42
42
 
43
43
  [Install]
44
- WantedBy=default.target`}const I="ShennianAgent";function Xe(e){c(e,{stdio:"pipe",windowsHide:!0})}function qe(){const e=process.getuid?.();if(typeof e=="number")return e;const n=u.userInfo();return typeof n.uid=="number"?n.uid:n.username}function ee(e=qe()){return`gui/${e}/${O}`}function Ye(e=Xe){try{return e(`launchctl unload "${p}" 2>/dev/null; launchctl load -w "${p}"`),e(`launchctl kickstart -k "${ee()}"`),!0}catch{try{return e(`launchctl load -w "${p}"`),e(`launchctl kickstart -k "${ee()}"`),!0}catch{return!1}}}function ze(){o.mkdirSync(f,{recursive:!0});const e={};for(const[n,t]of Object.entries(process.env))t!==void 0&&De(n)&&(e[n]=t);o.writeFileSync(h("env.json"),JSON.stringify(e,null,2))}function ne(e={}){o.mkdirSync(f,{recursive:!0});const n=S({cleanupStale:!0});if(n.pid!==null&&n.running&&!te(n)){e.quiet||console.log(a.green(`\u2713 Background service already running (PID ${n.pid})`));return}re(n);const t=o.openSync(d,"a"),r=Fe(P()),i=ue(r.command,r.args,Be(r,t));i.unref(),o.closeSync(t),He(i.pid),e.quiet||(console.log(a.green(`\u2713 Background service started (PID ${i.pid})`)),console.log(a.gray(` Logs: ${d}`)))}function te(e,n=A(),t=ke()){return e.pid===null||!e.running?!1:e.adopted===!0||n?.version!==t}function re(e=S({cleanupStale:!0})){if(!(!te(e)||e.pid===null))try{process.kill(e.pid,"SIGTERM"),V(e.pid,5e3)||(process.kill(e.pid,"SIGKILL"),V(e.pid,2e3));try{A()?.pid===e.pid&&o.unlinkSync(g)}catch{}}catch{}}function oe(){o.mkdirSync(f,{recursive:!0});const e=l();switch(P().mode==="direct"&&T(W)&&console.warn(a.yellow("\u26A0 Warning: Current CLI path is temporary (npx). Auto-start may not work after reboot.\n Run `npm install -g shennian@latest` for reliable auto-start.")),e){case"darwin":{const t=s.dirname(p);return o.mkdirSync(t,{recursive:!0}),o.writeFileSync(p,Ge()),Ye()}case"linux":{const t=s.dirname(D);o.mkdirSync(t,{recursive:!0}),o.writeFileSync(D,Ke());try{c("systemctl --user daemon-reload && systemctl --user enable shennian",{stdio:"pipe",windowsHide:!0})}catch{return!1}try{c(`loginctl enable-linger ${u.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 Ve()}}function Qe(){const n=S({cleanupStale:!0}).pid;if(n===null)return{};if(!v(n)){try{o.unlinkSync(g)}catch{}return{stalePid:n}}try{return process.kill(n,"SIGTERM"),{stoppedPid:n}}catch(t){return{error:t instanceof Error?t.message:String(t)}}}async function ie(e=5e3){const n=Qe();if(!n.stoppedPid)return n;if(!await C(n.stoppedPid,e))try{process.kill(n.stoppedPid,"SIGKILL"),await C(n.stoppedPid,2e3)}catch(r){return{...n,error:r instanceof Error?r.message:String(r)}}try{o.unlinkSync(g)}catch{}xe();for(const r of K())if(r!==n.stoppedPid)try{process.kill(r,"SIGTERM"),await C(r,2e3)}catch{}return n}function Ze(e={}){X(q(e.api)),Te(),re(),oe()||ne({quiet:e.json}),e.json?E(b(()=>S({cleanupStale:!0}))):console.log(a.green("\u2713 Background service started"))}async function en(e={}){switch(o.mkdirSync(f,{recursive:!0}),o.writeFileSync(R,new Date().toISOString()),l()){case"darwin":{if(o.existsSync(p))try{c(`launchctl unload -w "${p}"`,{stdio:"pipe",windowsHide:!0})}catch{}break}case"linux":{try{c("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}break}case"win32":{try{c(`schtasks /change /tn "${I}" /disable`,{stdio:"pipe",windowsHide:!0})}catch{}try{c(`schtasks /end /tn "${I}"`,{stdio:"pipe",windowsHide:!0})}catch{}break}}const t=await ie(),r=S({cleanupStale:!0});if(e.json){E(t.error?{...r,error:t.error}:r);return}t.error?console.error(a.red(`\u2717 Failed to stop: ${t.error}`)):console.log(a.green("\u2713 Background service stopped"))}function se(e){ze(),Ze(e)}async function $(e={}){await en(e)}async function C(e,n=5e3){const t=Date.now();for(;Date.now()-t<n;){if(!v(e))return!0;await new Promise(r=>setTimeout(r,100))}return!v(e)}async function nn(e={}){X(q(e.api));const n=l();n==="win32"&&Je();const t=await ie();if(t.error){e.json?E({...S(),error:t.error}):console.error(a.red(`\u2717 Failed to stop: ${t.error}`));return}if(t.stoppedPid!==void 0?e.json||console.log(a.green(`\u2713 Background service stopped (PID ${t.stoppedPid})`)):t.stalePid!==void 0?e.json||console.log(a.yellow(`\u26A0 Service process no longer exists, cleaned up stale PID ${t.stalePid}`)):e.json||console.log(a.gray("\u25CF Background service not running, starting it now")),(n==="linux"||n==="win32")&&oe()){e.json&&E(b(()=>S({cleanupStale:!0})));return}ne({quiet:e.json}),e.json&&E(b(()=>S({cleanupStale:!0})))}function ae(e={}){const n=S({cleanupStale:!0});if(e.json){E(n);return}if(n.pid===null){console.log(a.gray("\u25CF Background service not running"));return}n.running?(console.log(a.green(`\u25CF Background service running (PID ${n.pid})`)),console.log(a.gray(` Logs: ${d}`))):console.log(a.yellow(`\u25CF Background service stopped (PID ${n.pid} is stale)`))}function ce(e){if(!o.existsSync(d)){console.log(a.gray("No logs yet"));return}try{const n=c(u.platform()==="win32"?`powershell Get-Content -Tail ${e.lines} "${d}"`:`tail -n ${e.lines} "${d}"`,{encoding:"utf-8",windowsHide:!0});process.stdout.write(n)}catch{const n=o.readFileSync(d,"utf-8").split(`
44
+ WantedBy=default.target`}const k="ShennianAgent";function Xe(e){a(e,{stdio:"pipe",windowsHide:!0})}function qe(){const e=process.getuid?.();if(typeof e=="number")return e;const n=u.userInfo();return typeof n.uid=="number"?n.uid:n.username}function te(e=qe()){return`gui/${e}/${_}`}function Ye(e=Xe){try{return e(`launchctl unload "${p}" 2>/dev/null; launchctl load -w "${p}"`),e(`launchctl kickstart -k "${te()}"`),!0}catch{try{return e(`launchctl load -w "${p}"`),e(`launchctl kickstart -k "${te()}"`),!0}catch{return!1}}}function ze(){i.mkdirSync(S,{recursive:!0});const e={};for(const[n,t]of Object.entries(process.env))t!==void 0&&De(n)&&(e[n]=t);i.writeFileSync(h("env.json"),JSON.stringify(e,null,2))}function re(e={}){i.mkdirSync(S,{recursive:!0});const n=m({cleanupStale:!0});if(n.pid!==null&&n.running&&!$(n)){e.quiet||console.log(c.green(`\u2713 Background service already running (PID ${n.pid})`));return}ie(n);const t=i.openSync(d,"a"),r=Fe(A()),o=de(r.command,r.args,Be(r,t));o.unref(),i.closeSync(t),He(o.pid),e.quiet||(console.log(c.green(`\u2713 Background service started (PID ${o.pid})`)),console.log(c.gray(` Logs: ${d}`)))}function $(e,n=P(),t=F()){return e.pid===null||!e.running?!1:e.adopted===!0||n?.version!==t}function Qe(e=l()){switch(e){case"darwin":return i.existsSync(p);case"linux":return i.existsSync(I);case"win32":return i.existsSync(N)&&i.existsSync(b)&&i.existsSync(R)}}function Ze(e,n,t,r=P(),o=F()){return!n.api?.trim()&&e.pid!==null&&e.running&&!e.stale&&!e.remoteAccessDisabled&&t&&!$(e,r,o)}function ie(e=m({cleanupStale:!0})){if(!(!$(e)||e.pid===null))try{process.kill(e.pid,"SIGTERM"),G(e.pid,5e3)||(process.kill(e.pid,"SIGKILL"),G(e.pid,2e3));try{P()?.pid===e.pid&&i.unlinkSync(g)}catch{}}catch{}}function oe(){i.mkdirSync(S,{recursive:!0});const e=l();switch(A().mode==="direct"&&O(B)&&console.warn(c.yellow("\u26A0 Warning: Current CLI path is temporary (npx). Auto-start may not work after reboot.\n Run `npm install -g shennian@latest` for reliable auto-start.")),e){case"darwin":{const t=s.dirname(p);return i.mkdirSync(t,{recursive:!0}),i.writeFileSync(p,Ge()),Ye()}case"linux":{const t=s.dirname(I);i.mkdirSync(t,{recursive:!0}),i.writeFileSync(I,Ke());try{a("systemctl --user daemon-reload && systemctl --user enable shennian",{stdio:"pipe",windowsHide:!0})}catch{return!1}try{a(`loginctl enable-linger ${u.userInfo().username}`,{stdio:"pipe",windowsHide:!0})}catch{}try{return a("systemctl --user restart shennian",{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}case"win32":return Ve()}}function en(){const n=m({cleanupStale:!0}).pid;if(n===null)return{};if(!E(n)){try{i.unlinkSync(g)}catch{}return{stalePid:n}}try{return process.kill(n,"SIGTERM"),{stoppedPid:n}}catch(t){return{error:t instanceof Error?t.message:String(t)}}}async function se(e=5e3){const n=en();if(!n.stoppedPid)return n;if(!await M(n.stoppedPid,e))try{process.kill(n.stoppedPid,"SIGKILL"),await M(n.stoppedPid,2e3)}catch(r){return{...n,error:r instanceof Error?r.message:String(r)}}try{i.unlinkSync(g)}catch{}Oe();for(const r of q())if(r!==n.stoppedPid)try{process.kill(r,"SIGTERM"),await M(r,2e3)}catch{}return n}function nn(e={}){Y(z(e.api)),Te(),ie(),oe()||re({quiet:e.json}),e.json?y(L(()=>m({cleanupStale:!0}))):console.log(c.green("\u2713 Background service started"))}async function tn(e={}){switch(i.mkdirSync(S,{recursive:!0}),i.writeFileSync(x,new Date().toISOString()),l()){case"darwin":{if(i.existsSync(p))try{a(`launchctl unload -w "${p}"`,{stdio:"pipe",windowsHide:!0})}catch{}break}case"linux":{try{a("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}break}case"win32":{try{a(`schtasks /change /tn "${k}" /disable`,{stdio:"pipe",windowsHide:!0})}catch{}try{a(`schtasks /end /tn "${k}"`,{stdio:"pipe",windowsHide:!0})}catch{}break}}const t=await se(),r=m({cleanupStale:!0});if(e.json){y(t.error?{...r,error:t.error}:r);return}t.error?console.error(c.red(`\u2717 Failed to stop: ${t.error}`)):console.log(c.green("\u2713 Background service stopped"))}function ce(e){const n=m({cleanupStale:!0});if(Ze(n,e,Qe())){e.json?y(n):console.log(c.green(`\u2713 Background service already running (PID ${n.pid})`));return}ze(),nn(e)}async function C(e={}){await tn(e)}async function M(e,n=5e3){const t=Date.now();for(;Date.now()-t<n;){if(!E(e))return!0;await new Promise(r=>setTimeout(r,100))}return!E(e)}async function rn(e={}){Y(z(e.api));const n=l();n==="win32"&&Je();const t=await se();if(t.error){e.json?y({...m(),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")),(n==="linux"||n==="win32")&&oe()){e.json&&y(L(()=>m({cleanupStale:!0})));return}re({quiet:e.json}),e.json&&y(L(()=>m({cleanupStale:!0})))}function ae(e={}){const n=m({cleanupStale:!0});if(e.json){y(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: ${d}`))):console.log(c.yellow(`\u25CF Background service stopped (PID ${n.pid} is stale)`))}function ue(e){if(!i.existsSync(d)){console.log(c.gray("No logs yet"));return}try{const n=a(u.platform()==="win32"?`powershell Get-Content -Tail ${e.lines} "${d}"`:`tail -n ${e.lines} "${d}"`,{encoding:"utf-8",windowsHide:!0});process.stdout.write(n)}catch{const n=i.readFileSync(d,"utf-8").split(`
45
45
  `).slice(-e.lines);console.log(n.join(`
46
- `))}}async function tn(){switch(await $(),l()){case"darwin":{if(o.existsSync(p)){try{c(`launchctl unload -w "${p}"`,{stdio:"pipe",windowsHide:!0})}catch{}o.unlinkSync(p),console.log(a.green("\u2713 Auto-start uninstalled (launchd)"))}break}case"linux":{try{c("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}if(o.existsSync(D)){o.unlinkSync(D);try{c("systemctl --user daemon-reload",{stdio:"pipe",windowsHide:!0})}catch{}}console.log(a.green("\u2713 Auto-start uninstalled (systemd)"));break}case"win32":{z();try{o.unlinkSync(x)}catch{}try{o.unlinkSync(H)}catch{}try{o.unlinkSync(_)}catch{}Q(),console.log(a.green("\u2713 Auto-start uninstalled (Task Scheduler)"));break}}}function wn(e){e.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>se(r)),e.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(r=>$(r)),e.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>ae(r)),e.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>ce({lines:parseInt(r.lines,10)}));const n=e.command("daemon",{hidden:!0}).description("Deprecated: use top-level start/stop/status/logs"),t=(r,i)=>{i||console.error(a.yellow(`\u26A0 Deprecated command. Use \`shennian ${r}\` instead.`))};n.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("start",r.json),se(r)}),n.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(async r=>{t("stop",r.json),await $(r)}),n.command("restart").description("Restart the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("stop && shennian start",r.json),nn(r)}),n.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>{t("status",r.json),ae(r)}),n.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>{t("logs"),ce({lines:parseInt(r.lines,10)})}),n.command("uninstall").description("Uninstall auto-start service").action(tn)}export{Be as buildDaemonSpawnOptions,En as buildWindowsLauncherCommand,Pn as buildWindowsScheduledTaskCommands,In as buildWindowsScheduledTaskSuspendCommands,kn as buildWindowsScheduledTaskXml,An as buildWindowsStartupVbs,Z as captureEnvForService,xe as clearDaemonLauncher,gn as clearDaemonPidIfOwner,Te as clearRemoteAccessDisabled,Le as createDaemonInstanceId,K as findRunningDaemonProcessIds,J as getCurrentProcessDaemonLauncher,S as getDaemonStatus,ee as getLaunchdServiceTarget,oe as installService,T as isEphemeralCliPath,$e as isIsolatedDaemonE2eInstance,Re as isRemoteAccessDisabled,De as isSafeSnapshotEnvKey,bn as isShennianRunServiceCommand,He as recordStartedDaemon,wn as registerDaemonCommand,Ye as reloadLaunchdAgent,Ne as resolveServiceLaunchSpec,ze as saveEnvSnapshot,Ce as shouldDiscoverGlobalDaemonProcesses,te as shouldReplaceRunningDaemon,ne as startDaemonProcess,Dn as suspendWindowsScheduledTask,G as writeDaemonLauncher,Oe as writeDaemonPid};
46
+ `))}}async function on(){switch(await C(),l()){case"darwin":{if(i.existsSync(p)){try{a(`launchctl unload -w "${p}"`,{stdio:"pipe",windowsHide:!0})}catch{}i.unlinkSync(p),console.log(c.green("\u2713 Auto-start uninstalled (launchd)"))}break}case"linux":{try{a("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}if(i.existsSync(I)){i.unlinkSync(I);try{a("systemctl --user daemon-reload",{stdio:"pipe",windowsHide:!0})}catch{}}console.log(c.green("\u2713 Auto-start uninstalled (systemd)"));break}case"win32":{Z();try{i.unlinkSync(N)}catch{}try{i.unlinkSync(b)}catch{}try{i.unlinkSync(R)}catch{}ee(),console.log(c.green("\u2713 Auto-start uninstalled (Task Scheduler)"));break}}}function vn(e){e.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>ce(r)),e.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(r=>C(r)),e.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>ae(r)),e.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>ue({lines:parseInt(r.lines,10)}));const n=e.command("daemon",{hidden:!0}).description("Deprecated: use top-level start/stop/status/logs"),t=(r,o)=>{o||console.error(c.yellow(`\u26A0 Deprecated command. Use \`shennian ${r}\` instead.`))};n.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("start",r.json),ce(r)}),n.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(async r=>{t("stop",r.json),await C(r)}),n.command("restart").description("Restart the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("stop && shennian start",r.json),rn(r)}),n.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>{t("status",r.json),ae(r)}),n.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>{t("logs"),ue({lines:parseInt(r.lines,10)})}),n.command("uninstall").description("Uninstall auto-start service").action(on)}export{Be as buildDaemonSpawnOptions,An as buildWindowsLauncherCommand,In as buildWindowsScheduledTaskCommands,kn as buildWindowsScheduledTaskSuspendCommands,Dn as buildWindowsScheduledTaskXml,Nn as buildWindowsStartupVbs,Ze as canReuseRunningDaemonForStart,ne as captureEnvForService,Oe as clearDaemonLauncher,yn as clearDaemonPidIfOwner,Te as clearRemoteAccessDisabled,Le as createDaemonInstanceId,q as findRunningDaemonProcessIds,K as getCurrentProcessDaemonLauncher,m as getDaemonStatus,te as getLaunchdServiceTarget,oe as installService,Qe as isAutoStartServicePresent,O as isEphemeralCliPath,$e as isIsolatedDaemonE2eInstance,Re as isRemoteAccessDisabled,De as isSafeSnapshotEnvKey,Tn as isShennianRunServiceCommand,He as recordStartedDaemon,vn as registerDaemonCommand,Ye as reloadLaunchdAgent,Ne as resolveServiceLaunchSpec,ze as saveEnvSnapshot,Ce as shouldDiscoverGlobalDaemonProcesses,$ as shouldReplaceRunningDaemon,re as startDaemonProcess,bn as suspendWindowsScheduledTask,X as writeDaemonLauncher,xe as writeDaemonPid};
package/dist/src/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import{Command as oe}from"commander";import v from"chalk";import ne from"node:os";import $ from"node:fs";import{loadConfig as d,saveConfig as te,getConfigPath as re,getShennianDir as ie,resolveShennianPath as B,normalizeAutoUpgradePolicy as se}from"./config/index.js";import{CliRelayClient as ae}from"./relay/client.js";import{registerPairCommand as ce,runSmartStart as me}from"./commands/pair.js";import{clearDaemonPidIfOwner as F,createDaemonInstanceId as le,clearDaemonLauncher as G,findRunningDaemonProcessIds as de,isRemoteAccessDisabled as pe,isIsolatedDaemonE2eInstance as ge,registerDaemonCommand as ue,writeDaemonPid as J,writeDaemonLauncher as j}from"./commands/daemon.js";import{registerRuntimeCommand as he}from"./commands/runtime.js";import{registerManagerCommand as fe}from"./commands/manager.js";import{registerUpgradeCommand as ve}from"./commands/upgrade.js";import{registerToolsCommand as Se}from"./commands/tools.js";import{registerIntegrationCommand as we}from"./commands/integration.js";import{registerRoomCommand as Ie}from"./commands/room.js";import{SessionManager as Re}from"./session/manager.js";import{SERVERS as A,regionToUrl as ye,urlToRegion as ke}from"./region.js";import{getCurrentVersion as K,handleStartupCrashCheck as Ce}from"./upgrade/engine.js";import{AutoUpgradeScheduler as De}from"./upgrade/scheduler.js";import{detectAgents as Te}from"./agents/detect.js";import{augmentProcessPath as z}from"./env-path.js";z();const W=K();import{getCachedAgentInfos as Ue,resolveAgentInfos as Ee}from"./agents/model-registry.js";import{initCliLogReporter as $e,reportLog as S}from"./log-reporter.js";import{isNativeFusionEnabled as Ae}from"./native-fusion/config.js";import{NativeSessionFusionService as be}from"./native-fusion/service.js";import{startDaemonLogRetention as Oe}from"./daemon-log.js";import{startDaemonIpcServer as Ne}from"./daemon-ipc/server.js";import{RoomSessionRegistry as Pe}from"./room/session-registry.js";import{HttpRoomBindingClient as Me}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as Le}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as _e}from"./room/host-session-observations.js";import{RoomMirrorSyncService as xe}from"./room/mirror-sync-service.js";import{RoomActivationService as Ve}from"./room/activation-service.js";import{RoomActivationAudit as He}from"./room/activation-audit.js";import{RoomAttachmentCache as Be}from"./room/attachment-cache.js";import{RoomAttachmentUploader as Fe}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as Ge}from"./room/managed-context.js";import{createManagedRoomIpcHandlers as Je}from"./room/managed-room-ipc.js";const q=ie(),je=B("daemon.pid");function Ke(){try{const o=$.readFileSync(je,"utf-8").trim();if(o.startsWith("{")){const r=JSON.parse(o),s=Number(r.pid);return Number.isInteger(s)&&s>0?s:null}const n=parseInt(o,10);return Number.isInteger(n)&&n>0?n:null}catch{return null}}function ze(o){return o.replace(/^https:\/\//,"wss://").replace(/^http:\/\//,"ws://")}function b(){process.stdout.write(`${JSON.stringify({ok:!1,error:"integration_command_retired",message:"Room MCP and Hooks were retired. Use the Shennian Skill and CLI.",nextAction:{type:"open_install_guide",url:"https://shennian.net/install.md"}})}
2
- `),process.exitCode=1}function We(o){const n=[`trigger=${o.trigger??"unknown"}`,`phase=${o.phase}`,`code=${o.code??"unknown"}`,`attempt=${o.reconnectAttempt}`];return o.reason&&n.push(`reason=${o.reason}`),o.error&&n.push(`error=${o.error}`),n.join(" ")}async function Q(o,n=5e3){const r=Date.now();for(;Date.now()-r<n;){try{process.kill(o,0)}catch{return!0}await new Promise(s=>setTimeout(s,100))}try{return process.kill(o,0),!1}catch{return!0}}const t=new oe;t.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(W),t.enablePositionalOptions(),we(t),Ie(t),t.option("--api <url>","Server URL override").option("--name <name>","Machine name",ne.hostname()).action(async o=>{const n=d(),r=o.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??n.serverUrl??void 0;await me(r,o.name)}),t.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async o=>{const n=B("env.json");try{const e=JSON.parse($.readFileSync(n,"utf-8"));for(const[i,c]of Object.entries(e))process.env[i]||(process.env[i]=c);z()}catch{}const r=Oe();pe()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(r),process.exit(0));const s=!!(process.env.INVOCATION_ID||process.env.JOURNAL_STREAM||process.env.SHENNIAN_DESKTOP_SERVER_URL),O=async(e,i)=>{console.log(`[${new Date().toISOString()}] ${i} (PID ${e})`),process.kill(e,"SIGTERM"),await Q(e)||(process.kill(e,"SIGKILL"),await Q(e,2e3))};try{const e=Ke();if(e&&e!==process.pid)try{process.kill(e,0),s?await O(e,"managed start taking over from existing daemon"):(console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),process.exit(0))}catch{}}catch{}const D=ge(process.env,q)?[]:de(process.pid);if(D.length>0)if(s)for(const e of D)try{await O(e,"managed start taking over from orphan daemon")}catch{}else console.log(`[${new Date().toISOString()}] daemon already running (PID ${D[0]}), skipping duplicate start`),process.exit(0);const m=le();J(process.pid,m,{version:W}),j(process.pid,void 0,m),process.on("exit",()=>{F(process.pid,m),G(m)}),await Ce()&&(console.log(`[${new Date().toISOString()}] Rolled back to previous version, restarting...`),process.exit(0));const a=d(),g=o.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??a.serverUrl??A.cn.url,p=K(),T=Te(),u=T.map(e=>e.type),U=Ue(T),w=new Pe,I=new _e,N=new Ge(w),h=new Me(g),Y=new Be({actionClient:h}),Z=new Fe({actionClient:h});let P=null;const E=await Ne({handlers:{...Je({authority:N,actionClient:h,attachmentCache:Y,attachmentUploader:Z,observations:I,getMachineCredentials:()=>({machineToken:d().machineToken})})}});let M=!1;const R=()=>{M||(M=!0,E.close().finally(()=>process.exit(0)))};if(process.once("SIGINT",R),process.once("SIGTERM",R),!a.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await E.close(),process.exitCode=1;return}process.removeListener("SIGINT",R),process.removeListener("SIGTERM",R);const L=`${ze(g)}/relay/machine`,_=a.machineToken;if(!_)return;a.machineId&&$e(g,a.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${L}... (v${p}) agents: ${u.join(",")}`),S({level:"info",wsEvent:"daemon.start",metadata:{version:p,agents:u}});let y=null,k=null;const x=se(a.autoUpgrade),l=new ae({serverUrl:L,machineToken:_,cliVersion:p,agentList:u,autoUpgradePolicy:x,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),S({level:"info",wsEvent:"daemon.connected"}),U.some(e=>e.models.length>0)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:u,agents:U}}),Ee(T,{serverUrl:a.serverUrl??g,authToken:a.machineToken??a.accessToken}).then(e=>{JSON.stringify(e)!==JSON.stringify(U)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:u,agents:e}})}).catch(()=>{}),import("./upgrade/engine.js").then(({readUpgradeAttempt:e,clearUpgradeAttempt:i})=>{const c=e();i(),c&&(console.log(`[${new Date().toISOString()}] [upgrade] Reporting success: ${c.from} \u2192 ${c.to}`),l.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:c.from,to:c.to}}))}).catch(()=>{}),C.start(),y?.handleConnected(),k?.start()},onDisconnected:e=>{k?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${We(e)}`),S({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onReq:e=>{console.log(`[${new Date().toISOString()}] [req] ${e.method}`),S({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),f.handleReq(e)}});y=Ae()?new be(l):null,k=new xe({registry:w,observations:I,actionClient:h,getMachineCredentials:()=>({machineToken:d().machineToken}),onMessages:async(e,i,c,ee)=>{await P?.processMessages(e,i,c,ee)}});const C=new De({currentVersion:p,machineId:a.machineId??"unknown-machine",serverUrl:a.serverUrl??g,getIdleState:()=>f.getUpgradeIdleState(),startUpgrade:async e=>{const{handleUpgradeStart:i}=await import("./commands/upgrade.js");await i(l,"auto-upgrade",e,{currentVersion:p})}});C.setPolicy(x);const f=new Re(l,y,p,C,new Le({registry:w,observations:I,bindingClient:h,getMachineCredentials:()=>{const e=d();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${m}`}));P=new Ve({registry:w,observations:I,activate:e=>f.activateRoomSession(e),issueManagedRoomContext:e=>{const i=d().machineToken;if(!i)throw new Error("machine_not_authorized");return N.issue({registryKey:e,machineCredential:i})},abort:e=>f.abortRoomSession(e),audit:new He}),$.mkdirSync(q,{recursive:!0}),J(process.pid,m,{version:p}),j(process.pid,void 0,m),l.connect(),process.stdin.resume();let V=!1;const H=async()=>{if(!V){V=!0,console.log(v.gray(`
3
- Disconnecting...`)),S({level:"info",wsEvent:"daemon.stop"}),clearInterval(r);try{await f.cleanup(),C.stop(),y?.stop(),k?.stop(),l.disconnect(),await E.close(),F(process.pid,m),G(m)}finally{process.exit(0)}}};process.on("SIGINT",()=>{H()}),process.on("SIGTERM",()=>{H()})}),t.command("workbuddy-session-hook",{hidden:!0}).description("(retired) WorkBuddy Room Hook").allowUnknownOption(!0).action(()=>b()),t.command("room-session-hook",{hidden:!0}).description("(internal) Authorize the official Room tool for one trusted Agent Session").argument("<runtime>","Trusted runtime adapter").allowUnknownOption(!0).action(()=>b()),t.command("room-tool-server",{hidden:!0}).description("(internal) Run the official Shennian Room MCP tool").allowUnknownOption(!0).action(()=>b());const X=t.command("config").description("View or modify configuration");X.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${re()}`);const n={...d()};if(n.machineToken&&(n.machineToken=n.machineToken.slice(0,12)+"..."),n.serverUrl){const r=ke(n.serverUrl);console.log(`Region: ${r} (${A[r].label})`)}console.log(JSON.stringify(n,null,2))}),X.command("set").description("Update a config value").argument("<key>",'Config key (e.g. "server")').argument("<value>",'Config value (e.g. "cn" or "global")').action((o,n)=>{const r=d();if(o==="server"){n!=="cn"&&n!=="global"&&(console.error(v.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const s=n;r.serverUrl=ye(s),te(r),console.log(v.green(`\u2713 Server set to ${A[s].label}`)),console.log(v.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(v.red(`\u2717 Unknown config key: ${o}. Supported: server`)),process.exit(1)}),ce(t),ue(t),he(t),fe(t),ve(t),Se(t),t.parse();
1
+ import{Command as oe}from"commander";import v from"chalk";import ne from"node:os";import O from"node:fs";import{loadConfig as d,saveConfig as te,getConfigPath as re,getShennianDir as ie,resolveShennianPath as H,normalizeAutoUpgradePolicy as se}from"./config/index.js";import{CliRelayClient as ae}from"./relay/client.js";import{registerPairCommand as ce,runSmartStart as me}from"./commands/pair.js";import{clearDaemonPidIfOwner as F,createDaemonInstanceId as le,clearDaemonLauncher as G,findRunningDaemonProcessIds as de,isRemoteAccessDisabled as pe,isIsolatedDaemonE2eInstance as ge,registerDaemonCommand as ue,writeDaemonPid as J,writeDaemonLauncher as j}from"./commands/daemon.js";import{registerRuntimeCommand as he}from"./commands/runtime.js";import{registerManagerCommand as fe}from"./commands/manager.js";import{registerUpgradeCommand as ve}from"./commands/upgrade.js";import{registerToolsCommand as Se}from"./commands/tools.js";import{registerIntegrationCommand as we}from"./commands/integration.js";import{registerRoomCommand as Ie}from"./commands/room.js";import{SessionManager as Re}from"./session/manager.js";import{SERVERS as A,regionToUrl as ye,urlToRegion as Ce}from"./region.js";import{getCurrentVersion as K,handleStartupCrashCheck as ke}from"./upgrade/engine.js";import{AutoUpgradeScheduler as De}from"./upgrade/scheduler.js";import{detectAgents as Ee}from"./agents/detect.js";import{augmentProcessPath as z}from"./env-path.js";z();const W=K();import{getCachedAgentInfos as Te,resolveAgentInfos as Ue}from"./agents/model-registry.js";import{initCliLogReporter as Oe,reportLog as S}from"./log-reporter.js";import{isNativeFusionEnabled as Ae}from"./native-fusion/config.js";import{NativeSessionFusionService as $e}from"./native-fusion/service.js";import{startDaemonLogRetention as Ne}from"./daemon-log.js";import{startDaemonIpcServer as be}from"./daemon-ipc/server.js";import{RoomSessionRegistry as Pe}from"./room/session-registry.js";import{HttpRoomBindingClient as _e}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as Me}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as Le}from"./room/host-session-observations.js";import{RoomMirrorSyncService as xe}from"./room/mirror-sync-service.js";import{RoomActivationService as Ve}from"./room/activation-service.js";import{RoomActivationAudit as Be}from"./room/activation-audit.js";import{RoomAttachmentCache as He}from"./room/attachment-cache.js";import{RoomAttachmentUploader as Fe}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as Ge}from"./room/managed-context.js";import{createManagedRoomIpcHandlers as Je}from"./room/managed-room-ipc.js";import{SESSION_SYNC_PROTOCOL_VERSION as je,SESSION_SYNC_REQUIRED_CAPABILITIES as Ke}from"@shennian/wire";import{getDaemonInstallationId as ze}from"./session/store.js";const Y=ie(),We=H("daemon.pid");function Ye(){try{const o=O.readFileSync(We,"utf-8").trim();if(o.startsWith("{")){const r=JSON.parse(o),s=Number(r.pid);return Number.isInteger(s)&&s>0?s:null}const n=parseInt(o,10);return Number.isInteger(n)&&n>0?n:null}catch{return null}}function Qe(o){return o.replace(/^https:\/\//,"wss://").replace(/^http:\/\//,"ws://")}function $(){process.stdout.write(`${JSON.stringify({ok:!1,error:"integration_command_retired",message:"Room MCP and Hooks were retired. Use the Shennian Skill and CLI.",nextAction:{type:"open_install_guide",url:"https://shennian.net/install.md"}})}
2
+ `),process.exitCode=1}function qe(o){const n=[`trigger=${o.trigger??"unknown"}`,`phase=${o.phase}`,`code=${o.code??"unknown"}`,`attempt=${o.reconnectAttempt}`];return o.reason&&n.push(`reason=${o.reason}`),o.error&&n.push(`error=${o.error}`),n.join(" ")}async function Q(o,n=5e3){const r=Date.now();for(;Date.now()-r<n;){try{process.kill(o,0)}catch{return!0}await new Promise(s=>setTimeout(s,100))}try{return process.kill(o,0),!1}catch{return!0}}const t=new oe;t.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(W),t.enablePositionalOptions(),we(t),Ie(t),t.option("--api <url>","Server URL override").option("--name <name>","Machine name",ne.hostname()).action(async o=>{const n=d(),r=o.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??n.serverUrl??void 0;await me(r,o.name)}),t.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async o=>{const n=H("env.json");try{const e=JSON.parse(O.readFileSync(n,"utf-8"));for(const[i,c]of Object.entries(e))process.env[i]||(process.env[i]=c);z()}catch{}const r=Ne();pe()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(r),process.exit(0));const s=!!(process.env.INVOCATION_ID||process.env.JOURNAL_STREAM||process.env.SHENNIAN_DESKTOP_SERVER_URL),N=async(e,i)=>{console.log(`[${new Date().toISOString()}] ${i} (PID ${e})`),process.kill(e,"SIGTERM"),await Q(e)||(process.kill(e,"SIGKILL"),await Q(e,2e3))};try{const e=Ye();if(e&&e!==process.pid)try{process.kill(e,0),s?await N(e,"managed start taking over from existing daemon"):(console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),process.exit(0))}catch{}}catch{}const D=ge(process.env,Y)?[]:de(process.pid);if(D.length>0)if(s)for(const e of D)try{await N(e,"managed start taking over from orphan daemon")}catch{}else console.log(`[${new Date().toISOString()}] daemon already running (PID ${D[0]}), skipping duplicate start`),process.exit(0);const m=le();J(process.pid,m,{version:W}),j(process.pid,void 0,m),process.on("exit",()=>{F(process.pid,m),G(m)}),await ke()&&(console.log(`[${new Date().toISOString()}] Rolled back to previous version, restarting...`),process.exit(0));const a=d(),g=o.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??a.serverUrl??A.cn.url,p=K(),E=Ee(),u=E.map(e=>e.type),T=Te(E),w=new Pe,I=new Le,b=new Ge(w),h=new _e(g),X=new He({actionClient:h}),Z=new Fe({actionClient:h});let P=null;const U=await be({handlers:{...Je({authority:b,actionClient:h,attachmentCache:X,attachmentUploader:Z,observations:I,getMachineCredentials:()=>({machineToken:d().machineToken})})}});let _=!1;const R=()=>{_||(_=!0,U.close().finally(()=>process.exit(0)))};if(process.once("SIGINT",R),process.once("SIGTERM",R),!a.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await U.close(),process.exitCode=1;return}process.removeListener("SIGINT",R),process.removeListener("SIGTERM",R);const M=`${Qe(g)}/relay/machine`,L=a.machineToken;if(!L)return;a.machineId&&Oe(g,a.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${M}... (v${p}) agents: ${u.join(",")}`),S({level:"info",wsEvent:"daemon.start",metadata:{version:p,agents:u}});let y=null,C=null;const x=se(a.autoUpgrade),l=new ae({serverUrl:M,machineToken:L,cliVersion:p,agentList:u,autoUpgradePolicy:x,sessionSyncProtocolVersion:je,sessionSyncCapabilities:Ke,daemonInstallationId:ze(),onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),S({level:"info",wsEvent:"daemon.connected"}),T.some(e=>e.models.length>0)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:u,agents:T}}),Ue(E,{serverUrl:a.serverUrl??g,authToken:a.machineToken??a.accessToken}).then(e=>{JSON.stringify(e)!==JSON.stringify(T)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:u,agents:e}})}).catch(()=>{}),import("./upgrade/engine.js").then(({readUpgradeAttempt:e,clearUpgradeAttempt:i})=>{const c=e();i(),c&&(console.log(`[${new Date().toISOString()}] [upgrade] Reporting success: ${c.from} \u2192 ${c.to}`),l.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:c.from,to:c.to}}))}).catch(()=>{}),k.start(),y?.handleConnected(),C?.start()},onDisconnected:e=>{C?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${qe(e)}`),S({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onReq:e=>{console.log(`[${new Date().toISOString()}] [req] ${e.method}`),S({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),f.handleReq(e)}});y=Ae()?new $e(l):null,C=new xe({registry:w,observations:I,actionClient:h,getMachineCredentials:()=>({machineToken:d().machineToken}),onMessages:async(e,i,c,ee)=>{await P?.processMessages(e,i,c,ee)}});const k=new De({currentVersion:p,machineId:a.machineId??"unknown-machine",serverUrl:a.serverUrl??g,getIdleState:()=>f.getUpgradeIdleState(),startUpgrade:async e=>{const{handleUpgradeStart:i}=await import("./commands/upgrade.js");await i(l,"auto-upgrade",e,{currentVersion:p})}});k.setPolicy(x);const f=new Re(l,y,p,k,new Me({registry:w,observations:I,bindingClient:h,getMachineCredentials:()=>{const e=d();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${m}`}));P=new Ve({registry:w,observations:I,activate:e=>f.activateRoomSession(e),issueManagedRoomContext:e=>{const i=d().machineToken;if(!i)throw new Error("machine_not_authorized");return b.issue({registryKey:e,machineCredential:i})},abort:e=>f.abortRoomSession(e),audit:new Be}),O.mkdirSync(Y,{recursive:!0}),J(process.pid,m,{version:p}),j(process.pid,void 0,m),l.connect(),process.stdin.resume();let V=!1;const B=async()=>{if(!V){V=!0,console.log(v.gray(`
3
+ Disconnecting...`)),S({level:"info",wsEvent:"daemon.stop"}),clearInterval(r);try{await f.cleanup(),k.stop(),y?.stop(),C?.stop(),l.disconnect(),await U.close(),F(process.pid,m),G(m)}finally{process.exit(0)}}};process.on("SIGINT",()=>{B()}),process.on("SIGTERM",()=>{B()})}),t.command("workbuddy-session-hook",{hidden:!0}).description("(retired) WorkBuddy Room Hook").allowUnknownOption(!0).action(()=>$()),t.command("room-session-hook",{hidden:!0}).description("(internal) Authorize the official Room tool for one trusted Agent Session").argument("<runtime>","Trusted runtime adapter").allowUnknownOption(!0).action(()=>$()),t.command("room-tool-server",{hidden:!0}).description("(internal) Run the official Shennian Room MCP tool").allowUnknownOption(!0).action(()=>$());const q=t.command("config").description("View or modify configuration");q.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${re()}`);const n={...d()};if(n.machineToken&&(n.machineToken=n.machineToken.slice(0,12)+"..."),n.serverUrl){const r=Ce(n.serverUrl);console.log(`Region: ${r} (${A[r].label})`)}console.log(JSON.stringify(n,null,2))}),q.command("set").description("Update a config value").argument("<key>",'Config key (e.g. "server")').argument("<value>",'Config value (e.g. "cn" or "global")').action((o,n)=>{const r=d();if(o==="server"){n!=="cn"&&n!=="global"&&(console.error(v.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const s=n;r.serverUrl=ye(s),te(r),console.log(v.green(`\u2713 Server set to ${A[s].label}`)),console.log(v.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(v.red(`\u2717 Unknown config key: ${o}. Supported: server`)),process.exit(1)}),ce(t),ue(t),he(t),fe(t),ve(t),Se(t),t.parse();
@@ -1,4 +1,5 @@
1
1
  import type { ParsedNativeEvent } from './types.js';
2
+ export declare function lookupCodexThreadName(sourceSessionKey: string): string | null;
2
3
  export declare function listCodexRolloutFiles(): string[];
3
4
  export declare function parseCodexRolloutChunk(filePath: string, startOffset: number): {
4
5
  nextOffset: number;
@@ -1,6 +1,6 @@
1
- import T from"node:fs";import v from"node:os";import b from"node:path";import{buildApprovalPendingPayload as Q,buildUserMessagePayload as k,isSystemControlPayload as j,isToolPayload as q}from"@shennian/wire";import{buildImagePayload as V,buildToolPayload as X,inferMimeType as E,makeCursor as Y,makeEventKey as Z,normalizeText as d,parseStructuredString as P,readJsonlLines as N,readTimestamp as D,safeParse as I}from"./parser-common.js";function O(e){if(typeof e=="string"){const i=e.trim();return i?{path:i,name:b.basename(i)||"image.png",mimeType:E(i),kind:"image"}:null}if(typeof e!="object"||e===null)return null;const r=e,n=typeof r.path=="string"?r.path:typeof r.file_path=="string"?r.file_path:typeof r.saved_path=="string"?r.saved_path:"";if(!n.trim())return null;const t=typeof r.name=="string"&&r.name.trim()?r.name:b.basename(n)||"image.png",o=typeof r.mimeType=="string"&&r.mimeType.trim()?r.mimeType:E(n);return{path:n,name:t,mimeType:o,kind:"image"}}function R(e){return Array.isArray(e)?e.map(n=>{if(typeof n=="string")return n;if(typeof n=="object"&&n!==null){const t=n;return typeof t.text=="string"?t.text:""}return""}).map(n=>d(n)).filter(Boolean).join(`
1
+ import b from"node:fs";import I from"node:os";import C from"node:path";import{buildApprovalPendingPayload as Z,buildUserMessagePayload as N,isSystemControlPayload as v,isToolPayload as q}from"@shennian/wire";import{buildImagePayload as P,buildToolPayload as D,inferMimeType as W,makeCursor as U,makeEventKey as $,normalizeText as h,parseStructuredString as K,readJsonlLines as w,readTimestamp as O,safeParse as k}from"./parser-common.js";function tt(t){if(typeof t=="string"){const i=t.trim();return i?{path:i,name:C.basename(i)||"image.png",mimeType:W(i),kind:"image"}:null}if(typeof t!="object"||t===null)return null;const r=t,n=typeof r.path=="string"?r.path:typeof r.file_path=="string"?r.file_path:typeof r.saved_path=="string"?r.saved_path:"";if(!n.trim())return null;const e=typeof r.name=="string"&&r.name.trim()?r.name:C.basename(n)||"image.png",o=typeof r.mimeType=="string"&&r.mimeType.trim()?r.mimeType:W(n);return{path:n,name:e,mimeType:o,kind:"image"}}function F(t){return Array.isArray(t)?t.map(n=>{if(typeof n=="string")return n;if(typeof n=="object"&&n!==null){const e=n;return typeof e.text=="string"?e.text:""}return""}).map(n=>h(n)).filter(Boolean).join(`
2
2
 
3
- `):""}function U(e){const r=d(e);if(!r)return{text:"",strippedWrapper:!1};const n=r.match(/My request for Codex:\s*([\s\S]*)$/i);return n&&n[1]?{text:K(n[1]),strippedWrapper:!0}:{text:r,strippedWrapper:!1}}function K(e){return d(e.replace(/<image>\s*<\/image>/gi,""))}function w(e){const r=d(e);if(!r)return!1;if(j(r)||/^<environment_context>[\s\S]*<\/environment_context>$/i.test(r)&&r.includes("<current_date>")&&r.includes("<timezone>")&&r.includes("<workspace_roots>"))return!0;const n=r.startsWith("# AGENTS.md instructions for ")&&r.includes("<INSTRUCTIONS>")&&r.includes("</INSTRUCTIONS>"),t=r.includes("<environment_context>")&&r.includes("</environment_context>");return n&&t}function F(e){const r=R(e.text_elements),n=Array.isArray(e.input)?R(e.input):"",t=typeof e.message=="string"?U(e.message):{text:"",strippedWrapper:!1},o=t.text,i=r||n||o,c=t.strippedWrapper?"codex_app_context_wrapper":void 0,f=Array.isArray(e.local_images)?e.local_images.map(O).filter(a=>a!=null):[];return!i&&f.length===0||j(i)&&f.length===0||w(i)&&f.length===0?null:f.length>0?{payload:k(i,f),titleText:i,repairHint:c}:{payload:i,titleText:i,repairHint:c}}function tt(e){return e==="turn_completed"||e==="turn_complete"||e==="task_complete"}function et(e){return/approval|permission|confirm|user[_-]?input|input[_-]?required/i.test(e)}function nt(e){const r=typeof e.command=="string"?e.command:Array.isArray(e.command)?e.command.join(" "):"",n=typeof e.prompt=="string"?e.prompt:typeof e.reason=="string"?e.reason:typeof e.message=="string"?e.message:r?`Codex \u8BF7\u6C42\u6267\u884C\u547D\u4EE4\uFF1A${r}`:"Codex \u6B63\u5728\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u540E\u7EE7\u7EED\u3002";return Q({title:"\u9700\u8981\u7528\u6237\u786E\u8BA4",content:n,source:"codex",actionHint:"\u5F53\u524D\u795E\u5FF5\u6682\u4E0D\u652F\u6301\u8FDC\u7A0B\u786E\u8BA4\uFF0C\u8BF7\u56DE\u5230\u8FD0\u884C Codex \u7684\u7535\u8111/\u7EC8\u7AEF\u5B8C\u6210\u786E\u8BA4\u3002"})}function $(e,r){return Array.isArray(e)?d(e.map(n=>{if(typeof n=="string")return n;if(typeof n!="object"||n===null)return"";const t=n;return t.type!==r?"":typeof t.text=="string"?t.text:""}).filter(Boolean).join(`
3
+ `):""}function z(t){const r=h(t);if(!r)return{text:"",strippedWrapper:!1};const n=r.match(/My request for Codex:\s*([\s\S]*)$/i);return n&&n[1]?{text:et(n[1]),strippedWrapper:!0}:{text:r,strippedWrapper:!1}}function et(t){return h(t.replace(/<image>\s*<\/image>/gi,""))}function B(t){const r=h(t);if(!r)return!1;if(v(r)||/^<environment_context>[\s\S]*<\/environment_context>$/i.test(r)&&r.includes("<current_date>")&&r.includes("<timezone>")&&r.includes("<workspace_roots>"))return!0;const n=r.startsWith("<recommended_plugins>")&&r.includes("</recommended_plugins>"),e=r.includes("# AGENTS.md instructions for ")&&r.includes("<INSTRUCTIONS>")&&r.includes("</INSTRUCTIONS>"),o=r.includes("<environment_context>")&&r.includes("</environment_context>");return e&&o&&(r.startsWith("# AGENTS.md instructions for ")||n)}function H(t){const r=F(t.text_elements),n=Array.isArray(t.input)?F(t.input):"",e=typeof t.message=="string"?z(t.message):{text:"",strippedWrapper:!1},o=e.text,i=r||n||o,c=e.strippedWrapper?"codex_app_context_wrapper":void 0,f=Array.isArray(t.local_images)?t.local_images.map(tt).filter(a=>a!=null):[];return!i&&f.length===0||v(i)&&f.length===0||B(i)&&f.length===0?null:f.length>0?{payload:N(i,f),titleText:i,repairHint:c}:{payload:i,titleText:i,repairHint:c}}function nt(t){return t==="turn_completed"||t==="turn_complete"||t==="task_complete"}function rt(t){return/approval|permission|confirm|user[_-]?input|input[_-]?required/i.test(t)}function ot(t){const r=typeof t.command=="string"?t.command:Array.isArray(t.command)?t.command.join(" "):"",n=typeof t.prompt=="string"?t.prompt:typeof t.reason=="string"?t.reason:typeof t.message=="string"?t.message:r?`Codex \u8BF7\u6C42\u6267\u884C\u547D\u4EE4\uFF1A${r}`:"Codex \u6B63\u5728\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u540E\u7EE7\u7EED\u3002";return Z({title:"\u9700\u8981\u7528\u6237\u786E\u8BA4",content:n,source:"codex",actionHint:"\u5F53\u524D\u795E\u5FF5\u6682\u4E0D\u652F\u6301\u8FDC\u7A0B\u786E\u8BA4\uFF0C\u8BF7\u56DE\u5230\u8FD0\u884C Codex \u7684\u7535\u8111/\u7EC8\u7AEF\u5B8C\u6210\u786E\u8BA4\u3002"})}function L(t,r){return Array.isArray(t)?h(t.map(n=>{if(typeof n=="string")return n;if(typeof n!="object"||n===null)return"";const e=n;return e.type!==r?"":typeof e.text=="string"?e.text:""}).filter(Boolean).join(`
4
4
 
5
- `)):""}function rt(e){if(!Array.isArray(e))return[];const r=[];for(const n of e){if(typeof n!="object"||n===null)continue;const t=n;if(t.type!=="input_image")continue;const o=typeof t.path=="string"?t.path:typeof t.file_path=="string"?t.file_path:typeof t.saved_path=="string"?t.saved_path:typeof t.image_path=="string"?t.image_path:"";o.trim()&&r.push({path:o,name:b.basename(o)||"image.png",mimeType:E(o),kind:"image"})}return r}function z(e){if(e.type!=="message")return null;const r=e.role==="assistant"?"agent":e.role==="user"?"user":null;if(!r)return null;if(r==="agent"){const i=$(e.content,"output_text");return j(i)?null:i?{role:r,payload:i,titleText:i}:null}const n=U($(e.content,"input_text")),t=n.text,o=rt(e.content);return!t&&o.length===0||j(t)&&o.length===0||w(t)&&o.length===0?null:{role:r,payload:o.length>0?k(t,o):t,titleText:t,repairHint:n.strippedWrapper?"codex_app_context_wrapper":void 0}}function B(e){if(!e)return"";try{const r=JSON.parse(e);if(typeof r=="object"&&r!==null){const n=r;if(n.type==="user"&&typeof n.content=="string")return d(n.content)}}catch{}return d(e)}function ot(e,r,n,t){return e.agentType!=="codex"||e.role!==r||q(e.payload)||Math.abs(e.ts-t)>300*1e3?!1:B(e.payload)===B(n)}function W(e,r,n,t){for(let o=e.length-1;o>=0;o-=1){const i=e[o];if(i){if(ot(i,r,n,t))return o;if(i.agentType==="codex"&&Math.abs(i.ts-t)>300*1e3)break}}return-1}function C(e,r,n,t,o,i,c,f,a,u,s="agent",m=!1,p){c&&e.push({agentType:"codex",sourceMode:"codex_rollout_import",sourceSessionKey:o,sourceEventKey:Z(r,n,t),cursor:Y(r,n),role:s,ts:i,payload:c,title:f,modelId:a,workDir:u,terminal:m,repairHint:p})}function x(e,r,n,t,o,i,c,f,a,u,s,m){C(e,r,n,t,o,i,X(c,s,m),f,a,u)}function it(e,r,n,t,o,i,c,f,a){const u=typeof t.type=="string"?t.type:"";if(u){if(u==="message"){const s=z(t);if(!s||W(e,s.role,s.payload,i)>=0)return;C(e,r,n,`${u}:${s.role}`,o,i,s.payload,c,f,a,s.role,!1,s.repairHint);return}if(u==="function_call"){const s=typeof t.name=="string"?t.name:"function_call";x(e,r,n,u,o,i,s,c,f,a,P(t.arguments));return}if(u==="function_call_output"){x(e,r,n,u,o,i,"function_call_output",c,f,a,t.call_id,t.output);return}if(u==="custom_tool_call"){const s=typeof t.name=="string"?t.name:"custom_tool_call",m=t.input!==void 0?{input:t.input}:void 0;x(e,r,n,u,o,i,s,c,f,a,m);return}if(u==="custom_tool_call_output"){const s=typeof t.name=="string"?t.name:"custom_tool_call_output",m=t.output??t.result??t.content;x(e,r,n,u,o,i,s,c,f,a,t.call_id,m);return}if(u==="web_search_call"){const s=typeof t.action=="object"&&t.action!==null?t.action:null;x(e,r,n,u,o,i,"web_search",c,f,a,s??t.query);return}}}function st(e,r,n,t,o,i,c,f,a){const u=typeof t.type=="string"?t.type:"";if(u){if(u==="user_message"){const s=F(t);if(!s)return;C(e,r,n,u,o,i,s.payload,c,f,a,"user",!1,s.repairHint);return}if(u==="agent_message"){const s=typeof t.message=="string"?d(t.message):"";if(!s||j(s))return;C(e,r,n,u,o,i,s,c,f,a);return}if(u==="turn_aborted"||u==="error"){const s=typeof t.message=="string"?d(t.message):typeof t.error=="string"?d(t.error):"";if(!s)return;C(e,r,n,u,o,i,s,c,f,a,"agent",!0);return}if(et(u)){C(e,r,n,u,o,i,nt(t),c,f,a,"agent");return}if(u==="exec_command_end"){const s=Array.isArray(t.command)?t.command:t.parsed_cmd??t.command,m=t.exit_code??t.exitCode,p=[typeof t.aggregated_output=="string"?t.aggregated_output:"",m!=null?`
6
- (exit ${String(m)})`:""].join("");x(e,r,n,u,o,i,"command_execution",c,f,a,{command:s},p);return}if(u==="patch_apply_end"){x(e,r,n,u,o,i,"apply_patch",c,f,a,t.changes,{success:t.success,stdout:t.stdout,stderr:t.stderr});return}if(u==="mcp_tool_call_end"){const s=typeof t.invocation=="object"&&t.invocation!==null?t.invocation:null,m=typeof s?.tool=="string"?s.tool:"mcp_tool_call",p={server:s?.server,...typeof s?.arguments=="object"&&s.arguments!==null?s.arguments:{}};x(e,r,n,u,o,i,m,c,f,a,p,t.result);return}if(u==="dynamic_tool_call_request"){const s=typeof t.tool=="string"?t.tool:"dynamic_tool_call";x(e,r,n,u,o,i,s,c,f,a,t.arguments);return}if(u==="dynamic_tool_call_response"){const s=typeof t.tool=="string"?t.tool:"dynamic_tool_call";x(e,r,n,u,o,i,s,c,f,a,t.callId??t.call_id,t.result??t.output);return}if(u==="web_search_end"){const s=typeof t.action=="object"&&t.action!==null?t.action:null;x(e,r,n,u,o,i,"web_search",c,f,a,s??t.query,t.query);return}if(u==="image_generation_end"){const s=typeof t.saved_path=="string"?t.saved_path.trim():"";if(!s)return;C(e,r,n,u,o,i,V(s),c,f,a);return}u==="view_image_tool_call"&&x(e,r,n,u,o,i,"view_image",c,f,a,t.path??t.image_path??t)}}function A(e=!1){return{sourceSessionKey:"",workDir:"",modelId:null,title:"",skipImport:e}}function H(e){if((typeof e.thread_source=="string"?e.thread_source.trim().toLowerCase():"")==="subagent")return!0;const n=typeof e.source=="object"&&e.source!==null?e.source:null;if(n&&typeof n.subagent=="object"&&n.subagent!==null)return!0;const t=typeof e.parent_thread_id=="string"&&e.parent_thread_id.trim().length>0,o=typeof e.agent_nickname=="string"||typeof e.agent_role=="string"||typeof e.multi_agent_version=="string";return t&&o}function ut(e){const r=T.openSync(e,"r");try{const n=[];let t=0,o="";for(;t<1024*1024;){const a=Buffer.alloc(16384),u=T.readSync(r,a,0,a.length,t);if(u<=0)break;const s=a.subarray(0,u);n.push(s),t+=u;const m=Buffer.concat(n),p=m.indexOf(10);if(p>=0){o=m.subarray(0,p).toString("utf8");break}}!o&&n.length>0&&(o=Buffer.concat(n).toString("utf8"));const i=I(o);if(!i||i.type!=="session_meta")return A();const c=typeof i.payload=="object"&&i.payload!==null?i.payload:null;if(!c)return A();if(H(c))return A(!0);const f=typeof c.id=="string"?c.id:"";return{sourceSessionKey:f,workDir:typeof c.cwd=="string"?c.cwd:"",modelId:L(c),title:J(f)??"",skipImport:!1}}finally{T.closeSync(r)}return A()}function L(e){return(typeof e.model=="string"?e.model.trim():"")||null}function ct(e,r){if((typeof e.id=="string"?e.id:typeof e.thread_id=="string"?e.thread_id:typeof e.session_id=="string"?e.session_id:"")!==r)return null;const o=(typeof e.thread_name=="string"?e.thread_name:typeof e.title=="string"?e.title:"").trim();return o?o.slice(0,120):null}function J(e){if(!e)return null;const r=b.join(v.homedir(),".codex","session_index.jsonl");if(!T.existsSync(r))return null;let n=null;return N(r,0,t=>{if(!t.trim())return;const o=I(t);if(!o)return;const i=ct(o,e);i&&(n=i)}),n}function gt(){const e=[b.join(v.homedir(),".codex","sessions"),b.join(v.homedir(),".codex","archived_sessions")],r=[],n=t=>{for(const o of T.readdirSync(t,{withFileTypes:!0})){const i=b.join(t,o.name);o.isDirectory()?n(i):o.isFile()&&o.name.endsWith(".jsonl")&&r.push(i)}};for(const t of e)T.existsSync(t)&&n(t);return[...new Set(r)].sort()}function _t(e,r){const n=[];let{sourceSessionKey:t,workDir:o,modelId:i,title:c,skipImport:f}=ut(e);if(f)return{nextOffset:T.statSync(e).size,events:[]};let a=!1;return{nextOffset:N(e,r,(s,m)=>{const p=I(s);if(!p)return;const S=typeof p.type=="string"?p.type:"",l=typeof p.payload=="object"&&p.payload!==null?p.payload:null,M=D(p.timestamp);if(!(!l||!M)){if(S==="session_meta"){if(H(l)){n.length=0,f=!0;return}t=typeof l.id=="string"?l.id:t,o=typeof l.cwd=="string"?l.cwd:o,i=L(l)??i,c||(c=J(t)??"");return}if(!f&&t){if(S==="response_item"){const y=z(l);y?.role==="user"&&y.titleText&&!c&&(c=y.titleText.slice(0,80)),it(n,e,m,l,t,M,c,i,o);return}if(S==="event_msg"){const y=typeof l.type=="string"?l.type:"";if(tt(y)){let g=!1;for(let _=n.length-1;_>=0;_-=1){const h=n[_];if(h?.agentType==="codex"&&h.role==="agent"&&!q(h.payload)){h.terminal=!0,g=!0,a=!1;break}}if(!g)for(let _=n.length-1;_>=0;_-=1){const h=n[_];if(!(h?.agentType!=="codex"||h.role!=="agent")){h.terminal=!0,g=!0,a=!1;break}}g||(a=!0);return}if(y==="user_message"){const g=F(l);if(g?.titleText&&!c&&(c=g.titleText.slice(0,80)),g){const _=W(n,"user",g.payload,M);_>=0&&n.splice(_,1)}}else if(y==="agent_message"){const g=typeof l.message=="string"?d(l.message):"";if(g&&!j(g)){const _=W(n,"agent",g,M);_>=0&&n.splice(_,1)}}const G=n.length;st(n,e,m,l,t,M,c,i,o),a&&n.length>G&&(n[n.length-1].terminal=!0,a=!1)}}}}),events:n}}export{gt as listCodexRolloutFiles,_t as parseCodexRolloutChunk};
5
+ `)):""}function it(t){if(!Array.isArray(t))return[];const r=[];for(const n of t){if(typeof n!="object"||n===null)continue;const e=n;if(e.type!=="input_image")continue;const o=typeof e.path=="string"?e.path:typeof e.file_path=="string"?e.file_path:typeof e.saved_path=="string"?e.saved_path:typeof e.image_path=="string"?e.image_path:"";o.trim()&&r.push({path:o,name:C.basename(o)||"image.png",mimeType:W(o),kind:"image"})}return r}function G(t){if(t.type!=="message")return null;const r=t.role==="assistant"?"agent":t.role==="user"?"user":null;if(!r)return null;if(r==="agent"){const i=L(t.content,"output_text");return v(i)?null:i?{role:r,payload:i,titleText:i}:null}const n=z(L(t.content,"input_text")),e=n.text,o=it(t.content);return!e&&o.length===0||v(e)&&o.length===0||B(e)&&o.length===0?null:{role:r,payload:o.length>0?N(e,o):e,titleText:e,repairHint:n.strippedWrapper?"codex_app_context_wrapper":void 0}}function J(t){if(!t)return"";try{const r=JSON.parse(t);if(typeof r=="object"&&r!==null){const n=r;if(n.type==="user"&&typeof n.content=="string")return h(n.content)}}catch{}return h(t)}function st(t,r,n,e){return t.agentType!=="codex"||t.role!==r||q(t.payload)||Math.abs(t.ts-e)>300*1e3?!1:J(t.payload)===J(n)}function R(t,r,n,e){for(let o=t.length-1;o>=0;o-=1){const i=t[o];if(i){if(st(i,r,n,e))return o;if(i.agentType==="codex"&&Math.abs(i.ts-e)>300*1e3)break}}return-1}function M(t,r,n,e,o,i,c,f,a,u,s="agent",m=!1,l){c&&t.push({agentType:"codex",sourceMode:"codex_rollout_import",sourceSessionKey:o,sourceEventKey:$(r,n,e),cursor:U(r,n),role:s,ts:i,payload:c,title:f,modelId:a,workDir:u,terminal:m,repairHint:l})}function _(t,r,n,e,o,i,c,f,a,u,s,m){const l=$(r,n,e),A=`native:${l}`,y=1,j=m!==void 0?"completed":"running";t.push({agentType:"codex",sourceMode:"codex_rollout_import",sourceSessionKey:o,sourceEventKey:l,cursor:U(r,n),role:"agent",ts:i,payload:D({name:c,toolCallId:A,detailRevision:y,status:j}),title:f,modelId:a,workDir:u,localToolDetail:{toolCallId:A,detailRevision:y,name:c,...s!==void 0?{args:s}:{},...m!==void 0?{result:m}:{},status:j}})}function ut(t,r,n,e,o,i,c,f,a){const u=typeof e.type=="string"?e.type:"";if(u){if(u==="message"){const s=G(e);if(!s||R(t,s.role,s.payload,i)>=0)return;M(t,r,n,`${u}:${s.role}`,o,i,s.payload,c,f,a,s.role,!1,s.repairHint);return}if(u==="function_call"){const s=typeof e.name=="string"?e.name:"function_call";_(t,r,n,u,o,i,s,c,f,a,K(e.arguments));return}if(u==="function_call_output"){_(t,r,n,u,o,i,"function_call_output",c,f,a,e.call_id,e.output);return}if(u==="custom_tool_call"){const s=typeof e.name=="string"?e.name:"custom_tool_call",m=e.input!==void 0?{input:e.input}:void 0;_(t,r,n,u,o,i,s,c,f,a,m);return}if(u==="custom_tool_call_output"){const s=typeof e.name=="string"?e.name:"custom_tool_call_output",m=e.output??e.result??e.content;_(t,r,n,u,o,i,s,c,f,a,e.call_id,m);return}if(u==="web_search_call"){const s=typeof e.action=="object"&&e.action!==null?e.action:null;_(t,r,n,u,o,i,"web_search",c,f,a,s??e.query);return}}}function ct(t,r,n,e,o,i,c,f,a){const u=typeof e.type=="string"?e.type:"";if(u){if(u==="user_message"){const s=H(e);if(!s)return;M(t,r,n,u,o,i,s.payload,c,f,a,"user",!1,s.repairHint);return}if(u==="agent_message"){const s=typeof e.message=="string"?h(e.message):"";if(!s||v(s))return;M(t,r,n,u,o,i,s,c,f,a);return}if(u==="turn_aborted"||u==="error"){const s=typeof e.message=="string"?h(e.message):typeof e.error=="string"?h(e.error):"";if(!s)return;M(t,r,n,u,o,i,s,c,f,a,"agent",!0);return}if(rt(u)){M(t,r,n,u,o,i,ot(e),c,f,a,"agent");return}if(u==="exec_command_end"){const s=Array.isArray(e.command)?e.command:e.parsed_cmd??e.command,m=e.exit_code??e.exitCode,l=[typeof e.aggregated_output=="string"?e.aggregated_output:"",m!=null?`
6
+ (exit ${String(m)})`:""].join("");_(t,r,n,u,o,i,"command_execution",c,f,a,{command:s},l);return}if(u==="patch_apply_end"){_(t,r,n,u,o,i,"apply_patch",c,f,a,e.changes,{success:e.success,stdout:e.stdout,stderr:e.stderr});return}if(u==="mcp_tool_call_end"){const s=typeof e.invocation=="object"&&e.invocation!==null?e.invocation:null,m=typeof s?.tool=="string"?s.tool:"mcp_tool_call",l={server:s?.server,...typeof s?.arguments=="object"&&s.arguments!==null?s.arguments:{}};_(t,r,n,u,o,i,m,c,f,a,l,e.result);return}if(u==="dynamic_tool_call_request"){const s=typeof e.tool=="string"?e.tool:"dynamic_tool_call";_(t,r,n,u,o,i,s,c,f,a,e.arguments);return}if(u==="dynamic_tool_call_response"){const s=typeof e.tool=="string"?e.tool:"dynamic_tool_call";_(t,r,n,u,o,i,s,c,f,a,e.callId??e.call_id,e.result??e.output);return}if(u==="web_search_end"){const s=typeof e.action=="object"&&e.action!==null?e.action:null;_(t,r,n,u,o,i,"web_search",c,f,a,s??e.query,e.query);return}if(u==="image_generation_end"){const s=typeof e.saved_path=="string"?e.saved_path.trim():"";if(!s)return;M(t,r,n,u,o,i,P(s),c,f,a);return}u==="view_image_tool_call"&&_(t,r,n,u,o,i,"view_image",c,f,a,e.path??e.image_path??e)}}function E(t=!1){return{sourceSessionKey:"",workDir:"",modelId:null,title:"",skipImport:t}}function Q(t){if((typeof t.thread_source=="string"?t.thread_source.trim().toLowerCase():"")==="subagent")return!0;const n=typeof t.source=="object"&&t.source!==null?t.source:null;if(n&&typeof n.subagent=="object"&&n.subagent!==null)return!0;const e=typeof t.parent_thread_id=="string"&&t.parent_thread_id.trim().length>0,o=typeof t.agent_nickname=="string"||typeof t.agent_role=="string"||typeof t.multi_agent_version=="string";return e&&o}function ft(t){const r=b.openSync(t,"r");try{const n=[];let e=0,o="";for(;e<1024*1024;){const a=Buffer.alloc(16384),u=b.readSync(r,a,0,a.length,e);if(u<=0)break;const s=a.subarray(0,u);n.push(s),e+=u;const m=Buffer.concat(n),l=m.indexOf(10);if(l>=0){o=m.subarray(0,l).toString("utf8");break}}!o&&n.length>0&&(o=Buffer.concat(n).toString("utf8"));const i=k(o);if(!i||i.type!=="session_meta")return E();const c=typeof i.payload=="object"&&i.payload!==null?i.payload:null;if(!c)return E();if(Q(c))return E(!0);const f=typeof c.id=="string"?c.id:"";return{sourceSessionKey:f,workDir:typeof c.cwd=="string"?c.cwd:"",modelId:V(c),title:X(f)??"",skipImport:!1}}finally{b.closeSync(r)}return E()}function V(t){return(typeof t.model=="string"?t.model.trim():"")||null}function at(t,r){if((typeof t.id=="string"?t.id:typeof t.thread_id=="string"?t.thread_id:typeof t.session_id=="string"?t.session_id:"")!==r)return null;const o=(typeof t.thread_name=="string"?t.thread_name:typeof t.title=="string"?t.title:"").trim();return o?o.slice(0,120):null}function X(t){if(!t)return null;const r=C.join(I.homedir(),".codex","session_index.jsonl");if(!b.existsSync(r))return null;let n=null;return w(r,0,e=>{if(!e.trim())return;const o=k(e);if(!o)return;const i=at(o,t);i&&(n=i)}),n}function _t(){const t=[C.join(I.homedir(),".codex","sessions"),C.join(I.homedir(),".codex","archived_sessions")],r=[],n=e=>{for(const o of b.readdirSync(e,{withFileTypes:!0})){const i=C.join(e,o.name);o.isDirectory()?n(i):o.isFile()&&o.name.endsWith(".jsonl")&&r.push(i)}};for(const e of t)b.existsSync(e)&&n(e);return[...new Set(r)].sort()}function xt(t,r){const n=[],e=ft(t);let{sourceSessionKey:o,workDir:i,modelId:c,title:f,skipImport:a}=e,u=e.title;if(a)return{nextOffset:b.statSync(t).size,events:[]};let s=!1;const m=w(t,r,(l,A)=>{const y=k(l);if(!y)return;const j=typeof y.type=="string"?y.type:"",p=typeof y.payload=="object"&&y.payload!==null?y.payload:null,S=O(y.timestamp);if(!(!p||!S)){if(j==="session_meta"){if(Q(p)){n.length=0,a=!0;return}o=typeof p.id=="string"?p.id:o,i=typeof p.cwd=="string"?p.cwd:i,c=V(p)??c;const x=X(o);x&&(u=x,f=x);return}if(!a&&o){if(j==="response_item"){const x=G(p);x?.role==="user"&&x.titleText&&!f&&(f=x.titleText.slice(0,80)),ut(n,t,A,p,o,S,f,c,i);return}if(j==="event_msg"){const x=typeof p.type=="string"?p.type:"";if(nt(x)){let g=!1;for(let d=n.length-1;d>=0;d-=1){const T=n[d];if(T?.agentType==="codex"&&T.role==="agent"&&!q(T.payload)){T.terminal=!0,g=!0,s=!1;break}}if(!g)for(let d=n.length-1;d>=0;d-=1){const T=n[d];if(!(T?.agentType!=="codex"||T.role!=="agent")){T.terminal=!0,g=!0,s=!1;break}}g||(s=!0);return}if(x==="user_message"){const g=H(p);if(g?.titleText&&!f&&(f=g.titleText.slice(0,80)),g){const d=R(n,"user",g.payload,S);d>=0&&n.splice(d,1)}}else if(x==="agent_message"){const g=typeof p.message=="string"?h(p.message):"";if(g&&!v(g)){const d=R(n,"agent",g,S);d>=0&&n.splice(d,1)}}const Y=n.length;ct(n,t,A,p,o,S,f,c,i),s&&n.length>Y&&(n[n.length-1].terminal=!0,s=!1)}}}});for(const l of n)l.titleSource=u&&l.title===u?"agent_native":"first_user_fallback";return{nextOffset:m,events:n}}export{_t as listCodexRolloutFiles,X as lookupCodexThreadName,xt as parseCodexRolloutChunk};
@@ -0,0 +1,18 @@
1
+ type WatchHandle = {
2
+ close(): void;
3
+ };
4
+ type WatchListener = (eventType: string, filename: string | Buffer | null) => void;
5
+ type WatchFactory = (directory: string, listener: WatchListener) => WatchHandle;
6
+ export declare class NativeFusionFileWatcher {
7
+ private onChanged;
8
+ private watchFactory;
9
+ private watchers;
10
+ private filesByDirectory;
11
+ private debounceTimers;
12
+ constructor(onChanged: (filePaths: string[]) => void, watchFactory?: WatchFactory);
13
+ refresh(filePaths: readonly string[]): void;
14
+ stop(): void;
15
+ private handleChange;
16
+ private schedule;
17
+ }
18
+ export {};
@@ -0,0 +1 @@
1
+ import h from"node:fs";import i from"node:path";const a=250;class f{onChanged;watchFactory;watchers=new Map;filesByDirectory=new Map;debounceTimers=new Map;constructor(t,s=(e,o)=>h.watch(e,o)){this.onChanged=t,this.watchFactory=s}refresh(t){const s=new Map;for(const e of t){const o=i.dirname(e),r=s.get(o)??new Set;r.add(e),s.set(o,r)}for(const[e,o]of this.watchers.entries())s.has(e)||(o.close(),this.watchers.delete(e));this.filesByDirectory=s;for(const e of s.keys())if(!this.watchers.has(e))try{const o=this.watchFactory(e,(r,n)=>{this.handleChange(e,n)});this.watchers.set(e,o)}catch(o){console.warn(`[native-fusion] unable to watch ${e}`,o)}}stop(){for(const t of this.watchers.values())t.close();this.watchers.clear(),this.filesByDirectory.clear();for(const t of this.debounceTimers.values())clearTimeout(t);this.debounceTimers.clear()}handleChange(t,s){const e=s?i.join(t,s.toString()):null,o=e&&d(e)?[e]:[...this.filesByDirectory.get(t)??[]];for(const r of o)this.schedule(r)}schedule(t){const s=this.debounceTimers.get(t);s&&clearTimeout(s);const e=setTimeout(()=>{this.debounceTimers.delete(t),this.onChanged([t])},a);this.debounceTimers.set(t,e)}}function d(c){return c.endsWith(".jsonl")||c.endsWith(".opencode-session.json")}export{f as NativeFusionFileWatcher};