shennian 0.2.100 → 0.2.102

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/publish-build-manifest.json +70 -25
  2. package/dist/src/channels/runtime.d.ts +5 -1
  3. package/dist/src/channels/runtime.js +3 -3
  4. package/dist/src/channels/wechat-channel/helper-assets.d.ts +59 -1
  5. package/dist/src/channels/wechat-channel/helper-assets.js +1 -1
  6. package/dist/src/channels/wechat-channel/helper-client.js +2 -2
  7. package/dist/src/channels/wechat-channel/helper-protocol.d.ts +1 -1
  8. package/dist/src/channels/wechat-channel/observer.d.ts +32 -2
  9. package/dist/src/channels/wechat-channel/observer.js +6 -6
  10. package/dist/src/channels/wechat-channel/preflight.d.ts +7 -0
  11. package/dist/src/channels/wechat-channel/preflight.js +1 -1
  12. package/dist/src/channels/wechat-channel/runner.d.ts +0 -1
  13. package/dist/src/channels/wechat-channel/runner.js +1 -1
  14. package/dist/src/channels/wechat-channel/scheduler.d.ts +4 -3
  15. package/dist/src/channels/wechat-channel/scheduler.js +1 -1
  16. package/dist/src/channels/wechat-rpa.d.ts +5 -1
  17. package/dist/src/channels/wechat-rpa.js +4 -4
  18. package/dist/src/commands/runtime.d.ts +89 -0
  19. package/dist/src/commands/runtime.js +1 -0
  20. package/dist/src/commands/wechat/command.d.ts +2 -0
  21. package/dist/src/commands/wechat/command.js +1 -0
  22. package/dist/src/commands/wechat/direct.d.ts +14 -0
  23. package/dist/src/commands/wechat/direct.js +2 -0
  24. package/dist/src/commands/wechat/doctor.d.ts +2 -0
  25. package/dist/src/commands/wechat/doctor.js +1 -0
  26. package/dist/src/commands/wechat/ipc.d.ts +9 -0
  27. package/dist/src/commands/wechat/ipc.js +1 -0
  28. package/dist/src/commands/wechat/operations.d.ts +3 -0
  29. package/dist/src/commands/wechat/operations.js +4 -0
  30. package/dist/src/commands/wechat/output.d.ts +7 -0
  31. package/dist/src/commands/wechat/output.js +4 -0
  32. package/dist/src/commands/wechat/types.d.ts +141 -0
  33. package/dist/src/commands/wechat/types.js +1 -0
  34. package/dist/src/commands/wechat/utils.d.ts +14 -0
  35. package/dist/src/commands/wechat/utils.js +1 -0
  36. package/dist/src/commands/wechat.d.ts +6 -127
  37. package/dist/src/commands/wechat.js +1 -7
  38. package/dist/src/devtools/wechat-channel-action-smoke.d.ts +2 -0
  39. package/dist/src/devtools/wechat-channel-action-smoke.js +8 -8
  40. package/dist/src/index.js +2 -2
  41. package/dist/src/manager/runtime.d.ts +3 -1
  42. package/dist/src/manager/runtime.js +5 -5
  43. package/package.json +1 -1
@@ -0,0 +1,89 @@
1
+ import fs from 'node:fs';
2
+ import type { Command } from 'commander';
3
+ import { type WeChatChannelHelperRuntimePackageManifest } from '../channels/wechat-channel/helper-assets.js';
4
+ type RuntimeCommandPlatform = 'darwin' | 'win32';
5
+ export type HelperRuntimeInstallResult = {
6
+ ok: true;
7
+ action: 'installed' | 'already_installed';
8
+ platform: RuntimeCommandPlatform;
9
+ targetPlatform: RuntimeCommandPlatform;
10
+ installed: true;
11
+ repairable: false;
12
+ targetDir: string;
13
+ helperDir: string;
14
+ helperPath: string;
15
+ helperVersion: string;
16
+ runtimeVersion: string;
17
+ protocolVersion: number;
18
+ minCliVersion?: string;
19
+ sha256?: {
20
+ runtimeManifest: string;
21
+ entrypoint: string | null;
22
+ };
23
+ packageManifestPath?: string;
24
+ packageKind?: string;
25
+ installTarget?: WeChatChannelHelperRuntimePackageManifest['installTarget'];
26
+ signature?: WeChatChannelHelperRuntimePackageManifest['signature'];
27
+ message: string;
28
+ } | {
29
+ ok: false;
30
+ platform?: RuntimeCommandPlatform;
31
+ targetPlatform?: RuntimeCommandPlatform;
32
+ installed?: boolean;
33
+ repairable: boolean;
34
+ reasonCode: string;
35
+ message: string;
36
+ repairSuggestion?: string;
37
+ checkedSources?: string[];
38
+ };
39
+ export type HelperRuntimeStatusResult = {
40
+ ok: boolean;
41
+ platform?: RuntimeCommandPlatform;
42
+ targetPlatform?: RuntimeCommandPlatform;
43
+ installed: boolean;
44
+ repairable: boolean;
45
+ reasonCode?: string;
46
+ message: string;
47
+ helperDir?: string;
48
+ helperPath?: string;
49
+ helperVersion?: string;
50
+ runtimeVersion?: string;
51
+ protocolVersion?: number;
52
+ minCliVersion?: string;
53
+ sha256?: {
54
+ runtimeManifest: string;
55
+ entrypoint: string | null;
56
+ };
57
+ packageManifestPath?: string;
58
+ packageKind?: string;
59
+ installTarget?: WeChatChannelHelperRuntimePackageManifest['installTarget'];
60
+ signature?: WeChatChannelHelperRuntimePackageManifest['signature'];
61
+ runtimeRoot?: string | null;
62
+ repairSuggestion?: string;
63
+ checkedSources?: string[];
64
+ };
65
+ type RuntimeIo = {
66
+ existsSync?: typeof fs.existsSync;
67
+ readFileSync?: typeof fs.readFileSync;
68
+ mkdirSync?: typeof fs.mkdirSync;
69
+ rmSync?: typeof fs.rmSync;
70
+ cpSync?: typeof fs.cpSync;
71
+ renameSync?: typeof fs.renameSync;
72
+ chmodSync?: typeof fs.chmodSync;
73
+ homedir?: () => string;
74
+ };
75
+ export declare function registerRuntimeCommand(program: Command): void;
76
+ export declare function getHelperRuntimeStatus(input?: {
77
+ platform?: NodeJS.Platform | string;
78
+ env?: NodeJS.ProcessEnv;
79
+ homedir?: string;
80
+ }): HelperRuntimeStatusResult;
81
+ export declare function installHelperRuntime(input?: {
82
+ source?: string;
83
+ platform?: NodeJS.Platform | string;
84
+ env?: NodeJS.ProcessEnv;
85
+ homedir?: string;
86
+ force?: boolean;
87
+ io?: RuntimeIo;
88
+ }): HelperRuntimeInstallResult;
89
+ export {};
@@ -0,0 +1 @@
1
+ import f from"node:fs";import b from"node:os";import a from"node:path";import v from"chalk";import{getDefaultWeChatHelperRuntimeRoot as C,readWeChatChannelHelperRuntimePackageManifest as _,resolveWeChatChannelHelperAsset as y,SHENNIAN_HELPER_RUNTIME_DIR_ENV as I,WECHAT_CHANNEL_HELPER_DIR_ENV as A}from"../channels/wechat-channel/helper-assets.js";import{compareVersions as $,getCurrentVersion as w}from"../upgrade/engine.js";const u="helper-runtime-package.json";function Q(e){const r=e.command("runtime").description("Install, repair, and inspect Shennian local runtimes");r.command("status").description("Print local Shennian runtime status").option("--json","Output JSON",!1).option("--platform <platform>","Platform override: darwin or win32").action(i=>{const o=D({platform:i.platform});P(o,!!i.json),o.ok||(process.exitCode=1)}),r.command("install").description("Install a Shennian runtime component").argument("<component>","Runtime component, currently only: helper").option("--source <path>","Helper runtime package/source directory").option("--json","Output JSON",!1).option("--platform <platform>","Platform override: darwin or win32").action((i,o)=>{const n=i==="helper"?V({source:o.source,platform:o.platform}):R(i);P(n,!!o.json),n.ok||(process.exitCode=1)}),r.command("repair").description("Repair a Shennian runtime component").argument("[component]","Runtime component, currently only: helper","helper").option("--source <path>","Helper runtime package/source directory").option("--json","Output JSON",!1).option("--platform <platform>","Platform override: darwin or win32").action((i,o)=>{const n=i==="helper"?V({source:o.source,platform:o.platform,force:!0}):R(i);P(n,!!o.json),n.ok||(process.exitCode=1)})}function D(e={}){const r=j(e.platform);if(!r)return{ok:!1,installed:!1,repairable:!1,reasonCode:"unsupported_platform",message:"Shennian Helper runtime is only available on macOS and Windows.",repairSuggestion:c("unsupported_platform")};const i=e.env??process.env,o=e.homedir,n=y({platform:r,env:i,homedir:o,includeInstalledDesktop:!1}),t=C({platform:r,env:i,homedir:o});if(!n.ok)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:T(n.reasonCode),reasonCode:n.reasonCode,message:n.message,runtimeRoot:t,repairSuggestion:c(n.reasonCode),checkedSources:k({platform:r,env:i,homedir:o})};const s=_({platform:r,helperDir:n.helperDir});if(!s.ok)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!0,reasonCode:s.reasonCode,message:s.message,helperDir:n.helperDir,helperPath:n.helperPath,helperVersion:n.version,runtimeVersion:n.version,protocolVersion:n.manifest.protocolVersion,runtimeRoot:t,repairSuggestion:c(s.reasonCode),checkedSources:k({platform:r,env:i,homedir:o})};const l=M({platform:r,packageManifest:s.manifest,currentCliVersion:w(),helperVersion:n.version,protocolVersion:n.manifest.protocolVersion});return l.ok?{ok:!0,platform:r,targetPlatform:r,installed:!0,repairable:!1,message:`Shennian Helper runtime is installed: ${n.helperDir}`,helperDir:n.helperDir,helperPath:n.helperPath,helperVersion:n.version,runtimeVersion:n.version,protocolVersion:n.manifest.protocolVersion,minCliVersion:s.manifest.minCliVersion,sha256:s.manifest.sha256,packageManifestPath:s.manifestPath,packageKind:s.manifest.packageKind,installTarget:s.manifest.installTarget,signature:s.manifest.signature,runtimeRoot:t}:{ok:!1,platform:r,targetPlatform:r,installed:!0,repairable:l.repairable,reasonCode:l.reasonCode,message:l.message,helperDir:n.helperDir,helperPath:n.helperPath,helperVersion:n.version,runtimeVersion:n.version,protocolVersion:n.manifest.protocolVersion,minCliVersion:s.manifest.minCliVersion,sha256:s.manifest.sha256,packageManifestPath:s.manifestPath,packageKind:s.manifest.packageKind,installTarget:s.manifest.installTarget,signature:s.manifest.signature,runtimeRoot:t,repairSuggestion:c(l.reasonCode,s.manifest.minCliVersion),checkedSources:k({platform:r,env:i,homedir:o})}}function V(e={}){const r=j(e.platform);if(!r)return{ok:!1,reasonCode:"unsupported_platform",message:"Shennian Helper runtime is only available on macOS and Windows.",repairable:!1,repairSuggestion:c("unsupported_platform")};const i=e.env??process.env,o=E(e.io),n=e.homedir??o.homedir(),t=D({platform:r,env:i,homedir:n});if(t.ok&&!e.force&&t.helperDir&&t.helperPath&&t.helperVersion&&t.protocolVersion)return{ok:!0,action:"already_installed",platform:r,targetPlatform:r,installed:!0,repairable:!1,targetDir:N(r,t.helperDir),helperDir:t.helperDir,helperPath:t.helperPath,helperVersion:t.helperVersion,runtimeVersion:t.helperVersion,protocolVersion:t.protocolVersion,minCliVersion:t.minCliVersion,sha256:t.sha256,packageManifestPath:t.packageManifestPath,packageKind:t.packageKind,installTarget:t.installTarget,signature:t.signature,message:`Shennian Helper runtime is already installed: ${t.helperDir}`};const s=e.source?[a.resolve(e.source)]:k({platform:r,env:i,homedir:n}),l=s.map(S=>x({candidate:S,platform:r,io:o})).find(S=>S!=null);if(!l)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!0,reasonCode:"helper_runtime_source_missing",message:"No installable Shennian Helper runtime package was found. Open Shennian Desktop, pass --source, or download the official helper package.",repairSuggestion:c("helper_runtime_source_missing"),checkedSources:s};const h=_({platform:r,manifestPath:l.packageManifestPath});if(!h.ok)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!0,reasonCode:h.reasonCode,message:h.message,repairSuggestion:c(h.reasonCode),checkedSources:s};const d=M({platform:r,packageManifest:h.manifest,currentCliVersion:w()});if(!d.ok)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:d.repairable,reasonCode:d.reasonCode,message:d.message,repairSuggestion:c(d.reasonCode,h.manifest.minCliVersion),checkedSources:s};const g=L({platform:r,env:i,homedir:n,packageKind:l.packageKind});if(!g)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!1,reasonCode:"helper_runtime_target_unavailable",message:"Unable to determine Helper runtime install target for this platform.",repairSuggestion:c("helper_runtime_target_unavailable"),checkedSources:s};F({sourceDir:l.sourceDir,targetDir:g,io:o}),W({sourceManifestPath:l.packageManifestPath,targetManifestPath:l.packageManifestPathInTarget(g),io:o});const p=y({platform:r,baseDir:l.manifestDirInTarget(g),verifyIntegrity:!0});if(!p.ok)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!0,reasonCode:p.reasonCode,message:`Installed Helper runtime could not be verified: ${p.message}`,repairSuggestion:c(p.reasonCode),checkedSources:s};U(r,p.helperPath,o);const m=_({platform:r,helperDir:p.helperDir});return m.ok?{ok:!0,action:"installed",platform:r,targetPlatform:r,installed:!0,repairable:!1,targetDir:g,helperDir:p.helperDir,helperPath:p.helperPath,helperVersion:p.version,runtimeVersion:p.version,protocolVersion:p.manifest.protocolVersion,minCliVersion:m.manifest.minCliVersion,sha256:m.manifest.sha256,packageManifestPath:m.manifestPath,packageKind:m.manifest.packageKind,installTarget:m.manifest.installTarget,signature:m.manifest.signature,message:`Installed Shennian Helper runtime to ${g}`}:{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!0,reasonCode:m.reasonCode,message:`Installed Helper runtime package manifest could not be verified: ${m.message}`,repairSuggestion:c(m.reasonCode),checkedSources:s}}function j(e){const r=e??process.platform;return r==="darwin"||r==="win32"?r:null}function M(e){return e.packageManifest.platform!==e.platform?{ok:!1,reasonCode:"helper_runtime_platform_mismatch",message:`Helper runtime package platform ${e.packageManifest.platform} does not match ${e.platform}.`,repairable:!0}:e.helperVersion&&e.packageManifest.helperVersion!==e.helperVersion?{ok:!1,reasonCode:"helper_runtime_version_mismatch",message:`Helper runtime package version ${e.packageManifest.helperVersion} does not match installed helper ${e.helperVersion}.`,repairable:!0}:e.protocolVersion&&e.packageManifest.protocolVersion!==e.protocolVersion?{ok:!1,reasonCode:"helper_runtime_protocol_mismatch",message:`Helper runtime package protocol ${e.packageManifest.protocolVersion} does not match installed helper protocol ${e.protocolVersion}.`,repairable:!0}:$(e.currentCliVersion,e.packageManifest.minCliVersion)!=="none"?{ok:!1,reasonCode:"helper_runtime_cli_too_old",message:`Shennian CLI ${e.currentCliVersion} is older than required ${e.packageManifest.minCliVersion}.`,repairable:!1}:{ok:!0}}function T(e){return e!=="unsupported_platform"&&e!=="helper_runtime_cli_too_old"}function c(e,r){if(e==="unsupported_platform")return"Use Shennian Helper runtime on macOS or Windows.";if(e==="unsupported_runtime_component")return"Use `shennian runtime install helper` or `shennian runtime repair helper`.";if(e==="helper_runtime_cli_too_old")return`Upgrade Shennian CLI${r?` to ${r} or newer`:""}, then retry.`;if(e==="helper_runtime_target_unavailable")return"Set the current user home/LOCALAPPDATA correctly, then retry.";if(e==="helper_runtime_required")return"Install Shennian Desktop or run `shennian runtime install helper` with an official Helper runtime package.";if(e==="helper_runtime_source_missing")return"Open Shennian Desktop, download the official Helper runtime package, or pass `--source <path>`.";if(e==="helper_runtime_package_manifest_missing"||e==="helper_runtime_package_manifest_invalid")return"Reinstall or repair the official Shennian Helper runtime package.";if(e==="helper_runtime_platform_mismatch")return"Install the Helper runtime package for this platform.";if(e==="helper_runtime_version_mismatch"||e==="helper_runtime_protocol_mismatch")return"Run `shennian runtime repair helper` with the matching official Helper runtime package.";if(e==="manifest_missing"||e==="helper_missing"||e==="integrity_mismatch"||e==="helper_not_executable")return"Run `shennian runtime repair helper` or reinstall Shennian Desktop."}function R(e){return{ok:!1,repairable:!1,reasonCode:"unsupported_runtime_component",message:`Unsupported runtime component: ${e}. Supported component: helper.`,repairSuggestion:c("unsupported_runtime_component")}}function E(e){return{existsSync:e?.existsSync??f.existsSync,readFileSync:e?.readFileSync??f.readFileSync,mkdirSync:e?.mkdirSync??f.mkdirSync,rmSync:e?.rmSync??f.rmSync,cpSync:e?.cpSync??f.cpSync,renameSync:e?.renameSync??f.renameSync,chmodSync:e?.chmodSync??f.chmodSync,homedir:e?.homedir??b.homedir}}function x(e){const r=a.resolve(e.candidate);return e.io.existsSync(r)?e.platform==="darwin"?O(r,e.io):K(r,e.io):null}function O(e,r){const i=e.endsWith(".app")?e:a.join(e,"Shennian Helper.app");if(r.existsSync(a.join(i,"Contents","Resources","wechat-channel","macos","manifest.json")))return{sourceDir:i,packageKind:"macos-app",packageManifestPath:H(r,[a.join(i,"Contents","Resources","helper-runtime-package.json"),a.join(e,u)]),packageManifestPathInTarget:n=>a.join(n,"Contents","Resources",u),manifestDirInTarget:n=>a.join(n,"Contents","Resources","wechat-channel","macos")};const o=r.existsSync(a.join(e,"manifest.json"))?e:a.join(e,"wechat-channel","macos");return r.existsSync(a.join(o,"manifest.json"))?{sourceDir:o,packageKind:"asset-dir",packageManifestPath:a.join(o,"helper-runtime-package.json"),packageManifestPathInTarget:n=>a.join(n,u),manifestDirInTarget:n=>n}:null}function K(e,r){const i=a.basename(e).toLowerCase()==="shennian helper"?e:a.join(e,"Shennian Helper");if(r.existsSync(a.join(i,"resources","wechat-channel","windows","manifest.json")))return{sourceDir:i,packageKind:"windows-runtime",packageManifestPath:H(r,[a.join(i,"resources",u),a.join(e,u)]),packageManifestPathInTarget:n=>a.join(n,"resources",u),manifestDirInTarget:n=>a.join(n,"resources","wechat-channel","windows")};const o=r.existsSync(a.join(e,"manifest.json"))?e:a.join(e,"wechat-channel","windows");return r.existsSync(a.join(o,"manifest.json"))?{sourceDir:o,packageKind:"asset-dir",packageManifestPath:a.join(o,"helper-runtime-package.json"),packageManifestPathInTarget:n=>a.join(n,u),manifestDirInTarget:n=>n}:null}function L(e){const r=C(e);if(!r)return null;if(e.platform==="darwin")return e.packageKind==="macos-app"?a.join(r,"Shennian Helper.app"):a.join(r,"wechat-channel","macos");if(e.packageKind==="windows-runtime"){const i=e.env.LOCALAPPDATA||(e.homedir?a.join(e.homedir,"AppData","Local"):"");return i?a.join(i,"Programs","Shennian Helper"):null}return a.join(r,"wechat-channel","windows")}function N(e,r){const i=a.resolve(r);if(e==="darwin"){const n=`${a.sep}Contents${a.sep}Resources${a.sep}wechat-channel${a.sep}macos`;return i.endsWith(n)?i.slice(0,-n.length):i}const o=`${a.sep}resources${a.sep}wechat-channel${a.sep}windows`;return i.endsWith(o)?i.slice(0,-o.length):i}function F(e){const r=a.resolve(e.sourceDir),i=a.resolve(e.targetDir);if(r===i)return;const o=`${i}.tmp-${process.pid}-${Date.now()}`;e.io.rmSync(o,{recursive:!0,force:!0}),e.io.mkdirSync(a.dirname(i),{recursive:!0}),e.io.cpSync(r,o,{recursive:!0}),e.io.rmSync(i,{recursive:!0,force:!0}),e.io.renameSync(o,i)}function W(e){const r=a.resolve(e.sourceManifestPath),i=a.resolve(e.targetManifestPath);!e.io.existsSync(r)||r===i||(e.io.mkdirSync(a.dirname(i),{recursive:!0}),e.io.cpSync(r,i))}function H(e,r){return r.find(i=>e.existsSync(i))??r[0]}function U(e,r,i){if(e==="darwin")try{const o=f.statSync(r);(o.mode&73)===0&&i.chmodSync(r,o.mode|493)}catch{}}function k(e){const r=e.homedir||(e.platform==="win32"?e.env.USERPROFILE:e.env.HOME),i=[],o=n=>{if(!n)return;const t=a.resolve(n);i.includes(t)||i.push(t)};if(o(e.env.SHENNIAN_HELPER_PACKAGE_DIR),o(e.env[A]),o(e.env[I]),e.platform==="darwin")r&&(o(a.join(r,"Applications","Shennian.app","Contents","Resources")),o(a.join(r,"Applications","Shennian Helper.app"))),o(a.join("/Applications","Shennian.app","Contents","Resources")),o(a.join("/Applications","Shennian Helper.app"));else{const n=e.env.LOCALAPPDATA||(r?a.join(r,"AppData","Local"):""),t=e.env.ProgramFiles||e.env.PROGRAMFILES,s=e.env["ProgramFiles(x86)"]||e.env["PROGRAMFILES(X86)"];n&&(o(a.join(n,"Programs","Shennian","resources")),o(a.join(n,"Programs","Shennian Helper"))),t&&(o(a.join(t,"Shennian","resources")),o(a.join(t,"Shennian Helper"))),s&&(o(a.join(s,"Shennian","resources")),o(a.join(s,"Shennian Helper")))}return o(a.resolve(process.cwd(),"packages/helper-runtime/dist",e.platform==="darwin"?"macos":"windows")),o(a.resolve(process.cwd(),"packages/helper-runtime/wechat-channel",e.platform==="darwin"?"macos":"windows")),i}function P(e,r){if(r){console.log(JSON.stringify(e,null,2));return}if(e.ok){console.log(v.green(`\u2713 ${e.message}`)),"helperVersion"in e&&console.log(`version=${e.helperVersion} protocol=${e.protocolVersion}`),"helperPath"in e&&console.log(e.helperPath);return}console.error(v.red(`\u2717 ${e.reasonCode}`)),console.error(e.message)}export{D as getHelperRuntimeStatus,V as installHelperRuntime,Q as registerRuntimeCommand};
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerWeChatCommand(program: Command): void;
@@ -0,0 +1 @@
1
+ import{readExternalAttachment as p}from"../external-attachments.js";import{runWeChatDoctor as f}from"./doctor.js";import{runWeChatReadLatest as w,runWeChatSend as g}from"./operations.js";import{printReadLatestResult as k,printToolError as c,printToolResult as v}from"./output.js";import{clampPositiveInteger as m}from"./utils.js";function E(e){const o=e.command("wechat").description("Use WeChat through Shennian local runtime");o.option("--conversation <name>","WeChat conversation/group name"),o.command("doctor").description("Check local Shennian, pairing, entitlement, credits, and WeChat runtime readiness").option("--json","Output JSON",!1).action(async r=>{const t=await f();if(r.json)console.log(JSON.stringify(t,null,2));else for(const a of t.checks){const n=a.ok?"\u2713":a.status==="warning"?"!":"\u2717";console.log(`${n} ${a.id}: ${a.message}`)}t.ok||(process.exitCode=1)}),o.command("write").aliases(["send"]).description("Write a message to a local WeChat conversation through Shennian").argument("[text...]","Message text").option("--conversation <name>","WeChat conversation/group name").option("--text <text>","Message text").option("--file <path>","File attachment path").option("--image <path>","Image attachment path").option("--video <path>","Video attachment path").option("--session-id <id>","Bind to an existing Shennian chat/session; omit for direct local WeChat tool mode").option("--work-dir <path>","Local tool work dir; defaults under ~/.shennian/wechat-cli").option("--idempotency-key <key>","Idempotency key").option("--dry-run","Validate the request and output the planned write without sending",!1).option("--trace-id <id>","Trace id included in JSON output").option("--timeout <ms>","Manager IPC request timeout in milliseconds").option("--transport <mode>","Execution transport: auto, daemon, or direct","auto").option("--format <format>","Output format: json or text","text").option("--json","Output JSON",!1).action(async(r,t,a)=>{let n;try{n=u(a,t.conversation);const i=t.text??r.join(" "),s=C(t),h=await g({conversation:n,text:i,attachment:s,sessionId:t.sessionId,workDir:t.workDir,idempotencyKey:t.idempotencyKey,dryRun:t.dryRun,traceId:t.traceId,timeoutMs:t.timeout?m(t.timeout,24e4):void 0,transport:l(t.transport)});v(h,t.json||t.format==="json")}catch(i){c("write",n||t.conversation,i,t.json||t.format==="json")}}),o.command("read").aliases(["read-latest","read_latest","latest"]).description("Read recent messages from a local WeChat conversation through Shennian").argument("[count]","Number of latest messages","10").option("--conversation <name>","WeChat conversation/group name").option("--limit <n>","Number of latest messages").option("--format <format>","Output format: markdown or json","markdown").option("--download <mode>","Attachment download mode: auto or never","auto").option("--trace-id <id>","Trace id included in JSON output").option("--timeout <ms>","Manager IPC request timeout in milliseconds").option("--session-id <id>","Bind to an existing Shennian chat/session; omit for direct local WeChat tool mode").option("--work-dir <path>","Local tool work dir; defaults under ~/.shennian/wechat-cli").option("--transport <mode>","Execution transport: auto, daemon, or direct","auto").option("--json","Output JSON",!1).action(async(r,t,a)=>{let n,i="markdown";try{n=u(a,t.conversation),i=y(t.json?"json":t.format);const s=await w({conversation:n,limit:m(t.limit??r,10),download:j(t.download),traceId:t.traceId,timeoutMs:t.timeout?m(t.timeout,24e4):void 0,sessionId:t.sessionId,workDir:t.workDir,transport:l(t.transport)});k(s,i)}catch(s){c("read",n||t.conversation,s,t.json||i==="json")}})}function u(e,o){const d=e.parent?.opts()??{},r=o||d.conversation||"";if(!r.trim())throw new Error("--conversation is required");return r}function C(e){const o=[e.file?{kind:"file",path:e.file}:null,e.image?{kind:"image",path:e.image}:null,e.video?{kind:"video",path:e.video}:null].filter(d=>!!d);if(o.length>1)throw new Error("Pass only one of --file, --image, or --video");if(o.length!==0)return p(o[0].path,o[0].kind)}function y(e){const o=String(e||"markdown").trim().toLowerCase();if(o==="json")return"json";if(o==="markdown"||o==="md"||o==="text")return"markdown";throw new Error(`Unsupported --format: ${e}. Use markdown or json.`)}function j(e){const o=String(e||"auto").trim().toLowerCase();if(o==="auto"||o==="never")return o;throw new Error(`Unsupported --download: ${e}. Use auto or never.`)}function l(e){const o=String(e||"auto").trim().toLowerCase();if(o==="auto"||o==="daemon"||o==="direct")return o;throw new Error(`Unsupported --transport: ${e}. Use auto, daemon, or direct.`)}export{E as registerWeChatCommand};
@@ -0,0 +1,14 @@
1
+ import type { ExternalMessageEvent } from '../../channels/base.js';
2
+ import { type WeChatReadLatestOptions, type WeChatSendOptions, type WeChatSendResult, type WeChatToolBinding } from './types.js';
3
+ export declare function sendDirectWeChatMessageOnce(binding: WeChatToolBinding, options: WeChatSendOptions): Promise<{
4
+ pending?: boolean;
5
+ sendStatus?: WeChatSendResult['sendStatus'];
6
+ outDir?: string;
7
+ helperTracePath?: string | null;
8
+ }>;
9
+ export declare function readDirectWeChatLatestOnce(binding: WeChatToolBinding, options: WeChatReadLatestOptions): Promise<{
10
+ messages: ExternalMessageEvent[];
11
+ outDir: string;
12
+ helperTracePath: string | null;
13
+ activityGuardPath?: string | null;
14
+ }>;
@@ -0,0 +1,2 @@
1
+ import p from"node:fs";import f from"node:path";import{observedMessageToExternalEvent as P,weChatChannelBindingId as g}from"../../channels/wechat-rpa/product-channel.js";import{loadConfig as v}from"../../config/index.js";import{runWeChatChannelActionSmoke as w}from"../../devtools/wechat-channel-action-smoke.js";import{WeChatToolOperationError as k}from"./types.js";import{currentWeChatPlatform as E,isRecord as y,normalizeSendStatus as S,safePathSegment as A}from"./utils.js";async function B(e,t){const r=T(t.attachment),o=_(e,t,{kind:"send",steps:["open-read",r],text:t.text,attachment:t.attachment}),s=await w(o),a=s.steps.find(c=>c.step===r);if(!s.ok||!a?.ok){if(O(a))return{pending:!0,sendStatus:"sent_unconfirmed",outDir:s.outDir,helperTracePath:s.helperTracePath};const c=s.steps.find(h=>!h.ok&&!h.skipped),i=a?.reasonCode==="open_read_required"?c??a:a??c,u=i?.reasonCode||"wechat_direct_send_failed",m=i?.errorSummary||u;throw new k({reasonCode:u,message:m,outDir:s.outDir,helperTracePath:s.helperTracePath,traceId:t.traceId})}const n=S(a.details?.sendStatus)??"sent_unconfirmed";return{pending:n==="queued"||n==="pending"||n==="sent_unconfirmed",sendStatus:n,outDir:s.outDir,helperTracePath:s.helperTracePath}}async function H(e,t){const r=[],o=t.download!=="never",s=o?"download-visible-media":"structure-window",a=_(e,t,{kind:"read",steps:[s],allowEmptyMedia:o,activityGuardEvents:r}),n=await w(a),c=r.length>0?N(n.outDir,r):null,i=n.steps.find(d=>d.step===s),u=f.join(n.outDir,o?"download-visible-messages.json":"structure-window-messages.json"),m=W(u),h=o&&m.length>0&&(i?.reasonCode==="media_original_pending_or_metadata_only"||i?.reasonCode==="only_preview_media_downloaded");if(!n.ok&&i?.reasonCode!=="no_observed_messages"&&!h){const d=i?.reasonCode||n.steps.find(l=>!l.ok&&!l.skipped)?.reasonCode||"wechat_direct_read_failed",I=i?.errorSummary||n.steps.find(l=>!l.ok&&!l.skipped)?.errorSummary||d;throw new k({reasonCode:d,message:I,outDir:n.outDir,helperTracePath:n.helperTracePath,activityGuardPath:c,traceId:t.traceId})}const C=x(e,t),D=M(e);return{messages:m.map(d=>P(C,D,d)).filter(d=>d!=null),outDir:n.outDir,helperTracePath:n.helperTracePath,activityGuardPath:c}}function T(e){return e?e.kind==="image"?"send-image":e.kind==="video"?"send-video":"send-file":"send-text"}function _(e,t,r){const o=t.traceId||`wechat-cli-${r.kind}-${Date.now().toString(36)}`,s=f.join(e.workDir,"runs",A(o)),a=r.attachment;return{conversation:e.conversationName,mode:"custom",steps:r.steps,outDir:s,workDir:e.workDir,traceId:o,runtimeId:e.channelId,machineId:process.env.SHENNIAN_MACHINE_ID||v().machineId||"local",sessionId:e.sessionId,serverUrl:v().serverUrl,platform:E(),text:r.text,...a?.localPath&&a.kind==="image"?{imagePath:a.localPath}:{},...a?.localPath&&a.kind==="video"?{videoPath:a.localPath}:{},...a?.localPath&&a.kind!=="image"&&a.kind!=="video"?{filePath:a.localPath}:{},allowEmptyMedia:r.allowEmptyMedia,...r.kind==="read"?{activityGuard:{useAutomationLease:!0,onEvent:r.activityGuardEvents?n=>r.activityGuardEvents?.push(n):void 0}}:{},skipRuntimeUpsert:!0,requireServer:!1}}function N(e,t){const r=f.join(e,"activity-guard.json");return p.mkdirSync(f.dirname(r),{recursive:!0}),p.writeFileSync(r,`${JSON.stringify({schema:"wechat-direct-read-activity-guard/v1",events:t},null,2)}
2
+ `,"utf8"),r}function x(e,t){return{id:e.channelId,type:"wechat-rpa",name:`WeChat ${e.conversationName}`,sessionId:e.sessionId,managerSessionId:e.sessionId,workDir:e.workDir,enabled:!0,secretRef:`wechat-cli:${e.channelId}`}}function M(e){return{bindingId:g(e.channelId,e.conversationName),sessionId:e.sessionId,conversationDisplayName:e.conversationName,enabled:!0,allowReply:!0,downloadMedia:!0}}function W(e){try{const t=JSON.parse(p.readFileSync(e,"utf-8"));return Array.isArray(t)?t.filter(y):[]}catch{return[]}}function O(e){if(e?.reasonCode!=="sent_text_not_observed")return!1;const t=y(e.details)?e.details:{},r=t.sendAccepted===!0,o=S(t.sendStatus);return r&&(o==="sent_unconfirmed"||o==="sent"||o==="ok")}export{H as readDirectWeChatLatestOnce,B as sendDirectWeChatMessageOnce};
@@ -0,0 +1,2 @@
1
+ import type { WeChatDoctorOptions, WeChatDoctorResult } from './types.js';
2
+ export declare function runWeChatDoctor(options?: WeChatDoctorOptions): Promise<WeChatDoctorResult>;
@@ -0,0 +1 @@
1
+ import{loadConfig as k}from"../../config/index.js";import{SERVERS as g}from"../../region.js";import{isRecord as l,stringValue as y}from"./utils.js";async function U(e={}){const r=k(),t=_(e.serverUrl||r.serverUrl||g.cn.url),c=e.machineToken||r.machineToken,o=e.machineId||r.machineId,m=e.fetchImpl||fetch,n=[s("cli_installed","Shennian CLI is installed."),c?s("machine_paired","Machine token is configured."):h("machine_paired","machine_not_paired","Machine is not paired. Run shennian pair first.")];if(!c)return u({checks:n,serverUrl:t,machineId:o});try{const a=await m(`${t}/api/channels/wechat/runtime-policy`,{headers:{authorization:`Bearer ${c}`}}),i=await a.json().catch(()=>null);if(!a.ok||i?.ok===!1){const d=y(i?.reasonCode)||a.statusText||"request_failed";return n.push(h("server_policy",d,`WeChat runtime policy is blocked: ${d}`)),u({checks:n,serverUrl:t,machineId:o})}const f=l(i?.usagePolicy)?i.usagePolicy:{};n.push(s("server_policy","Server WeChat policy is reachable.")),n.push(s("entitlement","Account has Pro / Team external channel entitlement.")),n.push(s("credits",f.billingMode==="credits"?"Credit billing is enabled; balance is checked by server calls.":"Credits are not metered in current billing mode."));const p=l(i?.runtime)&&l(i.runtime.current)?i.runtime.current:null;n.push(p?s("runtime_registered","Local WeChat runtime is registered on the server."):v("runtime_registered","runtime_not_registered","Local WeChat runtime is not registered yet; open Shennian Desktop/daemon and enable Use WeChat."))}catch(a){n.push(h("server_policy","network_unavailable",a instanceof Error?a.message:"Unable to reach Shennian server."))}return u({checks:n,serverUrl:t,machineId:o})}function u(e){return{ok:e.checks.every(r=>r.ok||r.status==="warning"),checks:e.checks,serverUrl:e.serverUrl,machineId:e.machineId}}function s(e,r){return{id:e,ok:!0,status:"ready",message:r}}function v(e,r,t){return{id:e,ok:!1,status:"warning",reasonCode:r,message:t}}function h(e,r,t){return{id:e,ok:!1,status:"blocked",reasonCode:r,message:t}}function _(e){return e.replace(/\/+$/,"")}export{U as runWeChatDoctor};
@@ -0,0 +1,9 @@
1
+ import type { IpcContext, IpcResponse, WeChatManagerIpcResolution, WeChatToolBinding, WeChatToolOptions } from './types.js';
2
+ export declare function loadManagerIpcFromRuntimeFile(): IpcContext;
3
+ export declare function tryLoadManagerIpcFromRuntimeFile(): IpcContext | null;
4
+ export declare function resolveWeChatManagerIpc(options: WeChatToolOptions): WeChatManagerIpcResolution | null;
5
+ export declare function shouldFallbackManagerToolToDirect(options: WeChatToolOptions, error: unknown): boolean;
6
+ export declare function ensureWeChatToolChannel(ctx: IpcContext, binding: WeChatToolBinding, options: WeChatToolOptions): Promise<IpcResponse>;
7
+ export declare function ipc(ctx: IpcContext, pathname: string, body: Record<string, unknown>, fetchImpl?: typeof fetch, timeoutMs?: number): Promise<IpcResponse>;
8
+ export declare function buildWeChatToolBinding(options: WeChatToolOptions): WeChatToolBinding;
9
+ export declare function trySyncWeChatToolChannel(ctx: IpcContext, binding: WeChatToolBinding, options: WeChatToolOptions): Promise<IpcResponse | null>;
@@ -0,0 +1 @@
1
+ import p from"node:fs";import d from"node:path";import{resolveShennianPath as m}from"../../config/index.js";import{weChatChannelConversationId as w}from"../../channels/wechat-rpa/product-channel.js";import{clampPositiveInteger as h,isAbortError as I,processExists as g,safePathSegment as y,stableId as v}from"./utils.js";function c(){const r=m("runtime","manager-ipc.json");let e;try{e=JSON.parse(p.readFileSync(r,"utf-8"))}catch{throw new Error("Manager IPC is not available. Open Shennian Desktop or run `shennian start` first.")}const t=Number(e.pid);if(Number.isInteger(t)&&t>0&&!g(t))throw new Error(`Manager IPC runtime file is stale for PID ${t}. Restart Shennian Desktop or run \`shennian restart\`.`);const n=typeof e.url=="string"?e.url.replace(/\/+$/,""):"",a=typeof e.token=="string"?e.token:"";if(!n||!a)throw new Error(`Manager IPC runtime file is incomplete: ${r}`);return{url:n,token:a}}function P(){try{return c()}catch{return null}}function $(r){if(r.directRuntime)return null;const e=r.transport??"auto";return e==="direct"?null:r.ipc?{ctx:r.ipc,mode:"session"}:r.sessionId?.trim()?{ctx:c(),mode:"session"}:e==="daemon"?{ctx:c(),mode:"tool"}:null}function E(r,e){if((r.transport??"auto")!=="auto"||r.sessionId?.trim()||r.ipc)return!1;const n=e instanceof Error?e.message:String(e||"");return/Unknown manager IPC path:\s*\/wechat-rpa\/tool\//i.test(n)}async function T(r,e,t){return f(r,"/wechat-rpa/channel/upsert",{managerSessionId:e.sessionId,id:e.channelId,name:`WeChat ${e.conversationName}`,workDir:e.workDir,enabled:!0,groups:[{name:e.conversationName}],canReply:!0,source:t.source||"wechat-channel",recentLimit:h(t.recentLimit,20),pollIntervalMs:h(t.pollIntervalMs,3e4),forceForeground:!0,downloadAttachments:t.download!=="never",privacyConsentAccepted:!0},t.fetchImpl,t.timeoutMs)}async function f(r,e,t,n=fetch,a){const l=String(t.managerSessionId||"");if(!l)throw new Error("managerSessionId is required");const s=a?new AbortController:null,u=s?setTimeout(()=>s.abort(),a):null;try{const o=await n(`${r.url}${e}`,{method:"POST",headers:{authorization:`Bearer ${r.token}`,"content-type":"application/json","x-shennian-manager-session-id":l},body:JSON.stringify(t),signal:s?.signal}),i=await o.json().catch(()=>({ok:!1,error:o.statusText}));if(!o.ok||!i.ok)throw new Error(i.error||`Manager IPC failed: ${o.status}`);return i}finally{u&&clearTimeout(u)}}function D(r){const e=r.conversation.trim();if(!e)throw new Error("--conversation is required");const t=v("wechat-cli-conversation",e),n=r.sessionId?.trim()||`wechat-cli-${t.slice(-24)}`,a=`wechat-rpa:${n}`;return{sessionId:n,channelId:a,conversationId:w(e),conversationName:e,workDir:d.resolve(r.workDir||m("wechat-cli",y(e)))}}async function F(r,e,t){try{return await f(r,"/wechat-rpa/channel/sync",{managerSessionId:e.sessionId},t.fetchImpl,t.timeoutMs)}catch(n){if(I(n))return null;throw n}}export{D as buildWeChatToolBinding,T as ensureWeChatToolChannel,f as ipc,c as loadManagerIpcFromRuntimeFile,$ as resolveWeChatManagerIpc,E as shouldFallbackManagerToolToDirect,P as tryLoadManagerIpcFromRuntimeFile,F as trySyncWeChatToolChannel};
@@ -0,0 +1,3 @@
1
+ import type { WeChatReadLatestOptions, WeChatReadLatestResult, WeChatSendOptions, WeChatSendResult } from './types.js';
2
+ export declare function runWeChatSend(options: WeChatSendOptions): Promise<WeChatSendResult>;
3
+ export declare function runWeChatReadLatest(options: WeChatReadLatestOptions): Promise<WeChatReadLatestResult>;
@@ -0,0 +1,4 @@
1
+ import{readDirectWeChatLatestOnce as D,sendDirectWeChatMessageOnce as P}from"./direct.js";import{buildWeChatToolBinding as w,ensureWeChatToolChannel as y,ipc as g,resolveWeChatManagerIpc as C,shouldFallbackManagerToolToDirect as p,trySyncWeChatToolChannel as R}from"./ipc.js";import{clampPositiveInteger as k,isExternalMessageEvent as N,isRecord as v,normalizeSendStatus as T,stableId as W,stringValue as u}from"./utils.js";async function S(t){const s=t.text.trim();if(!s&&!t.attachment)throw new Error("Message text or attachment is required");const e=w(t),n=t.attachment?b(t.attachment):void 0;if(t.dryRun)return{ok:!0,operation:"write",conversation:e.conversationName,conversationName:e.conversationName,conversationId:e.conversationId,sessionId:e.sessionId,channelId:e.channelId,traceId:t.traceId,outDir:e.workDir,helperTracePath:null,sendStatus:"dry_run",pending:!1,attachment:n,attachments:n?[n]:[],dryRun:!0,reasonCode:null,message:"Dry run completed. No WeChat message was sent."};const d=C(t);if(!d){const r=t.directRuntime?await t.directRuntime.send(e,t):await P(e,t);return{ok:!0,operation:"write",conversation:e.conversationName,conversationName:e.conversationName,conversationId:e.conversationId,sessionId:e.sessionId,channelId:e.channelId,traceId:t.traceId,outDir:r.outDir??e.workDir,helperTracePath:r.helperTracePath??null,sendStatus:r.sendStatus??(r.pending?"pending":"sent"),pending:r.pending===!0,attachment:n,attachments:n?[n]:[],reasonCode:null,message:r.pending?"WeChat message is queued or waiting for confirmation.":"WeChat message sent."}}if(d.mode==="tool"){let r;try{r=await g(d.ctx,"/wechat-rpa/tool/send",{managerSessionId:e.sessionId,binding:e,text:s,attachment:t.attachment,traceId:t.traceId,timeoutMs:t.timeoutMs},t.fetchImpl,t.timeoutMs)}catch(h){if(p(t,h))return S({...t,transport:"direct"});throw h}const l=T(r.sendStatus)??(r.pending===!0?"pending":"sent");return{ok:!0,operation:"write",conversation:e.conversationName,conversationName:e.conversationName,conversationId:e.conversationId,sessionId:e.sessionId,channelId:e.channelId,traceId:t.traceId,outDir:u(r.outDir)||e.workDir,helperTracePath:u(r.helperTracePath)||null,sendStatus:l,pending:r.pending===!0,attachment:n,attachments:n?[n]:[],reasonCode:null,message:r.pending?"WeChat message is queued or waiting for confirmation.":"WeChat message sent."}}const m=d.ctx;await y(m,e,t);const c=t.idempotencyKey||W("wechat-cli-send",`${e.channelId}
2
+ ${e.conversationId}
3
+ ${s}
4
+ ${Date.now()}`),i=await g(m,"/external/reply",{managerSessionId:e.sessionId,channelId:e.channelId,conversationId:e.conversationId,text:s,attachment:t.attachment,idempotencyKey:c},t.fetchImpl,t.timeoutMs),o=await R(m,e,t),a=o?x(o,c,i.pending===!0):i.pending===!0?"queued":"ok";return{ok:!0,operation:"write",conversation:e.conversationName,conversationName:e.conversationName,conversationId:e.conversationId,sessionId:e.sessionId,channelId:e.channelId,traceId:t.traceId,outDir:e.workDir,helperTracePath:null,sendStatus:a,pending:i.pending===!0&&(a==="queued"||a==="pending"),attachment:n,attachments:n?[n]:[],reasonCode:null,message:o?a==="confirmed_echo"?"WeChat message sent and confirmed.":a==="manual_review"?"WeChat message was submitted but needs manual review.":"WeChat message submitted to local daemon.":"WeChat message submitted to local daemon; confirmation sync timed out."}}async function M(t){const s=k(t.limit,10),e=w(t),n=C(t);if(!n){const a=t.directRuntime?{messages:await t.directRuntime.readLatest(e,{...t,limit:s,recentLimit:t.recentLimit??s}),outDir:e.workDir,helperTracePath:null,activityGuardPath:null}:await D(e,{...t,limit:s,recentLimit:t.recentLimit??s}),r=a.messages.filter(h=>h.conversationId===e.conversationId||h.conversationName===e.conversationName).slice(-s),l=f(r);return{ok:!0,operation:"read",conversation:e.conversationName,conversationName:e.conversationName,conversationId:e.conversationId,sessionId:e.sessionId,channelId:e.channelId,traceId:t.traceId,outDir:a.outDir,helperTracePath:a.helperTracePath,activityGuardPath:a.activityGuardPath??null,messages:r,attachments:l,count:r.length,reasonCode:null,message:`Read ${r.length} WeChat message(s).`}}if(n.mode==="tool"){let a;try{a=await g(n.ctx,"/wechat-rpa/tool/read",{managerSessionId:e.sessionId,binding:e,limit:s,download:t.download,traceId:t.traceId,timeoutMs:t.timeoutMs},t.fetchImpl,t.timeoutMs)}catch(I){if(p(t,I))return M({...t,transport:"direct"});throw I}const l=(Array.isArray(a.messages)?a.messages.filter(N):[]).filter(I=>I.conversationId===e.conversationId||I.conversationName===e.conversationName).slice(-s),h=f(l);return{ok:!0,operation:"read",conversation:e.conversationName,conversationName:e.conversationName,conversationId:e.conversationId,sessionId:e.sessionId,channelId:e.channelId,traceId:t.traceId,outDir:u(a.outDir)||e.workDir,helperTracePath:u(a.helperTracePath)||null,activityGuardPath:u(a.activityGuardPath)||null,messages:l,attachments:h,count:l.length,reasonCode:null,message:`Read ${l.length} WeChat message(s).`}}const d=n.ctx;await y(d,e,{...t,recentLimit:t.recentLimit??s});const m=await g(d,"/wechat-rpa/channel/sync",{managerSessionId:e.sessionId},t.fetchImpl,t.timeoutMs),i=(Array.isArray(m.messages)?m.messages.filter(N):[]).filter(a=>a.conversationId===e.conversationId||a.conversationName===e.conversationName).slice(-s),o=f(i);return{ok:!0,operation:"read",conversation:e.conversationName,conversationName:e.conversationName,conversationId:e.conversationId,sessionId:e.sessionId,channelId:e.channelId,traceId:t.traceId,outDir:e.workDir,helperTracePath:null,activityGuardPath:null,messages:i,attachments:o,count:i.length,reasonCode:null,message:`Read ${i.length} WeChat message(s).`}}function x(t,s,e){const n=v(t.channel)?t.channel:{},m=(Array.isArray(n.wechatRpaPendingReplies)?n.wechatRpaPendingReplies:[]).filter(v).find(a=>u(a.idempotencyKey)===s),c=u(m?.status);if(c==="confirmed_echo"||c==="sent_unconfirmed"||c==="manual_review"||c==="queued")return c;const o=(Array.isArray(n.wechatRpaRecentTaskSummaries)?n.wechatRpaRecentTaskSummaries:[]).filter(v).map(a=>u(a.status)).find(Boolean);return o==="sent"?"sent":o==="confirmed_echo"?"confirmed_echo":o==="manual_review"?"manual_review":e?"pending":"ok"}function b(t){return{kind:t.kind,name:t.name,mimeType:t.mimeType,size:t.size,localPath:t.localPath}}function f(t){return t.flatMap(s=>s.attachments??[])}export{M as runWeChatReadLatest,S as runWeChatSend};
@@ -0,0 +1,7 @@
1
+ import type { WeChatReadLatestResult, WeChatSendResult, WeChatToolErrorResult } from './types.js';
2
+ export type WeChatReadOutputFormat = 'markdown' | 'json';
3
+ export declare function printToolResult(result: WeChatSendResult, json?: boolean): void;
4
+ export declare function printReadLatestResult(result: WeChatReadLatestResult, format: WeChatReadOutputFormat): void;
5
+ export declare function printToolError(operation: 'read' | 'write', conversation: string | undefined, error: unknown, json?: boolean): void;
6
+ export declare function buildWeChatToolErrorResult(operation: 'read' | 'write', conversation: string | undefined, error: unknown): WeChatToolErrorResult;
7
+ export declare function buildWeChatReadMarkdown(result: WeChatReadLatestResult): string;
@@ -0,0 +1,4 @@
1
+ import v from"node:path";import{cleanInline as a,stringValue as o}from"./utils.js";const b=["SHENNIAN_WECHAT_CHANNEL_HELPER_DIR","SHENNIAN_HELPER_RUNTIME_DIR","packages/helper-runtime/wechat-channel","packages\\helper-runtime\\wechat-channel","packages/helper-runtime/dist","packages\\helper-runtime\\dist","wechat-rpa-windows-product-send-runner","PowerShell one-shot runner","activity.snapshot","automation.lease."];function I(e,t){if(t){console.log(JSON.stringify(e,null,2));return}console.log(e.sendStatus)}function L(e,t){if(t==="json"){console.log(JSON.stringify(e,null,2));return}console.log(k(e).trimEnd())}function H(e,t,n,i){const r=y(e,t,n);i?console.log(JSON.stringify(r,null,2)):console.error(`${r.reasonCode}: ${r.message}`),process.exitCode=1}function y(e,t,n){const i=n instanceof Error?n.message:String(n||"wechat_tool_failed"),r=a(i)||"wechat_tool_failed",c=/^([a-z][a-z0-9_]*)(?::\s*(.*))?$/i.exec(r),s=o(n?.reasonCode)||c?.[1]||"wechat_tool_failed",l=W(s,r),_=a(c?.[2]||r),g=E(l,_),d=o(n?.outDir),p=o(n?.helperTracePath),h=o(n?.activityGuardPath),m=o(n?.traceId),f=q(l);return{ok:!1,operation:e,...t?.trim()?{conversation:t.trim()}:{},reasonCode:l,...s!==l&&!u(`${s} ${_}`)?{rawReasonCode:s}:{},message:g,...m?{traceId:m}:{},...d?{outDir:d}:{},...p?{helperTracePath:p}:{},...h?{activityGuardPath:h}:{},...f?{userAction:f}:{}}}function k(e){const t=[];if(e.messages.length===0)return`\uFF08\u6CA1\u6709\u8BFB\u5230\uFF09
2
+ `;for(const n of e.messages)t.push(`${S(n)}: ${$(n)}`);return`${t.join(`
3
+ `)}
4
+ `}function W(e,t=""){const n=e.trim().toLowerCase(),i=`${n} ${t}`.toLowerCase();return n?n==="insufficient_credits"?"insufficient_credits":n==="entitlement_required"||n==="enterprise_required"||n.includes("entitlement")?"entitlement_required":n==="manual_review_required"||n.includes("manual_review")?"manual_review_required":n==="attachment_pending"||/attachment.*pending|media_original_pending/.test(i)?"attachment_pending":/helper_runtime_required|helper_runtime_source_missing|helper_runtime_package_manifest_missing|manifest_missing|helper_missing|helper_not_executable/.test(n)?"helper_missing":/helper_runtime_cli_too_old|helper_runtime_version_mismatch|helper_runtime_protocol_mismatch|helper_runtime_platform_mismatch|helper_runtime_package_manifest_invalid|integrity_mismatch/.test(n)?"helper_version_incompatible":/^user_active|recent_(keyboard|mouse|scroll)|frontmost_app_changed|user_takeover|activity/.test(n)?"user_active_retry_later":/helper_command_timeout/.test(n)?"wechat_window_unavailable":u(i)?"wechat_tool_failed":/permission|accessibility|screen_recording|automation|mac_input|input-monitoring/.test(i)?"permission_required":/wechat_login_required|login_required|安全验证|扫码登录|重新登录/.test(i)?"wechat_login_required":/wechat_single_main_window_required|wechat_duplicate_instance|duplicate_main_window/.test(n)?"wechat_single_main_window_required":/visible_desktop|desktop_session|screen_locked|session_locked|rdp_disconnected|rdp.*visible|rdp.*desktop/.test(i)?"windows_visible_desktop_unavailable":/dpi_mapping_failed|display_topology|monitor_topology|multi_?monitor|multi_?screen/.test(i)?"dpi_mapping_failed":/conversation_not|conversation_title_not_confirmed|conversation_not_visible|open_read_required|search_result_not_visible/.test(n)?"conversation_not_found":/wechat_window|wechat_not_running|window_not|window_unavailable|window_unresponsive/.test(n)?"wechat_window_unavailable":n:"wechat_tool_failed"}function q(e){if(e==="wechat_login_required")return"Open WeChat on this machine, complete login or security verification, then retry.";if(e==="wechat_window_unavailable")return"Open the main WeChat window on the active desktop, then retry.";if(e==="wechat_single_main_window_required")return"Close duplicate WeChat main windows, login prompts, or security prompts, then retry with exactly one signed-in WeChat main window.";if(e==="windows_visible_desktop_unavailable")return"Unlock or reconnect the Windows visible desktop session, keep WeChat visible, then retry.";if(e==="dpi_mapping_failed")return"Move WeChat fully onto one visible display or use a supported display scaling setup, then retry.";if(e==="conversation_not_found")return"Check the WeChat conversation name and make sure the conversation can be searched.";if(e==="helper_missing")return"Open Shennian Desktop or run `shennian runtime install helper`, then retry.";if(e==="helper_version_incompatible")return"Run `shennian runtime repair helper` or upgrade Shennian CLI, then retry.";if(e==="permission_required")return"Grant Shennian Helper/Desktop the required macOS permissions, then retry.";if(e==="entitlement_required")return"Enable the WeChat capability for this account in Shennian.";if(e==="insufficient_credits")return"Add credits or adjust the account plan in Shennian.";if(e==="user_active_retry_later")return"Wait until the user is idle, then retry.";if(e==="attachment_pending")return"Open WeChat and let the attachment finish downloading, then retry.";if(e==="manual_review_required")return"Review the current WeChat window before retrying."}function E(e,t){return!t||t===e||u(t)?R(e):t}function u(e){const t=e.replace(/\\/g,"/").toLowerCase();return b.some(n=>{const i=n.replace(/\\/g,"/").toLowerCase();return t.includes(i)})}function R(e){return e==="wechat_login_required"?"WeChat needs login or security verification on this machine.":e==="wechat_window_unavailable"?"WeChat window is not available on the active desktop.":e==="wechat_single_main_window_required"?"Exactly one signed-in WeChat main window is required.":e==="windows_visible_desktop_unavailable"?"Windows visible desktop session is unavailable.":e==="dpi_mapping_failed"?"Windows display scaling or monitor geometry cannot be mapped safely.":e==="conversation_not_found"?"WeChat conversation was not found.":e==="helper_missing"?"Shennian Helper runtime is not installed or cannot be found.":e==="helper_version_incompatible"?"Shennian Helper runtime is incompatible with this CLI.":e==="permission_required"?"Required local permissions are missing.":e==="entitlement_required"?"WeChat capability is not enabled for this account.":e==="insufficient_credits"?"Insufficient credits.":e==="user_active_retry_later"?"User activity is in progress; retry later.":e==="attachment_pending"?"WeChat attachment is still pending.":e==="manual_review_required"?"Manual review is required before retrying.":e}function $(e){const t=e.attachments||[],n=a(e.text);return n&&t.length===0?n:n&&t.length>0?`${n} ${t.map(w).join("\u3001")}`:t.length>0?t.map(w).join("\u3001"):"\uFF08\u6D88\u606F\uFF09"}function S(e){return a(e.sender.name||e.sender.id||"\u5BF9\u65B9")}function w(e){const t=a(e.name||v.basename(e.localPath||"")||e.type||"\u9644\u4EF6"),n=e.localPath||e.url||"",i=e.availability&&e.availability!=="edge-local"?` (${e.availability})`:"";return n?`[${P(t)}](${C(n)})${i}`:`${t}${i}`}function C(e){const t=e.replace(/\\/g,"/");return/[\s()<>]/.test(t)?`<${t.replace(/[<>]/g,"")}>`:t}function P(e){return a(e).replace(/([\\\]])/g,"\\$1")}export{k as buildWeChatReadMarkdown,y as buildWeChatToolErrorResult,L as printReadLatestResult,H as printToolError,I as printToolResult};
@@ -0,0 +1,141 @@
1
+ import type { ExternalMessageAttachment, ExternalMessageEvent, ExternalReplyAttachment } from '../../channels/base.js';
2
+ export type WeChatDoctorCheck = {
3
+ id: string;
4
+ ok: boolean;
5
+ status: 'ready' | 'blocked' | 'warning';
6
+ reasonCode?: string;
7
+ message: string;
8
+ };
9
+ export type WeChatDoctorResult = {
10
+ ok: boolean;
11
+ checks: WeChatDoctorCheck[];
12
+ serverUrl?: string;
13
+ machineId?: string;
14
+ };
15
+ export type WeChatDoctorOptions = {
16
+ serverUrl?: string;
17
+ machineToken?: string;
18
+ machineId?: string;
19
+ fetchImpl?: typeof fetch;
20
+ };
21
+ export type IpcContext = {
22
+ url: string;
23
+ token: string;
24
+ };
25
+ export type IpcResponse = {
26
+ ok: boolean;
27
+ error?: string;
28
+ channel?: unknown;
29
+ messages?: unknown;
30
+ [key: string]: unknown;
31
+ };
32
+ export type WeChatToolBinding = {
33
+ sessionId: string;
34
+ channelId: string;
35
+ conversationId: string;
36
+ conversationName: string;
37
+ workDir: string;
38
+ };
39
+ export type WeChatManagerIpcResolution = {
40
+ ctx: IpcContext;
41
+ mode: 'session' | 'tool';
42
+ };
43
+ export type WeChatToolTransport = 'auto' | 'daemon' | 'direct';
44
+ export type WeChatToolOptions = {
45
+ conversation: string;
46
+ sessionId?: string;
47
+ workDir?: string;
48
+ source?: string;
49
+ download?: 'auto' | 'never';
50
+ transport?: WeChatToolTransport;
51
+ traceId?: string;
52
+ timeoutMs?: number;
53
+ recentLimit?: number;
54
+ pollIntervalMs?: number;
55
+ fetchImpl?: typeof fetch;
56
+ ipc?: IpcContext;
57
+ directRuntime?: WeChatDirectToolRuntime;
58
+ };
59
+ export type WeChatDirectToolRuntime = {
60
+ readLatest(binding: WeChatToolBinding, options: WeChatReadLatestOptions): Promise<ExternalMessageEvent[]>;
61
+ send(binding: WeChatToolBinding, options: WeChatSendOptions): Promise<{
62
+ pending?: boolean;
63
+ sendStatus?: WeChatSendResult['sendStatus'];
64
+ outDir?: string;
65
+ helperTracePath?: string | null;
66
+ }>;
67
+ };
68
+ export type WeChatSendOptions = WeChatToolOptions & {
69
+ text: string;
70
+ attachment?: ExternalReplyAttachment;
71
+ idempotencyKey?: string;
72
+ dryRun?: boolean;
73
+ };
74
+ export type WeChatReadLatestOptions = WeChatToolOptions & {
75
+ limit?: number;
76
+ };
77
+ export type WeChatSendResult = {
78
+ ok: true;
79
+ operation: 'write';
80
+ conversation: string;
81
+ conversationName: string;
82
+ conversationId: string;
83
+ sessionId: string;
84
+ channelId: string;
85
+ traceId?: string;
86
+ outDir: string;
87
+ helperTracePath?: string | null;
88
+ sendStatus: 'dry_run' | 'queued' | 'sent' | 'sent_unconfirmed' | 'confirmed_echo' | 'manual_review' | 'pending' | 'ok';
89
+ pending?: boolean;
90
+ attachment?: Pick<ExternalReplyAttachment, 'kind' | 'name' | 'mimeType' | 'size' | 'localPath'>;
91
+ attachments: Array<Pick<ExternalReplyAttachment, 'kind' | 'name' | 'mimeType' | 'size' | 'localPath'>>;
92
+ dryRun?: boolean;
93
+ reasonCode?: null;
94
+ message?: string;
95
+ };
96
+ export type WeChatReadLatestResult = {
97
+ ok: true;
98
+ operation: 'read';
99
+ conversation: string;
100
+ conversationName: string;
101
+ conversationId: string;
102
+ sessionId: string;
103
+ channelId: string;
104
+ traceId?: string;
105
+ outDir: string;
106
+ helperTracePath?: string | null;
107
+ activityGuardPath?: string | null;
108
+ messages: ExternalMessageEvent[];
109
+ attachments: ExternalMessageAttachment[];
110
+ count: number;
111
+ reasonCode?: null;
112
+ message?: string;
113
+ };
114
+ export type WeChatToolErrorResult = {
115
+ ok: false;
116
+ operation: 'read' | 'write';
117
+ conversation?: string;
118
+ reasonCode: string;
119
+ rawReasonCode?: string;
120
+ message: string;
121
+ traceId?: string;
122
+ outDir?: string;
123
+ helperTracePath?: string | null;
124
+ activityGuardPath?: string | null;
125
+ userAction?: string;
126
+ };
127
+ export declare class WeChatToolOperationError extends Error {
128
+ readonly reasonCode: string;
129
+ readonly outDir?: string;
130
+ readonly helperTracePath?: string | null;
131
+ readonly activityGuardPath?: string | null;
132
+ readonly traceId?: string;
133
+ constructor(input: {
134
+ reasonCode: string;
135
+ message?: string;
136
+ outDir?: string;
137
+ helperTracePath?: string | null;
138
+ activityGuardPath?: string | null;
139
+ traceId?: string;
140
+ });
141
+ }
@@ -0,0 +1 @@
1
+ class t extends Error{reasonCode;outDir;helperTracePath;activityGuardPath;traceId;constructor(e){const r=o(e.message||e.reasonCode);super(r&&r!==e.reasonCode?`${e.reasonCode}: ${r}`:e.reasonCode),this.name="WeChatToolOperationError",this.reasonCode=e.reasonCode,this.outDir=e.outDir,this.helperTracePath=e.helperTracePath,this.activityGuardPath=e.activityGuardPath,this.traceId=e.traceId}}function o(a){return String(a||"").replace(/\s+/g," ").trim()}export{t as WeChatToolOperationError};
@@ -0,0 +1,14 @@
1
+ import type { ExternalMessageEvent } from '../../channels/base.js';
2
+ import type { WeChatChannelRuntimePlatform } from '../../channels/wechat-channel/runtime.js';
3
+ import type { WeChatSendResult } from './types.js';
4
+ export declare function normalizeSendStatus(value: unknown): WeChatSendResult['sendStatus'] | null;
5
+ export declare function currentWeChatPlatform(): WeChatChannelRuntimePlatform | undefined;
6
+ export declare function cleanInline(value: unknown): string;
7
+ export declare function processExists(pid: number): boolean;
8
+ export declare function isAbortError(error: unknown): boolean;
9
+ export declare function stableId(prefix: string, value: string): string;
10
+ export declare function safePathSegment(value: string): string;
11
+ export declare function clampPositiveInteger(value: unknown, fallback: number): number;
12
+ export declare function isExternalMessageEvent(value: unknown): value is ExternalMessageEvent;
13
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
14
+ export declare function stringValue(value: unknown): string | undefined;
@@ -0,0 +1 @@
1
+ import o from"node:crypto";function i(t){return t==="dry_run"||t==="queued"||t==="sent"||t==="sent_unconfirmed"||t==="confirmed_echo"||t==="manual_review"||t==="pending"||t==="ok"?t:null}function c(){if(process.platform==="darwin")return"darwin";if(process.platform==="win32")return"win32"}function f(t){return String(t||"").replace(/\s+/g," ").trim()}function p(t){try{return process.kill(t,0),!0}catch{return!1}}function a(t){if(!t||typeof t!="object")return!1;const r=t,e=typeof r.name=="string"?r.name:"",n=typeof r.message=="string"?r.message:"";return e==="AbortError"||r.code==="ABORT_ERR"||/aborted|abort/i.test(n)}function u(t,r){return`${t}:${o.createHash("sha256").update(r).digest("hex").slice(0,32)}`}function m(t){return t.trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"conversation"}function d(t,r){const e=Number(t);return!Number.isFinite(e)||e<=0?r:Math.floor(e)}function g(t){return!!t&&typeof t=="object"&&!Array.isArray(t)&&t.type==="external.message"&&typeof t.channelId=="string"&&typeof t.conversationId=="string"&&typeof t.messageId=="string"&&typeof t.text=="string"}function y(t){return!!t&&typeof t=="object"&&!Array.isArray(t)}function x(t){return typeof t=="string"&&t?t:void 0}export{d as clampPositiveInteger,f as cleanInline,c as currentWeChatPlatform,a as isAbortError,g as isExternalMessageEvent,y as isRecord,i as normalizeSendStatus,p as processExists,m as safePathSegment,u as stableId,x as stringValue};
@@ -1,127 +1,6 @@
1
- import type { Command } from 'commander';
2
- import type { ExternalMessageEvent, ExternalMessageAttachment, ExternalReplyAttachment } from '../channels/base.js';
3
- export type WeChatDoctorCheck = {
4
- id: string;
5
- ok: boolean;
6
- status: 'ready' | 'blocked' | 'warning';
7
- reasonCode?: string;
8
- message: string;
9
- };
10
- export type WeChatDoctorResult = {
11
- ok: boolean;
12
- checks: WeChatDoctorCheck[];
13
- serverUrl?: string;
14
- machineId?: string;
15
- };
16
- export type WeChatDoctorOptions = {
17
- serverUrl?: string;
18
- machineToken?: string;
19
- machineId?: string;
20
- fetchImpl?: typeof fetch;
21
- };
22
- type IpcContext = {
23
- url: string;
24
- token: string;
25
- };
26
- export type WeChatToolBinding = {
27
- sessionId: string;
28
- channelId: string;
29
- conversationId: string;
30
- conversationName: string;
31
- workDir: string;
32
- };
33
- export type WeChatToolTransport = 'auto' | 'daemon' | 'direct';
34
- export type WeChatToolOptions = {
35
- conversation: string;
36
- sessionId?: string;
37
- workDir?: string;
38
- source?: string;
39
- download?: 'auto' | 'never';
40
- transport?: WeChatToolTransport;
41
- traceId?: string;
42
- timeoutMs?: number;
43
- recentLimit?: number;
44
- pollIntervalMs?: number;
45
- fetchImpl?: typeof fetch;
46
- ipc?: IpcContext;
47
- directRuntime?: WeChatDirectToolRuntime;
48
- };
49
- export type WeChatDirectToolRuntime = {
50
- readLatest(binding: WeChatToolBinding, options: WeChatReadLatestOptions): Promise<ExternalMessageEvent[]>;
51
- send(binding: WeChatToolBinding, options: WeChatSendOptions): Promise<{
52
- pending?: boolean;
53
- sendStatus?: WeChatSendResult['sendStatus'];
54
- outDir?: string;
55
- helperTracePath?: string | null;
56
- }>;
57
- };
58
- export type WeChatSendOptions = WeChatToolOptions & {
59
- text: string;
60
- attachment?: ExternalReplyAttachment;
61
- idempotencyKey?: string;
62
- dryRun?: boolean;
63
- };
64
- export type WeChatReadLatestOptions = WeChatToolOptions & {
65
- limit?: number;
66
- };
67
- export type WeChatSendResult = {
68
- ok: true;
69
- operation: 'write';
70
- conversation: string;
71
- conversationName: string;
72
- conversationId: string;
73
- sessionId: string;
74
- channelId: string;
75
- traceId?: string;
76
- outDir: string;
77
- helperTracePath?: string | null;
78
- sendStatus: 'dry_run' | 'queued' | 'sent' | 'sent_unconfirmed' | 'confirmed_echo' | 'manual_review' | 'pending' | 'ok';
79
- pending?: boolean;
80
- attachment?: Pick<ExternalReplyAttachment, 'kind' | 'name' | 'mimeType' | 'size' | 'localPath'>;
81
- attachments: Array<Pick<ExternalReplyAttachment, 'kind' | 'name' | 'mimeType' | 'size' | 'localPath'>>;
82
- dryRun?: boolean;
83
- reasonCode?: null;
84
- message?: string;
85
- };
86
- export type WeChatReadLatestResult = {
87
- ok: true;
88
- operation: 'read';
89
- conversation: string;
90
- conversationName: string;
91
- conversationId: string;
92
- sessionId: string;
93
- channelId: string;
94
- traceId?: string;
95
- outDir: string;
96
- helperTracePath?: string | null;
97
- messages: ExternalMessageEvent[];
98
- attachments: ExternalMessageAttachment[];
99
- count: number;
100
- reasonCode?: null;
101
- message?: string;
102
- };
103
- export type WeChatToolErrorResult = {
104
- ok: false;
105
- operation: 'read' | 'write';
106
- conversation?: string;
107
- reasonCode: string;
108
- message: string;
109
- };
110
- export declare function runWeChatDoctor(options?: WeChatDoctorOptions): Promise<WeChatDoctorResult>;
111
- export declare function runWeChatSend(options: WeChatSendOptions): Promise<WeChatSendResult>;
112
- export declare function runWeChatReadLatest(options: WeChatReadLatestOptions): Promise<WeChatReadLatestResult>;
113
- export declare function sendDirectWeChatMessageOnce(binding: WeChatToolBinding, options: WeChatSendOptions): Promise<{
114
- pending?: boolean;
115
- sendStatus?: WeChatSendResult['sendStatus'];
116
- outDir?: string;
117
- helperTracePath?: string | null;
118
- }>;
119
- export declare function readDirectWeChatLatestOnce(binding: WeChatToolBinding, options: WeChatReadLatestOptions): Promise<{
120
- messages: ExternalMessageEvent[];
121
- outDir: string;
122
- helperTracePath: string | null;
123
- }>;
124
- export declare function registerWeChatCommand(program: Command): void;
125
- export declare function buildWeChatToolErrorResult(operation: 'read' | 'write', conversation: string | undefined, error: unknown): WeChatToolErrorResult;
126
- export declare function buildWeChatReadMarkdown(result: WeChatReadLatestResult): string;
127
- export {};
1
+ export { registerWeChatCommand } from './wechat/command.js';
2
+ export * from './wechat/direct.js';
3
+ export * from './wechat/doctor.js';
4
+ export * from './wechat/operations.js';
5
+ export * from './wechat/output.js';
6
+ export * from './wechat/types.js';
@@ -1,7 +1 @@
1
- import F from"node:crypto";import x from"node:fs";import y from"node:path";import{SERVERS as J}from"../region.js";import{loadConfig as k,resolveShennianPath as $}from"../config/index.js";import{observedMessageToExternalEvent as K,weChatChannelConversationId as H,weChatChannelBindingId as V}from"../channels/wechat-rpa/product-channel.js";import{readExternalAttachment as Z}from"./external-attachments.js";import{runWeChatChannelActionSmoke as D}from"../devtools/wechat-channel-action-smoke.js";async function G(e={}){const t=k(),n=ne(e.serverUrl||t.serverUrl||J.cn.url),a=e.machineToken||t.machineToken,r=e.machineId||t.machineId,o=e.fetchImpl||fetch,s=[g("cli_installed","Shennian CLI is installed."),a?g("machine_paired","Machine token is configured."):S("machine_paired","machine_not_paired","Machine is not paired. Run shennian pair first.")];if(!a)return C({checks:s,serverUrl:n,machineId:r});try{const i=await o(`${n}/api/channels/wechat/runtime-policy`,{headers:{authorization:`Bearer ${a}`}}),c=await i.json().catch(()=>null);if(!i.ok||c?.ok===!1){const u=f(c?.reasonCode)||i.statusText||"request_failed";return s.push(S("server_policy",u,`WeChat runtime policy is blocked: ${u}`)),C({checks:s,serverUrl:n,machineId:r})}const d=p(c?.usagePolicy)?c.usagePolicy:{};s.push(g("server_policy","Server WeChat policy is reachable.")),s.push(g("entitlement","Account has Pro / Team external channel entitlement.")),s.push(g("credits",d.billingMode==="credits"?"Credit billing is enabled; balance is checked by server calls.":"Credits are not metered in current billing mode."));const l=p(c?.runtime)&&p(c.runtime.current)?c.runtime.current:null;s.push(l?g("runtime_registered","Local WeChat runtime is registered on the server."):ee("runtime_registered","runtime_not_registered","Local WeChat runtime is not registered yet; open Shennian Desktop/daemon and enable Use WeChat."))}catch(i){s.push(S("server_policy","network_unavailable",i instanceof Error?i.message:"Unable to reach Shennian server."))}return C({checks:s,serverUrl:n,machineId:r})}async function P(e){const t=e.text.trim();if(!t&&!e.attachment)throw new Error("Message text or attachment is required");const n=b(e),a=e.attachment?we(e.attachment):void 0;if(e.dryRun)return{ok:!0,operation:"write",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:n.workDir,helperTracePath:null,sendStatus:"dry_run",pending:!1,attachment:a,attachments:a?[a]:[],dryRun:!0,reasonCode:null,message:"Dry run completed. No WeChat message was sent."};const r=M(e);if(!r){const l=e.directRuntime?await e.directRuntime.send(n,e):await Q(n,e);return{ok:!0,operation:"write",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:l.outDir??n.workDir,helperTracePath:l.helperTracePath??null,sendStatus:l.sendStatus??(l.pending?"pending":"sent"),pending:l.pending===!0,attachment:a,attachments:a?[a]:[],reasonCode:null,message:l.pending?"WeChat message is queued or waiting for confirmation.":"WeChat message sent."}}if(r.mode==="tool"){let l;try{l=await w(r.ctx,"/wechat-rpa/tool/send",{managerSessionId:n.sessionId,binding:n,text:t,attachment:e.attachment,traceId:e.traceId,timeoutMs:e.timeoutMs},e.fetchImpl,e.timeoutMs)}catch(h){if(R(e,h))return P({...e,transport:"direct"});throw h}const u=j(l.sendStatus)??(l.pending===!0?"pending":"sent");return{ok:!0,operation:"write",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:f(l.outDir)||n.workDir,helperTracePath:f(l.helperTracePath)||null,sendStatus:u,pending:l.pending===!0,attachment:a,attachments:a?[a]:[],reasonCode:null,message:l.pending?"WeChat message is queued or waiting for confirmation.":"WeChat message sent."}}const o=r.ctx;await T(o,n,e);const s=e.idempotencyKey||q("wechat-cli-send",`${n.channelId}
2
- ${n.conversationId}
3
- ${t}
4
- ${Date.now()}`),i=await w(o,"/external/reply",{managerSessionId:n.sessionId,channelId:n.channelId,conversationId:n.conversationId,text:t,attachment:e.attachment,idempotencyKey:s},e.fetchImpl,e.timeoutMs),c=await te(o,n,e),d=c?ue(c,s,i.pending===!0):i.pending===!0?"queued":"ok";return{ok:!0,operation:"write",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:n.workDir,helperTracePath:null,sendStatus:d,pending:i.pending===!0&&(d==="queued"||d==="pending"),attachment:a,attachments:a?[a]:[],reasonCode:null,message:c?d==="confirmed_echo"?"WeChat message sent and confirmed.":d==="manual_review"?"WeChat message was submitted but needs manual review.":"WeChat message submitted to local daemon.":"WeChat message submitted to local daemon; confirmation sync timed out."}}async function W(e){const t=v(e.limit,10),n=b(e),a=M(e);if(!a){const d=e.directRuntime?{messages:await e.directRuntime.readLatest(n,{...e,limit:t,recentLimit:e.recentLimit??t}),outDir:n.workDir,helperTracePath:null}:await Y(n,{...e,limit:t,recentLimit:e.recentLimit??t}),l=d.messages.filter(h=>h.conversationId===n.conversationId||h.conversationName===n.conversationName).slice(-t),u=N(l);return{ok:!0,operation:"read",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:d.outDir,helperTracePath:d.helperTracePath,messages:l,attachments:u,count:l.length,reasonCode:null,message:`Read ${l.length} WeChat message(s).`}}if(a.mode==="tool"){let d;try{d=await w(a.ctx,"/wechat-rpa/tool/read",{managerSessionId:n.sessionId,binding:n,limit:t,download:e.download,traceId:e.traceId,timeoutMs:e.timeoutMs},e.fetchImpl,e.timeoutMs)}catch(m){if(R(e,m))return W({...e,transport:"direct"});throw m}const u=(Array.isArray(d.messages)?d.messages.filter(B):[]).filter(m=>m.conversationId===n.conversationId||m.conversationName===n.conversationName).slice(-t),h=N(u);return{ok:!0,operation:"read",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:f(d.outDir)||n.workDir,helperTracePath:f(d.helperTracePath)||null,messages:u,attachments:h,count:u.length,reasonCode:null,message:`Read ${u.length} WeChat message(s).`}}const r=a.ctx;await T(r,n,{...e,recentLimit:e.recentLimit??t});const o=await w(r,"/wechat-rpa/channel/sync",{managerSessionId:n.sessionId},e.fetchImpl,e.timeoutMs),i=(Array.isArray(o.messages)?o.messages.filter(B):[]).filter(d=>d.conversationId===n.conversationId||d.conversationName===n.conversationName).slice(-t),c=N(i);return{ok:!0,operation:"read",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:n.workDir,helperTracePath:null,messages:i,attachments:c,count:i.length,reasonCode:null,message:`Read ${i.length} WeChat message(s).`}}async function Q(e,t){const n=X(t.attachment),a=E(e,t,{kind:"send",steps:["open-read",n],text:t.text,attachment:t.attachment}),r=await D(a),o=r.steps.find(i=>i.step===n);if(!r.ok||!o?.ok){const i=r.steps.find(u=>!u.ok&&!u.skipped),c=o?.reasonCode==="open_read_required"?i??o:o??i,d=c?.reasonCode||"wechat_direct_send_failed",l=c?.errorSummary||d;throw new Error(`${d}: ${l}`)}const s=j(o.details?.sendStatus)??"sent_unconfirmed";return{pending:s==="queued"||s==="pending"||s==="sent_unconfirmed",sendStatus:s,outDir:r.outDir,helperTracePath:r.helperTracePath}}function X(e){return e?e.kind==="image"?"send-image":e.kind==="video"?"send-video":"send-file":"send-text"}async function Y(e,t){const n=t.download!=="never",a=n?"download-visible-media":"structure-window",r=E(e,t,{kind:"read",steps:[a],allowEmptyMedia:n}),o=await D(r),s=o.steps.find(u=>u.step===a);if(!o.ok&&s?.reasonCode!=="no_observed_messages"){const u=s?.reasonCode||o.steps.find(m=>!m.ok&&!m.skipped)?.reasonCode||"wechat_direct_read_failed",h=s?.errorSummary||o.steps.find(m=>!m.ok&&!m.skipped)?.errorSummary||u;throw new Error(`${u}: ${h}`)}const i=y.join(o.outDir,n?"download-visible-messages.json":"structure-window-messages.json"),c=oe(i),d=re(e,t),l=ae(e);return{messages:c.map(u=>K(d,l,u)).filter(u=>u!=null),outDir:o.outDir,helperTracePath:o.helperTracePath}}function We(e){const t=e.command("wechat").description("Use WeChat through Shennian local runtime");t.option("--conversation <name>","WeChat conversation/group name"),t.command("doctor").description("Check local Shennian, pairing, entitlement, credits, and WeChat runtime readiness").option("--json","Output JSON",!1).action(async a=>{const r=await G();if(a.json)console.log(JSON.stringify(r,null,2));else for(const o of r.checks){const s=o.ok?"\u2713":o.status==="warning"?"!":"\u2717";console.log(`${s} ${o.id}: ${o.message}`)}r.ok||(process.exitCode=1)}),t.command("write").aliases(["send"]).description("Write a message to a local WeChat conversation through Shennian").argument("[text...]","Message text").option("--conversation <name>","WeChat conversation/group name").option("--text <text>","Message text").option("--file <path>","File attachment path").option("--image <path>","Image attachment path").option("--video <path>","Video attachment path").option("--session-id <id>","Bind to an existing Shennian chat/session; omit for direct local WeChat tool mode").option("--work-dir <path>","Local tool work dir; defaults under ~/.shennian/wechat-cli").option("--idempotency-key <key>","Idempotency key").option("--dry-run","Validate the request and output the planned write without sending",!1).option("--trace-id <id>","Trace id included in JSON output").option("--timeout <ms>","Manager IPC request timeout in milliseconds").option("--transport <mode>","Execution transport: auto, daemon, or direct","auto").option("--format <format>","Output format: json or text","text").option("--json","Output JSON",!1).action(async(a,r,o)=>{let s;try{s=A(o,r.conversation);const i=r.text??a.join(" "),c=fe(r),d=await P({conversation:s,text:i,attachment:c,sessionId:r.sessionId,workDir:r.workDir,idempotencyKey:r.idempotencyKey,dryRun:r.dryRun,traceId:r.traceId,timeoutMs:r.timeout?v(r.timeout,24e4):void 0,transport:U(r.transport)});ie(d,r.json||r.format==="json")}catch(i){O("write",s||r.conversation,i,r.json||r.format==="json")}}),t.command("read").aliases(["read-latest","read_latest","latest"]).description("Read recent messages from a local WeChat conversation through Shennian").argument("[count]","Number of latest messages","10").option("--conversation <name>","WeChat conversation/group name").option("--limit <n>","Number of latest messages").option("--format <format>","Output format: markdown or json","markdown").option("--download <mode>","Attachment download mode: auto or never","auto").option("--trace-id <id>","Trace id included in JSON output").option("--timeout <ms>","Manager IPC request timeout in milliseconds").option("--session-id <id>","Bind to an existing Shennian chat/session; omit for direct local WeChat tool mode").option("--work-dir <path>","Local tool work dir; defaults under ~/.shennian/wechat-cli").option("--transport <mode>","Execution transport: auto, daemon, or direct","auto").option("--json","Output JSON",!1).action(async(a,r,o)=>{let s,i="markdown";try{s=A(o,r.conversation),i=pe(r.json?"json":r.format);const c=await W({conversation:s,limit:v(r.limit??a,10),download:ge(r.download),traceId:r.traceId,timeoutMs:r.timeout?v(r.timeout,24e4):void 0,sessionId:r.sessionId,workDir:r.workDir,transport:U(r.transport)});ce(c,i)}catch(c){O("read",s||r.conversation,c,r.json||i==="json")}})}function C(e){return{ok:e.checks.every(t=>t.ok||t.status==="warning"),checks:e.checks,serverUrl:e.serverUrl,machineId:e.machineId}}function g(e,t){return{id:e,ok:!0,status:"ready",message:t}}function ee(e,t,n){return{id:e,ok:!1,status:"warning",reasonCode:t,message:n}}function S(e,t,n){return{id:e,ok:!1,status:"blocked",reasonCode:t,message:n}}function ne(e){return e.replace(/\/+$/,"")}function _(){const e=$("runtime","manager-ipc.json");let t;try{t=JSON.parse(x.readFileSync(e,"utf-8"))}catch{throw new Error("Manager IPC is not available. Open Shennian Desktop or run `shennian start` first.")}const n=Number(t.pid);if(Number.isInteger(n)&&n>0&&!ye(n))throw new Error(`Manager IPC runtime file is stale for PID ${n}. Restart Shennian Desktop or run \`shennian restart\`.`);const a=typeof t.url=="string"?t.url.replace(/\/+$/,""):"",r=typeof t.token=="string"?t.token:"";if(!a||!r)throw new Error(`Manager IPC runtime file is incomplete: ${e}`);return{url:a,token:r}}function Me(){try{return _()}catch{return null}}function M(e){if(e.directRuntime)return null;const t=e.transport??"auto";return t==="direct"?null:e.ipc?{ctx:e.ipc,mode:"session"}:e.sessionId?.trim()?{ctx:_(),mode:"session"}:t==="daemon"?{ctx:_(),mode:"tool"}:null}function R(e,t){if((e.transport??"auto")!=="auto"||e.sessionId?.trim()||e.ipc)return!1;const a=t instanceof Error?t.message:String(t||"");return/Unknown manager IPC path:\s*\/wechat-rpa\/tool\//i.test(a)}async function T(e,t,n){return w(e,"/wechat-rpa/channel/upsert",{managerSessionId:t.sessionId,id:t.channelId,name:`WeChat ${t.conversationName}`,workDir:t.workDir,enabled:!0,groups:[{name:t.conversationName}],canReply:!0,source:n.source||"wechat-channel",recentLimit:v(n.recentLimit,20),pollIntervalMs:v(n.pollIntervalMs,3e4),forceForeground:!0,downloadAttachments:n.download!=="never",privacyConsentAccepted:!0},n.fetchImpl,n.timeoutMs)}async function w(e,t,n,a=fetch,r){const o=String(n.managerSessionId||"");if(!o)throw new Error("managerSessionId is required");const s=r?new AbortController:null,i=s?setTimeout(()=>s.abort(),r):null;try{const c=await a(`${e.url}${t}`,{method:"POST",headers:{authorization:`Bearer ${e.token}`,"content-type":"application/json","x-shennian-manager-session-id":o},body:JSON.stringify(n),signal:s?.signal}),d=await c.json().catch(()=>({ok:!1,error:c.statusText}));if(!c.ok||!d.ok)throw new Error(d.error||`Manager IPC failed: ${c.status}`);return d}finally{i&&clearTimeout(i)}}function b(e){const t=e.conversation.trim();if(!t)throw new Error("--conversation is required");const n=q("wechat-cli-conversation",t),a=e.sessionId?.trim()||`wechat-cli-${n.slice(-24)}`,r=`wechat-rpa:${a}`;return{sessionId:a,channelId:r,conversationId:H(t),conversationName:t,workDir:y.resolve(e.workDir||$("wechat-cli",z(t)))}}async function te(e,t,n){try{return await w(e,"/wechat-rpa/channel/sync",{managerSessionId:t.sessionId},n.fetchImpl,n.timeoutMs)}catch(a){if(ke(a))return null;throw a}}function E(e,t,n){const a=t.traceId||`wechat-cli-${n.kind}-${Date.now().toString(36)}`,r=y.join(e.workDir,"runs",z(a)),o=n.attachment;return{conversation:e.conversationName,mode:"custom",steps:n.steps,outDir:r,workDir:e.workDir,traceId:a,runtimeId:e.channelId,machineId:process.env.SHENNIAN_MACHINE_ID||k().machineId||"local",sessionId:e.sessionId,serverUrl:k().serverUrl,platform:se(),text:n.text,...o?.localPath&&o.kind==="image"?{imagePath:o.localPath}:{},...o?.localPath&&o.kind==="video"?{videoPath:o.localPath}:{},...o?.localPath&&o.kind!=="image"&&o.kind!=="video"?{filePath:o.localPath}:{},allowEmptyMedia:n.allowEmptyMedia,skipRuntimeUpsert:!0,requireServer:!1}}function re(e,t){return{id:e.channelId,type:"wechat-rpa",name:`WeChat ${e.conversationName}`,sessionId:e.sessionId,managerSessionId:e.sessionId,workDir:e.workDir,enabled:!0,secretRef:`wechat-cli:${e.channelId}`}}function ae(e){return{bindingId:V(e.channelId,e.conversationName),sessionId:e.sessionId,conversationDisplayName:e.conversationName,enabled:!0,allowReply:!0,downloadMedia:!0}}function oe(e){try{const t=JSON.parse(x.readFileSync(e,"utf-8"));return Array.isArray(t)?t.filter(p):[]}catch{return[]}}function j(e){return e==="dry_run"||e==="queued"||e==="sent"||e==="sent_unconfirmed"||e==="confirmed_echo"||e==="manual_review"||e==="pending"||e==="ok"?e:null}function se(){if(process.platform==="darwin")return"darwin";if(process.platform==="win32")return"win32"}function A(e,t){const n=e.parent?.opts()??{},a=t||n.conversation||"";if(!a.trim())throw new Error("--conversation is required");return a}function ie(e,t){if(t){console.log(JSON.stringify(e,null,2));return}console.log(e.sendStatus)}function ce(e,t){if(t==="json"){console.log(JSON.stringify(e,null,2));return}console.log(le(e).trimEnd())}function O(e,t,n,a){const r=de(e,t,n);a?console.log(JSON.stringify(r,null,2)):console.error(`${r.reasonCode}: ${r.message}`),process.exitCode=1}function de(e,t,n){const a=n instanceof Error?n.message:String(n||"wechat_tool_failed"),r=I(a)||"wechat_tool_failed",o=/^([a-z][a-z0-9_]*)(?::\s*(.*))?$/i.exec(r),s=o?.[1]||"wechat_tool_failed",i=I(o?.[2]||r),c=i&&i!==s?i:s;return{ok:!1,operation:e,...t?.trim()?{conversation:t.trim()}:{},reasonCode:s,message:c}}function le(e){const t=[];if(e.messages.length===0)return`\uFF08\u6CA1\u6709\u8BFB\u5230\uFF09
5
- `;for(const n of e.messages)t.push(`${he(n)}: ${me(n)}`);return`${t.join(`
6
- `)}
7
- `}function ue(e,t,n){const a=p(e.channel)?e.channel:{},o=(Array.isArray(a.wechatRpaPendingReplies)?a.wechatRpaPendingReplies:[]).filter(p).find(d=>f(d.idempotencyKey)===t),s=f(o?.status);if(s==="confirmed_echo"||s==="sent_unconfirmed"||s==="manual_review"||s==="queued")return s;const c=(Array.isArray(a.wechatRpaRecentTaskSummaries)?a.wechatRpaRecentTaskSummaries:[]).filter(p).map(d=>f(d.status)).find(Boolean);return c==="sent"?"sent":c==="confirmed_echo"?"confirmed_echo":c==="manual_review"?"manual_review":n?"pending":"ok"}function me(e){const t=e.attachments||[],n=I(e.text);return n&&t.length===0?n:n&&t.length>0?`${n} ${t.map(L).join("\u3001")}`:t.length>0?t.map(L).join("\u3001"):"\uFF08\u6D88\u606F\uFF09"}function he(e){return I(e.sender.name||e.sender.id||"\u5BF9\u65B9")}function L(e){const t=I(e.name||y.basename(e.localPath||"")||e.type||"\u9644\u4EF6"),n=e.localPath||e.url||"",a=e.availability&&e.availability!=="edge-local"?` (${e.availability})`:"";return n?`[${ve(t)}](${Ie(n)})${a}`:`${t}${a}`}function fe(e){const t=[e.file?{kind:"file",path:e.file}:null,e.image?{kind:"image",path:e.image}:null,e.video?{kind:"video",path:e.video}:null].filter(n=>!!n);if(t.length>1)throw new Error("Pass only one of --file, --image, or --video");if(t.length!==0)return Z(t[0].path,t[0].kind)}function pe(e){const t=String(e||"markdown").trim().toLowerCase();if(t==="json")return"json";if(t==="markdown"||t==="md"||t==="text")return"markdown";throw new Error(`Unsupported --format: ${e}. Use markdown or json.`)}function ge(e){const t=String(e||"auto").trim().toLowerCase();if(t==="auto"||t==="never")return t;throw new Error(`Unsupported --download: ${e}. Use auto or never.`)}function U(e){const t=String(e||"auto").trim().toLowerCase();if(t==="auto"||t==="daemon"||t==="direct")return t;throw new Error(`Unsupported --transport: ${e}. Use auto, daemon, or direct.`)}function we(e){return{kind:e.kind,name:e.name,mimeType:e.mimeType,size:e.size,localPath:e.localPath}}function N(e){return e.flatMap(t=>t.attachments??[])}function Ie(e){const t=e.replace(/\\/g,"/");return/[\s()<>]/.test(t)?`<${t.replace(/[<>]/g,"")}>`:t}function ve(e){return I(e).replace(/([\\\]])/g,"\\$1")}function I(e){return String(e||"").replace(/\s+/g," ").trim()}function ye(e){try{return process.kill(e,0),!0}catch{return!1}}function ke(e){if(!e||typeof e!="object")return!1;const t=e,n=typeof t.name=="string"?t.name:"",a=typeof t.message=="string"?t.message:"";return n==="AbortError"||t.code==="ABORT_ERR"||/aborted|abort/i.test(a)}function q(e,t){return`${e}:${F.createHash("sha256").update(t).digest("hex").slice(0,32)}`}function z(e){return e.trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"conversation"}function v(e,t){const n=Number(e);return!Number.isFinite(n)||n<=0?t:Math.floor(n)}function B(e){return!!e&&typeof e=="object"&&!Array.isArray(e)&&e.type==="external.message"&&typeof e.channelId=="string"&&typeof e.conversationId=="string"&&typeof e.messageId=="string"&&typeof e.text=="string"}function p(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function f(e){return typeof e=="string"&&e?e:void 0}export{le as buildWeChatReadMarkdown,de as buildWeChatToolErrorResult,Y as readDirectWeChatLatestOnce,We as registerWeChatCommand,G as runWeChatDoctor,W as runWeChatReadLatest,P as runWeChatSend,Q as sendDirectWeChatMessageOnce};
1
+ import{registerWeChatCommand as e}from"./wechat/command.js";export*from"./wechat/direct.js";export*from"./wechat/doctor.js";export*from"./wechat/operations.js";export*from"./wechat/output.js";export*from"./wechat/types.js";export{e as registerWeChatCommand};