@foxden-app/foxclaw 0.2.0

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 (126) hide show
  1. package/.env.example +36 -0
  2. package/LICENSE +22 -0
  3. package/README.md +244 -0
  4. package/README_EN.md +244 -0
  5. package/dist/channels/bridge_messaging_router.d.ts +27 -0
  6. package/dist/channels/bridge_messaging_router.js +85 -0
  7. package/dist/channels/telegram/telegram_channel_adapter.d.ts +12 -0
  8. package/dist/channels/telegram/telegram_channel_adapter.js +21 -0
  9. package/dist/channels/telegram/telegram_messaging_port.d.ts +25 -0
  10. package/dist/channels/telegram/telegram_messaging_port.js +51 -0
  11. package/dist/channels/weixin/account_store.d.ts +15 -0
  12. package/dist/channels/weixin/account_store.js +54 -0
  13. package/dist/channels/weixin/ilink/aes_ecb.d.ts +3 -0
  14. package/dist/channels/weixin/ilink/aes_ecb.js +12 -0
  15. package/dist/channels/weixin/ilink/api.d.ts +44 -0
  16. package/dist/channels/weixin/ilink/api.js +187 -0
  17. package/dist/channels/weixin/ilink/cdn_upload.d.ts +11 -0
  18. package/dist/channels/weixin/ilink/cdn_upload.js +60 -0
  19. package/dist/channels/weixin/ilink/cdn_url.d.ts +7 -0
  20. package/dist/channels/weixin/ilink/cdn_url.js +7 -0
  21. package/dist/channels/weixin/ilink/constants.d.ts +7 -0
  22. package/dist/channels/weixin/ilink/constants.js +27 -0
  23. package/dist/channels/weixin/ilink/context.d.ts +13 -0
  24. package/dist/channels/weixin/ilink/context.js +13 -0
  25. package/dist/channels/weixin/ilink/login_qr.d.ts +34 -0
  26. package/dist/channels/weixin/ilink/login_qr.js +233 -0
  27. package/dist/channels/weixin/ilink/media_image.d.ts +11 -0
  28. package/dist/channels/weixin/ilink/media_image.js +44 -0
  29. package/dist/channels/weixin/ilink/mime.d.ts +3 -0
  30. package/dist/channels/weixin/ilink/mime.js +36 -0
  31. package/dist/channels/weixin/ilink/pic_decrypt.d.ts +2 -0
  32. package/dist/channels/weixin/ilink/pic_decrypt.js +56 -0
  33. package/dist/channels/weixin/ilink/random.d.ts +2 -0
  34. package/dist/channels/weixin/ilink/random.js +7 -0
  35. package/dist/channels/weixin/ilink/redact.d.ts +4 -0
  36. package/dist/channels/weixin/ilink/redact.js +34 -0
  37. package/dist/channels/weixin/ilink/runtime_attach.d.ts +3 -0
  38. package/dist/channels/weixin/ilink/runtime_attach.js +13 -0
  39. package/dist/channels/weixin/ilink/send.d.ts +21 -0
  40. package/dist/channels/weixin/ilink/send.js +108 -0
  41. package/dist/channels/weixin/ilink/session_guard.d.ts +6 -0
  42. package/dist/channels/weixin/ilink/session_guard.js +39 -0
  43. package/dist/channels/weixin/ilink/types.d.ts +155 -0
  44. package/dist/channels/weixin/ilink/types.js +10 -0
  45. package/dist/channels/weixin/ilink/upload.d.ts +15 -0
  46. package/dist/channels/weixin/ilink/upload.js +75 -0
  47. package/dist/channels/weixin/sync_buf_store.d.ts +3 -0
  48. package/dist/channels/weixin/sync_buf_store.js +19 -0
  49. package/dist/channels/weixin/weixin_channel_adapter.d.ts +18 -0
  50. package/dist/channels/weixin/weixin_channel_adapter.js +273 -0
  51. package/dist/channels/weixin/weixin_messaging_port.d.ts +29 -0
  52. package/dist/channels/weixin/weixin_messaging_port.js +113 -0
  53. package/dist/codex_app/client.d.ts +176 -0
  54. package/dist/codex_app/client.js +1230 -0
  55. package/dist/codex_app/deeplink.d.ts +7 -0
  56. package/dist/codex_app/deeplink.js +29 -0
  57. package/dist/codex_app/local_usage.d.ts +16 -0
  58. package/dist/codex_app/local_usage.js +123 -0
  59. package/dist/config.d.ts +44 -0
  60. package/dist/config.js +131 -0
  61. package/dist/controller/access.d.ts +11 -0
  62. package/dist/controller/access.js +33 -0
  63. package/dist/controller/activity.d.ts +62 -0
  64. package/dist/controller/activity.js +330 -0
  65. package/dist/controller/commands.d.ts +6 -0
  66. package/dist/controller/commands.js +17 -0
  67. package/dist/controller/controller.d.ts +326 -0
  68. package/dist/controller/controller.js +7503 -0
  69. package/dist/controller/observer.d.ts +16 -0
  70. package/dist/controller/observer.js +98 -0
  71. package/dist/controller/presentation.d.ts +80 -0
  72. package/dist/controller/presentation.js +568 -0
  73. package/dist/controller/service_tier.d.ts +9 -0
  74. package/dist/controller/service_tier.js +32 -0
  75. package/dist/controller/session_observer.d.ts +22 -0
  76. package/dist/controller/session_observer.js +259 -0
  77. package/dist/controller/status.d.ts +10 -0
  78. package/dist/controller/status.js +28 -0
  79. package/dist/core/bridge_scope.d.ts +18 -0
  80. package/dist/core/bridge_scope.js +46 -0
  81. package/dist/core/channel_port.d.ts +15 -0
  82. package/dist/core/channel_port.js +1 -0
  83. package/dist/i18n.d.ts +1108 -0
  84. package/dist/i18n.js +1154 -0
  85. package/dist/lock.d.ts +7 -0
  86. package/dist/lock.js +80 -0
  87. package/dist/logger.d.ts +12 -0
  88. package/dist/logger.js +57 -0
  89. package/dist/main.d.ts +2 -0
  90. package/dist/main.js +236 -0
  91. package/dist/runtime.d.ts +3 -0
  92. package/dist/runtime.js +14 -0
  93. package/dist/store/database.d.ts +79 -0
  94. package/dist/store/database.js +489 -0
  95. package/dist/store/migrate_bridge_scope.d.ts +6 -0
  96. package/dist/store/migrate_bridge_scope.js +59 -0
  97. package/dist/telegram/addressing.d.ts +33 -0
  98. package/dist/telegram/addressing.js +57 -0
  99. package/dist/telegram/api.d.ts +14 -0
  100. package/dist/telegram/api.js +89 -0
  101. package/dist/telegram/gateway.d.ts +76 -0
  102. package/dist/telegram/gateway.js +383 -0
  103. package/dist/telegram/media.d.ts +34 -0
  104. package/dist/telegram/media.js +180 -0
  105. package/dist/telegram/rendering.d.ts +10 -0
  106. package/dist/telegram/rendering.js +21 -0
  107. package/dist/telegram/scope.d.ts +6 -0
  108. package/dist/telegram/scope.js +24 -0
  109. package/dist/telegram/text.d.ts +7 -0
  110. package/dist/telegram/text.js +47 -0
  111. package/dist/types.d.ts +343 -0
  112. package/dist/types.js +1 -0
  113. package/docs/agent-assisted-install.md +84 -0
  114. package/docs/install-for-beginners.md +287 -0
  115. package/docs/troubleshooting.md +239 -0
  116. package/package.json +62 -0
  117. package/scripts/doctor.sh +3 -0
  118. package/scripts/launchd/install.sh +54 -0
  119. package/scripts/status.sh +3 -0
  120. package/scripts/systemd/install.sh +83 -0
  121. package/scripts/systemd/uninstall.sh +15 -0
  122. package/skills/foxclaw/SKILL.md +167 -0
  123. package/skills/foxclaw/agents/openai.yaml +4 -0
  124. package/skills/foxclaw/references/telegram-setup.md +93 -0
  125. package/skills/foxclaw/scripts/bootstrap_host.py +350 -0
  126. package/skills/foxclaw/scripts/bootstrap_remote.py +67 -0
@@ -0,0 +1,113 @@
1
+ import { parseWeixinBridgeScope } from '../../core/bridge_scope.js';
2
+ import { getConfig, sendTyping } from './ilink/api.js';
3
+ import { sendMessageWeixin } from './ilink/send.js';
4
+ import { TypingStatus } from './ilink/types.js';
5
+ const DEFAULT_TYPING_API = { getConfig, sendTyping };
6
+ function stripHtmlBasic(html) {
7
+ return html
8
+ .replace(/<br\s*\/?>/gi, '\n')
9
+ .replace(/<\/p>/gi, '\n')
10
+ .replace(/<[^>]+>/g, '')
11
+ .replace(/&nbsp;/g, ' ')
12
+ .replace(/&amp;/g, '&')
13
+ .replace(/&lt;/g, '<')
14
+ .replace(/&gt;/g, '>')
15
+ .trim();
16
+ }
17
+ /**
18
+ * Weixin (iLink) outbound: addressed by `weixin:<accountId>:<peerUserId>` scope ids.
19
+ * Editing/deleting Telegram messages is approximated as new plain text sends or no-ops.
20
+ */
21
+ export class WeixinMessagingPort {
22
+ store;
23
+ loadAccount;
24
+ typingApi;
25
+ nextSyntheticId = 1;
26
+ constructor(store, loadAccount, typingApi = DEFAULT_TYPING_API) {
27
+ this.store = store;
28
+ this.loadAccount = loadAccount;
29
+ this.typingApi = typingApi;
30
+ }
31
+ allocMessageId() {
32
+ const id = this.nextSyntheticId;
33
+ this.nextSyntheticId += 1;
34
+ return id;
35
+ }
36
+ async sendPlain(scopeId, text, keyboard) {
37
+ void keyboard;
38
+ const parsed = parseWeixinBridgeScope(scopeId);
39
+ if (!parsed) {
40
+ throw new Error(`Invalid weixin scope: ${scopeId}`);
41
+ }
42
+ const account = this.loadAccount(parsed.accountId);
43
+ if (!account) {
44
+ throw new Error(`Weixin account not found: ${parsed.accountId}`);
45
+ }
46
+ const contextToken = this.store.getWeixinContextToken(scopeId);
47
+ await sendMessageWeixin({
48
+ to: parsed.fromUserId,
49
+ text,
50
+ opts: {
51
+ baseUrl: account.baseUrl,
52
+ token: account.botToken,
53
+ ...(contextToken !== null && contextToken !== '' ? { contextToken } : {}),
54
+ },
55
+ });
56
+ return this.allocMessageId();
57
+ }
58
+ async sendHtml(scopeId, html, keyboard) {
59
+ return this.sendPlain(scopeId, stripHtmlBasic(html), keyboard);
60
+ }
61
+ async editPlain(scopeId, _messageId, text, keyboard) {
62
+ await this.sendPlain(scopeId, text, keyboard);
63
+ }
64
+ async editHtml(scopeId, messageId, html, keyboard) {
65
+ await this.sendHtml(scopeId, html, keyboard);
66
+ }
67
+ async deleteMessage(scopeId, messageId) {
68
+ void scopeId;
69
+ void messageId;
70
+ }
71
+ async sendTypingInScope(scopeId) {
72
+ const parsed = parseWeixinBridgeScope(scopeId);
73
+ if (!parsed) {
74
+ return;
75
+ }
76
+ const account = this.loadAccount(parsed.accountId);
77
+ if (!account) {
78
+ return;
79
+ }
80
+ const contextToken = this.store.getWeixinContextToken(scopeId);
81
+ try {
82
+ const config = await this.typingApi.getConfig({
83
+ baseUrl: account.baseUrl,
84
+ token: account.botToken,
85
+ ilinkUserId: parsed.fromUserId,
86
+ ...(contextToken !== null && contextToken !== '' ? { contextToken } : {}),
87
+ });
88
+ const typingTicket = config.typing_ticket?.trim();
89
+ if (!typingTicket) {
90
+ return;
91
+ }
92
+ await this.typingApi.sendTyping({
93
+ baseUrl: account.baseUrl,
94
+ token: account.botToken,
95
+ body: {
96
+ ilink_user_id: parsed.fromUserId,
97
+ typing_ticket: typingTicket,
98
+ status: TypingStatus.TYPING,
99
+ },
100
+ });
101
+ }
102
+ catch {
103
+ // Typing is a best-effort hint; never block the actual reply path.
104
+ }
105
+ }
106
+ async clearInlineKeyboard(scopeId, messageId) {
107
+ void scopeId;
108
+ void messageId;
109
+ }
110
+ async sendDraft(scopeId, _draftId, text) {
111
+ await this.sendPlain(scopeId, text);
112
+ }
113
+ }
@@ -0,0 +1,176 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import type { Logger } from '../logger.js';
3
+ import type { AppThread, AppThreadSnapshot, AppTurnSnapshot, CodexAccountInfo, CodexAccountRateLimits, CodexAppInfo, CodexCollaborationMode, CodexCollaborationModePreset, CodexConfigRequirements, CodexEffectiveConfig, CodexExperimentalFeature, CodexFuzzyFileResult, CodexHooksListEntry, CodexLoginDeviceCode, CodexMcpResourceContent, CodexMcpServerStatus, CodexModelProviderCapabilities, CodexPluginDetail, CodexPluginMarketplace, CodexSkillsListEntry, CodexThreadGoal, ModelInfo, ReasoningEffortValue, ReviewTarget, SandboxModeValue, ThreadSessionState, ThreadGoalStatusValue } from '../types.js';
4
+ export interface JsonRpcNotification {
5
+ method: string;
6
+ params?: any;
7
+ }
8
+ export interface JsonRpcServerRequest {
9
+ id: string | number;
10
+ method: string;
11
+ params?: any;
12
+ }
13
+ interface ListThreadsOptions {
14
+ limit: number;
15
+ searchTerm?: string | null;
16
+ archived?: boolean;
17
+ }
18
+ interface StartThreadOptions {
19
+ cwd: string | null;
20
+ approvalPolicy: string;
21
+ sandboxMode: SandboxModeValue;
22
+ model: string | null;
23
+ }
24
+ interface ResumeThreadOptions {
25
+ threadId: string;
26
+ cwd?: string | null;
27
+ approvalPolicy?: string | null;
28
+ sandboxMode?: SandboxModeValue | null;
29
+ model?: string | null;
30
+ }
31
+ export interface TextTurnInput {
32
+ type: 'text';
33
+ text: string;
34
+ text_elements: [];
35
+ }
36
+ export interface LocalImageTurnInput {
37
+ type: 'localImage';
38
+ path: string;
39
+ }
40
+ export type TurnInput = TextTurnInput | LocalImageTurnInput;
41
+ interface StartTurnOptions {
42
+ threadId: string;
43
+ input: TurnInput[];
44
+ approvalPolicy: string;
45
+ sandboxMode: SandboxModeValue;
46
+ cwd: string | null;
47
+ model: string | null;
48
+ effort: ReasoningEffortValue | null;
49
+ serviceTier?: string | null | undefined;
50
+ collaborationMode: CodexCollaborationMode | null;
51
+ }
52
+ interface CodexAppServerRuntimeStatus {
53
+ pid: number | null;
54
+ port: number | null;
55
+ running: boolean;
56
+ managed: boolean;
57
+ }
58
+ interface StopOptions {
59
+ terminateServer?: boolean;
60
+ }
61
+ export declare class CodexAppClient extends EventEmitter {
62
+ private readonly codexCliBin;
63
+ private readonly launchCommand;
64
+ private readonly autolaunch;
65
+ private readonly serverStatePath;
66
+ private readonly serverLogPath;
67
+ private readonly logger;
68
+ private child;
69
+ private socket;
70
+ private requestId;
71
+ private pending;
72
+ private desiredRunning;
73
+ private reconnectTimer;
74
+ private starting;
75
+ private port;
76
+ private connected;
77
+ private userAgent;
78
+ constructor(codexCliBin: string, launchCommand: string, autolaunch: boolean, serverStatePath: string, serverLogPath: string, logger: Logger);
79
+ isConnected(): boolean;
80
+ getUserAgent(): string | null;
81
+ getServerStatus(): CodexAppServerRuntimeStatus;
82
+ start(): Promise<void>;
83
+ stop(options?: StopOptions): Promise<void>;
84
+ restart(): Promise<void>;
85
+ listThreads(options: ListThreadsOptions): Promise<AppThread[]>;
86
+ listLoadedThreads(): Promise<string[]>;
87
+ readThread(threadId: string, includeTurns?: boolean): Promise<AppThread | null>;
88
+ readThreadSnapshot(threadId: string): Promise<AppThreadSnapshot | null>;
89
+ startThread(options: StartThreadOptions): Promise<ThreadSessionState>;
90
+ resumeThread(options: ResumeThreadOptions): Promise<ThreadSessionState>;
91
+ startTurn(options: StartTurnOptions): Promise<{
92
+ id: string;
93
+ status: string;
94
+ }>;
95
+ steerTurn(threadId: string, expectedTurnId: string, input: TurnInput[]): Promise<{
96
+ turnId: string;
97
+ }>;
98
+ forkThread(options: {
99
+ threadId: string;
100
+ cwd: string | null;
101
+ approvalPolicy: string;
102
+ sandboxMode: SandboxModeValue;
103
+ model: string | null;
104
+ serviceTier?: string | null;
105
+ }): Promise<ThreadSessionState>;
106
+ rollbackThread(threadId: string, numTurns: number): Promise<AppThreadSnapshot | null>;
107
+ setThreadName(threadId: string, name: string): Promise<void>;
108
+ compactThread(threadId: string): Promise<void>;
109
+ getThreadGoal(threadId: string): Promise<CodexThreadGoal | null>;
110
+ setThreadGoal(options: {
111
+ threadId: string;
112
+ objective?: string | null;
113
+ status?: ThreadGoalStatusValue | null;
114
+ tokenBudget?: number | null | undefined;
115
+ }): Promise<CodexThreadGoal>;
116
+ clearThreadGoal(threadId: string): Promise<boolean>;
117
+ listThreadTurns(threadId: string, limit?: number): Promise<AppTurnSnapshot[]>;
118
+ archiveThread(threadId: string): Promise<void>;
119
+ unarchiveThread(threadId: string): Promise<AppThread | null>;
120
+ startReview(threadId: string, target: ReviewTarget, delivery?: 'inline' | 'detached'): Promise<{
121
+ turnId: string;
122
+ reviewThreadId: string;
123
+ }>;
124
+ listModels(): Promise<ModelInfo[]>;
125
+ readAccount(): Promise<CodexAccountInfo | null>;
126
+ startDeviceLogin(): Promise<CodexLoginDeviceCode>;
127
+ cancelLogin(loginId: string): Promise<void>;
128
+ logoutAccount(): Promise<void>;
129
+ sendAddCreditsNudgeEmail(creditType: 'credits' | 'usage_limit'): Promise<void>;
130
+ readAccountRateLimits(): Promise<CodexAccountRateLimits | null>;
131
+ readEffectiveConfig(cwd: string | null): Promise<CodexEffectiveConfig>;
132
+ listCollaborationModes(): Promise<CodexCollaborationModePreset[]>;
133
+ listSkills(cwd: string | null, forceReload?: boolean): Promise<CodexSkillsListEntry[]>;
134
+ writeSkillConfig(selector: {
135
+ name?: string | null;
136
+ path?: string | null;
137
+ }, enabled: boolean): Promise<void>;
138
+ listHooks(cwd: string | null): Promise<CodexHooksListEntry[]>;
139
+ listPlugins(cwd: string | null): Promise<CodexPluginMarketplace[]>;
140
+ readPlugin(pluginName: string, options?: {
141
+ marketplacePath?: string | null;
142
+ remoteMarketplaceName?: string | null;
143
+ }): Promise<CodexPluginDetail | null>;
144
+ readPluginSkill(remoteMarketplaceName: string, remotePluginId: string, skillName: string): Promise<string | null>;
145
+ listApps(threadId?: string | null, forceRefetch?: boolean): Promise<CodexAppInfo[]>;
146
+ readConfig(cwd: string | null, includeLayers?: boolean): Promise<Record<string, unknown>>;
147
+ readConfigRequirements(): Promise<CodexConfigRequirements | null>;
148
+ listExperimentalFeatures(): Promise<CodexExperimentalFeature[]>;
149
+ readModelProviderCapabilities(): Promise<CodexModelProviderCapabilities>;
150
+ fuzzyFileSearch(query: string, roots: string[]): Promise<CodexFuzzyFileResult[]>;
151
+ listMcpServerStatus(detail?: 'full' | 'toolsAndAuthOnly'): Promise<CodexMcpServerStatus[]>;
152
+ reloadMcpServers(): Promise<void>;
153
+ loginMcpServer(name: string): Promise<string>;
154
+ readMcpResource(server: string, uri: string, threadId?: string | null): Promise<CodexMcpResourceContent[]>;
155
+ interruptTurn(threadId: string, turnId: string): Promise<void>;
156
+ revealThread(threadId: string): Promise<void>;
157
+ respond(requestId: string | number, result: unknown): Promise<void>;
158
+ respondError(requestId: string | number, message: string): Promise<void>;
159
+ private startServer;
160
+ private attachPersistedServer;
161
+ private connectWebSocket;
162
+ private initialize;
163
+ private request;
164
+ private send;
165
+ private handleMessage;
166
+ private handleDisconnect;
167
+ private rejectPending;
168
+ private scheduleReconnect;
169
+ private terminateServer;
170
+ private openServerLogFiles;
171
+ private readServerState;
172
+ private writeServerState;
173
+ private clearServerState;
174
+ private clearServerStateForPid;
175
+ }
176
+ export {};