shennian 0.5.4 → 0.5.6

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.
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "jsMinified": true,
4
4
  "minifier": "esbuild",
5
- "fileCount": 248,
5
+ "fileCount": 250,
6
6
  "files": [
7
7
  {
8
8
  "file": "bin/shennian-adapter-host.js",
@@ -461,8 +461,8 @@
461
461
  },
462
462
  {
463
463
  "file": "src/commands/room.js",
464
- "beforeBytes": 25537,
465
- "afterBytes": 15202
464
+ "beforeBytes": 26027,
465
+ "afterBytes": 15403
466
466
  },
467
467
  {
468
468
  "file": "src/commands/runtime.js",
@@ -474,6 +474,11 @@
474
474
  "beforeBytes": 1288,
475
475
  "afterBytes": 936
476
476
  },
477
+ {
478
+ "file": "src/commands/skill.js",
479
+ "beforeBytes": 1616,
480
+ "afterBytes": 991
481
+ },
477
482
  {
478
483
  "file": "src/commands/support.js",
479
484
  "beforeBytes": 4571,
@@ -511,8 +516,8 @@
511
516
  },
512
517
  {
513
518
  "file": "src/daemon-ipc/control.js",
514
- "beforeBytes": 4016,
515
- "afterBytes": 2227
519
+ "beforeBytes": 4433,
520
+ "afterBytes": 2484
516
521
  },
517
522
  {
518
523
  "file": "src/daemon-ipc/json.js",
@@ -576,8 +581,8 @@
576
581
  },
577
582
  {
578
583
  "file": "src/index.js",
579
- "beforeBytes": 42923,
580
- "afterBytes": 21315
584
+ "beforeBytes": 43108,
585
+ "afterBytes": 21430
581
586
  },
582
587
  {
583
588
  "file": "src/log-redaction.js",
@@ -796,8 +801,8 @@
796
801
  },
797
802
  {
798
803
  "file": "src/room/invite-qr.js",
799
- "beforeBytes": 888,
800
- "afterBytes": 519
804
+ "beforeBytes": 1033,
805
+ "afterBytes": 600
801
806
  },
802
807
  {
803
808
  "file": "src/room/live-delivery.js",
@@ -826,8 +831,8 @@
826
831
  },
827
832
  {
828
833
  "file": "src/room/mirror-sync-service.js",
829
- "beforeBytes": 12375,
830
- "afterBytes": 5688
834
+ "beforeBytes": 12390,
835
+ "afterBytes": 5700
831
836
  },
832
837
  {
833
838
  "file": "src/room/public-attachment-cache.js",
@@ -1021,8 +1026,8 @@
1021
1026
  },
1022
1027
  {
1023
1028
  "file": "src/session/handlers/room-managed.js",
1024
- "beforeBytes": 5010,
1025
- "afterBytes": 2679
1029
+ "beforeBytes": 5106,
1030
+ "afterBytes": 2698
1026
1031
  },
1027
1032
  {
1028
1033
  "file": "src/session/handlers/session-refresh.js",
@@ -1061,8 +1066,8 @@
1061
1066
  },
1062
1067
  {
1063
1068
  "file": "src/session/manager.js",
1064
- "beforeBytes": 40963,
1065
- "afterBytes": 21571
1069
+ "beforeBytes": 41108,
1070
+ "afterBytes": 21675
1066
1071
  },
1067
1072
  {
1068
1073
  "file": "src/session/native-cleanup-outbox.js",
@@ -1184,6 +1189,11 @@
1184
1189
  "beforeBytes": 164,
1185
1190
  "afterBytes": 1
1186
1191
  },
1192
+ {
1193
+ "file": "src/skill/updater.js",
1194
+ "beforeBytes": 14594,
1195
+ "afterBytes": 7316
1196
+ },
1187
1197
  {
1188
1198
  "file": "src/support/support-client.js",
1189
1199
  "beforeBytes": 3060,
@@ -1,4 +1,4 @@
1
- import x from"node:crypto";import q from"node:fs";import I from"node:path";import{PublicAttachmentCache as T}from"../room/public-attachment-cache.js";import{PublicRoomClient as $,PublicRoomClientError as O}from"../room/public-room-client.js";import{callDaemonIpc as E}from"../daemon-ipc/client.js";import{DaemonIpcError as N}from"../daemon-ipc/protocol.js";import{MANAGED_ROOM_IPC_ROOT_ENV as C,resolveManagedRoomIpcPaths as V}from"../daemon-ipc/paths.js";import{MANAGED_ROOM_CONTEXT_ENV as F}from"../room/managed-context.js";import{createRoomInviteQr as G}from"../room/invite-qr.js";import{externalRoomNextAction as j,managedRoomNextAction as H,SHENNIAN_INSTALL_URL as M}from"../room/room-error-guidance.js";import{attachmentMediaKind as J}from"../room/attachment-upload.js";const Q={write:o=>{process.stdout.write(o)},fail:()=>{process.exitCode=1}};function pe(o,n={}){const t=n.client??new $,c=n.cache??new T({client:t}),a=n.io??Q,v=n.createInviteQr??G,p=async(i,e)=>{const s=await v(i).catch(()=>({imagePath:null,mimeType:"image/png",payload:i}));return{inviteUrl:i,installUrl:M,text:Z(i,e),inviteQr:s}},m=o.command("room").description("Use one explicitly referenced Shennian Room");m.command("schedule").description("Manage scheduled work in the current managed conversation").requiredOption("--operation <operation>","list/create/update/pause/resume/delete").option("--id <id>").option("--version <version>").option("--operation-id <operationId>").option("--time-description <timeDescription>").option("--timezone <timezone>").option("--prompt <prompt>").action(async i=>l("room.schedule",a,async()=>{const e=b();return e||r("managed_room_context_required","Scheduling is only available in managed Room context.",j()),g(e,"schedule",{...i,version:i.version?Number(i.version):void 0})})),m.command("create").description("Create a Room as the paired user").requiredOption("--name <name>","Room name").action(async i=>l("room.create",a,async()=>{u("create");const e=i.name.trim();(!e||e.length>80)&&r("invalid_room_name","Room name must contain 1 to 80 characters.");const s=await t.create(e,x.randomUUID()),d=f(s.room,"invalid_room_response");return typeof d.inviteUrl!="string"&&r("invalid_room_response","Shennian returned an invalid Room invitation."),{...s,sharing:await p(d.inviteUrl,typeof d.name=="string"?d.name:e)}})),m.command("list").description("List Rooms for the paired user; duplicate names remain separate candidates").action(()=>l("room.list",a,()=>(u("list"),t.list()))),m.command("get <room-ref>").description("Get one explicitly referenced Room and its current version").action(i=>l("room.get",a,()=>(u("get"),t.get(h(i))))),m.command("update <room-ref>").description("Update one Room using the version returned by room get or room list").requiredOption("--version <version>","Exact Room version returned by Shennian").option("--name <name>","New Room name").option("--visibility <visibility>","private or public_read").option("--join-policy <policy>","open, approval, or closed").action((i,e)=>l("room.update",a,()=>{u("update");const s=e.name?.trim();return e.name!==void 0&&(!s||s.length>80)&&r("invalid_room_name","Room name must contain 1 to 80 characters."),e.visibility!==void 0&&e.visibility!=="private"&&e.visibility!=="public_read"&&r("invalid_room_visibility","--visibility must be private or public_read."),e.joinPolicy!==void 0&&e.joinPolicy!=="open"&&e.joinPolicy!=="approval"&&e.joinPolicy!=="closed"&&r("invalid_room_join_policy","--join-policy must be open, approval, or closed."),s===void 0&&e.visibility===void 0&&e.joinPolicy===void 0&&r("room_update_required","Provide --name, --visibility, or --join-policy."),t.update(h(i),{version:z(e.version),...s===void 0?{}:{name:s},...e.visibility===void 0?{}:{visibility:e.visibility},...e.joinPolicy===void 0?{}:{joinPolicy:e.joinPolicy}})})),m.command("delete <room-ref>").description("Delete this user's Room chat record without physically deleting the Room").requiredOption("--version <version>","Exact Room version returned by Shennian").action((i,e)=>l("room.delete",a,()=>(u("delete"),t.deleteRecord(h(i),z(e.version))))),m.command("join <invite-url>").description("Join or request access through an official Room invitation").option("--message <message>","Introduction and reason required when owner approval is enabled").action(async(i,e)=>l("room.join",a,async()=>{u("join");const s=P(i,"invite"),d=e.message?.trim();return d&&d.length>500&&r("join_message_too_long","Join request message must be at most 500 characters."),t.join(s.roomId,s.inviteToken,d)})),m.command("open <room-url>").description("Open one Room where the paired user is already a member").action(async i=>l("room.open",a,async()=>{u("open");const e=P(i,"stable");return t.open(e.roomId)})),m.command("status [room-ref]").description("Show the managed Room, or one explicitly referenced Room").action(async i=>l("room.status",a,()=>{const e=b();return e?(i&&r("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),g(e,"status",{})):t.status(A(i))})),m.command("read [room-ref]").description("Read recent, older, or newer messages from one Room").option("--before <sequence>","Read messages before this sequence").option("--after <sequence>","Read messages after this sequence").option("--limit <count>","Maximum messages, 1 to 50","50").action(async(i,e)=>{await l("room.read",a,async()=>{e.before&&e.after&&r("ambiguous_sequence_cursor","--before and --after are mutually exclusive."),e.before&&!L(e.before)&&r("invalid_sequence","--before sequence is invalid."),e.after&&!L(e.after)&&r("invalid_sequence","--after sequence is invalid.");const s=Number(e.limit);(!Number.isInteger(s)||s<1||s>50)&&r("invalid_limit","--limit must be between 1 and 50.");const d=b();if(d)return i&&r("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),g(d,"read",{...e.before?{before:e.before}:{},...e.after?{after:e.after}:{},limit:s});const R=A(i),w=await t.read(R,{before:e.before,after:e.after,limit:s});return W(w,R,c)})}),m.command("send [room-ref]").description("Send text and up to 10 files as the paired user").option("--text <text>","Message text").option("--file <path>","Local file path; repeat for multiple files",K,[]).action(async(i,e)=>{await l("room.send",a,async()=>{const s=e.text?.trim()??"";!s&&e.file.length===0&&r("room_message_empty","Provide --text or at least one --file."),e.file.length>10&&r("room_attachments_too_many","A message can contain at most 10 files.");const d=b();if(d)return i&&r("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),g(d,"send",{clientMessageId:`managed:${x.randomUUID()}`,text:s,files:e.file});const R=A(i),w=[];for(const k of e.file)w.push(await B(t,R,k));return t.send(R,{clientMessageId:`cli:${x.randomUUID()}`,...s?{text:s}:{},...w.length>0?{attachmentIds:w}:{}})})}),m.command("pause").description("Pause the current managed Agent in its bound Room").requiredOption("--reason <reason>","Short reason shown to all Room members").action(async i=>l("room.pause",a,()=>{const e=b();return e||r("managed_room_context_required","Pause is only available in managed Room context.",j()),g(e,"pause",{reason:i.reason})})),m.command("attachment").description("Download Room attachments through Shennian").command("get [room-ref] [attachment-id]").description("Download and verify one Room attachment").action(async(i,e)=>{await l("room.attachment.get",a,async()=>{const s=b(),d=s?i:e;return(!d||!/^ratt_[A-Za-z0-9_-]{1,191}$/.test(d))&&r("invalid_attachment_ref","attachment-id is invalid."),s?g(s,"attachment.get",{attachmentId:d}):c.materialize(A(i),d)})}),m.command("leave <room-ref>").description("Leave one explicitly referenced Room").action(async i=>l("room.leave",a,()=>(u("leave"),t.leave(h(i)))));const y=m.command("invite").description("Read or reset the current Room invitation");y.command("get <room-ref>").description("Get the current invitation and version for one Room").action(i=>l("room.invite.get",a,async()=>{u("invite get");const e=f(await t.getInvite(h(i)),"invalid_room_response"),s=f(e.invite,"invalid_room_response");return typeof s.url!="string"&&r("invalid_room_response","Shennian returned an invalid Room invitation."),{...e,sharing:await p(s.url)}})),y.command("reset <room-ref>").description("Reset an invitation using the version returned by room invite get").requiredOption("--version <version>","Positive invitation version").action((i,e)=>l("room.invite.reset",a,async()=>{u("invite reset");const s=f(await t.resetInvite(h(i),D(e.version,"room_invite_version")),"invalid_room_response"),d=f(s.invite,"invalid_room_response");return typeof d.url!="string"&&r("invalid_room_response","Shennian returned an invalid Room invitation."),{...s,sharing:await p(d.url)}}));const _=m.command("member").description("List or remove Room members");_.command("list <room-ref>").description("List public member references and versions for one Room").action(i=>l("room.member.list",a,()=>(u("member list"),t.listMembers(h(i))))),_.command("remove <room-ref>").description("Remove one exact member reference using its listed version").requiredOption("--member-ref <member-ref>","Public member reference returned by room member list").requiredOption("--version <version>","Positive member version returned by room member list").action((i,e)=>l("room.member.remove",a,()=>(u("member remove"),/^member_[A-Za-z0-9_-]{32,64}$/.test(e.memberRef)||r("invalid_room_member_ref","Use a memberRef returned by room member list."),t.removeMember(h(i),e.memberRef,D(e.version,"room_member_version"))))),m.command("search",{hidden:!0}).allowUnknownOption(!0).action(()=>S(a,"command_removed","Room search is not available to Agents.",null)),m.command("agent",{hidden:!0}).allowUnknownOption(!0).action(()=>S(a,"command_removed","Managed Agents are created in the Shennian mobile App.",null))}function Z(o,n){return[n?"\u9080\u8BF7\u4F60\u52A0\u5165\u795E\u5FF5\u7FA4\u804A\u300C"+n+"\u300D\u3002":"\u70B9\u51FB\u52A0\u5165\u795E\u5FF5\u7FA4\u804A\uFF0C\u6216\u628A\u672C\u6D88\u606F\u53D1\u7ED9\u4F60\u7684 AI\uFF1A",...n?["\u76F4\u63A5\u70B9\u51FB\uFF0C\u6216\u628A\u672C\u6D88\u606F\u53D1\u7ED9\u4F60\u7684 AI\uFF0C\u8BA9\u5B83\u5B89\u88C5\u795E\u5FF5\u3001\u5B8C\u6210\u914D\u5BF9\u5E76\u52A0\u5165\u7FA4\u804A\u3002"]:[],"\u9080\u8BF7\uFF1A"+o,"\u5B89\u88C5\uFF1A"+M].join(`
2
- `)}async function l(o,n,t){try{const c=await t();n.write(`${JSON.stringify({ok:!0,command:o,result:c})}
3
- `)}catch(c){if(c instanceof O){S(n,c.code,c.message,c.nextAction);return}if(c instanceof N){S(n,c.code,c.message,H(c.code));return}S(n,"room_command_failed","Shennian Room command failed.",null)}}function b(){return process.env[F]?.trim()||null}async function g(o,n,t){const c=process.env[C]?.trim();if(!c||!I.isAbsolute(c))throw new N("managed_room_context_unavailable","Managed Room IPC context is unavailable.");return E({method:"managed-room.execute",params:{context:o,action:n,...t},paths:V(c),timeoutMs:n==="send"||n==="attachment.get"?18e4:15e3})}function u(o){b()&&r("managed_room_action_forbidden",`${o} is not available in managed Room context.`)}function A(o){return o||r("room_ref_required","Use an official Room URL returned by Shennian.",j()),h(o)}function h(o){return P(o,"stable").roomId}function P(o,n){let t;const c=n==="invite"?"invalid_invite_url":"invalid_room_ref",a=n==="invite"?"Use an official Shennian Room invitation URL.":"Use an official Room URL returned by Shennian.";try{t=new URL(o)}catch{r(c,a)}const v=t.pathname.match(/^\/spaces\/([A-Za-z0-9_-]{1,191})$/),p=t.searchParams.get("invite");return(t.protocol!=="https:"||t.hostname!=="app.shennian.net"||t.port||t.username||t.password||t.hash||!v||t.searchParams.has("access")||[...t.searchParams.keys()].some(m=>m!=="invite")||n==="stable"&&p!=null||n==="invite"&&(p==null||!/^[A-Za-z0-9_-]{32,256}$/.test(p)))&&r(c,a),{roomId:v[1],...p?{inviteToken:p}:{}}}function L(o){return/^(0|[1-9]\d{0,19})$/.test(o)}function z(o){const n=new Date(o);return(Number.isNaN(n.getTime())||n.toISOString()!==o)&&r("invalid_room_version","Use the exact version returned by room get or room list."),o}function D(o,n){/^[1-9]\d*$/.test(o)||r(n,"Version must be a positive integer.");const t=Number(o);return Number.isSafeInteger(t)||r(n,"Version must be a positive integer."),t}function K(o,n){return[...n,o]}async function B(o,n,t){const c=I.resolve(t);let a;try{a=await q.promises.lstat(c)}catch{r("file_not_found",`File does not exist: ${t}`)}(!a.isFile()||a.isSymbolicLink())&&r("invalid_file",`Not a regular file: ${t}`),(a.size<1||a.size>100*1024*1024)&&r("invalid_file_size","Each file must be between 1 byte and 100 MB.");const v=await q.promises.readFile(c),p=x.createHash("sha256").update(v).digest("hex"),m=X(c),U=await o.initializeAttachment(n,{clientUploadId:`cli:${x.randomUUID()}`,name:I.basename(c),mediaKind:J(I.basename(c),m),mimeType:m,byteSize:a.size,sha256:p}),y=f(U.attachment,"invalid_attachment_response"),_=f(U.upload,"invalid_attachment_response");(typeof y.id!="string"||typeof _.url!="string"||_.method!=="PUT")&&r("invalid_attachment_response","Shennian returned an invalid attachment response.");const i=f(_.headers,"invalid_attachment_response");return Object.values(i).some(e=>typeof e!="string")&&r("invalid_attachment_response","Shennian returned invalid upload headers."),Y(_.url),await o.upload(_.url,i,v),await o.completeAttachment(n,y.id),y.id}function X(o){const n=I.extname(o).toLowerCase();return{".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webp":"image/webp",".gif":"image/gif",".heic":"image/heic",".heif":"image/heif",".mp3":"audio/mpeg",".wav":"audio/wav",".m4a":"audio/mp4",".mp4":"video/mp4",".mov":"video/quicktime",".pdf":"application/pdf",".txt":"text/plain",".md":"text/markdown",".json":"application/json",".csv":"text/csv",".zip":"application/zip",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}[n]??"application/octet-stream"}function W(o,n,t){return Array.isArray(o.messages)||r("invalid_server_response","Shennian returned invalid messages."),{messages:o.messages.map(c=>{const a=f(c,"invalid_server_response"),v=Array.isArray(a.attachments)?a.attachments:[];return{...a,attachments:v.map(p=>{const m=f(p,"invalid_server_response");return(typeof m.attachmentId!="string"||typeof m.name!="string"||typeof m.mimeType!="string"||typeof m.byteSize!="string"||typeof m.sha256!="string")&&r("invalid_server_response","Shennian returned invalid attachment metadata."),{...m,...t.lookup(n,m)}})}})}}function f(o,n){return(!o||typeof o!="object"||Array.isArray(o))&&r(n,"Invalid structured response."),o}function Y(o){let n;try{n=new URL(o)}catch{r("invalid_attachment_response","Invalid upload URL.")}const t=n.protocol==="http:"&&(n.hostname==="localhost"||n.hostname==="127.0.0.1");(n.protocol!=="https:"&&!t||n.username||n.password||n.hash)&&r("invalid_attachment_response","Invalid upload URL.")}function r(o,n,t=null){throw new O(o,n,t)}function S(o,n,t,c){o.write(`${JSON.stringify({ok:!1,error:n,message:t,nextAction:c})}
4
- `),o.fail()}export{pe as registerRoomCommand};
1
+ import x from"node:crypto";import O from"node:fs";import I from"node:path";import{PublicAttachmentCache as $}from"../room/public-attachment-cache.js";import{PublicRoomClient as E,PublicRoomClientError as N}from"../room/public-room-client.js";import{callDaemonIpc as C}from"../daemon-ipc/client.js";import{DaemonIpcError as M}from"../daemon-ipc/protocol.js";import{MANAGED_ROOM_IPC_ROOT_ENV as F,resolveManagedRoomIpcPaths as V}from"../daemon-ipc/paths.js";import{MANAGED_ROOM_CONTEXT_ENV as G}from"../room/managed-context.js";import{createRoomInviteQr as H}from"../room/invite-qr.js";import{externalRoomNextAction as j,managedRoomNextAction as J,SHENNIAN_INSTALL_URL as z}from"../room/room-error-guidance.js";import{attachmentMediaKind as Q}from"../room/attachment-upload.js";const Z={write:n=>{process.stdout.write(n)},fail:()=>{process.exitCode=1}};function ue(n,o={}){const t=o.client??new E,m=o.cache??new $({client:t}),a=o.io??Z,v=o.createInviteQr??H,p=async(i,e)=>{const s=await v(i).catch(()=>({imagePath:null,mimeType:"image/png",payload:i,dataUrl:null}));return{inviteUrl:i,installUrl:z,text:K(i,e),inviteQr:s}},c=n.command("room").description("Use one explicitly referenced Shennian Room");c.command("schedule").description("Manage scheduled work in the current managed conversation").requiredOption("--operation <operation>","list/create/update/pause/resume/delete").option("--id <id>").option("--version <version>").option("--operation-id <operationId>").option("--time-description <timeDescription>").option("--timezone <timezone>").option("--prompt <prompt>").action(async i=>l("room.schedule",a,async()=>{const e=b();return e||r("managed_room_context_required","Scheduling is only available in managed Room context.",j()),g(e,"schedule",{...i,version:i.version?Number(i.version):void 0})})),c.command("create").description("Create a Room as the paired user").requiredOption("--name <name>","Room name").action(async i=>l("room.create",a,async()=>{u("create");const e=i.name.trim();(!e||e.length>80)&&r("invalid_room_name","Room name must contain 1 to 80 characters.");const s=await t.create(e,x.randomUUID()),d=f(s.room,"invalid_room_response");return typeof d.inviteUrl!="string"&&r("invalid_room_response","Shennian returned an invalid Room invitation."),{...s,sharing:await p(d.inviteUrl,typeof d.name=="string"?d.name:e)}})),c.command("list").description("List Rooms for the paired user; duplicate names remain separate candidates").action(()=>l("room.list",a,()=>(u("list"),t.list()))),c.command("get <room-ref>").description("Get one explicitly referenced Room and its current version").action(i=>l("room.get",a,()=>(u("get"),t.get(h(i))))),c.command("update <room-ref>").description("Update one Room using the version returned by room get or room list").requiredOption("--version <version>","Exact Room version returned by Shennian").option("--name <name>","New Room name").option("--visibility <visibility>","private or public_read").option("--join-policy <policy>","open, approval, or closed").action((i,e)=>l("room.update",a,()=>{u("update");const s=e.name?.trim();return e.name!==void 0&&(!s||s.length>80)&&r("invalid_room_name","Room name must contain 1 to 80 characters."),e.visibility!==void 0&&e.visibility!=="private"&&e.visibility!=="public_read"&&r("invalid_room_visibility","--visibility must be private or public_read."),e.joinPolicy!==void 0&&e.joinPolicy!=="open"&&e.joinPolicy!=="approval"&&e.joinPolicy!=="closed"&&r("invalid_room_join_policy","--join-policy must be open, approval, or closed."),s===void 0&&e.visibility===void 0&&e.joinPolicy===void 0&&r("room_update_required","Provide --name, --visibility, or --join-policy."),t.update(h(i),{version:D(e.version),...s===void 0?{}:{name:s},...e.visibility===void 0?{}:{visibility:e.visibility},...e.joinPolicy===void 0?{}:{joinPolicy:e.joinPolicy}})})),c.command("delete <room-ref>").description("Delete this user's Room chat record without physically deleting the Room").requiredOption("--version <version>","Exact Room version returned by Shennian").action((i,e)=>l("room.delete",a,()=>(u("delete"),t.deleteRecord(h(i),D(e.version))))),c.command("join <invite-url>").description("Join or request access through an official Room invitation").option("--message <message>","Introduction and reason required when owner approval is enabled").action(async(i,e)=>l("room.join",a,async()=>{u("join");const s=q(i,"invite"),d=e.message?.trim();return d&&d.length>500&&r("join_message_too_long","Join request message must be at most 500 characters."),t.join(s.roomId,s.inviteToken,d)})),c.command("open <room-url>").description("Open one Room where the paired user is already a member").action(async i=>l("room.open",a,async()=>{u("open");const e=q(i,"stable");return t.open(e.roomId)})),c.command("status [room-ref]").description("Show the managed Room, or one explicitly referenced Room").action(async i=>l("room.status",a,()=>{const e=b();return e?(i&&r("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),g(e,"status",{})):t.status(S(i))})),c.command("read [room-ref]").description("Read recent, older, or newer messages from one Room").option("--before <sequence>","Read messages before this sequence").option("--after <sequence>","Read messages after this sequence").option("--limit <count>","Maximum messages, 1 to 50","50").action(async(i,e)=>{await l("room.read",a,async()=>{e.before&&e.after&&r("ambiguous_sequence_cursor","--before and --after are mutually exclusive."),e.before&&!L(e.before)&&r("invalid_sequence","--before sequence is invalid."),e.after&&!L(e.after)&&r("invalid_sequence","--after sequence is invalid.");const s=Number(e.limit);(!Number.isInteger(s)||s<1||s>50)&&r("invalid_limit","--limit must be between 1 and 50.");const d=b();if(d)return i&&r("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),g(d,"read",{...e.before?{before:e.before}:{},...e.after?{after:e.after}:{},limit:s});const R=S(i),w=await t.read(R,{before:e.before,after:e.after,limit:s});return Y(w,R,m)})}),c.command("send [room-ref]").description("Send text and up to 10 files as the paired user").option("--text <text>","Message text").option("--file <path>","Local file path; repeat for multiple files",B,[]).action(async(i,e)=>{await l("room.send",a,async()=>{const s=e.text?.trim()??"";!s&&e.file.length===0&&r("room_message_empty","Provide --text or at least one --file."),e.file.length>10&&r("room_attachments_too_many","A message can contain at most 10 files.");const d=b();if(d)return i&&r("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),g(d,"send",{clientMessageId:`managed:${x.randomUUID()}`,text:s,files:e.file});const R=S(i),w=[];for(const T of e.file)w.push(await X(t,R,T));return t.send(R,{clientMessageId:`cli:${x.randomUUID()}`,...s?{text:s}:{},...w.length>0?{attachmentIds:w}:{}})})}),c.command("pause").description("Pause the current managed Agent in its bound Room").requiredOption("--reason <reason>","Short reason shown to all Room members").action(async i=>l("room.pause",a,()=>{const e=b();return e||r("managed_room_context_required","Pause is only available in managed Room context.",j()),g(e,"pause",{reason:i.reason})})),c.command("attachment").description("Download Room attachments through Shennian").command("get [room-ref] [attachment-id]").description("Download and verify one Room attachment").action(async(i,e)=>{await l("room.attachment.get",a,async()=>{const s=b(),d=s?i:e;return(!d||!/^ratt_[A-Za-z0-9_-]{1,191}$/.test(d))&&r("invalid_attachment_ref","attachment-id is invalid."),s?g(s,"attachment.get",{attachmentId:d}):m.materialize(S(i),d)})}),c.command("leave <room-ref>").description("Leave one explicitly referenced Room").action(async i=>l("room.leave",a,()=>(u("leave"),t.leave(h(i)))));const y=c.command("invite").description("Read or reset the current Room invitation");y.command("get <room-ref>").description("Get the current invitation and version for one Room").action(i=>l("room.invite.get",a,async()=>{u("invite get");const e=f(await t.getInvite(h(i)),"invalid_room_response"),s=f(e.invite,"invalid_room_response");return typeof s.url!="string"&&r("invalid_room_response","Shennian returned an invalid Room invitation."),{...e,sharing:await p(s.url)}})),y.command("reset <room-ref>").description("Reset an invitation using the version returned by room invite get").requiredOption("--version <version>","Positive invitation version").action((i,e)=>l("room.invite.reset",a,async()=>{u("invite reset");const s=f(await t.resetInvite(h(i),k(e.version,"room_invite_version")),"invalid_room_response"),d=f(s.invite,"invalid_room_response");return typeof d.url!="string"&&r("invalid_room_response","Shennian returned an invalid Room invitation."),{...s,sharing:await p(d.url)}}));const _=c.command("member").description("List or remove Room members");_.command("list <room-ref>").description("List public member references and versions for one Room").action(i=>l("room.member.list",a,()=>(u("member list"),t.listMembers(h(i))))),_.command("remove <room-ref>").description("Remove one exact member reference using its listed version").requiredOption("--member-ref <member-ref>","Public member reference returned by room member list").requiredOption("--version <version>","Positive member version returned by room member list").action((i,e)=>l("room.member.remove",a,()=>(u("member remove"),/^member_[A-Za-z0-9_-]{32,64}$/.test(e.memberRef)||r("invalid_room_member_ref","Use a memberRef returned by room member list."),t.removeMember(h(i),e.memberRef,k(e.version,"room_member_version"))))),c.command("search",{hidden:!0}).allowUnknownOption(!0).action(()=>A(a,"command_removed","Room search is not available to Agents.",null)),c.command("agent",{hidden:!0}).allowUnknownOption(!0).action(()=>A(a,"command_removed","Managed Agents are created in the Shennian mobile App.",null))}function K(n,o){return[o?"\u9080\u8BF7\u4F60\u52A0\u5165\u795E\u5FF5\u7FA4\u804A\u300C"+o+"\u300D\u3002":"\u70B9\u51FB\u52A0\u5165\u795E\u5FF5\u7FA4\u804A\uFF0C\u6216\u628A\u672C\u6D88\u606F\u53D1\u7ED9\u4F60\u7684 AI\uFF1A",...o?["\u76F4\u63A5\u70B9\u51FB\uFF0C\u6216\u628A\u672C\u6D88\u606F\u53D1\u7ED9\u4F60\u7684 AI\uFF0C\u8BA9\u5B83\u5B89\u88C5\u795E\u5FF5\u3001\u5B8C\u6210\u914D\u5BF9\u5E76\u52A0\u5165\u7FA4\u804A\u3002"]:[],"\u9080\u8BF7\uFF1A"+n,"\u5B89\u88C5\uFF1A"+z].join(`
2
+ `)}async function l(n,o,t){try{const m=await t();o.write(`${JSON.stringify({ok:!0,command:n,result:P(m)})}
3
+ `)}catch(m){if(m instanceof N){A(o,m.code,m.message,m.nextAction);return}if(m instanceof M){A(o,m.code,m.message,J(m.code));return}A(o,"room_command_failed","Shennian Room command failed.",null)}}function P(n){if(Array.isArray(n))return n.map(P);if(!n||typeof n!="object")return n;const o={};for(const[t,m]of Object.entries(n))t==="launchUrl"||t==="access"||(o[t]=P(m));return o}function b(){return process.env[G]?.trim()||null}async function g(n,o,t){const m=process.env[F]?.trim();if(!m||!I.isAbsolute(m))throw new M("managed_room_context_unavailable","Managed Room IPC context is unavailable.");return C({method:"managed-room.execute",params:{context:n,action:o,...t},paths:V(m),timeoutMs:o==="send"||o==="attachment.get"?18e4:15e3})}function u(n){b()&&r("managed_room_action_forbidden",`${n} is not available in managed Room context.`)}function S(n){return n||r("room_ref_required","Use an official Room URL returned by Shennian.",j()),h(n)}function h(n){return q(n,"stable").roomId}function q(n,o){let t;const m=o==="invite"?"invalid_invite_url":"invalid_room_ref",a=o==="invite"?"Use an official Shennian Room invitation URL.":"Use an official Room URL returned by Shennian.";try{t=new URL(n)}catch{r(m,a)}const v=t.pathname.match(/^\/spaces\/([A-Za-z0-9_-]{1,191})$/),p=t.searchParams.get("invite");return(t.protocol!=="https:"||t.hostname!=="app.shennian.net"||t.port||t.username||t.password||t.hash||!v||t.searchParams.has("access")||[...t.searchParams.keys()].some(c=>c!=="invite")||o==="stable"&&p!=null||o==="invite"&&(p==null||!/^[A-Za-z0-9_-]{32,256}$/.test(p)))&&r(m,a),{roomId:v[1],...p?{inviteToken:p}:{}}}function L(n){return/^(0|[1-9]\d{0,19})$/.test(n)}function D(n){const o=new Date(n);return(Number.isNaN(o.getTime())||o.toISOString()!==n)&&r("invalid_room_version","Use the exact version returned by room get or room list."),n}function k(n,o){/^[1-9]\d*$/.test(n)||r(o,"Version must be a positive integer.");const t=Number(n);return Number.isSafeInteger(t)||r(o,"Version must be a positive integer."),t}function B(n,o){return[...o,n]}async function X(n,o,t){const m=I.resolve(t);let a;try{a=await O.promises.lstat(m)}catch{r("file_not_found",`File does not exist: ${t}`)}(!a.isFile()||a.isSymbolicLink())&&r("invalid_file",`Not a regular file: ${t}`),(a.size<1||a.size>100*1024*1024)&&r("invalid_file_size","Each file must be between 1 byte and 100 MB.");const v=await O.promises.readFile(m),p=x.createHash("sha256").update(v).digest("hex"),c=W(m),U=await n.initializeAttachment(o,{clientUploadId:`cli:${x.randomUUID()}`,name:I.basename(m),mediaKind:Q(I.basename(m),c),mimeType:c,byteSize:a.size,sha256:p}),y=f(U.attachment,"invalid_attachment_response"),_=f(U.upload,"invalid_attachment_response");(typeof y.id!="string"||typeof _.url!="string"||_.method!=="PUT")&&r("invalid_attachment_response","Shennian returned an invalid attachment response.");const i=f(_.headers,"invalid_attachment_response");return Object.values(i).some(e=>typeof e!="string")&&r("invalid_attachment_response","Shennian returned invalid upload headers."),ee(_.url),await n.upload(_.url,i,v),await n.completeAttachment(o,y.id),y.id}function W(n){const o=I.extname(n).toLowerCase();return{".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webp":"image/webp",".gif":"image/gif",".heic":"image/heic",".heif":"image/heif",".mp3":"audio/mpeg",".wav":"audio/wav",".m4a":"audio/mp4",".mp4":"video/mp4",".mov":"video/quicktime",".pdf":"application/pdf",".txt":"text/plain",".md":"text/markdown",".json":"application/json",".csv":"text/csv",".zip":"application/zip",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}[o]??"application/octet-stream"}function Y(n,o,t){return Array.isArray(n.messages)||r("invalid_server_response","Shennian returned invalid messages."),{messages:n.messages.map(m=>{const a=f(m,"invalid_server_response"),v=Array.isArray(a.attachments)?a.attachments:[];return{...a,attachments:v.map(p=>{const c=f(p,"invalid_server_response");return(typeof c.attachmentId!="string"||typeof c.name!="string"||typeof c.mimeType!="string"||typeof c.byteSize!="string"||typeof c.sha256!="string")&&r("invalid_server_response","Shennian returned invalid attachment metadata."),{...c,...t.lookup(o,c)}})}})}}function f(n,o){return(!n||typeof n!="object"||Array.isArray(n))&&r(o,"Invalid structured response."),n}function ee(n){let o;try{o=new URL(n)}catch{r("invalid_attachment_response","Invalid upload URL.")}const t=o.protocol==="http:"&&(o.hostname==="localhost"||o.hostname==="127.0.0.1");(o.protocol!=="https:"&&!t||o.username||o.password||o.hash)&&r("invalid_attachment_response","Invalid upload URL.")}function r(n,o,t=null){throw new N(n,o,t)}function A(n,o,t,m){n.write(`${JSON.stringify({ok:!1,error:o,message:t,nextAction:m})}
4
+ `),n.fail()}export{ue as registerRoomCommand};
@@ -0,0 +1,3 @@
1
+ import{getCurrentVersion as i}from"../upgrade/engine.js";import{updateOfficialSkill as c}from"../skill/updater.js";function a(t){t.command("skill").description("Check or update one explicitly referenced official Skill").command("update").requiredOption("--path <path>","Absolute path to the current Shennian SKILL.md").option("--force-check","Ignore the 24-hour check cache").option("--replace","Explicitly replace an unregistered or locally modified copy").option("--json","Print machine-readable result JSON").action(async e=>{try{const o=await c(e.path,{clientVersion:i(),forceCheck:e.forceCheck,replace:e.replace});e.json?process.stdout.write(`${JSON.stringify({ok:!0,command:"skill.update",result:o})}
2
+ `):console.log(o.message)}catch(o){const r=o instanceof Error?o.message:"skill_update_failed";e.json?process.stdout.write(`${JSON.stringify({ok:!1,command:"skill.update",error:r})}
3
+ `):console.error(`Skill update failed: ${r}`),process.exitCode=1}})}export{a as registerSkillCommand};
@@ -1 +1 @@
1
- import o from"node:fs";import{getAcpLocalMetrics as d}from"../agents/acp/metrics.js";const g=["protocol.info","daemon.status","agent.list","agent.discover","agent.check","agent.auth.completed","log.read","upgrade.start","upgrade.prepare-external","upgrade.cancel-external"],p=32*1024,m=64*1024;function _(t){return{"protocol.info":()=>{const e=t.getStatus();return{daemonVersion:t.daemonVersion,protocolVersion:t.protocolVersion,launcher:e.launcher,capabilities:[...g]}},"daemon.status":()=>t.getStatus(),"agent.list":()=>({agents:t.getAgents().map(e=>({type:e.type,...e.version?{version:e.version}:{},...e.runtimePlatform?{runtimePlatform:e.runtimePlatform}:{},...e.runtimeIsWsl!==void 0?{runtimeIsWsl:e.runtimeIsWsl}:{}})),acpMetrics:d()}),"agent.auth.completed":async e=>{const a=e&&typeof e=="object"&&!Array.isArray(e)&&typeof e.agentType=="string"?e.agentType:"";if(!/^acp:[a-z0-9][a-z0-9._-]{0,63}$/.test(a))throw new Error("invalid_acp_agent");return{resumed:await t.completeAgentAuthentication?.(a)??!1}},"log.read":e=>y(t.getStatus().logFile,e),"upgrade.start":async e=>{if(!t.startUpgrade)throw new Error("upgrade_unavailable");return t.startUpgrade(e)},"upgrade.prepare-external":async e=>{if(!t.prepareExternalUpgrade)throw new Error("external_upgrade_unavailable");return t.prepareExternalUpgrade(e)},"upgrade.cancel-external":async()=>{if(!t.cancelExternalUpgrade)throw new Error("external_upgrade_unavailable");return t.cancelExternalUpgrade()}}}function y(t,e){const a=e&&typeof e=="object"&&!Array.isArray(e)?e:{},i=typeof a.limitBytes=="number"&&Number.isFinite(a.limitBytes)?a.limitBytes:p,u=Math.max(1,Math.min(Math.floor(i),m));let s=0;try{s=o.statSync(t).size}catch{return{text:"",start:0,end:0,nextBefore:null,eof:!0}}const f=typeof a.before=="number"&&Number.isFinite(a.before)?Math.floor(a.before):s,n=Math.max(0,Math.min(f,s)),r=Math.max(0,n-u);if(n===r)return{text:"",start:r,end:n,nextBefore:r>0?r:null,eof:r===0};const l=Buffer.allocUnsafe(n-r),c=o.openSync(t,"r");try{o.readSync(c,l,0,l.length,r)}finally{o.closeSync(c)}return{text:l.toString("utf8"),start:r,end:n,nextBefore:r>0?r:null,eof:r===0}}export{g as DAEMON_CONTROL_CAPABILITIES,_ as createDaemonControlHandlers,y as readDaemonLogPage};
1
+ import o from"node:fs";import{getAcpLocalMetrics as g}from"../agents/acp/metrics.js";const u=["protocol.info","daemon.status","agent.list","agent.discover","agent.check","room.agent.bind-managed","agent.auth.completed","log.read","upgrade.start","upgrade.prepare-external","upgrade.cancel-external"],m=32*1024,y=64*1024;function _(t){return{"protocol.info":()=>{const e=t.getStatus();return{daemonVersion:t.daemonVersion,protocolVersion:t.protocolVersion,launcher:e.launcher,capabilities:[...u]}},"daemon.status":()=>t.getStatus(),"agent.list":()=>({agents:t.getAgents().map(e=>({type:e.type,...e.version?{version:e.version}:{},...e.runtimePlatform?{runtimePlatform:e.runtimePlatform}:{},...e.runtimeIsWsl!==void 0?{runtimeIsWsl:e.runtimeIsWsl}:{}})),acpMetrics:g()}),"agent.auth.completed":async e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)&&typeof e.agentType=="string"?e.agentType:"";if(!/^acp:[a-z0-9][a-z0-9._-]{0,63}$/.test(n))throw new Error("invalid_acp_agent");return{resumed:await t.completeAgentAuthentication?.(n)??!1}},"room.agent.bind-managed":async e=>{if(!e||typeof e!="object"||Array.isArray(e))throw new Error("invalid_managed_room_binding");if(!t.bindManagedRoom)throw new Error("managed_room_binding_unavailable");return t.bindManagedRoom(e)},"log.read":e=>p(t.getStatus().logFile,e),"upgrade.start":async e=>{if(!t.startUpgrade)throw new Error("upgrade_unavailable");return t.startUpgrade(e)},"upgrade.prepare-external":async e=>{if(!t.prepareExternalUpgrade)throw new Error("external_upgrade_unavailable");return t.prepareExternalUpgrade(e)},"upgrade.cancel-external":async()=>{if(!t.cancelExternalUpgrade)throw new Error("external_upgrade_unavailable");return t.cancelExternalUpgrade()}}}function p(t,e){const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},c=typeof n.limitBytes=="number"&&Number.isFinite(n.limitBytes)?n.limitBytes:m,d=Math.max(1,Math.min(Math.floor(c),y));let l=0;try{l=o.statSync(t).size}catch{return{text:"",start:0,end:0,nextBefore:null,eof:!0}}const f=typeof n.before=="number"&&Number.isFinite(n.before)?Math.floor(n.before):l,a=Math.max(0,Math.min(f,l)),r=Math.max(0,a-d);if(a===r)return{text:"",start:r,end:a,nextBefore:r>0?r:null,eof:r===0};const i=Buffer.allocUnsafe(a-r),s=o.openSync(t,"r");try{o.readSync(s,i,0,i.length,r)}finally{o.closeSync(s)}return{text:i.toString("utf8"),start:r,end:a,nextBefore:r>0?r:null,eof:r===0}}export{u as DAEMON_CONTROL_CAPABILITIES,_ as createDaemonControlHandlers,p as readDaemonLogPage};
package/dist/src/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import{registerScheduleCommand as Se}from"./commands/schedule.js";import{resolvePersonalScheduleContext as ye}from"./schedules/personal-context.js";import{executeServerSchedule as oe,deliverServerSchedule as we}from"./schedules/server-client.js";import{Command as Ce}from"commander";import E from"chalk";import Re from"node:os";import G from"node:fs";import{loadConfig as p,saveConfig as be,getConfigPath as ke,getShennianDir as Pe,resolveShennianPath as W,normalizeAutoUpgradePolicy as Ae}from"./config/index.js";import{CliRelayClient as Ee}from"./relay/client.js";import{collectKnownLogSecrets as ne,installCliLogRedaction as Te,sanitizeCliLogValue as De}from"./log-redaction.js";import{registerPairCommand as Ue,runSmartStart as Me}from"./commands/pair.js";import{registerLogoutCommand as _e}from"./commands/logout.js";import{clearDaemonPidIfOwner as te,createDaemonInstanceId as Ne,clearDaemonLauncher as re,findRunningDaemonProcessIds as xe,isRemoteAccessDisabled as ae,prepareRemoteAccessPause as Oe,stopPreparedRemoteAccessService as $e,isIsolatedDaemonE2eInstance as Ve,getDaemonStatus as ie,registerDaemonCommand as Le,writeDaemonPid as se,writeDaemonLauncher as ce}from"./commands/daemon.js";import{registerRuntimeCommand as je}from"./commands/runtime.js";import{registerAdapterCommand as He}from"./commands/adapter.js";import{registerAgentCommand as Be}from"./commands/agent.js";import{registerManagerCommand as Fe}from"./commands/manager.js";import{registerWorkerCommand as Ge}from"./commands/worker.js";import{handleUpgradeStart as We,registerUpgradeCommand as ze}from"./commands/upgrade.js";import{registerToolsCommand as Je}from"./commands/tools.js";import{registerQueueCommand as Ke}from"./commands/queue.js";import{registerIntegrationCommand as qe}from"./commands/integration.js";import{registerRoomCommand as Qe}from"./commands/room.js";import{registerSupportCommand as Ye}from"./commands/support.js";import{SessionManager as Ze}from"./session/manager.js";import{createTrustedProjectFsRequestResolver as Xe}from"./session/handlers/project-fs.js";import{SERVERS as z,regionToUrl as eo,urlToRegion as oo}from"./region.js";import{clearUpgradeAttempt as no,getCurrentVersion as de,handleStartupCrashCheck as to,readUpgradeAttempt as ro}from"./upgrade/engine.js";import{AutoUpgradeScheduler as ao}from"./upgrade/scheduler.js";import{ensureUpdaterInstalled as io}from"./upgrade/updater-bootstrap.js";import{launchUpdater as so,markUpdaterHealthy as co}from"./upgrade/updater-launcher.js";import{detectAgents as lo}from"./agents/detect.js";import{augmentProcessPath as le}from"./env-path.js";import{acquireDaemonInstanceLock as mo}from"./commands/daemon-instance-lock.js";le();const O=de();import{getCachedAgentInfos as $,resolveAgentInfos as po}from"./agents/model-registry.js";import{initCliLogReporter as go,reportLog as T,reportUpgradeAggregateMetric as uo}from"./log-reporter.js";import{isNativeFusionEnabled as fo}from"./native-fusion/config.js";import{NativeSessionFusionService as ho}from"./native-fusion/service.js";import{startDaemonLogRetention as vo}from"./daemon-log.js";import{startDaemonIpcServer as me}from"./daemon-ipc/server.js";import{resolveDaemonIpcPaths as Io,resolveManagedRoomIpcPaths as So}from"./daemon-ipc/paths.js";import{createDaemonControlHandlers as yo}from"./daemon-ipc/control.js";import{createAgentReadinessHandlers as wo}from"./daemon-ipc/agent-readiness.js";import{scanMachineAgents as Co}from"./session/handlers/agents.js";import{validateAgentWorkspaceConfiguration as Ro}from"./session/handlers/agent-workspace-validation.js";import{DAEMON_IPC_VERSION as bo}from"./daemon-ipc/protocol.js";import{RoomSessionRegistry as ko}from"./room/session-registry.js";import{HttpRoomBindingClient as Po}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as Ao}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as Eo}from"./room/host-session-observations.js";import{RoomMirrorSyncService as To}from"./room/mirror-sync-service.js";import{handleLiveRoomDelivery as Do}from"./room/live-delivery.js";import{NativeSourceDeletionStore as Uo}from"./native-fusion/native-source-deletions.js";import{matchesRoomOwner as Mo,isCurrentRegistration as _o}from"./room/registration-authority.js";import{RoomActivationService as No,resolveManagedAgentPolicy as xo}from"./room/activation-service.js";import{RoomActivationAudit as Oo}from"./room/activation-audit.js";import{RoomAttachmentCache as $o}from"./room/attachment-cache.js";import{RoomAttachmentUploader as Vo}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as Lo}from"./room/managed-context.js";import{createManagedRoomExecutor as jo}from"./room/managed-room-ipc.js";import{SESSION_SYNC_PROTOCOL_VERSION as pe,SESSION_SYNC_ADVERTISED_CAPABILITIES as ge}from"@shennian/wire";import{getDaemonInstallationId as Ho}from"./session/store.js";import{OWNER_DIRECT_TURN_OPTIONS as Bo}from"./session/queue-helpers.js";import{synchronizeMachinePersonalAuthority as Fo}from"./personal-sync/machine-authority.js";import{CLI_SESSION_SYNC_DATA_EPOCH as ue,ensurePersonalDataEpoch as Go}from"./personal-sync/epoch.js";import{DeletionSyncRetryWaitError as Wo,DeletionSyncTerminalError as zo}from"./session/deletion-sync-state.js";const fe=Pe(),Jo=W("daemon.pid"),Ko=W("daemon.instance.lock");function qo(){try{const a=G.readFileSync(Jo,"utf-8").trim();if(a.startsWith("{")){const g=JSON.parse(a),c=Number(g.pid);return Number.isInteger(c)&&c>0?c:null}const s=parseInt(a,10);return Number.isInteger(s)&&s>0?s:null}catch{return null}}function Qo(a){return a.replace(/^https:\/\//,"wss://").replace(/^http:\/\//,"ws://")}function J(){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 Yo(a){const s=[`trigger=${a.trigger??"unknown"}`,`phase=${a.phase}`,`code=${a.code??"unknown"}`,`attempt=${a.reconnectAttempt}`];return a.reason&&s.push(`reason=${a.reason}`),a.error&&s.push(`error=${a.error}`),s.join(" ")}const i=new Ce;i.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(O),i.enablePositionalOptions(),qe(i),Qe(i),Ye(i),Se(i),i.option("--api <url>","Server URL override").option("--name <name>","Machine name",Re.hostname()).action(async a=>{const s=p(),g=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??void 0;await Me(g,a.name)}),i.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async a=>{const s=W("env.json");try{const e=JSON.parse(G.readFileSync(s,"utf-8"));for(const[n,o]of Object.entries(e))process.env[n]||(process.env[n]=o);le()}catch{}Te(ne(process.env,p()));const g=vo();ae()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(g),process.exit(0));const c=Ne(),w=mo({lockPath:Ko,instanceId:c});w.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(w.ownerPid?` (PID ${w.ownerPid})`:"")+", skipping duplicate start"),clearInterval(g),process.exit(0));try{const e=qo();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),w.release(),process.exit(0)}catch{}}catch{}const K=Ve(process.env,fe)?[]:xe(process.pid);K.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${K[0]}), skipping duplicate start`),w.release(),process.exit(0)),se(process.pid,c,{version:O}),ce(process.pid,void 0,c),process.on("exit",()=>{te(process.pid,c),re(c)}),process.on("exit",w.release);let V=null;try{V=io()}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const D=await to();if(D){V||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{so({fromVersion:O,targetVersion:D.from,oldDaemonPid:process.pid,operation:"rollback",rollbackJobId:D.jobId},{readiness:V}),console.error(`[upgrade] New version crashed ${D.attemptCount} times; external rollback scheduled.`),process.stdin.resume();return}catch(e){console.error(`[upgrade] Crash-loop rollback could not start: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}try{Go()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const m=p(),u=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??m.serverUrl??z.cn.url,h=de(),l=await lo({serverUrl:u,authToken:m.machineToken??m.accessToken}),C=l.map(e=>e.type),L=$(l),f=new ko,U=new Eo,R=new Lo(f),S=new Po(u),ve=new $o({actionClient:S}),Ie=new Vo({actionClient:S});let y=null,r;const q=jo({authority:R,actionClient:S,attachmentCache:ve,attachmentUploader:Ie,observations:U,getMachineCredentials:()=>({machineToken:p().machineToken}),managerExecutor:e=>r.executeManagedManagerAction(e),onSendOutcome:e=>r.noteManagedRoomSendOutcome(e),getActiveRunId:(e,n)=>r.getManagedRoomActiveRunId(e,n),scheduleExecutor:(e,n,o)=>oe({serverUrl:u,machineToken:p().machineToken,sessionId:e,action:n,args:o}),onPaused:({registryKey:e,sessionId:n})=>{const t=f.getByRegistryKey(e)?.binding;t&&f.setBinding({registryKey:e,bindingId:t.bindingId,roomId:t.roomId,agentIdentityId:t.agentIdentityId,agentMembershipId:t.agentMembershipId,status:"paused",activationMode:"paused"}),R.revokeSession(e),r?.discardPendingManagedRoomMessages(n)}});let j=null,H=null,B=null;const b=await me({paths:Io(),handlers:{...yo({daemonVersion:h,protocolVersion:bo,getStatus:()=>ie(),getAgents:()=>l,completeAgentAuthentication:e=>r.resumePendingAcpAuthentication(e),startUpgrade:async e=>{if(!j)throw new Error("upgrade_unavailable");return j(e)},prepareExternalUpgrade:e=>{if(!H)throw new Error("external_upgrade_unavailable");return H(e)},cancelExternalUpgrade:()=>{if(!B)throw new Error("external_upgrade_unavailable");return B()}}),...wo({getStatus:()=>ie(),getSnapshot:()=>$(l).map(e=>{const n=l.find(o=>o.type===e.type);return{...e,runtimePlatform:n?.runtimePlatform,runtimeIsWsl:n?.runtimeIsWsl}}),discover:async e=>{if(!r)throw new Error("daemon_starting");const n=await Co(!0,e);if(e){const t=l.filter(I=>I.type!==e);l.splice(0,l.length,...t,...n.detectedAgents)}else l.splice(0,l.length,...n.detectedAgents);const o=$(l);return d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:l.map(t=>t.type),agents:o}}),n.agents.map(t=>{const I=n.detectedAgents.find(F=>F.type===t.type);return{...t,runtimePlatform:I?.runtimePlatform,runtimeIsWsl:I?.runtimeIsWsl}})},validate:async e=>{if(!r)throw new Error("daemon_starting");return Ro(e,{ignoreNativeSession:(n,o)=>P?.ignoreNativeSource(n,o)})}})}});let k;try{k=await me({paths:So(),handlers:{"managed-room.execute":e=>q(e),"schedule.personal":async e=>{const n=e,o=ye(n?.context);if(f.getByNianSessionId(o)?.binding)throw new Error("schedule_personal_context_required");return await oe({serverUrl:u,machineToken:p().machineToken,sessionId:o,action:n.operation,args:n})}}})}catch(e){throw await b.close(),e}let Q=!1;const M=()=>{Q||(Q=!0,Promise.allSettled([b.close(),k.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",M),process.once("SIGTERM",M),!m.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await Promise.allSettled([b.close(),k.close()]),process.exitCode=1;return}process.removeListener("SIGINT",M),process.removeListener("SIGTERM",M);const Y=`${Qo(u)}/relay/machine`,_=m.machineToken;if(!_)return;try{await Fo({serverUrl:u,machineToken:_,machineId:m.machineId})}catch(e){console.error(`[personal-sync] machine authority verification failed: ${e instanceof Error?e.message:String(e)}`),await Promise.allSettled([b.close(),k.close()]),process.exit(1)}m.machineId&&go(u,m.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${Y}... (v${h}) agents: ${C.join(",")}`),T({level:"info",wsEvent:"daemon.start",metadata:{version:h,agents:C}});let P=null,A=null,N=0,v=ro();const Z=Ae(m.autoUpgrade),d=new Ee({serverUrl:Y,machineToken:_,cliVersion:h,agentList:C,autoUpgradePolicy:Z,sessionSyncProtocolVersion:pe,sessionSyncCapabilities:ge,daemonInstallationId:Ho(),dataEpoch:ue,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),T({level:"info",wsEvent:"daemon.connected"}),L.some(o=>o.models.length>0)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:C,agents:L}}),po(l,{serverUrl:m.serverUrl??u,authToken:m.machineToken??m.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(L)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:C,agents:o}})}).catch(()=>{}),v&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:v.from,to:v.to,...v.jobId?{jobId:v.jobId}:{},activeRunCount:r.getPendingRecoveryCount()}}),x.start(),A?.start();const e=++N,n=async()=>{try{if(await r.synchronizeSessionDeletions(),await r.recoverInterruptedRuns(),v){const o=v;v=null,o.jobId&&co(o.jobId,o.to),no(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),uo({kind:"upgrade",result:o.operation==="rollback"?"rolled_back":"healthy",stage:"terminal"}),d.sendEvent({type:"event",event:"upgrade",payload:{state:o.operation==="rollback"?"rolled_back":"healthy",machineId:"self",from:o.from,to:o.to,...o.jobId?{jobId:o.jobId}:{}}}),o.operation!=="rollback"&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:o.from,to:o.to}})}e===N&&d.getState()==="connected"&&P?.handleConnected()}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===N&&d.getState()==="connected"){if(o instanceof zo)return;const t=o instanceof Wo?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},t).unref?.()}}};n()},onDisconnected:e=>{N+=1,r?.revokeProjectFsHandles(),A?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${Yo(e)}`),T({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onProjectFsCapabilityChanged:e=>{e||r?.revokeProjectFsHandles()},onReq:e=>{if(console.log(`[${new Date().toISOString()}] [req] ${e.method}`),T({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),e.method==="room.message.deliver"){Do(d,A,e);return}r.handleReq(e)}});P=fo()?new ho(d):null,A=new To({registry:f,observations:U,actionClient:S,getMachineCredentials:()=>({machineToken:p().machineToken}),isSessionDeleted:e=>new Uo().hasSession(e),onMessages:async(e,n,o,t)=>{await y?.processMessages(e,n,o,t)},onBindingPaused:e=>{e.lifecycleState==="closed"?(R.revokeSession(e.registryKey),r?.retireManagedRoomSession(e.nianSessionId).catch(()=>{console.warn("[room.sync] retired session cleanup failed")})):r?.discardPendingManagedRoomMessages(e.nianSessionId)}});const x=new ao({currentVersion:h,machineId:m.machineId??"unknown-machine",serverUrl:m.serverUrl??u,getIdleState:()=>r.getUpgradeIdleState(),sessionSyncProtocolVersion:pe,dataEpoch:ue,advertisedCapabilities:ge,updaterProtocolVersion:1,startUpgrade:async(e,n)=>{const{handleUpgradeStart:o}=await import("./commands/upgrade.js");await o(d,"auto-upgrade",e,{currentVersion:h,prepareForUpgrade:t=>r.prepareForUpgrade(t),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0,waitForCompletion:!0,canHandoff:n?void 0:()=>r.getUpgradeIdleState().idle})}});x.setPolicy(Z),r=new Ze(d,P,h,x,new Ao({registry:f,observations:U,bindingClient:S,getMachineCredentials:()=>{const e=p();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${c}`}),{prepare:()=>Oe(),isDisabled:()=>ae(),shutdownAfterResponse:()=>{setTimeout(()=>{$e(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>R.revokeToken(e),q,null,Xe(_),()=>$(l)),j=async e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=typeof n.targetVersion=="string"?n.targetVersion:void 0,t=n.confirmedMajor===!0;return await We(d,`local-upgrade-${Date.now()}`,o,{currentVersion:h,confirmedMajor:t,prepareForUpgrade:F=>r.prepareForUpgrade(F),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},H=e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},t=`desktop-${(typeof n.targetVersion=="string"?n.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:t,...r.prepareForUpgrade(t)}},B=()=>(r.cancelUpgradePreparation(),{cancelled:!0}),y=new No({registry:f,observations:U,enqueueConversationBatch:e=>r.enqueueManagedRoomBatch(e),loadContext:async e=>{const n=p().machineToken?.trim(),o=e.binding;if(!n||!o)throw new Error("managed_room_context_unavailable");const t=await S.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});if(!_o(f,e)||!Mo(e,t))throw new Error("managed_room_binding_unavailable");return{roomName:t.room.name,memberCount:t.room.memberCount,members:t.room.members,agentsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent}:{},effectiveModelsBySessionId:t.effectiveModelId!==void 0?{[e.nianSessionId]:t.effectiveModelId}:void 0,agentSystemPromptsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,n,o,t)=>{const I=p().machineToken;if(!I)throw new Error("machine_not_authorized");return R.issue({registryKey:e,machineCredential:I,activationCauseMessageId:n,allowedActions:o,propagateWorkerRoomOrigin:t})},audit:new Oo,resolveManagedAgentPolicy:e=>xo({...e,runtime:l.find(n=>n.type===e.registration.runtimeAdapter)})}),r.scheduleHandler=e=>we({serverUrl:u,machineToken:p().machineToken,req:e,client:d,queue:r.getScheduleQueue(),registry:f}),r.getScheduleQueue().validateScheduledTarget=async e=>{if(!e.bindingId||!e.roomId)return"ready";if(f.getByNianSessionId(e.sessionId)?.binding?.bindingId!==e.bindingId)return"ended";const o=await S.status({machineToken:p().machineToken,roomId:e.roomId,bindingId:e.bindingId});return o.status==="active"&&o.room.status==="active"?"ready":"ended"},r.setManagedRoomTurnPreparer(e=>y.prepareConversationTurn(e)),r.setManagedAgentDirectTurnPreparer((e,n)=>y.prepareDirectConversationTurn(e,n)),r.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?y.prepareDirectConversationTurn(e.sessionId,Bo):e.source==="manager_event"?y.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[],managerEvent:!0}):e.source!=="managed_room"||!e.roomContextRegistryKey||!e.activationCauseMessageId||!e.rootCauseMessageId||!e.activationSequence||e.activationHopCount===null?null:y.prepareConversationTurn([{registryKey:e.roomContextRegistryKey,causeMessageId:e.activationCauseMessageId,rootCauseMessageId:e.rootCauseMessageId,sequence:e.activationSequence,hopCount:e.activationHopCount}])),G.mkdirSync(fe,{recursive:!0}),se(process.pid,c,{version:h}),ce(process.pid,void 0,c),d.connect(),process.stdin.resume();let X=!1;const ee=async()=>{if(!X){X=!0,console.log(E.gray(`
3
- Disconnecting...`)),T({level:"info",wsEvent:"daemon.stop"}),clearInterval(g);try{await r.cleanup(),x.stop(),P?.stop(),A?.stop(),d.disconnect(),await Promise.allSettled([b.close(),k.close()]),te(process.pid,c),re(c)}finally{process.exit(0)}}};process.on("SIGINT",()=>{ee()}),process.on("SIGTERM",()=>{ee()})}),i.command("workbuddy-session-hook",{hidden:!0}).description("(retired) WorkBuddy Room Hook").allowUnknownOption(!0).action(()=>J()),i.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(()=>J()),i.command("room-tool-server",{hidden:!0}).description("(internal) Run the official Shennian Room MCP tool").allowUnknownOption(!0).action(()=>J());const he=i.command("config").description("View or modify configuration");he.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${ke()}`);const a=p(),s=De(a,ne(process.env,a));if(a.serverUrl){const g=oo(a.serverUrl);console.log(`Region: ${g} (${z[g].label})`)}console.log(JSON.stringify(s,null,2))}),he.command("set").description("Update a config value").argument("<key>",'Config key (e.g. "server")').argument("<value>",'Config value (e.g. "cn" or "global")').action((a,s)=>{const g=p();if(a==="server"){s!=="cn"&&s!=="global"&&(console.error(E.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const c=s;g.serverUrl=eo(c),be(g),console.log(E.green(`\u2713 Server set to ${z[c].label}`)),console.log(E.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(E.red(`\u2717 Unknown config key: ${a}. Supported: server`)),process.exit(1)}),Ue(i),_e(i),Le(i),je(i),He(i,{clientVersion:O}),Be(i),Fe(i),Ge(i),ze(i),Je(i),Ke(i),i.parse();
1
+ import{registerScheduleCommand as Se}from"./commands/schedule.js";import{resolvePersonalScheduleContext as ye}from"./schedules/personal-context.js";import{executeServerSchedule as oe,deliverServerSchedule as we}from"./schedules/server-client.js";import{Command as Ce}from"commander";import E from"chalk";import Re from"node:os";import G from"node:fs";import{loadConfig as p,saveConfig as be,getConfigPath as ke,getShennianDir as Pe,resolveShennianPath as W,normalizeAutoUpgradePolicy as Ae}from"./config/index.js";import{CliRelayClient as Ee}from"./relay/client.js";import{collectKnownLogSecrets as ne,installCliLogRedaction as Te,sanitizeCliLogValue as De}from"./log-redaction.js";import{registerPairCommand as Ue,runSmartStart as Me}from"./commands/pair.js";import{registerLogoutCommand as _e}from"./commands/logout.js";import{clearDaemonPidIfOwner as te,createDaemonInstanceId as Ne,clearDaemonLauncher as re,findRunningDaemonProcessIds as xe,isRemoteAccessDisabled as ae,prepareRemoteAccessPause as Oe,stopPreparedRemoteAccessService as $e,isIsolatedDaemonE2eInstance as Ve,getDaemonStatus as ie,registerDaemonCommand as Le,writeDaemonPid as se,writeDaemonLauncher as ce}from"./commands/daemon.js";import{registerRuntimeCommand as je}from"./commands/runtime.js";import{registerAdapterCommand as He}from"./commands/adapter.js";import{registerAgentCommand as Be}from"./commands/agent.js";import{registerManagerCommand as Fe}from"./commands/manager.js";import{registerWorkerCommand as Ge}from"./commands/worker.js";import{handleUpgradeStart as We,registerUpgradeCommand as ze}from"./commands/upgrade.js";import{registerToolsCommand as Je}from"./commands/tools.js";import{registerQueueCommand as Ke}from"./commands/queue.js";import{registerSkillCommand as qe}from"./commands/skill.js";import{registerIntegrationCommand as Qe}from"./commands/integration.js";import{registerRoomCommand as Ye}from"./commands/room.js";import{registerSupportCommand as Ze}from"./commands/support.js";import{SessionManager as Xe}from"./session/manager.js";import{createTrustedProjectFsRequestResolver as eo}from"./session/handlers/project-fs.js";import{SERVERS as z,regionToUrl as oo,urlToRegion as no}from"./region.js";import{clearUpgradeAttempt as to,getCurrentVersion as de,handleStartupCrashCheck as ro,readUpgradeAttempt as ao}from"./upgrade/engine.js";import{AutoUpgradeScheduler as io}from"./upgrade/scheduler.js";import{ensureUpdaterInstalled as so}from"./upgrade/updater-bootstrap.js";import{launchUpdater as co,markUpdaterHealthy as lo}from"./upgrade/updater-launcher.js";import{detectAgents as mo}from"./agents/detect.js";import{augmentProcessPath as le}from"./env-path.js";import{acquireDaemonInstanceLock as po}from"./commands/daemon-instance-lock.js";le();const O=de();import{getCachedAgentInfos as $,resolveAgentInfos as go}from"./agents/model-registry.js";import{initCliLogReporter as uo,reportLog as T,reportUpgradeAggregateMetric as fo}from"./log-reporter.js";import{isNativeFusionEnabled as ho}from"./native-fusion/config.js";import{NativeSessionFusionService as vo}from"./native-fusion/service.js";import{startDaemonLogRetention as Io}from"./daemon-log.js";import{startDaemonIpcServer as me}from"./daemon-ipc/server.js";import{resolveDaemonIpcPaths as So,resolveManagedRoomIpcPaths as yo}from"./daemon-ipc/paths.js";import{createDaemonControlHandlers as wo}from"./daemon-ipc/control.js";import{createAgentReadinessHandlers as Co}from"./daemon-ipc/agent-readiness.js";import{scanMachineAgents as Ro}from"./session/handlers/agents.js";import{validateAgentWorkspaceConfiguration as bo}from"./session/handlers/agent-workspace-validation.js";import{DAEMON_IPC_VERSION as ko}from"./daemon-ipc/protocol.js";import{RoomSessionRegistry as Po}from"./room/session-registry.js";import{HttpRoomBindingClient as Ao}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as Eo}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as To}from"./room/host-session-observations.js";import{RoomMirrorSyncService as Do}from"./room/mirror-sync-service.js";import{handleLiveRoomDelivery as Uo}from"./room/live-delivery.js";import{NativeSourceDeletionStore as Mo}from"./native-fusion/native-source-deletions.js";import{matchesRoomOwner as _o,isCurrentRegistration as No}from"./room/registration-authority.js";import{RoomActivationService as xo,resolveManagedAgentPolicy as Oo}from"./room/activation-service.js";import{RoomActivationAudit as $o}from"./room/activation-audit.js";import{RoomAttachmentCache as Vo}from"./room/attachment-cache.js";import{RoomAttachmentUploader as Lo}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as jo}from"./room/managed-context.js";import{createManagedRoomExecutor as Ho}from"./room/managed-room-ipc.js";import{SESSION_SYNC_PROTOCOL_VERSION as pe,SESSION_SYNC_ADVERTISED_CAPABILITIES as ge}from"@shennian/wire";import{getDaemonInstallationId as Bo}from"./session/store.js";import{OWNER_DIRECT_TURN_OPTIONS as Fo}from"./session/queue-helpers.js";import{synchronizeMachinePersonalAuthority as Go}from"./personal-sync/machine-authority.js";import{CLI_SESSION_SYNC_DATA_EPOCH as ue,ensurePersonalDataEpoch as Wo}from"./personal-sync/epoch.js";import{DeletionSyncRetryWaitError as zo,DeletionSyncTerminalError as Jo}from"./session/deletion-sync-state.js";const fe=Pe(),Ko=W("daemon.pid"),qo=W("daemon.instance.lock");function Qo(){try{const a=G.readFileSync(Ko,"utf-8").trim();if(a.startsWith("{")){const g=JSON.parse(a),c=Number(g.pid);return Number.isInteger(c)&&c>0?c:null}const s=parseInt(a,10);return Number.isInteger(s)&&s>0?s:null}catch{return null}}function Yo(a){return a.replace(/^https:\/\//,"wss://").replace(/^http:\/\//,"ws://")}function J(){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 Zo(a){const s=[`trigger=${a.trigger??"unknown"}`,`phase=${a.phase}`,`code=${a.code??"unknown"}`,`attempt=${a.reconnectAttempt}`];return a.reason&&s.push(`reason=${a.reason}`),a.error&&s.push(`error=${a.error}`),s.join(" ")}const i=new Ce;i.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(O),i.enablePositionalOptions(),Qe(i),Ye(i),Ze(i),Se(i),i.option("--api <url>","Server URL override").option("--name <name>","Machine name",Re.hostname()).action(async a=>{const s=p(),g=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??void 0;await Me(g,a.name)}),i.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async a=>{const s=W("env.json");try{const e=JSON.parse(G.readFileSync(s,"utf-8"));for(const[n,o]of Object.entries(e))process.env[n]||(process.env[n]=o);le()}catch{}Te(ne(process.env,p()));const g=Io();ae()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(g),process.exit(0));const c=Ne(),w=po({lockPath:qo,instanceId:c});w.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(w.ownerPid?` (PID ${w.ownerPid})`:"")+", skipping duplicate start"),clearInterval(g),process.exit(0));try{const e=Qo();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),w.release(),process.exit(0)}catch{}}catch{}const K=Ve(process.env,fe)?[]:xe(process.pid);K.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${K[0]}), skipping duplicate start`),w.release(),process.exit(0)),se(process.pid,c,{version:O}),ce(process.pid,void 0,c),process.on("exit",()=>{te(process.pid,c),re(c)}),process.on("exit",w.release);let V=null;try{V=so()}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const D=await ro();if(D){V||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{co({fromVersion:O,targetVersion:D.from,oldDaemonPid:process.pid,operation:"rollback",rollbackJobId:D.jobId},{readiness:V}),console.error(`[upgrade] New version crashed ${D.attemptCount} times; external rollback scheduled.`),process.stdin.resume();return}catch(e){console.error(`[upgrade] Crash-loop rollback could not start: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}try{Wo()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const m=p(),u=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??m.serverUrl??z.cn.url,h=de(),l=await mo({serverUrl:u,authToken:m.machineToken??m.accessToken}),C=l.map(e=>e.type),L=$(l),f=new Po,U=new To,R=new jo(f),S=new Ao(u),ve=new Vo({actionClient:S}),Ie=new Lo({actionClient:S});let y=null,r;const q=Ho({authority:R,actionClient:S,attachmentCache:ve,attachmentUploader:Ie,observations:U,getMachineCredentials:()=>({machineToken:p().machineToken}),managerExecutor:e=>r.executeManagedManagerAction(e),onSendOutcome:e=>r.noteManagedRoomSendOutcome(e),getActiveRunId:(e,n)=>r.getManagedRoomActiveRunId(e,n),scheduleExecutor:(e,n,o)=>oe({serverUrl:u,machineToken:p().machineToken,sessionId:e,action:n,args:o}),onPaused:({registryKey:e,sessionId:n})=>{const t=f.getByRegistryKey(e)?.binding;t&&f.setBinding({registryKey:e,bindingId:t.bindingId,roomId:t.roomId,agentIdentityId:t.agentIdentityId,agentMembershipId:t.agentMembershipId,status:"paused",activationMode:"paused"}),R.revokeSession(e),r?.discardPendingManagedRoomMessages(n)}});let j=null,H=null,B=null;const b=await me({paths:So(),handlers:{...wo({daemonVersion:h,protocolVersion:ko,getStatus:()=>ie(),getAgents:()=>l,completeAgentAuthentication:e=>r.resumePendingAcpAuthentication(e),bindManagedRoom:e=>r.bindManagedRoomFromLocal(e),startUpgrade:async e=>{if(!j)throw new Error("upgrade_unavailable");return j(e)},prepareExternalUpgrade:e=>{if(!H)throw new Error("external_upgrade_unavailable");return H(e)},cancelExternalUpgrade:()=>{if(!B)throw new Error("external_upgrade_unavailable");return B()}}),...Co({getStatus:()=>ie(),getSnapshot:()=>$(l).map(e=>{const n=l.find(o=>o.type===e.type);return{...e,runtimePlatform:n?.runtimePlatform,runtimeIsWsl:n?.runtimeIsWsl}}),discover:async e=>{if(!r)throw new Error("daemon_starting");const n=await Ro(!0,e);if(e){const t=l.filter(I=>I.type!==e);l.splice(0,l.length,...t,...n.detectedAgents)}else l.splice(0,l.length,...n.detectedAgents);const o=$(l);return d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:l.map(t=>t.type),agents:o}}),n.agents.map(t=>{const I=n.detectedAgents.find(F=>F.type===t.type);return{...t,runtimePlatform:I?.runtimePlatform,runtimeIsWsl:I?.runtimeIsWsl}})},validate:async e=>{if(!r)throw new Error("daemon_starting");return bo(e,{ignoreNativeSession:(n,o)=>P?.ignoreNativeSource(n,o)})}})}});let k;try{k=await me({paths:yo(),handlers:{"managed-room.execute":e=>q(e),"schedule.personal":async e=>{const n=e,o=ye(n?.context);if(f.getByNianSessionId(o)?.binding)throw new Error("schedule_personal_context_required");return await oe({serverUrl:u,machineToken:p().machineToken,sessionId:o,action:n.operation,args:n})}}})}catch(e){throw await b.close(),e}let Q=!1;const M=()=>{Q||(Q=!0,Promise.allSettled([b.close(),k.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",M),process.once("SIGTERM",M),!m.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await Promise.allSettled([b.close(),k.close()]),process.exitCode=1;return}process.removeListener("SIGINT",M),process.removeListener("SIGTERM",M);const Y=`${Yo(u)}/relay/machine`,_=m.machineToken;if(!_)return;try{await Go({serverUrl:u,machineToken:_,machineId:m.machineId})}catch(e){console.error(`[personal-sync] machine authority verification failed: ${e instanceof Error?e.message:String(e)}`),await Promise.allSettled([b.close(),k.close()]),process.exit(1)}m.machineId&&uo(u,m.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${Y}... (v${h}) agents: ${C.join(",")}`),T({level:"info",wsEvent:"daemon.start",metadata:{version:h,agents:C}});let P=null,A=null,N=0,v=ao();const Z=Ae(m.autoUpgrade),d=new Ee({serverUrl:Y,machineToken:_,cliVersion:h,agentList:C,autoUpgradePolicy:Z,sessionSyncProtocolVersion:pe,sessionSyncCapabilities:ge,daemonInstallationId:Bo(),dataEpoch:ue,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),T({level:"info",wsEvent:"daemon.connected"}),L.some(o=>o.models.length>0)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:C,agents:L}}),go(l,{serverUrl:m.serverUrl??u,authToken:m.machineToken??m.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(L)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:C,agents:o}})}).catch(()=>{}),v&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:v.from,to:v.to,...v.jobId?{jobId:v.jobId}:{},activeRunCount:r.getPendingRecoveryCount()}}),x.start(),A?.start();const e=++N,n=async()=>{try{if(await r.synchronizeSessionDeletions(),await r.recoverInterruptedRuns(),v){const o=v;v=null,o.jobId&&lo(o.jobId,o.to),to(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),fo({kind:"upgrade",result:o.operation==="rollback"?"rolled_back":"healthy",stage:"terminal"}),d.sendEvent({type:"event",event:"upgrade",payload:{state:o.operation==="rollback"?"rolled_back":"healthy",machineId:"self",from:o.from,to:o.to,...o.jobId?{jobId:o.jobId}:{}}}),o.operation!=="rollback"&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:o.from,to:o.to}})}e===N&&d.getState()==="connected"&&P?.handleConnected()}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===N&&d.getState()==="connected"){if(o instanceof Jo)return;const t=o instanceof zo?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},t).unref?.()}}};n()},onDisconnected:e=>{N+=1,r?.revokeProjectFsHandles(),A?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${Zo(e)}`),T({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onProjectFsCapabilityChanged:e=>{e||r?.revokeProjectFsHandles()},onReq:e=>{if(console.log(`[${new Date().toISOString()}] [req] ${e.method}`),T({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),e.method==="room.message.deliver"){Uo(d,A,e);return}r.handleReq(e)}});P=ho()?new vo(d):null,A=new Do({registry:f,observations:U,actionClient:S,getMachineCredentials:()=>({machineToken:p().machineToken}),isSessionDeleted:e=>new Mo().hasSession(e),onMessages:async(e,n,o,t)=>{await y?.processMessages(e,n,o,t)},onBindingPaused:e=>{e.lifecycleState==="closed"?(R.revokeSession(e.registryKey),r?.retireManagedRoomSession(e.nianSessionId).catch(()=>{console.warn("[room.sync] retired session cleanup failed")})):r?.discardPendingManagedRoomMessages(e.nianSessionId)}});const x=new io({currentVersion:h,machineId:m.machineId??"unknown-machine",serverUrl:m.serverUrl??u,getIdleState:()=>r.getUpgradeIdleState(),sessionSyncProtocolVersion:pe,dataEpoch:ue,advertisedCapabilities:ge,updaterProtocolVersion:1,startUpgrade:async(e,n)=>{const{handleUpgradeStart:o}=await import("./commands/upgrade.js");await o(d,"auto-upgrade",e,{currentVersion:h,prepareForUpgrade:t=>r.prepareForUpgrade(t),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0,waitForCompletion:!0,canHandoff:n?void 0:()=>r.getUpgradeIdleState().idle})}});x.setPolicy(Z),r=new Xe(d,P,h,x,new Eo({registry:f,observations:U,bindingClient:S,getMachineCredentials:()=>{const e=p();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${c}`}),{prepare:()=>Oe(),isDisabled:()=>ae(),shutdownAfterResponse:()=>{setTimeout(()=>{$e(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>R.revokeToken(e),q,null,eo(_),()=>$(l)),j=async e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=typeof n.targetVersion=="string"?n.targetVersion:void 0,t=n.confirmedMajor===!0;return await We(d,`local-upgrade-${Date.now()}`,o,{currentVersion:h,confirmedMajor:t,prepareForUpgrade:F=>r.prepareForUpgrade(F),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},H=e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},t=`desktop-${(typeof n.targetVersion=="string"?n.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:t,...r.prepareForUpgrade(t)}},B=()=>(r.cancelUpgradePreparation(),{cancelled:!0}),y=new xo({registry:f,observations:U,enqueueConversationBatch:e=>r.enqueueManagedRoomBatch(e),loadContext:async e=>{const n=p().machineToken?.trim(),o=e.binding;if(!n||!o)throw new Error("managed_room_context_unavailable");const t=await S.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});if(!No(f,e)||!_o(e,t))throw new Error("managed_room_binding_unavailable");return{roomName:t.room.name,memberCount:t.room.memberCount,members:t.room.members,agentsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent}:{},effectiveModelsBySessionId:t.effectiveModelId!==void 0?{[e.nianSessionId]:t.effectiveModelId}:void 0,agentSystemPromptsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,n,o,t)=>{const I=p().machineToken;if(!I)throw new Error("machine_not_authorized");return R.issue({registryKey:e,machineCredential:I,activationCauseMessageId:n,allowedActions:o,propagateWorkerRoomOrigin:t})},audit:new $o,resolveManagedAgentPolicy:e=>Oo({...e,runtime:l.find(n=>n.type===e.registration.runtimeAdapter)})}),r.scheduleHandler=e=>we({serverUrl:u,machineToken:p().machineToken,req:e,client:d,queue:r.getScheduleQueue(),registry:f}),r.getScheduleQueue().validateScheduledTarget=async e=>{if(!e.bindingId||!e.roomId)return"ready";if(f.getByNianSessionId(e.sessionId)?.binding?.bindingId!==e.bindingId)return"ended";const o=await S.status({machineToken:p().machineToken,roomId:e.roomId,bindingId:e.bindingId});return o.status==="active"&&o.room.status==="active"?"ready":"ended"},r.setManagedRoomTurnPreparer(e=>y.prepareConversationTurn(e)),r.setManagedAgentDirectTurnPreparer((e,n)=>y.prepareDirectConversationTurn(e,n)),r.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?y.prepareDirectConversationTurn(e.sessionId,Fo):e.source==="manager_event"?y.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[],managerEvent:!0}):e.source!=="managed_room"||!e.roomContextRegistryKey||!e.activationCauseMessageId||!e.rootCauseMessageId||!e.activationSequence||e.activationHopCount===null?null:y.prepareConversationTurn([{registryKey:e.roomContextRegistryKey,causeMessageId:e.activationCauseMessageId,rootCauseMessageId:e.rootCauseMessageId,sequence:e.activationSequence,hopCount:e.activationHopCount}])),G.mkdirSync(fe,{recursive:!0}),se(process.pid,c,{version:h}),ce(process.pid,void 0,c),d.connect(),process.stdin.resume();let X=!1;const ee=async()=>{if(!X){X=!0,console.log(E.gray(`
3
+ Disconnecting...`)),T({level:"info",wsEvent:"daemon.stop"}),clearInterval(g);try{await r.cleanup(),x.stop(),P?.stop(),A?.stop(),d.disconnect(),await Promise.allSettled([b.close(),k.close()]),te(process.pid,c),re(c)}finally{process.exit(0)}}};process.on("SIGINT",()=>{ee()}),process.on("SIGTERM",()=>{ee()})}),i.command("workbuddy-session-hook",{hidden:!0}).description("(retired) WorkBuddy Room Hook").allowUnknownOption(!0).action(()=>J()),i.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(()=>J()),i.command("room-tool-server",{hidden:!0}).description("(internal) Run the official Shennian Room MCP tool").allowUnknownOption(!0).action(()=>J());const he=i.command("config").description("View or modify configuration");he.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${ke()}`);const a=p(),s=De(a,ne(process.env,a));if(a.serverUrl){const g=no(a.serverUrl);console.log(`Region: ${g} (${z[g].label})`)}console.log(JSON.stringify(s,null,2))}),he.command("set").description("Update a config value").argument("<key>",'Config key (e.g. "server")').argument("<value>",'Config value (e.g. "cn" or "global")').action((a,s)=>{const g=p();if(a==="server"){s!=="cn"&&s!=="global"&&(console.error(E.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const c=s;g.serverUrl=oo(c),be(g),console.log(E.green(`\u2713 Server set to ${z[c].label}`)),console.log(E.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(E.red(`\u2717 Unknown config key: ${a}. Supported: server`)),process.exit(1)}),Ue(i),_e(i),Le(i),je(i),He(i,{clientVersion:O}),Be(i),Fe(i),Ge(i),ze(i),Je(i),Ke(i),qe(i),i.parse();
@@ -1 +1 @@
1
- import i from"node:crypto";import t from"node:fs";import a from"node:path";import n from"qrcode";import{resolveShennianPath as p}from"../config/index.js";async function f(e,r=p("room-invites")){t.mkdirSync(r,{recursive:!0,mode:448});const m=i.createHash("sha256").update(e).digest("hex").slice(0,24),o=a.resolve(r,`room-invite-${m}.png`);return await n.toFile(o,e,{type:"png",width:640,margin:4,errorCorrectionLevel:"M"}),t.chmodSync(o,384),{imagePath:o,mimeType:"image/png",payload:e}}export{f as createRoomInviteQr};
1
+ import m from"node:crypto";import r from"node:fs";import n from"node:path";import p from"qrcode";import{resolveShennianPath as s}from"../config/index.js";async function y(o,t=s("room-invites")){r.mkdirSync(t,{recursive:!0,mode:448});const a=m.createHash("sha256").update(o).digest("hex").slice(0,24),e=n.resolve(t,`room-invite-${a}.png`);await p.toFile(e,o,{type:"png",width:640,margin:4,errorCorrectionLevel:"M"}),r.chmodSync(e,384);const i=r.readFileSync(e);return{imagePath:e,mimeType:"image/png",payload:o,dataUrl:`data:image/png;base64,${i.toString("base64")}`}}export{y as createRoomInviteQr};
@@ -1 +1 @@
1
- import{RoomActionHttpError as y}from"./room-action-client.js";import p from"node:crypto";import{isCurrentRegistration as h,matchesRoomOwner as f}from"./registration-authority.js";class B{deps;timer=null;running=!1;started=!1;idleStreak=0;sawActivity=!1;constructor(i){this.deps=i}start(){this.started||(this.started=!0,this.idleStreak=0,this.sawActivity=!1,this.arm(this.deps.initialDelayMs??0))}stop(){this.started=!1,this.timer&&(this.deps.clearTimeout??clearTimeout)(this.timer),this.timer=null}async deliverLive(i){const n=this.deps.registry.list().filter(s=>s.binding?.roomId!==i.roomId||s.binding.status!=="active"||s.binding.activationMode==="paused"||!h(this.deps.registry,s)?!1:BigInt(i.sequence)>BigInt(s.binding.lastDeliveredSequence));if(n.length===0)return{accepted:!1,registrationCount:0};this.sawActivity=!0,this.deps.onMessages&&await this.deps.onMessages(i.roomId,[i],n,{roomName:"\u5F53\u524D\u7FA4\u804A"});for(const s of n){const o=this.deps.registry.getByRegistryKey(s.registryKey);o?.binding?.status==="active"&&o.binding.activationMode!=="paused"&&BigInt(i.sequence)>BigInt(o.binding.lastDeliveredSequence)&&this.deps.registry.advanceCursor({registryKey:o.registryKey,cursor:"delivered",sequence:i.sequence})}return{accepted:!0,registrationCount:n.length}}async runOnce(){if(this.running)return;const i=this.deps.getMachineCredentials().machineToken?.trim();if(i){this.running=!0;try{for(const s of this.deps.registry.list())s.lifecycleState!=="closed"&&this.deps.isSessionDeleted?.(s.nianSessionId)&&this.retire(s);const n=v(this.deps.registry.list());for(const[s,o]of n)await this.syncRoom(s,o,i)}finally{this.running=!1}}}async syncRoom(i,n,s){const o=n.filter(t=>t.binding&&(t.binding.status==="active"||t.binding.status==="paused"));if(o.length===0)return;let g=0,c=null,r=null;for(const t of o){if(!h(this.deps.registry,t))continue;const a=t.binding,u={machineToken:s,roomId:i,bindingId:a.bindingId};try{const e=await this.deps.actionClient.status(u);if(!h(this.deps.registry,t))continue;if((a.status!==e.status||a.activationMode!==e.activationMode)&&(this.sawActivity=!0),!f(t,e)||this.deps.isSessionDeleted?.(t.nianSessionId)){this.retire(t);continue}e.activationCapability==="managed"&&e.managedWorkspacePath&&!this.deps.observations?.getBySource(t)&&this.deps.observations?.observe({machineId:t.machineId,runtimeAdapter:t.runtimeAdapter,sourceSessionKey:t.sourceSessionKey,hostContextHash:l(t.hostProcessIdentity),workspaceHash:l(e.managedWorkspacePath),workspacePath:e.managedWorkspacePath,lifecycleState:"active",eventName:"ManagedSync"});const m=this.deps.registry.setBinding({registryKey:t.registryKey,bindingId:e.bindingId,roomId:e.roomId,agentIdentityId:e.agentIdentityId,agentMembershipId:e.agentMembershipId,status:e.status,activationMode:e.activationMode});if((e.status==="paused"||e.activationMode==="paused")&&this.deps.onBindingPaused?.(m),e.status==="detached"){g+=1;continue}if(e.status==="pending_approval")continue;c??=u,r??={roomName:e.room.name,memberCount:e.room.memberCount,members:e.room.members,agentsByIdentityId:{},agentSystemPromptsByIdentityId:{}},e.effectiveModelId!==void 0&&(r.effectiveModelsBySessionId??={},r.effectiveModelsBySessionId[t.nianSessionId]=e.effectiveModelId),e.agent&&(r.agentsByIdentityId[e.agentIdentityId]=e.agent,r.agentSystemPromptsByIdentityId[e.agentIdentityId]=e.agent.systemPrompt??"")}catch(e){if(I(e)){this.deps.registry.detachBinding(t.registryKey),this.sawActivity=!0,g+=1;continue}return}}if(c){const t=n.filter(a=>h(this.deps.registry,a)).map(a=>this.deps.registry.getByRegistryKey(a.registryKey)??a);await this.pullPages(c,i,t,r);return}}async pullPages(i,n,s,o){if(s.length===0)return;const g=this.deps.maxPagesPerRun??100;let c=s.map(r=>r.binding?.lastDeliveredSequence??"0").reduce((r,t)=>BigInt(r)<BigInt(t)?r:t);for(let r=0;r<g;r+=1){const t=await this.deps.actionClient.read({...i,afterSequence:c,limit:100});t.messages.length>0&&(this.sawActivity=!0);const a=s.filter(u=>h(this.deps.registry,u));if(a.length===0)return;this.deps.onMessages&&await this.deps.onMessages(n,t.messages,a,o??{roomName:t.roomName});for(const u of a){const e=this.deps.registry.getByRegistryKey(u.registryKey);e?.binding&&(e.binding.status==="active"||e.binding.status==="paused")&&BigInt(t.nextSequence)>BigInt(e.binding.lastDeliveredSequence)&&this.deps.registry.advanceCursor({registryKey:u.registryKey,cursor:"delivered",sequence:t.nextSequence})}if(!t.hasMore)return;if(t.nextSequence===c)throw new y("room_sync_no_progress","Room mirror synchronization made no progress.");c=t.nextSequence}}retire(i){this.sawActivity=!0;const n=this.deps.registry.updateLifecycle(i.registryKey,"closed");this.deps.onBindingPaused?.(n)}arm(i){if(!this.started||this.timer)return;const n=this.deps.setTimeout??setTimeout;this.timer=n(()=>{this.timer=null,this.runOnce().catch(()=>{console.warn("[room.sync] delivery failed; retrying on next poll")}).finally(()=>{this.idleStreak=this.sawActivity?0:Math.min(this.idleStreak+1,10),this.sawActivity=!1,this.started&&this.arm(this.nextDelayMs())})},i),this.timer.unref?.()}nextDelayMs(){const i=this.deps.intervalMs??5e3,n=Math.max(i,this.deps.maxIntervalMs??3e4),s=Math.min(n,i*2**Math.max(0,this.idleStreak-1));return Math.round(s*(.8+.4*(this.deps.random??Math.random)()))}}function l(d){return p.createHash("sha256").update(d,"utf8").digest("hex")}function v(d){const i=new Map;for(const n of d){if(!n.binding||n.lifecycleState==="closed")continue;const s=i.get(n.binding.roomId)??[];s.push(n),i.set(n.binding.roomId,s)}return i}function I(d){return d instanceof y&&(d.code==="room_binding_unavailable"||d.code==="room_action_http_403")}export{B as RoomMirrorSyncService};
1
+ import{RoomActionHttpError as y}from"./room-action-client.js";import p from"node:crypto";import{isCurrentRegistration as h,matchesRoomOwner as f}from"./registration-authority.js";class B{deps;timer=null;running=!1;started=!1;idleStreak=0;sawActivity=!1;constructor(i){this.deps=i}start(){this.started||(this.started=!0,this.idleStreak=0,this.sawActivity=!1,this.arm(this.deps.initialDelayMs??0))}stop(){this.started=!1,this.timer&&(this.deps.clearTimeout??clearTimeout)(this.timer),this.timer=null}async deliverLive(i){const s=this.deps.registry.list().filter(n=>n.binding?.roomId!==i.roomId||n.binding.status!=="active"||n.binding.activationMode==="paused"||!h(this.deps.registry,n)?!1:BigInt(i.sequence)>BigInt(n.binding.lastDeliveredSequence));if(s.length===0)return{accepted:!1,registrationCount:0};this.sawActivity=!0,this.deps.onMessages&&await this.deps.onMessages(i.roomId,[i],s,{roomName:"\u5F53\u524D\u7FA4\u804A"});for(const n of s){const o=this.deps.registry.getByRegistryKey(n.registryKey);o?.binding?.status==="active"&&o.binding.activationMode!=="paused"&&BigInt(i.sequence)>BigInt(o.binding.lastDeliveredSequence)&&this.deps.registry.advanceCursor({registryKey:o.registryKey,cursor:"delivered",sequence:i.sequence})}return{accepted:!0,registrationCount:s.length}}async runOnce(){if(this.running)return;const i=this.deps.getMachineCredentials().machineToken?.trim();if(i){this.running=!0;try{for(const n of this.deps.registry.list())n.lifecycleState!=="closed"&&this.deps.isSessionDeleted?.(n.nianSessionId)&&this.retire(n);const s=v(this.deps.registry.list());for(const[n,o]of s)await this.syncRoom(n,o,i)}finally{this.running=!1}}}async syncRoom(i,s,n){const o=s.filter(t=>t.binding&&(t.binding.status==="active"||t.binding.status==="paused"));if(o.length===0)return;let g=0,c=null,r=null;for(const t of o){if(!h(this.deps.registry,t))continue;const a=t.binding,u={machineToken:n,roomId:i,bindingId:a.bindingId};try{const e=await this.deps.actionClient.status(u);if(!h(this.deps.registry,t))continue;if((a.status!==e.status||a.activationMode!==e.activationMode)&&(this.sawActivity=!0),!f(t,e)||this.deps.isSessionDeleted?.(t.nianSessionId)){this.retire(t);continue}e.activationCapability==="managed"&&e.managedWorkspacePath&&!this.deps.observations?.getBySource(t)&&this.deps.observations?.observe({machineId:t.machineId,runtimeAdapter:t.runtimeAdapter,sourceSessionKey:t.sourceSessionKey,hostContextHash:l(t.hostProcessIdentity),workspaceHash:l(e.managedWorkspacePath),workspacePath:e.managedWorkspacePath,lifecycleState:"active",eventName:"ManagedSync"});const m=this.deps.registry.setBinding({registryKey:t.registryKey,bindingId:e.bindingId,roomId:e.roomId,agentIdentityId:e.agentIdentityId,agentMembershipId:e.agentMembershipId,status:e.status,activationMode:e.activationMode});if((e.status==="paused"||e.activationMode==="paused")&&this.deps.onBindingPaused?.(m),e.status==="detached"){g+=1;continue}if(e.status==="pending_approval")continue;c??=u,r??={roomName:e.room.name,memberCount:e.room.memberCount,members:e.room.members,agentsByIdentityId:{},agentSystemPromptsByIdentityId:{}},e.effectiveModelId!==void 0&&(r.effectiveModelsBySessionId??={},r.effectiveModelsBySessionId[t.nianSessionId]=e.effectiveModelId),e.agent&&(r.agentsByIdentityId[e.agentIdentityId]=e.agent,r.agentSystemPromptsByIdentityId[e.agentIdentityId]=e.agent.systemPrompt??"")}catch(e){if(I(e)){this.deps.registry.detachBinding(t.registryKey),this.sawActivity=!0,g+=1;continue}return}}if(c){const t=s.filter(a=>h(this.deps.registry,a)).map(a=>this.deps.registry.getByRegistryKey(a.registryKey)??a);await this.pullPages(c,i,t,r);return}}async pullPages(i,s,n,o){if(n.length===0)return;const g=this.deps.maxPagesPerRun??100;let c=n.map(r=>r.binding?.lastDeliveredSequence??"0").reduce((r,t)=>BigInt(r)<BigInt(t)?r:t);for(let r=0;r<g;r+=1){const t=await this.deps.actionClient.read({...i,afterSequence:c,limit:100});t.messages.length>0&&(this.sawActivity=!0);const a=n.filter(u=>h(this.deps.registry,u));if(a.length===0)return;this.deps.onMessages&&await this.deps.onMessages(s,t.messages,a,o??{roomName:t.roomName});for(const u of a){const e=this.deps.registry.getByRegistryKey(u.registryKey);e?.binding&&(e.binding.status==="active"||e.binding.status==="paused")&&BigInt(t.nextSequence)>BigInt(e.binding.lastDeliveredSequence)&&this.deps.registry.advanceCursor({registryKey:u.registryKey,cursor:"delivered",sequence:t.nextSequence})}if(!t.hasMore)return;if(t.nextSequence===c)throw new y("room_sync_no_progress","Room mirror synchronization made no progress.");c=t.nextSequence}}retire(i){this.sawActivity=!0;const s=this.deps.registry.updateLifecycle(i.registryKey,"closed");this.deps.onBindingPaused?.(s)}arm(i){if(!this.started||this.timer)return;const s=this.deps.setTimeout??setTimeout;this.timer=s(()=>{this.timer=null,this.runOnce().catch(()=>{console.warn("[room.sync] delivery failed; retrying on next poll")}).finally(()=>{this.idleStreak=this.sawActivity?0:Math.min(this.idleStreak+1,10),this.sawActivity=!1,this.started&&this.arm(this.nextDelayMs())})},i),this.timer.unref?.()}nextDelayMs(){const i=this.deps.intervalMs??5e3,s=Math.max(i,this.deps.maxIntervalMs??3e4),n=Math.min(s,i*2**Math.max(0,this.idleStreak-1));return Math.min(s,Math.round(n*(.8+.4*(this.deps.random??Math.random)())))}}function l(d){return p.createHash("sha256").update(d,"utf8").digest("hex")}function v(d){const i=new Map;for(const s of d){if(!s.binding||s.lifecycleState==="closed")continue;const n=i.get(s.binding.roomId)??[];n.push(s),i.set(s.binding.roomId,n)}return i}function I(d){return d instanceof y&&(d.code==="room_binding_unavailable"||d.code==="room_action_http_403")}export{B as RoomMirrorSyncService};
@@ -1 +1 @@
1
- import{isAvailableAgentType as w}from"@shennian/wire";const f=/^[A-Za-z0-9_.:@/-]{1,191}$/,S=new Set(["roomId","agentIdentityId","nianSessionId","assignmentIntentId","agentType","activationMode"]),l=new Set(["roomId","bindingId","agentIdentityId","previousNianSessionId","nianSessionId","assignmentIntentId","agentType"]);async function T(n,t,e){if(!t)throw new Error("Managed Room binding is unavailable.");if(Object.keys(e.params).some(m=>!S.has(m)))throw new Error("Managed Room binding contains unsupported parameters.");const r=a(e.params.roomId,"roomId"),d=a(e.params.agentIdentityId,"agentIdentityId"),s=a(e.params.nianSessionId,"nianSessionId"),o=a(e.params.assignmentIntentId,"assignmentIntentId"),i=c(e.params.agentType),I=R(e.params.activationMode);await p(n,s);const g=await t.bind({roomId:r,agentIdentityId:d,nianSessionId:s,assignmentIntentId:o,sourceSessionKey:y(s),agentType:i,activationMode:I});n.client.sendRes({type:"res",id:e.id,ok:!0,payload:g})}async function b(n,t,e){if(!t)throw new Error("Managed Room binding is unavailable.");if(Object.keys(e.params).some(u=>!l.has(u)))throw new Error("Managed Room restart contains unsupported parameters.");const r=a(e.params.roomId,"roomId"),d=a(e.params.bindingId,"bindingId"),s=a(e.params.agentIdentityId,"agentIdentityId"),o=a(e.params.previousNianSessionId,"previousNianSessionId"),i=a(e.params.nianSessionId,"nianSessionId"),I=a(e.params.assignmentIntentId,"assignmentIntentId"),g=c(e.params.agentType);await n.managerRuntime?.retireManagerAndWorkers(o),await p(n,o),n.chatQueue?.purgeSession(o),await p(n,i);const m=await t.restart({roomId:r,bindingId:d,agentIdentityId:s,previousNianSessionId:o,nianSessionId:i,assignmentIntentId:I,sourceSessionKey:y(i),agentType:g});n.client.sendRes({type:"res",id:e.id,ok:!0,payload:m})}function a(n,t){if(typeof n!="string"||!f.test(n))throw new Error(`Invalid ${t}.`);return n}function c(n){if(typeof n!="string"||!w(n)||n==="manager"||n==="pi"||n==="openclaw")throw new Error("managed_agent_runtime_unsupported");return n}function R(n){if(n===void 0)return"auto_follow";if(n==="auto_follow"||n==="mention_only"||n==="paused")return n;throw new Error("Invalid activationMode.")}function y(n){return`managed:${n}`}async function p(n,t){n.runJournal?.markSessionAborted(t),n.recoveryBarrier?.end(t);const e=n.sessions.get(t);e&&(n.sessions.delete(t),e.managedRoomContextToken&&(n.revokeManagedRoomContextToken?.(e.managedRoomContextToken),e.managedRoomContextToken=null),e.heartbeatTimer&&clearInterval(e.heartbeatTimer),e.adapter.removeAllListeners(),await e.adapter.stop())}export{p as discardPreBindingSession,T as handleManagedRoomBind,b as handleManagedRoomRestart};
1
+ import{isAvailableAgentType as w}from"@shennian/wire";const f=/^[A-Za-z0-9_.:@/-]{1,191}$/,S=new Set(["roomId","agentIdentityId","nianSessionId","assignmentIntentId","agentType","activationMode"]),l=new Set(["roomId","bindingId","agentIdentityId","previousNianSessionId","nianSessionId","assignmentIntentId","agentType"]);async function b(n,t,e){const s=await R(n,t,e.params);n.client.sendRes({type:"res",id:e.id,ok:!0,payload:s})}async function R(n,t,e){if(!t)throw new Error("Managed Room binding is unavailable.");if(Object.keys(e).some(g=>!S.has(g)))throw new Error("Managed Room binding contains unsupported parameters.");const s=o(e.roomId,"roomId"),d=o(e.agentIdentityId,"agentIdentityId"),i=o(e.nianSessionId,"nianSessionId"),a=o(e.assignmentIntentId,"assignmentIntentId"),r=m(e.agentType),I=M(e.activationMode);return await c(n,i),t.bind({roomId:s,agentIdentityId:d,nianSessionId:i,assignmentIntentId:a,sourceSessionKey:p(i),agentType:r,activationMode:I})}async function h(n,t,e){if(!t)throw new Error("Managed Room binding is unavailable.");if(Object.keys(e.params).some(u=>!l.has(u)))throw new Error("Managed Room restart contains unsupported parameters.");const s=o(e.params.roomId,"roomId"),d=o(e.params.bindingId,"bindingId"),i=o(e.params.agentIdentityId,"agentIdentityId"),a=o(e.params.previousNianSessionId,"previousNianSessionId"),r=o(e.params.nianSessionId,"nianSessionId"),I=o(e.params.assignmentIntentId,"assignmentIntentId"),g=m(e.params.agentType);await n.managerRuntime?.retireManagerAndWorkers(a),await c(n,a),n.chatQueue?.purgeSession(a),await c(n,r);const y=await t.restart({roomId:s,bindingId:d,agentIdentityId:i,previousNianSessionId:a,nianSessionId:r,assignmentIntentId:I,sourceSessionKey:p(r),agentType:g});n.client.sendRes({type:"res",id:e.id,ok:!0,payload:y})}function o(n,t){if(typeof n!="string"||!f.test(n))throw new Error(`Invalid ${t}.`);return n}function m(n){if(typeof n!="string"||!w(n)||n==="manager"||n==="pi"||n==="openclaw")throw new Error("managed_agent_runtime_unsupported");return n}function M(n){if(n===void 0)return"auto_follow";if(n==="auto_follow"||n==="mention_only"||n==="paused")return n;throw new Error("Invalid activationMode.")}function p(n){return`managed:${n}`}async function c(n,t){n.runJournal?.markSessionAborted(t),n.recoveryBarrier?.end(t);const e=n.sessions.get(t);e&&(n.sessions.delete(t),e.managedRoomContextToken&&(n.revokeManagedRoomContextToken?.(e.managedRoomContextToken),e.managedRoomContextToken=null),e.heartbeatTimer&&clearInterval(e.heartbeatTimer),e.adapter.removeAllListeners(),await e.adapter.stop())}export{R as bindManagedRoom,c as discardPreBindingSession,b as handleManagedRoomBind,h as handleManagedRoomRestart};
@@ -1 +1 @@
1
- import b from"node:crypto";import{getRegisteredAgents as S,unregisterAgent as k}from"../agents/adapter.js";import{resetCommandSpecCache as w}from"../agents/command-spec.js";import{loadConfig as A}from"../config/index.js";import{handleUpgradeStart as I,handleUpgradeStatus as T}from"../commands/upgrade.js";import{handleAgentsRefresh as P,handleModelsRefresh as C,refreshMachineAgents as M}from"./handlers/agents.js";import{AcpDiscoveryWatcher as x}from"../agents/acp/watcher.js";import{handleAgentCapabilitiesList as D}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as F,handleAgentConfigGet as E,handleAgentConfigTest as Q,handleAgentConfigUpsert as j}from"./handlers/agent-config.js";import{handleChatAbort as z,handleChatSend as O}from"./handlers/chat.js";import{recoverJournaledRun as B}from"./handlers/chat-recovery.js";import{handleSessionRefresh as W}from"./handlers/session-refresh.js";import{handleSessionToolDetail as _}from"./handlers/tool-detail.js";import{handleSessionTitleSet as H}from"./handlers/title.js";import{handleSessionMessageStatus as U}from"./handlers/message-status.js";import{handleAgentWorkspaceValidation as J}from"./handlers/agent-workspace-validation.js";import{handleVoiceSessionBodyWindow as $}from"./handlers/voice-window.js";import{handleRemoteAccessPause as V}from"./handlers/remote-access.js";import{cleanupPendingTransfers as N,handleFsLs as L,handleFsRead as G,handleFsRename as K,handleFsWrite as X,handleFsTransferAbort as Z,handleFsTransferChunk as Y,handleFsTransferFinish as q,handleFsTransferStart as ee,handleFsExportMarkdownPdf as se,handleFsExportMarkdownPdfSetup as te,handleFsArchiveZip as ae}from"./handlers/fs.js";import{handleRegionProbe as re,handleRegionSwitch as ne,handleUpgradeSetPolicy as ie}from"./handlers/control.js";import{ManagerRuntimeService as oe,setManagerRuntimeService as h}from"../manager/runtime.js";import{ChatQueueManager as ce}from"./queue.js";import{createAuthorizedFsRoot as le,createAuthorizedFsRootAsync as ue,resolveAuthorizedPath as he,resolveAuthorizedPathAsync as de,resolveSessionWorkDir as me}from"../fs/boundary.js";import"../agents/claude.js";import"../agents/codex.js";import"../agents/workbuddy.js";import"../agents/gemini.js";import"../agents/cursor.js";import"../agents/opencode.js";import"../agents/pi-coding-agent.js";import"../agents/manager.js";import{registerCustomAgent as pe}from"../agents/custom.js";import{listNodeAdapters as ge}from"../adapter-kit/registry.js";import{syncNodeAdapterAgents as fe}from"../agents/node-adapter.js";import{handleManagedRoomBind as ve,handleManagedRoomRestart as ye,discardPreBindingSession as Re}from"./handlers/room-managed.js";import{getDaemonInstallationId as d,listSessionRecords as u,purgeCanonicalSession as be,purgeSessionRecord as Se,recordSession as ke,upsertMessage as we}from"./store.js";import{requestSessionIndexPublish as m}from"./index-publisher.js";import{checkRunStalls as Ae}from"./run-stall.js";import{reportUpgradeAggregateMetric as Ie}from"../log-reporter.js";import{PersonalHistorySubscriptions as Te}from"./history-subscriptions.js";import{sweepAttachmentTransfers as Pe}from"./attachment-transfer-store.js";import{NativeSourceDeletionStore as Ce}from"../native-fusion/native-source-deletions.js";import{collectLocalNativeSources as Me}from"../native-fusion/session-native-sources.js";import{purgeToolDetails as xe}from"./tool-detail-store.js";import{NativeSessionCleanupOutbox as De}from"./native-cleanup-outbox.js";import{assertPersonalSyncIdentity as o}from"../personal-sync/epoch.js";import{ActiveRunJournal as Fe}from"./run-recovery/journal.js";import{RunRecoveryBarrier as Ee}from"./run-recovery/barrier.js";import{RunRecoveryCoordinator as Qe}from"./run-recovery/coordinator.js";import{ProjectFsHandleStore as je}from"./project-fs-store.js";import{handleProjectFsRequest as ze}from"./handlers/project-fs.js";import{handleAgentPermissionResponse as Oe,handleAcpAgentRequest as Be}from"./handlers/agent-control.js";import{SessionDeletionSynchronizer as We}from"./deletion-synchronizer.js";import{dispatchManagedChat as _e,dispatchQueuedChat as He,interruptQueuedChat as Ue}from"./queued-chat-dispatch.js";const Je=50;import{resolveSessionWorkDir as Ks}from"../fs/boundary.js";function $e(){return"\u795E\u5FF5\u5347\u7EA7\u4E2D\u65AD\u4E86\u672C\u6B21\u8FD0\u884C\u3002\u5DF2\u5B8C\u6210\u7684\u56DE\u590D\u6216\u64CD\u4F5C\u4ECD\u7136\u6709\u6548\uFF0C\u672A\u5B8C\u6210\u90E8\u5206\u53EF\u91CD\u8BD5\u3002\u539F\u4F1A\u8BDD\u5DF2\u4FDD\u7559\u3002"}class Ns{client;nativeFusion;cliVersion;upgradePolicyController;managedRoomBinder;remoteAccessPauseController;managedRoomContextRevoker;managedRoomExecutor;trustedProjectFsRequestResolver;getAgentSnapshot;scheduleHandler;getScheduleQueue(){return this.chatQueue}sessions=new Map;processedReqIds=new Set;runTextAcc=new Map;pendingTransfers=new Map;managerRuntime;chatQueue;historySubscriptions;nativeCleanupOutbox;activityProbeTimer=null;attachmentCleanupTimer=null;activeRequests=0;acpDiscoveryWatcher;runJournal;recoveryBarrier=new Ee;recoveryCoordinator;recoveryPass=null;prepareManagedRecovery;projectFsHandleStore=new je;deletionSynchronizer;constructor(e,t=null,s,n=null,r=null,l=null,p=null,g=null,f=null,v=null,y=null){this.client=e,this.nativeFusion=t,this.cliVersion=s,this.upgradePolicyController=n,this.managedRoomBinder=r,this.remoteAccessPauseController=l,this.managedRoomContextRevoker=p,this.managedRoomExecutor=g,this.trustedProjectFsRequestResolver=v,this.getAgentSnapshot=y,this.runJournal=f??new Fe(d());for(const a of this.runJournal.loadPendingReconciliation().records)this.recoveryBarrier.begin(a.sessionId);this.recoveryCoordinator=new Qe({journal:this.runJournal,barrier:this.recoveryBarrier,recover:(a,i)=>B(this.getRuntime(),a,i),commitTerminal:(a,i)=>this.commitRecoveryTerminal(a,i),isCanonicalTerminal:a=>{const i=u().find(R=>R.sessionId===a.sessionId),c=`terminal:${b.createHash("sha256").update(`${a.runId}\0`).digest("hex")}`;return i?.lastAttentionId===c},onOutcome:a=>Ie({kind:"recovery",adapterType:a.adapterType,result:a.result,stage:a.result==="continued"?"continue":a.result==="reattached"?"reattach":a.result==="resumed"?"resume":a.result==="terminal_reconciled"?"inspect":"terminal",durationMs:a.durationMs,retries:Math.max(0,a.attempts-1),...a.reason?{errorCode:a.reason}:{}})}),this.managerRuntime=new oe({getRuntime:()=>this.getRuntime(),getAgentSnapshot:this.getAgentSnapshot??void 0,dispatchReq:a=>this.handleReq(a,{propagateErrors:!0})}),this.chatQueue=new ce({getRuntime:()=>this.getRuntime(),dispatchReq:a=>this.handleReq(a),dispatchManagedReq:(a,i)=>_e(this.getRuntime(),a,i),dispatchQueuedReq:(a,i)=>He(this.getRuntime(),a,i),interruptQueuedSession:a=>Ue(this.getRuntime(),a)}),this.deletionSynchronizer=new We(e,a=>this.applySessionDeletion(a)),this.historySubscriptions=new Te(this.client),this.nativeCleanupOutbox=new De({logger:console.info}),this.acpDiscoveryWatcher=new x(async()=>{w(),await M(this.getRuntime())}),this.nativeFusion?.setExternalTerminalHandler?.(a=>{this.handleExternalTerminal(a)}),this.nativeFusion?.setExternalClaimHandler?.((a,i)=>{this.chatQueue.noteExternalClaim(a,i)}),this.nativeCleanupOutbox.start(),h(this.managerRuntime),this.managerRuntime.start(),this.reloadCustomAgents();try{this.acpDiscoveryWatcher.start()}catch(a){console.warn(`[acp.discovery] watcher unavailable: ${a instanceof Error?a.message:String(a)}`)}this.activityProbeTimer=setInterval(()=>{Ae(this.sessions.entries(),()=>m(this.client)),this.publishManagedActivitySnapshots().catch(a=>{console.error("[session.activity] managed probe failed",a)})},15e3),this.activityProbeTimer.unref?.(),this.attachmentCleanupTimer=setInterval(()=>{const a=new Set([...u().map(i=>i.workDir),...Array.from(this.pendingTransfers.values()).map(i=>i.rootPath)]);for(const i of a)try{Pe(this.resolvePath(i))}catch(c){console.error("[attachment.cleanup] failed",c instanceof Error?c.message:String(c))}},6e4),this.attachmentCleanupTimer.unref?.()}executeManagedManagerAction(e){return this.managerRuntime.executeScopedWorkerAction(e.managerSessionId,e.action,e.params,e.roomOrigin)}noteManagedRoomSendOutcome(e){for(const t of this.sessions.values()){if(t.managedRoomContextToken!==e.token)continue;const s=t.roomSendOutcome??(t.roomSendOutcome={successes:0,failureCodes:[]});e.ok?s.successes+=1:s.failureCodes.length<20&&s.failureCodes.push(e.errorCode??"managed_room_action_failed");return}}getManagedRoomActiveRunId(e,t){const s=this.sessions.get(t);if(s?.currentRunId)return s.currentRunId;for(const n of this.sessions.values())if(n.managedRoomContextToken===e)return n.currentRunId??null;return null}getRuntime(){return{client:this.client,pendingTransfers:this.pendingTransfers,processedReqIds:this.processedReqIds,reloadCustomAgents:()=>this.reloadCustomAgents(),getAgentSnapshot:this.getAgentSnapshot??void 0,resolvePath:e=>this.resolvePath(e),resolveAuthorizedPath:(e,t)=>this.resolveAuthorizedPath(e,t),resolveAuthorizedPathAsync:(e,t)=>this.resolveAuthorizedPathAsync(e,t),runTextAcc:this.runTextAcc,sessions:this.sessions,evictIdleSessions:()=>this.evictIdleSessions(),nativeFusion:this.nativeFusion,managerRuntime:this.managerRuntime,chatQueue:this.chatQueue,activityPublisher:{publish:(e,t)=>this.publishSessionActivity(e,t)},upgradePolicyController:this.upgradePolicyController,revokeManagedRoomContextToken:this.managedRoomContextRevoker,managedRoomExecutor:this.managedRoomExecutor,runJournal:this.runJournal,recoveryBarrier:this.recoveryBarrier,prepareManagedRecovery:this.prepareManagedRecovery}}async resumePendingAcpAuthentication(e){const t=[...this.sessions.values()].find(s=>s.agentType===e&&s.adapter.hasPendingAuthenticationRetry?.());return t?.adapter.resumeAfterExternalAuthentication?(await t.adapter.resumeAfterExternalAuthentication(),!0):!1}getUpgradeIdleState(){const e=[];return[...this.sessions.values()].some(t=>t.currentRunId!==null)&&e.push("active-agent-turn"),this.pendingTransfers.size>0&&e.push("file-transfer"),this.activeRequests>0&&e.push("active-daemon-request"),{idle:e.length===0,reasons:e}}prepareForUpgrade(e){this.recoveryBarrier.freezeDispatch();const t=this.runJournal.markPlannedUpgrade(e);return this.runJournal.sync(),{activeRunCount:t}}cancelUpgradePreparation(){this.recoveryBarrier.unfreezeDispatch()}async recoverInterruptedRuns(){this.recoveryPass??=this.recoveryCoordinator.recoverAll(),await this.recoveryPass}getPendingRecoveryCount(){return this.runJournal.loadPendingReconciliation().records.length}revokeProjectFsHandles(){this.projectFsHandleStore.revokeAll()}async commitRecoveryTerminal(e,t){const s=this.sessions.get(e.sessionId);s&&(s.currentRunId=null,s.currentRunPhase=null,s.journalRunId=null,s.heartbeatTimer&&(clearInterval(s.heartbeatTimer),s.heartbeatTimer=null)),this.runTextAcc.delete(`${e.sessionId}:${e.providerRunId??e.runId}`),this.publishSessionActivity(e.sessionId,null);const n=$e();we(e.sessionId,{id:`upgrade-recovery-terminal-${e.runId}`,sessionId:e.sessionId,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:"error",content:n,error:!0,recoveryState:t.result,recoveryReason:t.reason})}),ke({sessionId:e.sessionId,agentType:e.agentType,sessionMode:e.sessionMode,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,status:"failed",attentionId:`upgrade-recovery:${e.runId}`,lastActivityAt:new Date().toISOString()}),this.managerRuntime.noteAgentEvent(e.sessionId,{state:"error",runId:e.providerRunId??e.runId,seq:e.lastObservedEventSeq+1,message:n}),m(this.client);const r=e.lastObservedEventSeq+1;this.client.sendAgentEvent({type:"event",event:"agent",payload:t.result==="interrupted_by_upgrade"?{state:"interrupted_by_upgrade",sessionId:e.sessionId,runId:e.providerRunId??e.runId,seq:r,reason:t.reason}:{state:"recovery_failed",sessionId:e.sessionId,runId:e.providerRunId??e.runId,seq:r,reason:t.reason,...t.message?{message:t.message}:{}},seq:r,id:`agent-recovery-terminal-${e.runId}`}),this.chatQueue.noteTerminal(e.sessionId)}setManagedRoomTurnPreparer(e){this.chatQueue.setManagedRoomTurnPreparer(e)}setManagedAgentDirectTurnPreparer(e){this.chatQueue.setManagedAgentDirectTurnPreparer(e)}setManagedRecoveryPreparer(e){this.prepareManagedRecovery=e}async enqueueManagedRoomBatch(e){const t=e.map(s=>{const n=u().find(r=>r.sessionId===s.params.sessionId);return{context:s.context,params:{...s.params,modelId:n?.modelId??s.params.modelId,sessionMode:n?.sessionMode??s.params.sessionMode,managerConfig:n?.managerConfig??s.params.managerConfig,managerDefaultWorkerAgentType:n?.managerDefaultWorkerAgentType??s.params.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:n?.managerDefaultWorkerModelId??s.params.managerDefaultWorkerModelId}}});await this.chatQueue.enqueueManagedRoomBatch(t)}async retireManagedRoomSession(e){this.chatQueue.purgeSession(e),await Re(this.getRuntime(),e)}discardPendingManagedRoomMessages(e){return this.chatQueue.discardPendingManagedRoomMessages(e)}async synchronizeSessionDeletions(){await this.deletionSynchronizer.synchronize()}async applySessionDeletion(e){const t=new Ce({daemonInstallationId:d()});let s=[];try{s=Me(e.sessionId,{records:u()})}catch(r){console.warn("[session.deletions] local native source lookup failed",{error:r instanceof Error?r.message:String(r)})}t.recordSession(e);for(const r of[...e.sources,...s]){const l=t.find(r.agentType,r.sourceSessionKey);l&&l.sessionId!==e.sessionId||t.record({...e,...r})}this.runJournal.markSessionAborted(e.sessionId),this.recoveryBarrier.end(e.sessionId);const n=this.sessions.get(e.sessionId);if(n){this.revokeManagedRoomToken(n);try{await n.adapter.stop()}catch{}this.sessions.delete(e.sessionId)}this.historySubscriptions.closeSession(e.sessionId),this.nativeFusion?.handleSessionDeleted(e.sessionId),this.chatQueue.purgeSession(e.sessionId),xe(e.sessionId),be(e.sessionId),Se(e.sessionId),this.runTextAcc.delete(e.sessionId),this.publishSessionActivity(e.sessionId,null),this.nativeCleanupOutbox.enqueue(e),this.nativeCleanupOutbox.kick()}publishSessionActivity(e,t){this.client.sendEvent({type:"event",event:"session.activity",payload:{sessionId:e,activity:t}})}async handleExternalTerminal(e){const t=this.sessions.get(e);t&&(t.journalRunId&&(this.recoveryCoordinator.completeFromProvider(e,t.journalRunId),t.journalRunId=null),this.sessions.delete(e),this.revokeManagedRoomToken(t),t.heartbeatTimer&&clearInterval(t.heartbeatTimer),t.adapter.removeAllListeners(),await t.adapter.stop().catch(()=>{})),this.chatQueue.noteTerminal(e)}async publishManagedActivitySnapshots(){for(const[e,t]of this.sessions.entries()){if(t.heartbeatTimer||!t.currentRunId||!t.adapter.getStatus)continue;const s=await t.adapter.getStatus().catch(()=>null);if(!s?.active||!s.runPhase)continue;const n=new Date().toISOString();this.publishSessionActivity(e,{sessionId:e,runId:s.runId||t.currentRunId,runPhase:s.runPhase,startedAt:new Date(t.lastActiveAt).toISOString(),updatedAt:n,canStop:s.canStop??!0});const r=t.heartbeatSeq++;this.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:s.runId||t.currentRunId,seq:r,runPhase:s.runPhase,canStop:s.canStop??!0},seq:r,id:`agent-status-${s.runId||t.currentRunId}-${Date.now()}`})}}reloadCustomAgents(){for(const t of S())t.startsWith("custom:")&&k(t);const e=A();for(const[t,s]of Object.entries(e.customAgents??{}))pe(t,s);fe(ge(),new Set(Object.keys(e.customAgents??{})))}async handleReq(e,t={}){const s=this.getRuntime();let n;this.activeRequests++;try{switch(e.method){case"schedule.deliver":if(!this.scheduleHandler)throw new Error("schedules_unavailable");await this.scheduleHandler(e);break;case"chat.send":if(e.params.scheduledDelivery)throw new Error("schedule_source_untrusted");o(e.params),this.recoveryBarrier.isDispatchFrozen()||this.recoveryBarrier.isRecovering(String(e.params.sessionId??""))?n={durableQueueHandoff:await this.chatQueue.handleEnqueue({...e,method:"chat.enqueue"})}:await O(s,e);break;case"chat.enqueue":o(e.params),n={durableQueueHandoff:await this.chatQueue.handleEnqueue(e)};break;case"chat.queue.get":o(e.params),await this.chatQueue.handleGet(e);break;case"chat.queue.edit":o(e.params),await this.chatQueue.handleEdit(e);break;case"chat.queue.delete":o(e.params),await this.chatQueue.handleDelete(e);break;case"chat.queue.promote":o(e.params),await this.chatQueue.handlePromote(e);break;case"chat.abort":o(e.params),await z(s,e);break;case"session.refresh":o(e.params),await W(s,e);break;case"session.history.open":this.historySubscriptions.handleOpen(e);break;case"session.history.sync":this.historySubscriptions.handleSync(e);break;case"session.history.page":this.historySubscriptions.handlePage(e);break;case"session.history.close":this.historySubscriptions.handleClose(e);break;case"session.body.replica.subscribe":this.historySubscriptions.handleReplicaSubscribe(e);break;case"session.body.replica.unsubscribe":this.historySubscriptions.handleReplicaUnsubscribe(e);break;case"session.history":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"upgrade_required"});break;case"session.message.status":U(s,e);break;case"session.tool.detail":await _(s,e);break;case"voice.session.body.window":$(s,e);break;case"session.title.set":await H(s,e);break;case"room.agent.bind-managed":await ve(s,this.managedRoomBinder,e);break;case"room.agent.restart-managed":await ye(s,this.managedRoomBinder,e);break;case"fs.ls":await L(s,e);break;case"project.fs.open":case"project.fs.list":case"project.fs.read":case"project.fs.close":await ze(this.client,this.projectFsHandleStore,e,this.trustedProjectFsRequestResolver);break;case"fs.read":await G(s,e);break;case"fs.write":await X(s,e);break;case"fs.export.markdown-pdf":await se(s,e);break;case"fs.export.markdown-pdf.setup":await te(s,e);break;case"fs.archive.zip":await ae(s,e);break;case"fs.rename":await K(s,e);break;case"fs.transfer.start":await ee(s,e);break;case"fs.transfer.chunk":await Y(s,e);break;case"fs.transfer.finish":await q(s,e);break;case"fs.transfer.abort":await Z(s,e);break;case"region.probe":await re(s,e);break;case"region.switch":await ne(s,e);break;case"upgrade.start":await I(this.client,e.id,e.params.version,{currentVersion:this.cliVersion,confirmedMajor:e.params.confirmedMajor===!0,prepareForUpgrade:r=>this.prepareForUpgrade(r),cancelUpgradePreparation:()=>this.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()}});break;case"upgrade.status":await T(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await ie(s,e);break;case"machine.remote-access.pause":await V(this.client,this.remoteAccessPauseController,e);break;case"agents.refresh":await P(s,e);break;case"agent.permission.respond":await Oe(s,e);break;case"agent.auth.start":case"agent.auth.logout":case"agent.session-options.get":case"agent.external-sessions.list":case"agent.external-sessions.import":case"agent.external-sessions.close":case"agent.external-sessions.delete":await Be(s,e);break;case"models.refresh":await C(s,e);break;case"agent.config.get":await E(s,e);break;case"agent.config.upsert":await j(s,e);break;case"agent.config.clear":await F(s,e);break;case"agent.config.test":await Q(s,e);break;case"agent.workspace.validate":await J(s,e);break;case"agent.capabilities.list":await D(s,e);break;case"skill.list":case"skill.install":case"skill.doctor":case"skill.setup":case"skill.use":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;case"session.wechat-rpa.channel.get":case"session.wechat-rpa.channel.upsert":case"session.wechat-rpa.channel.sync":case"manager.wechat-rpa.channel.get":case"manager.wechat-rpa.channel.upsert":case"manager.wechat-rpa.channel.sync":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;default:this.client.sendRes({type:"res",id:e.id,ok:!1,error:`Unknown method: ${e.method}`})}}catch(r){if(t.propagateErrors)throw r;this.client.sendRes({type:"res",id:e.id,ok:!1,error:r instanceof Error?r.message:String(r)})}finally{this.activeRequests--}return n}evictIdleSessions(){if(this.sessions.size<Je)return;let e=null;for(const[t,s]of this.sessions)(!e||s.lastActiveAt<e.session.lastActiveAt)&&(e={key:t,session:s});e&&(this.revokeManagedRoomToken(e.session),e.session.heartbeatTimer&&(clearInterval(e.session.heartbeatTimer),e.session.heartbeatTimer=null),e.session.adapter.removeAllListeners(),e.session.adapter.stop().catch(()=>{}),this.sessions.delete(e.key))}resolvePath(e){return me(e)}resolveAuthorizedPath(e,t){return he(e,le(t))}async resolveAuthorizedPathAsync(e,t){return de(e,await ue(t))}async cleanup(){this.chatQueue.stop(),this.projectFsHandleStore.revokeAll(),this.acpDiscoveryWatcher.stop(),this.nativeFusion?.setExternalTerminalHandler?.(null),this.nativeCleanupOutbox.stop(),this.historySubscriptions.closeAll(),this.activityProbeTimer&&(clearInterval(this.activityProbeTimer),this.activityProbeTimer=null),this.attachmentCleanupTimer&&(clearInterval(this.attachmentCleanupTimer),this.attachmentCleanupTimer=null);for(const[,e]of this.sessions)this.revokeManagedRoomToken(e),e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),e.adapter.removeAllListeners(),await e.adapter.stop().catch(()=>{});this.sessions.clear(),this.runTextAcc.clear(),N(this.getRuntime()),await this.managerRuntime.stop(),h(null)}revokeManagedRoomToken(e){const t=e.managedRoomContextToken;e.managedRoomContextToken=null,t&&this.managedRoomContextRevoker?.(t)}}export{Ns as SessionManager,Ks as resolveSessionWorkDir,$e as upgradeRecoveryVisibleMessage};
1
+ import b from"node:crypto";import{getRegisteredAgents as S,unregisterAgent as k}from"../agents/adapter.js";import{resetCommandSpecCache as w}from"../agents/command-spec.js";import{loadConfig as A}from"../config/index.js";import{handleUpgradeStart as I,handleUpgradeStatus as T}from"../commands/upgrade.js";import{handleAgentsRefresh as P,handleModelsRefresh as C,refreshMachineAgents as M}from"./handlers/agents.js";import{AcpDiscoveryWatcher as x}from"../agents/acp/watcher.js";import{handleAgentCapabilitiesList as D}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as F,handleAgentConfigGet as E,handleAgentConfigTest as Q,handleAgentConfigUpsert as j}from"./handlers/agent-config.js";import{handleChatAbort as z,handleChatSend as O}from"./handlers/chat.js";import{recoverJournaledRun as B}from"./handlers/chat-recovery.js";import{handleSessionRefresh as W}from"./handlers/session-refresh.js";import{handleSessionToolDetail as _}from"./handlers/tool-detail.js";import{handleSessionTitleSet as H}from"./handlers/title.js";import{handleSessionMessageStatus as U}from"./handlers/message-status.js";import{handleAgentWorkspaceValidation as J}from"./handlers/agent-workspace-validation.js";import{handleVoiceSessionBodyWindow as $}from"./handlers/voice-window.js";import{handleRemoteAccessPause as V}from"./handlers/remote-access.js";import{cleanupPendingTransfers as L,handleFsLs as N,handleFsRead as G,handleFsRename as K,handleFsWrite as X,handleFsTransferAbort as Z,handleFsTransferChunk as Y,handleFsTransferFinish as q,handleFsTransferStart as ee,handleFsExportMarkdownPdf as se,handleFsExportMarkdownPdfSetup as te,handleFsArchiveZip as ae}from"./handlers/fs.js";import{handleRegionProbe as re,handleRegionSwitch as ne,handleUpgradeSetPolicy as ie}from"./handlers/control.js";import{ManagerRuntimeService as oe,setManagerRuntimeService as h}from"../manager/runtime.js";import{ChatQueueManager as ce}from"./queue.js";import{createAuthorizedFsRoot as le,createAuthorizedFsRootAsync as ue,resolveAuthorizedPath as he,resolveAuthorizedPathAsync as de,resolveSessionWorkDir as me}from"../fs/boundary.js";import"../agents/claude.js";import"../agents/codex.js";import"../agents/workbuddy.js";import"../agents/gemini.js";import"../agents/cursor.js";import"../agents/opencode.js";import"../agents/pi-coding-agent.js";import"../agents/manager.js";import{registerCustomAgent as pe}from"../agents/custom.js";import{listNodeAdapters as ge}from"../adapter-kit/registry.js";import{syncNodeAdapterAgents as fe}from"../agents/node-adapter.js";import{bindManagedRoom as ve,handleManagedRoomBind as ye,handleManagedRoomRestart as Re,discardPreBindingSession as be}from"./handlers/room-managed.js";import{getDaemonInstallationId as d,listSessionRecords as u,purgeCanonicalSession as Se,purgeSessionRecord as ke,recordSession as we,upsertMessage as Ae}from"./store.js";import{requestSessionIndexPublish as m}from"./index-publisher.js";import{checkRunStalls as Ie}from"./run-stall.js";import{reportUpgradeAggregateMetric as Te}from"../log-reporter.js";import{PersonalHistorySubscriptions as Pe}from"./history-subscriptions.js";import{sweepAttachmentTransfers as Ce}from"./attachment-transfer-store.js";import{NativeSourceDeletionStore as Me}from"../native-fusion/native-source-deletions.js";import{collectLocalNativeSources as xe}from"../native-fusion/session-native-sources.js";import{purgeToolDetails as De}from"./tool-detail-store.js";import{NativeSessionCleanupOutbox as Fe}from"./native-cleanup-outbox.js";import{assertPersonalSyncIdentity as o}from"../personal-sync/epoch.js";import{ActiveRunJournal as Ee}from"./run-recovery/journal.js";import{RunRecoveryBarrier as Qe}from"./run-recovery/barrier.js";import{RunRecoveryCoordinator as je}from"./run-recovery/coordinator.js";import{ProjectFsHandleStore as ze}from"./project-fs-store.js";import{handleProjectFsRequest as Oe}from"./handlers/project-fs.js";import{handleAgentPermissionResponse as Be,handleAcpAgentRequest as We}from"./handlers/agent-control.js";import{SessionDeletionSynchronizer as _e}from"./deletion-synchronizer.js";import{dispatchManagedChat as He,dispatchQueuedChat as Ue,interruptQueuedChat as Je}from"./queued-chat-dispatch.js";const $e=50;import{resolveSessionWorkDir as Xs}from"../fs/boundary.js";function Ve(){return"\u795E\u5FF5\u5347\u7EA7\u4E2D\u65AD\u4E86\u672C\u6B21\u8FD0\u884C\u3002\u5DF2\u5B8C\u6210\u7684\u56DE\u590D\u6216\u64CD\u4F5C\u4ECD\u7136\u6709\u6548\uFF0C\u672A\u5B8C\u6210\u90E8\u5206\u53EF\u91CD\u8BD5\u3002\u539F\u4F1A\u8BDD\u5DF2\u4FDD\u7559\u3002"}class Ns{client;nativeFusion;cliVersion;upgradePolicyController;managedRoomBinder;remoteAccessPauseController;managedRoomContextRevoker;managedRoomExecutor;trustedProjectFsRequestResolver;getAgentSnapshot;scheduleHandler;getScheduleQueue(){return this.chatQueue}sessions=new Map;processedReqIds=new Set;runTextAcc=new Map;pendingTransfers=new Map;managerRuntime;chatQueue;historySubscriptions;nativeCleanupOutbox;activityProbeTimer=null;attachmentCleanupTimer=null;activeRequests=0;acpDiscoveryWatcher;runJournal;recoveryBarrier=new Qe;recoveryCoordinator;recoveryPass=null;prepareManagedRecovery;projectFsHandleStore=new ze;deletionSynchronizer;constructor(e,t=null,s,n=null,r=null,l=null,p=null,g=null,f=null,v=null,y=null){this.client=e,this.nativeFusion=t,this.cliVersion=s,this.upgradePolicyController=n,this.managedRoomBinder=r,this.remoteAccessPauseController=l,this.managedRoomContextRevoker=p,this.managedRoomExecutor=g,this.trustedProjectFsRequestResolver=v,this.getAgentSnapshot=y,this.runJournal=f??new Ee(d());for(const a of this.runJournal.loadPendingReconciliation().records)this.recoveryBarrier.begin(a.sessionId);this.recoveryCoordinator=new je({journal:this.runJournal,barrier:this.recoveryBarrier,recover:(a,i)=>B(this.getRuntime(),a,i),commitTerminal:(a,i)=>this.commitRecoveryTerminal(a,i),isCanonicalTerminal:a=>{const i=u().find(R=>R.sessionId===a.sessionId),c=`terminal:${b.createHash("sha256").update(`${a.runId}\0`).digest("hex")}`;return i?.lastAttentionId===c},onOutcome:a=>Te({kind:"recovery",adapterType:a.adapterType,result:a.result,stage:a.result==="continued"?"continue":a.result==="reattached"?"reattach":a.result==="resumed"?"resume":a.result==="terminal_reconciled"?"inspect":"terminal",durationMs:a.durationMs,retries:Math.max(0,a.attempts-1),...a.reason?{errorCode:a.reason}:{}})}),this.managerRuntime=new oe({getRuntime:()=>this.getRuntime(),getAgentSnapshot:this.getAgentSnapshot??void 0,dispatchReq:a=>this.handleReq(a,{propagateErrors:!0})}),this.chatQueue=new ce({getRuntime:()=>this.getRuntime(),dispatchReq:a=>this.handleReq(a),dispatchManagedReq:(a,i)=>He(this.getRuntime(),a,i),dispatchQueuedReq:(a,i)=>Ue(this.getRuntime(),a,i),interruptQueuedSession:a=>Je(this.getRuntime(),a)}),this.deletionSynchronizer=new _e(e,a=>this.applySessionDeletion(a)),this.historySubscriptions=new Pe(this.client),this.nativeCleanupOutbox=new Fe({logger:console.info}),this.acpDiscoveryWatcher=new x(async()=>{w(),await M(this.getRuntime())}),this.nativeFusion?.setExternalTerminalHandler?.(a=>{this.handleExternalTerminal(a)}),this.nativeFusion?.setExternalClaimHandler?.((a,i)=>{this.chatQueue.noteExternalClaim(a,i)}),this.nativeCleanupOutbox.start(),h(this.managerRuntime),this.managerRuntime.start(),this.reloadCustomAgents();try{this.acpDiscoveryWatcher.start()}catch(a){console.warn(`[acp.discovery] watcher unavailable: ${a instanceof Error?a.message:String(a)}`)}this.activityProbeTimer=setInterval(()=>{Ie(this.sessions.entries(),()=>m(this.client)),this.publishManagedActivitySnapshots().catch(a=>{console.error("[session.activity] managed probe failed",a)})},15e3),this.activityProbeTimer.unref?.(),this.attachmentCleanupTimer=setInterval(()=>{const a=new Set([...u().map(i=>i.workDir),...Array.from(this.pendingTransfers.values()).map(i=>i.rootPath)]);for(const i of a)try{Ce(this.resolvePath(i))}catch(c){console.error("[attachment.cleanup] failed",c instanceof Error?c.message:String(c))}},6e4),this.attachmentCleanupTimer.unref?.()}executeManagedManagerAction(e){return this.managerRuntime.executeScopedWorkerAction(e.managerSessionId,e.action,e.params,e.roomOrigin)}noteManagedRoomSendOutcome(e){for(const t of this.sessions.values()){if(t.managedRoomContextToken!==e.token)continue;const s=t.roomSendOutcome??(t.roomSendOutcome={successes:0,failureCodes:[]});e.ok?s.successes+=1:s.failureCodes.length<20&&s.failureCodes.push(e.errorCode??"managed_room_action_failed");return}}getManagedRoomActiveRunId(e,t){const s=this.sessions.get(t);if(s?.currentRunId)return s.currentRunId;for(const n of this.sessions.values())if(n.managedRoomContextToken===e)return n.currentRunId??null;return null}getRuntime(){return{client:this.client,pendingTransfers:this.pendingTransfers,processedReqIds:this.processedReqIds,reloadCustomAgents:()=>this.reloadCustomAgents(),getAgentSnapshot:this.getAgentSnapshot??void 0,resolvePath:e=>this.resolvePath(e),resolveAuthorizedPath:(e,t)=>this.resolveAuthorizedPath(e,t),resolveAuthorizedPathAsync:(e,t)=>this.resolveAuthorizedPathAsync(e,t),runTextAcc:this.runTextAcc,sessions:this.sessions,evictIdleSessions:()=>this.evictIdleSessions(),nativeFusion:this.nativeFusion,managerRuntime:this.managerRuntime,chatQueue:this.chatQueue,activityPublisher:{publish:(e,t)=>this.publishSessionActivity(e,t)},upgradePolicyController:this.upgradePolicyController,revokeManagedRoomContextToken:this.managedRoomContextRevoker,managedRoomExecutor:this.managedRoomExecutor,runJournal:this.runJournal,recoveryBarrier:this.recoveryBarrier,prepareManagedRecovery:this.prepareManagedRecovery}}async resumePendingAcpAuthentication(e){const t=[...this.sessions.values()].find(s=>s.agentType===e&&s.adapter.hasPendingAuthenticationRetry?.());return t?.adapter.resumeAfterExternalAuthentication?(await t.adapter.resumeAfterExternalAuthentication(),!0):!1}getUpgradeIdleState(){const e=[];return[...this.sessions.values()].some(t=>t.currentRunId!==null)&&e.push("active-agent-turn"),this.pendingTransfers.size>0&&e.push("file-transfer"),this.activeRequests>0&&e.push("active-daemon-request"),{idle:e.length===0,reasons:e}}bindManagedRoomFromLocal(e){return ve(this.getRuntime(),this.managedRoomBinder,e)}prepareForUpgrade(e){this.recoveryBarrier.freezeDispatch();const t=this.runJournal.markPlannedUpgrade(e);return this.runJournal.sync(),{activeRunCount:t}}cancelUpgradePreparation(){this.recoveryBarrier.unfreezeDispatch()}async recoverInterruptedRuns(){this.recoveryPass??=this.recoveryCoordinator.recoverAll(),await this.recoveryPass}getPendingRecoveryCount(){return this.runJournal.loadPendingReconciliation().records.length}revokeProjectFsHandles(){this.projectFsHandleStore.revokeAll()}async commitRecoveryTerminal(e,t){const s=this.sessions.get(e.sessionId);s&&(s.currentRunId=null,s.currentRunPhase=null,s.journalRunId=null,s.heartbeatTimer&&(clearInterval(s.heartbeatTimer),s.heartbeatTimer=null)),this.runTextAcc.delete(`${e.sessionId}:${e.providerRunId??e.runId}`),this.publishSessionActivity(e.sessionId,null);const n=Ve();Ae(e.sessionId,{id:`upgrade-recovery-terminal-${e.runId}`,sessionId:e.sessionId,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:"error",content:n,error:!0,recoveryState:t.result,recoveryReason:t.reason})}),we({sessionId:e.sessionId,agentType:e.agentType,sessionMode:e.sessionMode,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,status:"failed",attentionId:`upgrade-recovery:${e.runId}`,lastActivityAt:new Date().toISOString()}),this.managerRuntime.noteAgentEvent(e.sessionId,{state:"error",runId:e.providerRunId??e.runId,seq:e.lastObservedEventSeq+1,message:n}),m(this.client);const r=e.lastObservedEventSeq+1;this.client.sendAgentEvent({type:"event",event:"agent",payload:t.result==="interrupted_by_upgrade"?{state:"interrupted_by_upgrade",sessionId:e.sessionId,runId:e.providerRunId??e.runId,seq:r,reason:t.reason}:{state:"recovery_failed",sessionId:e.sessionId,runId:e.providerRunId??e.runId,seq:r,reason:t.reason,...t.message?{message:t.message}:{}},seq:r,id:`agent-recovery-terminal-${e.runId}`}),this.chatQueue.noteTerminal(e.sessionId)}setManagedRoomTurnPreparer(e){this.chatQueue.setManagedRoomTurnPreparer(e)}setManagedAgentDirectTurnPreparer(e){this.chatQueue.setManagedAgentDirectTurnPreparer(e)}setManagedRecoveryPreparer(e){this.prepareManagedRecovery=e}async enqueueManagedRoomBatch(e){const t=e.map(s=>{const n=u().find(r=>r.sessionId===s.params.sessionId);return{context:s.context,params:{...s.params,modelId:n?.modelId??s.params.modelId,sessionMode:n?.sessionMode??s.params.sessionMode,managerConfig:n?.managerConfig??s.params.managerConfig,managerDefaultWorkerAgentType:n?.managerDefaultWorkerAgentType??s.params.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:n?.managerDefaultWorkerModelId??s.params.managerDefaultWorkerModelId}}});await this.chatQueue.enqueueManagedRoomBatch(t)}async retireManagedRoomSession(e){this.chatQueue.purgeSession(e),await be(this.getRuntime(),e)}discardPendingManagedRoomMessages(e){return this.chatQueue.discardPendingManagedRoomMessages(e)}async synchronizeSessionDeletions(){await this.deletionSynchronizer.synchronize()}async applySessionDeletion(e){const t=new Me({daemonInstallationId:d()});let s=[];try{s=xe(e.sessionId,{records:u()})}catch(r){console.warn("[session.deletions] local native source lookup failed",{error:r instanceof Error?r.message:String(r)})}t.recordSession(e);for(const r of[...e.sources,...s]){const l=t.find(r.agentType,r.sourceSessionKey);l&&l.sessionId!==e.sessionId||t.record({...e,...r})}this.runJournal.markSessionAborted(e.sessionId),this.recoveryBarrier.end(e.sessionId);const n=this.sessions.get(e.sessionId);if(n){this.revokeManagedRoomToken(n);try{await n.adapter.stop()}catch{}this.sessions.delete(e.sessionId)}this.historySubscriptions.closeSession(e.sessionId),this.nativeFusion?.handleSessionDeleted(e.sessionId),this.chatQueue.purgeSession(e.sessionId),De(e.sessionId),Se(e.sessionId),ke(e.sessionId),this.runTextAcc.delete(e.sessionId),this.publishSessionActivity(e.sessionId,null),this.nativeCleanupOutbox.enqueue(e),this.nativeCleanupOutbox.kick()}publishSessionActivity(e,t){this.client.sendEvent({type:"event",event:"session.activity",payload:{sessionId:e,activity:t}})}async handleExternalTerminal(e){const t=this.sessions.get(e);t&&(t.journalRunId&&(this.recoveryCoordinator.completeFromProvider(e,t.journalRunId),t.journalRunId=null),this.sessions.delete(e),this.revokeManagedRoomToken(t),t.heartbeatTimer&&clearInterval(t.heartbeatTimer),t.adapter.removeAllListeners(),await t.adapter.stop().catch(()=>{})),this.chatQueue.noteTerminal(e)}async publishManagedActivitySnapshots(){for(const[e,t]of this.sessions.entries()){if(t.heartbeatTimer||!t.currentRunId||!t.adapter.getStatus)continue;const s=await t.adapter.getStatus().catch(()=>null);if(!s?.active||!s.runPhase)continue;const n=new Date().toISOString();this.publishSessionActivity(e,{sessionId:e,runId:s.runId||t.currentRunId,runPhase:s.runPhase,startedAt:new Date(t.lastActiveAt).toISOString(),updatedAt:n,canStop:s.canStop??!0});const r=t.heartbeatSeq++;this.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:s.runId||t.currentRunId,seq:r,runPhase:s.runPhase,canStop:s.canStop??!0},seq:r,id:`agent-status-${s.runId||t.currentRunId}-${Date.now()}`})}}reloadCustomAgents(){for(const t of S())t.startsWith("custom:")&&k(t);const e=A();for(const[t,s]of Object.entries(e.customAgents??{}))pe(t,s);fe(ge(),new Set(Object.keys(e.customAgents??{})))}async handleReq(e,t={}){const s=this.getRuntime();let n;this.activeRequests++;try{switch(e.method){case"schedule.deliver":if(!this.scheduleHandler)throw new Error("schedules_unavailable");await this.scheduleHandler(e);break;case"chat.send":if(e.params.scheduledDelivery)throw new Error("schedule_source_untrusted");o(e.params),this.recoveryBarrier.isDispatchFrozen()||this.recoveryBarrier.isRecovering(String(e.params.sessionId??""))?n={durableQueueHandoff:await this.chatQueue.handleEnqueue({...e,method:"chat.enqueue"})}:await O(s,e);break;case"chat.enqueue":o(e.params),n={durableQueueHandoff:await this.chatQueue.handleEnqueue(e)};break;case"chat.queue.get":o(e.params),await this.chatQueue.handleGet(e);break;case"chat.queue.edit":o(e.params),await this.chatQueue.handleEdit(e);break;case"chat.queue.delete":o(e.params),await this.chatQueue.handleDelete(e);break;case"chat.queue.promote":o(e.params),await this.chatQueue.handlePromote(e);break;case"chat.abort":o(e.params),await z(s,e);break;case"session.refresh":o(e.params),await W(s,e);break;case"session.history.open":this.historySubscriptions.handleOpen(e);break;case"session.history.sync":this.historySubscriptions.handleSync(e);break;case"session.history.page":this.historySubscriptions.handlePage(e);break;case"session.history.close":this.historySubscriptions.handleClose(e);break;case"session.body.replica.subscribe":this.historySubscriptions.handleReplicaSubscribe(e);break;case"session.body.replica.unsubscribe":this.historySubscriptions.handleReplicaUnsubscribe(e);break;case"session.history":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"upgrade_required"});break;case"session.message.status":U(s,e);break;case"session.tool.detail":await _(s,e);break;case"voice.session.body.window":$(s,e);break;case"session.title.set":await H(s,e);break;case"room.agent.bind-managed":await ye(s,this.managedRoomBinder,e);break;case"room.agent.restart-managed":await Re(s,this.managedRoomBinder,e);break;case"fs.ls":await N(s,e);break;case"project.fs.open":case"project.fs.list":case"project.fs.read":case"project.fs.close":await Oe(this.client,this.projectFsHandleStore,e,this.trustedProjectFsRequestResolver);break;case"fs.read":await G(s,e);break;case"fs.write":await X(s,e);break;case"fs.export.markdown-pdf":await se(s,e);break;case"fs.export.markdown-pdf.setup":await te(s,e);break;case"fs.archive.zip":await ae(s,e);break;case"fs.rename":await K(s,e);break;case"fs.transfer.start":await ee(s,e);break;case"fs.transfer.chunk":await Y(s,e);break;case"fs.transfer.finish":await q(s,e);break;case"fs.transfer.abort":await Z(s,e);break;case"region.probe":await re(s,e);break;case"region.switch":await ne(s,e);break;case"upgrade.start":await I(this.client,e.id,e.params.version,{currentVersion:this.cliVersion,confirmedMajor:e.params.confirmedMajor===!0,prepareForUpgrade:r=>this.prepareForUpgrade(r),cancelUpgradePreparation:()=>this.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()}});break;case"upgrade.status":await T(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await ie(s,e);break;case"machine.remote-access.pause":await V(this.client,this.remoteAccessPauseController,e);break;case"agents.refresh":await P(s,e);break;case"agent.permission.respond":await Be(s,e);break;case"agent.auth.start":case"agent.auth.logout":case"agent.session-options.get":case"agent.external-sessions.list":case"agent.external-sessions.import":case"agent.external-sessions.close":case"agent.external-sessions.delete":await We(s,e);break;case"models.refresh":await C(s,e);break;case"agent.config.get":await E(s,e);break;case"agent.config.upsert":await j(s,e);break;case"agent.config.clear":await F(s,e);break;case"agent.config.test":await Q(s,e);break;case"agent.workspace.validate":await J(s,e);break;case"agent.capabilities.list":await D(s,e);break;case"skill.list":case"skill.install":case"skill.doctor":case"skill.setup":case"skill.use":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;case"session.wechat-rpa.channel.get":case"session.wechat-rpa.channel.upsert":case"session.wechat-rpa.channel.sync":case"manager.wechat-rpa.channel.get":case"manager.wechat-rpa.channel.upsert":case"manager.wechat-rpa.channel.sync":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;default:this.client.sendRes({type:"res",id:e.id,ok:!1,error:`Unknown method: ${e.method}`})}}catch(r){if(t.propagateErrors)throw r;this.client.sendRes({type:"res",id:e.id,ok:!1,error:r instanceof Error?r.message:String(r)})}finally{this.activeRequests--}return n}evictIdleSessions(){if(this.sessions.size<$e)return;let e=null;for(const[t,s]of this.sessions)(!e||s.lastActiveAt<e.session.lastActiveAt)&&(e={key:t,session:s});e&&(this.revokeManagedRoomToken(e.session),e.session.heartbeatTimer&&(clearInterval(e.session.heartbeatTimer),e.session.heartbeatTimer=null),e.session.adapter.removeAllListeners(),e.session.adapter.stop().catch(()=>{}),this.sessions.delete(e.key))}resolvePath(e){return me(e)}resolveAuthorizedPath(e,t){return he(e,le(t))}async resolveAuthorizedPathAsync(e,t){return de(e,await ue(t))}async cleanup(){this.chatQueue.stop(),this.projectFsHandleStore.revokeAll(),this.acpDiscoveryWatcher.stop(),this.nativeFusion?.setExternalTerminalHandler?.(null),this.nativeCleanupOutbox.stop(),this.historySubscriptions.closeAll(),this.activityProbeTimer&&(clearInterval(this.activityProbeTimer),this.activityProbeTimer=null),this.attachmentCleanupTimer&&(clearInterval(this.attachmentCleanupTimer),this.attachmentCleanupTimer=null);for(const[,e]of this.sessions)this.revokeManagedRoomToken(e),e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),e.adapter.removeAllListeners(),await e.adapter.stop().catch(()=>{});this.sessions.clear(),this.runTextAcc.clear(),L(this.getRuntime()),await this.managerRuntime.stop(),h(null)}revokeManagedRoomToken(e){const t=e.managedRoomContextToken;e.managedRoomContextToken=null,t&&this.managedRoomContextRevoker?.(t)}}export{Ns as SessionManager,Xs as resolveSessionWorkDir,Ve as upgradeRecoveryVisibleMessage};
@@ -0,0 +1 @@
1
+ import _ from"node:crypto";import a from"node:fs";import f from"node:path";import{resolveShennianPath as A}from"../config/index.js";const b="https://shennian.net/skill-manifest.json",m="https://shennian.net/skill.md",h=1440*6e4;async function j(r,t){const e=V(r),s=x(e),i=t.registryPath??A("skill-updates.json"),n=$(i),o=n.targets[e],u=n.checks[e],c=t.now??Date.now(),k=R(e);if(o&&s.sha256!==o.sha256&&!t.replace)return S("local_modified",e,s.version,k);if(u&&u.sha256===s.sha256&&!t.forceCheck&&!t.replace&&c-u.lastCheckedAt<h)return{status:u.lastStatus,path:e,localVersion:s.version,remoteVersion:u.remoteVersion,checked:!1,nextCheckAt:u.lastCheckedAt+h,message:"The previous official Skill check is still fresh; the local file was not changed.",...u.lastStatus==="current"||u.lastStatus==="registered"?{}:{officialUrl:m,manualCommand:k}};if(o&&!t.forceCheck&&!t.replace&&c-o.lastCheckedAt<h)return{status:o.lastStatus==="updated"?"current":o.lastStatus,path:e,localVersion:s.version,checked:!1,nextCheckAt:o.lastCheckedAt+h,message:"Official Skill check is still fresh; using the registered local copy."};let l;try{l=await L(t.fetchImpl??fetch)}catch{return o?(o.lastCheckedAt=c,o.lastStatus="check_failed"):y(n,e,s,c,"check_failed"),d(i,n),{status:"check_failed",path:e,localVersion:s.version,checked:!0,nextCheckAt:c+h,message:"Could not verify the official Skill manifest; continuing with the local copy.",officialUrl:m}}if(!E(t.clientVersion,l.skill))return y(n,e,s,c,"client_incompatible",l.skill.version),d(i,n),{status:"client_incompatible",path:e,localVersion:s.version,remoteVersion:l.skill.version,checked:!0,nextCheckAt:c+h,message:"The official Skill is not compatible with this Shennian Client version.",officialUrl:m,manualCommand:k};if(!o&&s.sha256===l.skill.sha256)return n.targets[e]={path:e,version:s.version,sha256:s.sha256,lastCheckedAt:c,lastStatus:"registered"},d(i,n),{status:"registered",path:e,localVersion:s.version,remoteVersion:l.skill.version,checked:!0,nextCheckAt:c+h,message:"Registered this exact official Skill copy for future safe updates."};if(!o&&!t.replace)return y(n,e,s,c,"manual_update_required",l.skill.version),d(i,n),S("manual_update_required",e,s.version,k,l.skill.version);if(o&&s.sha256!==o.sha256&&!t.replace)return S("local_modified",e,s.version,k,l.skill.version);if(s.sha256===l.skill.sha256)return n.targets[e]={path:e,version:s.version,sha256:s.sha256,lastCheckedAt:c,lastStatus:"current"},d(i,n),{status:"current",path:e,localVersion:s.version,remoteVersion:l.skill.version,checked:!0,nextCheckAt:c+h,message:"The registered official Skill is current."};if(!F(e))return{...S("not_writable",e,s.version,k,l.skill.version),message:"The Skill file is read-only or not a regular file; use the host update mechanism."};let g;try{g=await I(t.fetchImpl??fetch,l)}catch{return o?(o.lastCheckedAt=c,o.lastStatus="check_failed"):y(n,e,s,c,"check_failed",l.skill.version),d(i,n),{status:"check_failed",path:e,localVersion:s.version,remoteVersion:l.skill.version,checked:!0,nextCheckAt:c+h,message:"Could not verify the official Skill file; continuing with the local copy.",officialUrl:m}}return U(e,g),n.targets[e]={path:e,version:l.skill.version,sha256:l.skill.sha256,lastCheckedAt:c,lastStatus:"updated"},d(i,n),{status:"updated",path:e,localVersion:l.skill.version,remoteVersion:l.skill.version,checked:!0,nextCheckAt:c+h,message:"Updated the registered official Skill atomically. Reload the AI host before using it."}}function V(r){if(!f.isAbsolute(r))throw new Error("skill_path_must_be_absolute");const t=f.resolve(r);if(f.basename(t)!=="SKILL.md")throw new Error("skill_path_must_name_SKILL.md");return t}function x(r){const t=a.lstatSync(r);if(!t.isFile()||t.isSymbolicLink())throw new Error("skill_path_must_be_regular_file");const e=a.readFileSync(r);return{version:v(e.toString("utf8")).version,sha256:w(e)}}function v(r){const t=/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(r)?.[1];if(!t||!/^name:\s*shennian\s*$/m.test(t))throw new Error("not_official_shennian_skill");const e=/^version:\s*([^\s]+)\s*$/m.exec(t)?.[1];if(!e||!p(e))throw new Error("invalid_skill_version");return{version:e}}async function L(r){const t=await r(b,{redirect:"error",headers:{Accept:"application/json"},signal:AbortSignal.timeout(1e4)});if(!t.ok)throw new Error("skill_manifest_unavailable");const e=await t.json(),s=e.skill;if(e.schemaVersion!==1||e.product!=="shennian"||!s||s.name!=="shennian"||s.url!==m||!p(s.version)||!p(s.minimumClientVersion)||s.maximumClientVersion!==null&&!p(s.maximumClientVersion)||!/^[a-f0-9]{64}$/.test(s.sha256??""))throw new Error("invalid_skill_manifest");return e}async function I(r,t){const e=await r(m,{redirect:"error",headers:{Accept:"text/markdown"},signal:AbortSignal.timeout(1e4)});if(!e.ok)throw new Error("skill_download_unavailable");const s=Number(e.headers.get("content-length"));if(Number.isFinite(s)&&s>512e3)throw new Error("skill_too_large");const i=Buffer.from(await e.arrayBuffer());if(i.length===0||i.length>512e3)throw new Error("skill_too_large");if(w(i)!==t.skill.sha256)throw new Error("skill_integrity_mismatch");if(v(i.toString("utf8")).version!==t.skill.version)throw new Error("skill_version_mismatch");return i}function E(r,t){return!p(r)||C(r,t.minimumClientVersion)<0?!1:t.maximumClientVersion===null||C(r,t.maximumClientVersion)<=0}function F(r){try{const t=a.lstatSync(r);return!t.isFile()||t.isSymbolicLink()?!1:(a.accessSync(r,a.constants.W_OK),a.accessSync(f.dirname(r),a.constants.W_OK),!0)}catch{return!1}}function U(r,t){const e=f.dirname(r),s=a.statSync(r).mode&511,i=f.join(e,`.SKILL.md.shennian-${process.pid}-${_.randomUUID()}.tmp`);let n=null;try{n=a.openSync(i,"wx",s),a.writeFileSync(n,t),a.fsyncSync(n),a.closeSync(n),n=null,a.renameSync(i,r);try{const o=a.openSync(e,"r");a.fsyncSync(o),a.closeSync(o)}catch{}}finally{n!==null&&a.closeSync(n);try{a.unlinkSync(i)}catch{}}}function $(r){try{const t=JSON.parse(a.readFileSync(r,"utf8"));if(t.schemaVersion===1&&t.targets&&typeof t.targets=="object")return{schemaVersion:1,targets:t.targets,checks:t.checks&&typeof t.checks=="object"?t.checks:{}}}catch{}return{schemaVersion:1,targets:{},checks:{}}}function d(r,t){a.mkdirSync(f.dirname(r),{recursive:!0,mode:448});const e=`${r}.${process.pid}.${_.randomUUID()}.tmp`;a.writeFileSync(e,JSON.stringify(t,null,2),{mode:384}),a.renameSync(e,r);try{a.chmodSync(r,384)}catch{}}function S(r,t,e,s,i){return{status:r,path:t,localVersion:e,remoteVersion:i,checked:!0,message:r==="local_modified"?"The local Skill differs from its registered official copy; it was not overwritten.":"This local Skill copy is not registered as the current official file; it was not overwritten.",officialUrl:m,manualCommand:s}}function y(r,t,e,s,i,n){r.checks[t]={sha256:e.sha256,version:e.version,lastCheckedAt:s,lastStatus:i,remoteVersion:n}}function R(r){return`shennian skill update --path ${JSON.stringify(r)} --replace --json`}function w(r){return _.createHash("sha256").update(r).digest("hex")}function p(r){return typeof r=="string"&&/^\d+\.\d+\.\d+$/.test(r)}function C(r,t){const e=r.split(".").map(Number),s=t.split(".").map(Number);for(let i=0;i<3;i+=1)if(e[i]!==s[i])return e[i]-s[i];return 0}export{b as OFFICIAL_SKILL_MANIFEST_URL,m as OFFICIAL_SKILL_URL,h as SKILL_CHECK_INTERVAL_MS,j as updateOfficialSkill};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "description": "Shennian \u2014 AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {