@moxxy/cli 0.0.12 → 0.1.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 (148) hide show
  1. package/README.md +278 -112
  2. package/bin/moxxy +10 -0
  3. package/package.json +36 -53
  4. package/src/api-client.js +286 -0
  5. package/src/cli.js +341 -0
  6. package/src/commands/agent.js +413 -0
  7. package/src/commands/auth.js +326 -0
  8. package/src/commands/channel.js +285 -0
  9. package/src/commands/doctor.js +261 -0
  10. package/src/commands/events.js +80 -0
  11. package/src/commands/gateway.js +428 -0
  12. package/src/commands/heartbeat.js +145 -0
  13. package/src/commands/init.js +767 -0
  14. package/src/commands/mcp.js +278 -0
  15. package/src/commands/plugin.js +583 -0
  16. package/src/commands/provider.js +1934 -0
  17. package/src/commands/skill.js +125 -0
  18. package/src/commands/template.js +237 -0
  19. package/src/commands/uninstall.js +196 -0
  20. package/src/commands/update.js +406 -0
  21. package/src/commands/vault.js +219 -0
  22. package/src/help.js +368 -0
  23. package/src/lib/plugin-registry.js +98 -0
  24. package/src/platform.js +40 -0
  25. package/src/sse-client.js +79 -0
  26. package/src/tui/action-wizards.js +130 -0
  27. package/src/tui/app.jsx +859 -0
  28. package/src/tui/components/action-picker.jsx +86 -0
  29. package/src/tui/components/chat-panel.jsx +120 -0
  30. package/src/tui/components/footer.jsx +13 -0
  31. package/src/tui/components/header.jsx +45 -0
  32. package/src/tui/components/input-area.jsx +384 -0
  33. package/src/tui/components/messages/ask-message.jsx +13 -0
  34. package/src/tui/components/messages/assistant-message.jsx +165 -0
  35. package/src/tui/components/messages/channel-message.jsx +18 -0
  36. package/src/tui/components/messages/event-message.jsx +22 -0
  37. package/src/tui/components/messages/hive-status.jsx +34 -0
  38. package/src/tui/components/messages/skill-message.jsx +31 -0
  39. package/src/tui/components/messages/system-message.jsx +12 -0
  40. package/src/tui/components/messages/thinking.jsx +25 -0
  41. package/src/tui/components/messages/tool-group.jsx +62 -0
  42. package/src/tui/components/messages/tool-message.jsx +66 -0
  43. package/src/tui/components/messages/user-message.jsx +12 -0
  44. package/src/tui/components/model-picker.jsx +138 -0
  45. package/src/tui/components/multiline-input.jsx +72 -0
  46. package/src/tui/events-handler.js +730 -0
  47. package/src/tui/helpers.js +59 -0
  48. package/src/tui/hooks/use-command-handler.js +451 -0
  49. package/src/tui/index.jsx +55 -0
  50. package/src/tui/input-utils.js +26 -0
  51. package/src/tui/markdown-renderer.js +66 -0
  52. package/src/tui/mcp-wizard.js +136 -0
  53. package/src/tui/model-picker.js +174 -0
  54. package/src/tui/slash-commands.js +26 -0
  55. package/src/tui/store.js +12 -0
  56. package/src/tui/theme.js +17 -0
  57. package/src/ui.js +109 -0
  58. package/bin/moxxy.js +0 -2
  59. package/dist/chunk-23LZYKQ6.mjs +0 -1131
  60. package/dist/chunk-2FZEA3NG.mjs +0 -457
  61. package/dist/chunk-3KDPLS22.mjs +0 -1131
  62. package/dist/chunk-3QRJTRBT.mjs +0 -1102
  63. package/dist/chunk-6DZX6EAA.mjs +0 -37
  64. package/dist/chunk-A4WRDUNY.mjs +0 -1242
  65. package/dist/chunk-C46NSEKG.mjs +0 -211
  66. package/dist/chunk-CAUXONEF.mjs +0 -1131
  67. package/dist/chunk-CPL5V56X.mjs +0 -1131
  68. package/dist/chunk-CTBVTTBG.mjs +0 -440
  69. package/dist/chunk-FHHLXTEZ.mjs +0 -1121
  70. package/dist/chunk-FXY3GPVA.mjs +0 -1126
  71. package/dist/chunk-GSNMMI3H.mjs +0 -530
  72. package/dist/chunk-HHOAOGUS.mjs +0 -1242
  73. package/dist/chunk-ITBO7BKI.mjs +0 -1243
  74. package/dist/chunk-J33O35WX.mjs +0 -532
  75. package/dist/chunk-N5JTPB6U.mjs +0 -820
  76. package/dist/chunk-NGVL4Q5C.mjs +0 -1102
  77. package/dist/chunk-Q2OCMNYI.mjs +0 -1131
  78. package/dist/chunk-QDVRLN6D.mjs +0 -1121
  79. package/dist/chunk-QO2JONHP.mjs +0 -1131
  80. package/dist/chunk-RVAPILHA.mjs +0 -1242
  81. package/dist/chunk-S7YBOV7E.mjs +0 -1131
  82. package/dist/chunk-SHIG6Y5L.mjs +0 -1074
  83. package/dist/chunk-SOFST2PV.mjs +0 -1242
  84. package/dist/chunk-SUNUYS6G.mjs +0 -1243
  85. package/dist/chunk-TMZWETMH.mjs +0 -1242
  86. package/dist/chunk-TYD7NMMI.mjs +0 -581
  87. package/dist/chunk-TYQ3YS42.mjs +0 -1068
  88. package/dist/chunk-UALWCJ7F.mjs +0 -1131
  89. package/dist/chunk-UQZKODNW.mjs +0 -1124
  90. package/dist/chunk-USC6R2ON.mjs +0 -1242
  91. package/dist/chunk-W32EQCVC.mjs +0 -823
  92. package/dist/chunk-WMB5ENMC.mjs +0 -1242
  93. package/dist/chunk-WNHA5JAP.mjs +0 -1242
  94. package/dist/cli-2AIWTL6F.mjs +0 -8
  95. package/dist/cli-2QKJ5UUL.mjs +0 -8
  96. package/dist/cli-4RIS6DQX.mjs +0 -8
  97. package/dist/cli-5RH4VBBL.mjs +0 -7
  98. package/dist/cli-7MK4YGOP.mjs +0 -7
  99. package/dist/cli-B4KH6MZI.mjs +0 -8
  100. package/dist/cli-CGO2LZ6Z.mjs +0 -8
  101. package/dist/cli-CVP26EL2.mjs +0 -8
  102. package/dist/cli-DDRVVNAV.mjs +0 -8
  103. package/dist/cli-E7U56QVQ.mjs +0 -8
  104. package/dist/cli-EQNRMLL3.mjs +0 -8
  105. package/dist/cli-F5RUHHH4.mjs +0 -8
  106. package/dist/cli-LX6FFSEF.mjs +0 -8
  107. package/dist/cli-LY74GWKR.mjs +0 -6
  108. package/dist/cli-MAT3ZJHI.mjs +0 -8
  109. package/dist/cli-NJXXTQYF.mjs +0 -8
  110. package/dist/cli-O4ZGFAZG.mjs +0 -8
  111. package/dist/cli-ORVLI3UQ.mjs +0 -8
  112. package/dist/cli-PV43ZVKA.mjs +0 -8
  113. package/dist/cli-REVD6ISM.mjs +0 -8
  114. package/dist/cli-TBX76KQX.mjs +0 -8
  115. package/dist/cli-THCGF7SQ.mjs +0 -8
  116. package/dist/cli-TLX5ENVM.mjs +0 -8
  117. package/dist/cli-TMNI5ZYE.mjs +0 -8
  118. package/dist/cli-TNJHCBQA.mjs +0 -6
  119. package/dist/cli-TUX22CZP.mjs +0 -8
  120. package/dist/cli-XJVH7EEP.mjs +0 -8
  121. package/dist/cli-XXOW4VXJ.mjs +0 -8
  122. package/dist/cli-XZ5RESNB.mjs +0 -6
  123. package/dist/cli-YCBYZ76Q.mjs +0 -8
  124. package/dist/cli-ZLMQCU7X.mjs +0 -8
  125. package/dist/dist-2VGKJRBH.mjs +0 -6820
  126. package/dist/dist-37BNX4QG.mjs +0 -7081
  127. package/dist/dist-7LTHRYKA.mjs +0 -11569
  128. package/dist/dist-7XJPQW5C.mjs +0 -6950
  129. package/dist/dist-AYMVOW7T.mjs +0 -7123
  130. package/dist/dist-BHUWCDRS.mjs +0 -7132
  131. package/dist/dist-FAXRJMEN.mjs +0 -6812
  132. package/dist/dist-HQGANM3P.mjs +0 -6976
  133. package/dist/dist-KATLOZQV.mjs +0 -7054
  134. package/dist/dist-KLSB6YHV.mjs +0 -6964
  135. package/dist/dist-LKIOZQ42.mjs +0 -17
  136. package/dist/dist-UYA4RJUH.mjs +0 -2792
  137. package/dist/dist-ZYHCBILM.mjs +0 -6993
  138. package/dist/index.d.mts +0 -23
  139. package/dist/index.d.ts +0 -23
  140. package/dist/index.js +0 -25531
  141. package/dist/index.mjs +0 -18
  142. package/dist/src-APP5P3UD.mjs +0 -1386
  143. package/dist/src-D5HMDDVE.mjs +0 -1324
  144. package/dist/src-EK3WD4AU.mjs +0 -1327
  145. package/dist/src-LSZFLMFN.mjs +0 -1400
  146. package/dist/src-T77DFTFP.mjs +0 -1407
  147. package/dist/src-WIOCZRAC.mjs +0 -1397
  148. package/dist/src-YK6CHCMW.mjs +0 -1400
package/src/help.js ADDED
@@ -0,0 +1,368 @@
1
+ export const COMMAND_HELP = {
2
+ init: `Usage: moxxy init
3
+
4
+ First-time setup wizard. Configures the Moxxy home directory, auth mode,
5
+ API token, and optional channel setup.
6
+
7
+ Steps:
8
+ 1. Creates ~/.moxxy directory structure
9
+ 2. Configures gateway URL
10
+ 3. Selects auth mode (token or loopback)
11
+ 4. Bootstraps an API token
12
+ 5. Optionally sets up a Telegram/Discord channel`,
13
+
14
+ auth: `Usage: moxxy auth token <action> [options]
15
+
16
+ Manage API tokens for gateway authentication.
17
+
18
+ Actions:
19
+ create Create a new API token
20
+ list List all tokens
21
+ revoke Revoke an existing token
22
+
23
+ Options:
24
+ --scopes <s> Comma-separated scopes (e.g. "*", "agents:read,runs:write")
25
+ --ttl <seconds> Token time-to-live in seconds (omit for no expiry)
26
+ --description <d> Optional token description
27
+ --json Output as JSON
28
+
29
+ Valid scopes:
30
+ * agents:read agents:write runs:write vault:read vault:write
31
+ tokens:admin events:read channels:read channels:write
32
+
33
+ Examples:
34
+ moxxy auth token create --scopes "*"
35
+ moxxy auth token create --scopes "agents:read,runs:write" --ttl 86400
36
+ moxxy auth token list --json
37
+ moxxy auth token revoke <token-id>`,
38
+
39
+ agent: `Usage: moxxy agent <action> [options]
40
+
41
+ Create and manage agents.
42
+
43
+ Actions:
44
+ create Provision a new agent
45
+ run Start a task run on an agent
46
+ stop Stop a running agent
47
+ status Check agent status
48
+ update Change provider, model, or temperature
49
+ delete Permanently remove an agent
50
+
51
+ Options:
52
+ --provider <id> Provider ID (e.g. openai, anthropic)
53
+ --model <id> Model ID (e.g. gpt-4o, claude-sonnet-4-20250514)
54
+ --name <name> Agent name (create only)
55
+ --persona <text> Agent persona/system prompt (create only)
56
+ --temperature <n> Sampling temperature (default: 0.7)
57
+ --id <name> Agent name (run/stop/status/update/delete)
58
+ --task <text> Task description (run only)
59
+ --policy <profile> Policy profile name (create only)
60
+ --json Output as JSON
61
+
62
+ Examples:
63
+ moxxy agent create --name my-agent --provider openai --model gpt-4o
64
+ moxxy agent run --id my-agent --task "Summarize the README"
65
+ moxxy agent status --id my-agent --json
66
+ moxxy agent stop --id my-agent
67
+ moxxy agent update --id my-agent --model gpt-4o-mini
68
+ moxxy agent delete --id my-agent`,
69
+
70
+ provider: `Usage: moxxy provider <action> [options]
71
+
72
+ Manage LLM providers.
73
+
74
+ Actions:
75
+ install Add a built-in or custom provider
76
+ login OAuth/subscription login (currently openai-codex)
77
+ list Show installed providers
78
+
79
+ Options:
80
+ --id <id> Provider ID for install (e.g. openai, anthropic, xai)
81
+ --method <m> Login method for OAuth providers: browser | headless
82
+ --no-browser Do not auto-open browser (print URL only)
83
+ --originator <id> Advanced: override OAuth originator (default: Codex Desktop)
84
+ --allowed_workspace_id <id> Advanced: constrain browser OAuth to a workspace/org id
85
+ --organization_id <id> Advanced: pass explicit organization id in OAuth URL
86
+ --project_id <id> Advanced: pass explicit project id in OAuth URL
87
+ --api_key <key> Fallback: manually provide OpenAI API key if OAuth issuance fails
88
+ (automatic fallback) If API-key issuance fails, uses ChatGPT OAuth session mode
89
+ --model <id> Custom model ID to add
90
+ --name <name> Display name (custom providers)
91
+ --api_base <url> API base URL (custom providers)
92
+ --json Output as JSON
93
+
94
+ Built-in providers:
95
+ anthropic Anthropic (Claude models)
96
+ openai OpenAI (GPT models)
97
+ openai-codex OpenAI subscription OAuth login
98
+ xai xAI (Grok models)
99
+ google Google (Gemini models)
100
+ deepseek DeepSeek
101
+
102
+ Examples:
103
+ moxxy provider list
104
+ moxxy provider install --id openai
105
+ moxxy provider login --id openai-codex --method browser
106
+ moxxy provider login --id openai-codex --method headless --no-browser
107
+ moxxy provider login --id openai-codex --method browser --api_key sk-...
108
+ moxxy provider install --id anthropic --model claude-sonnet-4-20250514`,
109
+
110
+ skill: `Usage: moxxy skill <action> [options]
111
+
112
+ Create and manage agent skills.
113
+
114
+ Actions:
115
+ create Create a skill on an agent
116
+ remove Remove a skill from an agent
117
+ list List skills for an agent
118
+
119
+ Options:
120
+ --agent <id> Agent ID
121
+ --skill <id> Skill ID (remove)
122
+ --content <c> Skill content/markdown (create)
123
+
124
+ Examples:
125
+ moxxy skill create --agent <id> --content "..."
126
+ moxxy skill list --agent <id>
127
+ moxxy skill remove --agent <id> --skill <skill-id>`,
128
+
129
+ template: `Usage: moxxy template <action> [options]
130
+
131
+ Manage agent templates (archetypes).
132
+
133
+ Actions:
134
+ list List all templates
135
+ get View template details
136
+ create Create a new template
137
+ update Update an existing template
138
+ remove Delete a template
139
+ assign Assign a template to an agent
140
+
141
+ Options:
142
+ --slug <slug> Template slug (get/update/remove)
143
+ --content <c> Template content (create/update)
144
+ --agent <id> Agent ID (assign)
145
+ --template <slug> Template slug (assign)
146
+
147
+ Examples:
148
+ moxxy template list
149
+ moxxy template get builder
150
+ moxxy template create --content "---\\nname: Custom\\n..."
151
+ moxxy template assign --agent my-agent --template builder
152
+ moxxy template remove builder`,
153
+
154
+ heartbeat: `Usage: moxxy heartbeat <action> [options]
155
+
156
+ Schedule recurring heartbeat rules for agents.
157
+
158
+ Actions:
159
+ set Configure a heartbeat rule
160
+ list Show heartbeat rules for an agent
161
+ disable Disable a heartbeat rule
162
+
163
+ Options:
164
+ --agent <id> Agent ID
165
+ --interval <min> Interval in minutes (default: 5)
166
+ --action_type <t> Action type: notify_cli, webhook, restart
167
+ --payload <data> Webhook URL or payload (webhook action_type)
168
+ --id <id> Heartbeat ID (disable)
169
+
170
+ Examples:
171
+ moxxy heartbeat set --agent <id> --interval 10 --action_type notify_cli
172
+ moxxy heartbeat set --agent <id> --interval 30 --action_type webhook --payload https://...
173
+ moxxy heartbeat list --agent <id>
174
+ moxxy heartbeat disable --agent <id> --id <heartbeat-id>`,
175
+
176
+ vault: `Usage: moxxy vault <action> [options]
177
+
178
+ Manage secrets and access grants.
179
+
180
+ Actions:
181
+ add Register a new secret reference
182
+ grant Grant an agent access to a secret
183
+ revoke Revoke an agent's secret access
184
+ list Show all secrets and grants
185
+
186
+ Options:
187
+ --key <name> Secret key name (e.g. OPENAI_API_KEY)
188
+ --backend <key> Backend key reference (e.g. env:OPENAI_API_KEY)
189
+ --label <label> Policy label (optional)
190
+ --agent <id> Agent ID (grant)
191
+ --secret <id> Secret ref ID (grant)
192
+ --id <id> Grant ID (revoke)
193
+
194
+ Examples:
195
+ moxxy vault add --key OPENAI_API_KEY --backend env:OPENAI_API_KEY
196
+ moxxy vault grant --agent <agent-id> --secret <secret-id>
197
+ moxxy vault list
198
+ moxxy vault revoke --id <grant-id>`,
199
+
200
+ channel: `Usage: moxxy channel <action> [options]
201
+
202
+ Manage messaging channels (Telegram, Discord).
203
+
204
+ Actions:
205
+ create Create a new channel
206
+ list List all channels
207
+ pair --code <code> --agent <id> Pair a chat to an agent
208
+ delete <id> Delete a channel
209
+ bindings <id> List bindings for a channel
210
+ unbind <channel-id> <binding-id> Unbind a chat
211
+
212
+ Options:
213
+ --code <code> 6-digit pairing code from the bot
214
+ --agent <id> Agent ID to bind
215
+ --json Output as JSON
216
+
217
+ Examples:
218
+ moxxy channel create
219
+ moxxy channel list
220
+ moxxy channel pair --code 123456 --agent <agent-id>
221
+ moxxy channel delete <channel-id>
222
+ moxxy channel bindings <channel-id>
223
+ moxxy channel unbind <channel-id> <binding-id>`,
224
+
225
+ events: `Usage: moxxy events tail [options]
226
+
227
+ Stream live events from the gateway via SSE.
228
+
229
+ Options:
230
+ --agent <id> Filter events by agent ID
231
+ --run <id> Filter events by run ID
232
+ --json Output raw JSON per event
233
+
234
+ Examples:
235
+ moxxy events tail
236
+ moxxy events tail --agent <agent-id>
237
+ moxxy events tail --agent <agent-id> --json`,
238
+
239
+ gateway: `Usage: moxxy gateway <action>
240
+
241
+ Manage the Moxxy gateway process.
242
+
243
+ Actions:
244
+ start Start the gateway (launchd on macOS, systemd on Linux, fallback elsewhere)
245
+ stop Stop the gateway
246
+ restart Restart the gateway
247
+ status Show gateway status and health check
248
+ logs Tail gateway log output
249
+
250
+ Examples:
251
+ moxxy gateway start
252
+ moxxy gateway status
253
+ moxxy gateway logs
254
+ moxxy gateway restart
255
+ moxxy gateway stop`,
256
+
257
+ doctor: `Usage: moxxy doctor
258
+
259
+ Diagnose the Moxxy installation. Checks:
260
+ - Moxxy home directory (~/.moxxy)
261
+ - Environment variables (MOXXY_TOKEN, MOXXY_API_URL)
262
+ - Gateway connectivity and health
263
+ - Authentication
264
+ - Installed providers and agents
265
+ - Bun runtime (>= 1.2.0), Rust toolchain, Git, Chrome
266
+ - Provider API keys`,
267
+
268
+ update: `Usage: moxxy update [options]
269
+
270
+ Check for and install updates to the gateway binary and CLI.
271
+
272
+ Options:
273
+ --check Check for updates without installing
274
+ --force Force update even if already up to date
275
+ --rollback Restore previous gateway binary from backup
276
+ --json Output as JSON
277
+
278
+ Examples:
279
+ moxxy update --check
280
+ moxxy update
281
+ moxxy update --force
282
+ moxxy update --rollback`,
283
+
284
+ uninstall: `Usage: moxxy uninstall
285
+
286
+ Remove all Moxxy data from the system. This includes:
287
+ - ~/.moxxy directory (database, agents, config)
288
+ - Stops the gateway if running
289
+
290
+ Does NOT remove the CLI package itself. To fully remove:
291
+ npm remove -g @moxxy/cli`,
292
+
293
+ mcp: `Usage: moxxy mcp <action> [options]
294
+
295
+ Manage MCP (Model Context Protocol) servers for agents.
296
+
297
+ Actions:
298
+ list List MCP servers for an agent
299
+ add Register a new MCP server
300
+ remove Remove an MCP server
301
+ test Test connectivity to an MCP server
302
+
303
+ Options:
304
+ --agent <name> Agent name
305
+ --id <id> MCP server ID
306
+ --transport <type> Transport type: stdio or sse (add only)
307
+ --command <cmd> Command to run (stdio transport)
308
+ --args <arg> Command arguments (stdio, repeatable)
309
+ --url <url> Server URL (sse transport)
310
+
311
+ Examples:
312
+ moxxy mcp list --agent my-agent
313
+ moxxy mcp add --agent my-agent --id fs-server --transport stdio --command npx --args -y --args @modelcontextprotocol/server-filesystem --args /tmp
314
+ moxxy mcp add --agent my-agent --id remote --transport sse --url http://localhost:8080/sse
315
+ moxxy mcp remove --agent my-agent --id fs-server
316
+ moxxy mcp test --agent my-agent --id fs-server`,
317
+
318
+ plugin: `Usage: moxxy plugin <action> [options]
319
+
320
+ Manage CLI plugins that extend Moxxy functionality.
321
+
322
+ Actions:
323
+ list List installed plugins
324
+ install <package> Install a plugin from npm
325
+ start <name> Start a plugin
326
+ stop <name> Stop a plugin
327
+ restart <name> Restart a plugin
328
+ update <name> Update a plugin to the latest version
329
+ enable <name> Enable auto-start
330
+ disable <name> Disable auto-start
331
+ uninstall <name> Remove a plugin
332
+ logs <name> Tail plugin logs
333
+
334
+ Built-in plugins:
335
+ @moxxy/web-plugin Web Dashboard (browser UI)
336
+ @moxxy/virtual-office-plugin Virtual Office
337
+
338
+ Examples:
339
+ moxxy plugin install @moxxy/web-plugin
340
+ moxxy plugin start @moxxy/web-plugin
341
+ moxxy plugin list
342
+ moxxy plugin logs @moxxy/web-plugin
343
+ moxxy plugin uninstall my-custom-plugin`,
344
+
345
+ tui: `Usage: moxxy tui [options]
346
+ moxxy chat [options]
347
+
348
+ Full-screen terminal chat interface.
349
+
350
+ Options:
351
+ --agent <id> Start with a specific agent pre-selected
352
+
353
+ Keyboard shortcuts:
354
+ Enter Send message
355
+ Ctrl+X Stop running agent
356
+ /help Show available slash commands
357
+ /exit Exit the TUI`,
358
+ };
359
+
360
+ /**
361
+ * Show styled help for a command using @clack/prompts.
362
+ * Falls back to console.log if p is not available.
363
+ */
364
+ export function showHelp(commandName, p) {
365
+ const text = COMMAND_HELP[commandName];
366
+ if (!text) return;
367
+ p.note(text, `moxxy ${commandName}`);
368
+ }
@@ -0,0 +1,98 @@
1
+ import { getMoxxyHome } from '../commands/init.js';
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+
5
+ const DEFAULT_REGISTRY = { version: 1, plugins: {} };
6
+
7
+ export function pluginPaths() {
8
+ const home = getMoxxyHome();
9
+ const pluginsDir = join(home, 'plugins');
10
+ const registryFile = join(pluginsDir, 'registry.json');
11
+ const logsDir = join(pluginsDir, 'logs');
12
+ const packageJsonFile = join(pluginsDir, 'package.json');
13
+ return { pluginsDir, registryFile, logsDir, packageJsonFile };
14
+ }
15
+
16
+ export function readRegistry() {
17
+ const { registryFile } = pluginPaths();
18
+ if (!existsSync(registryFile)) return { ...DEFAULT_REGISTRY, plugins: {} };
19
+ try {
20
+ return JSON.parse(readFileSync(registryFile, 'utf-8'));
21
+ } catch {
22
+ return { ...DEFAULT_REGISTRY, plugins: {} };
23
+ }
24
+ }
25
+
26
+ export function writeRegistry(registry) {
27
+ const { registryFile } = pluginPaths();
28
+ writeFileSync(registryFile, JSON.stringify(registry, null, 2));
29
+ }
30
+
31
+ export function ensurePluginsDir() {
32
+ const { pluginsDir, logsDir, packageJsonFile } = pluginPaths();
33
+ mkdirSync(pluginsDir, { recursive: true });
34
+ mkdirSync(logsDir, { recursive: true });
35
+ if (!existsSync(packageJsonFile)) {
36
+ writeFileSync(packageJsonFile, JSON.stringify({ private: true, dependencies: {} }, null, 2));
37
+ }
38
+ }
39
+
40
+ export function readPluginMeta(pluginName) {
41
+ const { pluginsDir } = pluginPaths();
42
+ const pkgPath = join(pluginsDir, 'node_modules', pluginName, 'package.json');
43
+ if (!existsSync(pkgPath)) return null;
44
+ try {
45
+ return JSON.parse(readFileSync(pkgPath, 'utf-8'));
46
+ } catch {
47
+ return null;
48
+ }
49
+ }
50
+
51
+ export function validatePluginMeta(meta) {
52
+ const errors = [];
53
+ if (!meta) {
54
+ return { valid: false, errors: ['Plugin package.json not found'] };
55
+ }
56
+ if (!meta.moxxy || meta.moxxy.type !== 'plugin') {
57
+ errors.push('Missing or invalid moxxy.type (must be "plugin")');
58
+ }
59
+ if (!meta.scripts || !meta.scripts['plugin:start']) {
60
+ errors.push('Missing required script "plugin:start"');
61
+ }
62
+ return { valid: errors.length === 0, errors };
63
+ }
64
+
65
+ export function isProcessAlive(pid) {
66
+ try {
67
+ process.kill(pid, 0);
68
+ return true;
69
+ } catch {
70
+ return false;
71
+ }
72
+ }
73
+
74
+ export function sanitizeLogFileName(pluginName) {
75
+ return pluginName.replace(/\//g, '--') + '.log';
76
+ }
77
+
78
+ export function buildPluginEnv(pluginName, port) {
79
+ const apiUrl = process.env.MOXXY_API_URL || 'http://localhost:3000';
80
+ const token = process.env.MOXXY_TOKEN || '';
81
+ return {
82
+ ...process.env,
83
+ MOXXY_API_URL: apiUrl,
84
+ MOXXY_TOKEN: token,
85
+ MOXXY_PLUGIN_NAME: pluginName,
86
+ MOXXY_PLUGIN_PORT: port ? String(port) : '',
87
+ MOXXY_HOME: getMoxxyHome(),
88
+ PORT: port ? String(port) : '',
89
+ // Vite exposes only VITE_-prefixed vars to browser code
90
+ VITE_MOXXY_API_URL: apiUrl,
91
+ VITE_MOXXY_TOKEN: token,
92
+ };
93
+ }
94
+
95
+ export const BUILTIN_PLUGINS = [
96
+ { name: '@moxxy/web-plugin', label: 'Web Dashboard', hint: 'browser-based dashboard', defaultPort: 17900 },
97
+ { name: '@moxxy/virtual-office-plugin', label: 'Virtual Office', hint: 'virtual office environment', defaultPort: 17901 },
98
+ ];
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Platform-aware helpers for shell profile instructions.
3
+ */
4
+ import { platform } from 'node:os';
5
+
6
+ /**
7
+ * Returns a shell instruction to export an environment variable.
8
+ * - Unix: `export NAME="value"`
9
+ * - Windows: `[Environment]::SetEnvironmentVariable("NAME", "value", "User")`
10
+ */
11
+ export function shellExportInstruction(name, value) {
12
+ if (platform() === 'win32') {
13
+ return `[Environment]::SetEnvironmentVariable("${name}", "${value}", "User")`;
14
+ }
15
+ return `export ${name}="${value}"`;
16
+ }
17
+
18
+ /**
19
+ * Returns a shell instruction to unset an environment variable.
20
+ * - Unix: `unset NAME`
21
+ * - Windows: `[Environment]::SetEnvironmentVariable("NAME", $null, "User")`
22
+ */
23
+ export function shellUnsetInstruction(name) {
24
+ if (platform() === 'win32') {
25
+ return `[Environment]::SetEnvironmentVariable("${name}", $null, "User")`;
26
+ }
27
+ return `unset ${name}`;
28
+ }
29
+
30
+ /**
31
+ * Returns the name of the shell profile file for the current platform.
32
+ * - Unix: `~/.zshrc or ~/.bashrc`
33
+ * - Windows: `PowerShell $PROFILE`
34
+ */
35
+ export function shellProfileName() {
36
+ if (platform() === 'win32') {
37
+ return 'PowerShell $PROFILE';
38
+ }
39
+ return '~/.zshrc or ~/.bashrc';
40
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * SSE stream reader for event tailing.
3
+ * Uses native fetch with streaming reader.
4
+ */
5
+
6
+ export function buildSseUrl(baseUrl, filters) {
7
+ const url = new URL('/v1/events/stream', baseUrl);
8
+ for (const [k, v] of Object.entries(filters)) {
9
+ if (v) url.searchParams.set(k, v);
10
+ }
11
+ return url.toString();
12
+ }
13
+
14
+ export function parseSseEvent(line) {
15
+ if (line.startsWith('data: ')) {
16
+ return JSON.parse(line.slice(6));
17
+ }
18
+ return null;
19
+ }
20
+
21
+ export function createSseClient(baseUrl, token, filters) {
22
+ const url = buildSseUrl(baseUrl, filters);
23
+ let controller = null;
24
+
25
+ return {
26
+ url,
27
+ token,
28
+
29
+ reconnect() {
30
+ if (controller) {
31
+ controller.abort();
32
+ }
33
+ controller = new AbortController();
34
+ return controller;
35
+ },
36
+
37
+ async *stream() {
38
+ controller = new AbortController();
39
+ const headers = {};
40
+ if (token) {
41
+ headers['authorization'] = `Bearer ${token}`;
42
+ }
43
+ const resp = await fetch(url, {
44
+ headers,
45
+ signal: controller.signal,
46
+ });
47
+
48
+ if (!resp.ok) {
49
+ throw new Error(`SSE connection failed: HTTP ${resp.status}`);
50
+ }
51
+
52
+ const reader = resp.body.getReader();
53
+ const decoder = new TextDecoder();
54
+ let buffer = '';
55
+
56
+ try {
57
+ while (true) {
58
+ const { done, value } = await reader.read();
59
+ if (done) break;
60
+
61
+ buffer += decoder.decode(value, { stream: true });
62
+ const lines = buffer.split('\n');
63
+ buffer = lines.pop() || '';
64
+
65
+ for (const line of lines) {
66
+ const event = parseSseEvent(line);
67
+ if (event) yield event;
68
+ }
69
+ }
70
+ } finally {
71
+ reader.releaseLock();
72
+ }
73
+ },
74
+
75
+ disconnect() {
76
+ controller?.abort();
77
+ },
78
+ };
79
+ }