@symerian/symi 3.0.17 → 3.0.19

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 (259) hide show
  1. package/dist/{audio-preflight-CBDFctZN.js → audio-preflight-BfmZbg4Y.js} +4 -4
  2. package/dist/{audio-preflight-gsZSpG-6.js → audio-preflight-DcuC-liM.js} +4 -4
  3. package/dist/build-info.json +3 -3
  4. package/dist/bundled/boot-md/handler.js +8 -8
  5. package/dist/bundled/session-memory/handler.js +7 -7
  6. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  7. package/dist/{chrome-nPMY1XTJ.js → chrome-Bo7cbvFK.js} +5 -5
  8. package/dist/{chrome-BjVab8gM.js → chrome-DYp18Q0t.js} +5 -5
  9. package/dist/{deliver-D-QFqm31.js → deliver-ChSIbiMM.js} +1 -1
  10. package/dist/{deliver-B4-bcot9.js → deliver-DEgRQM4J.js} +1 -1
  11. package/dist/extensionAPI.js +7 -7
  12. package/dist/{image-CDwtQjmt.js → image-Bx-hvoNJ.js} +1 -1
  13. package/dist/{image-CcS-vzTA.js → image-CQl_mjWk.js} +1 -1
  14. package/dist/llm-slug-generator.js +7 -7
  15. package/dist/{manager-BnEdHzmO.js → manager-D_pn0urG.js} +1 -1
  16. package/dist/{manager-09r0qPze.js → manager-YQxK2t0C.js} +1 -1
  17. package/dist/{pi-embedded-CWsY69-4.js → pi-embedded-CLw_ZzEZ.js} +16 -16
  18. package/dist/{pi-embedded-helpers-BBMy-lqr.js → pi-embedded-helpers-B5I53aw6.js} +4 -4
  19. package/dist/{pi-embedded-helpers-ChEYbgVj.js → pi-embedded-helpers-sUAEIC9X.js} +4 -4
  20. package/dist/plugin-sdk/{accounts-BfyWsC_i.js → accounts-CWFytwbR.js} +3 -3
  21. package/dist/plugin-sdk/{active-listener-DcJW7xAT.js → active-listener-BkZ4jHrL.js} +2 -2
  22. package/dist/plugin-sdk/{agent-scope-ChbGV6of.js → agent-scope-C9gfY_Gk.js} +2 -2
  23. package/dist/plugin-sdk/{audio-preflight-D3GtNLqW.js → audio-preflight-HKbdzXLZ.js} +21 -21
  24. package/dist/plugin-sdk/{bindings-CN2Qmefj.js → bindings-BaKIqPPy.js} +2 -2
  25. package/dist/plugin-sdk/{channel-web-DTyqujjA.js → channel-web-D5nWiTH1.js} +18 -18
  26. package/dist/plugin-sdk/{chrome-BKzAKr3K.js → chrome-klTSnz-9.js} +3 -3
  27. package/dist/plugin-sdk/{chunk-DhDkBujV.js → chunk-BbrYSny_.js} +1 -1
  28. package/dist/plugin-sdk/{command-format-CVrYFyZS.js → command-format-BN6tyZt6.js} +1 -1
  29. package/dist/plugin-sdk/{commands-registry-17yfZkHZ.js → commands-registry-CTzKKtY6.js} +4 -4
  30. package/dist/plugin-sdk/{config-7wk65zKC.js → config-Crv2qEdJ.js} +9 -9
  31. package/dist/plugin-sdk/{consolidate-exbAW0ml.js → consolidate-DT1QH65Q.js} +2 -2
  32. package/dist/plugin-sdk/{deliver-TxAcw7J5.js → deliver-7rOvAlrc.js} +12 -12
  33. package/dist/plugin-sdk/{diagnostic-Debx4frd.js → diagnostic-0nsxhWp7.js} +1 -1
  34. package/dist/plugin-sdk/{fs-safe-wBYbAkJF.js → fs-safe-DfWYBeWF.js} +1 -1
  35. package/dist/plugin-sdk/{gemini-auth-7U2pm2Ky.js → gemini-auth-C0N0_u49.js} +1 -1
  36. package/dist/plugin-sdk/{image-BtDVmYA5.js → image-WOSl2apK.js} +4 -4
  37. package/dist/plugin-sdk/index.js +43 -43
  38. package/dist/plugin-sdk/{ir-CKMvRrGW.js → ir-9J84MTls.js} +4 -4
  39. package/dist/plugin-sdk/{local-roots-c_gaPs01.js → local-roots-OLRDbvyY.js} +3 -3
  40. package/dist/plugin-sdk/{login-DUym1Jy0.js → login-C7x4q0i2.js} +7 -7
  41. package/dist/plugin-sdk/{login-qr-B-WBdvrX.js → login-qr-Dv5_MoAW.js} +9 -9
  42. package/dist/plugin-sdk/{manager-B71SCzos.js → manager-C83tK17x.js} +8 -8
  43. package/dist/plugin-sdk/{manifest-registry-Dnic6Chh.js → manifest-registry-CJMV-PI7.js} +1 -1
  44. package/dist/plugin-sdk/{markdown-tables-Dur7OTlM.js → markdown-tables-DXNKz5y_.js} +1 -1
  45. package/dist/plugin-sdk/{message-channel-BrAhJJV_.js → message-channel-aGy1HbQQ.js} +1 -1
  46. package/dist/plugin-sdk/{model-selection-B9qaVQSJ.js → model-selection-C-3-tpe7.js} +4 -4
  47. package/dist/plugin-sdk/{outbound-DB1wDM8b.js → outbound-DquCeSy5.js} +6 -6
  48. package/dist/plugin-sdk/{pi-auth-json-ZO118hoy.js → pi-auth-json-D9PDCXGn.js} +1 -1
  49. package/dist/plugin-sdk/{pi-embedded-helpers-s_U0Un7j.js → pi-embedded-helpers-D3ygfH7l.js} +16 -16
  50. package/dist/plugin-sdk/{plugins-DF81oSaI.js → plugins-DOwnSg9D.js} +4 -4
  51. package/dist/plugin-sdk/{pw-ai-CTwP02uv.js → pw-ai-rlengLjb.js} +8 -8
  52. package/dist/plugin-sdk/{qmd-manager-CBaSGant.js → qmd-manager-BzxFjRFa.js} +4 -4
  53. package/dist/plugin-sdk/{registry-CZVURNhF.js → registry-5iFfixlB.js} +2 -2
  54. package/dist/plugin-sdk/{replies-hwRbkU3z.js → replies-BXOzO_H5.js} +7 -7
  55. package/dist/plugin-sdk/{reply-prefix-CaXmzZlx.js → reply-prefix-INAKTqCU.js} +1 -1
  56. package/dist/plugin-sdk/{resolve-outbound-target-fxVSOBmk.js → resolve-outbound-target-DvbxHtqp.js} +2 -2
  57. package/dist/plugin-sdk/{resolve-route-ClCyiOeu.js → resolve-route-URXlY3AK.js} +3 -3
  58. package/dist/plugin-sdk/{runner-Cq5jvwQ7.js → runner-Bv0_DWoH.js} +9 -9
  59. package/dist/plugin-sdk/{session-B_TkB65Y.js → session-C3r8l7ou.js} +4 -4
  60. package/dist/plugin-sdk/{skill-commands-0LF9HTGr.js → skill-commands-KjLUGIdZ.js} +5 -5
  61. package/dist/plugin-sdk/{skills-BIT_O7J0.js → skills-BrsD4L5c.js} +7 -7
  62. package/dist/plugin-sdk/{sqlite-Bx5Y5U5X.js → sqlite-CjW7ME1H.js} +1 -1
  63. package/dist/plugin-sdk/{subsystem-CXqYeDy-.js → subsystem-DcOg1xJr.js} +1 -1
  64. package/dist/plugin-sdk/{synthesis-DtsYAj1E.js → synthesis-CY7YAasV.js} +38 -38
  65. package/dist/plugin-sdk/{target-errors-B8mokOeH.js → target-errors-BVWJGWFq.js} +2 -2
  66. package/dist/plugin-sdk/{thinking-Ca0DhqzO.js → thinking-CtsTDPOi.js} +3 -3
  67. package/dist/plugin-sdk/{tokens-CvlONEqh.js → tokens-8lqOTZCB.js} +1 -1
  68. package/dist/plugin-sdk/{tool-images-DpBaWEHT.js → tool-images-Cl_rGIUZ.js} +2 -2
  69. package/dist/plugin-sdk/{tool-loop-detection-BOvUFa0f.js → tool-loop-detection-Da4WUT_P.js} +2 -2
  70. package/dist/plugin-sdk/{unified-runner-CnM7lyNd.js → unified-runner-nwMnsZyj.js} +60 -60
  71. package/dist/plugin-sdk/web-BlweOZDp.js +54 -0
  72. package/dist/plugin-sdk/{whatsapp-actions-CvnfsFJm.js → whatsapp-actions-DpfaGYs7.js} +21 -21
  73. package/dist/{pw-ai-BW8_KeDf.js → pw-ai-BqxJG-Wh.js} +1 -1
  74. package/dist/{pw-ai-j9IE1K0-.js → pw-ai-C-NSGye0.js} +1 -1
  75. package/dist/{runner-8ALr2UII.js → runner-COGFTeDw.js} +1 -1
  76. package/dist/{runner-C4-9kFdR.js → runner-DhCi2lT1.js} +1 -1
  77. package/dist/{synthesis-Cph3LhA1.js → synthesis-CXZu24Vx.js} +7 -7
  78. package/dist/{synthesis-Cus0A2dL.js → synthesis-DrPxcMlQ.js} +7 -7
  79. package/dist/{unified-runner-CX80YMTk.js → unified-runner-iByUazvW.js} +16 -16
  80. package/dist/{web-ChozvJ7I.js → web-EsMQBIYf.js} +7 -7
  81. package/dist/{web-DFlsbXmQ.js → web-PPg5y6xI.js} +7 -7
  82. package/package.json +1 -1
  83. package/dist/plugin-sdk/web-CIPJBHAU.js +0 -54
  84. package/extensions/copilot-proxy/README.md +0 -24
  85. package/extensions/copilot-proxy/index.ts +0 -154
  86. package/extensions/copilot-proxy/node_modules/.bin/symi +0 -21
  87. package/extensions/copilot-proxy/package.json +0 -15
  88. package/extensions/copilot-proxy/symi.plugin.json +0 -9
  89. package/extensions/device-pair/index.ts +0 -642
  90. package/extensions/device-pair/symi.plugin.json +0 -20
  91. package/extensions/diagnostics-otel/index.ts +0 -15
  92. package/extensions/diagnostics-otel/node_modules/.bin/acorn +0 -21
  93. package/extensions/diagnostics-otel/node_modules/.bin/symi +0 -21
  94. package/extensions/diagnostics-otel/package.json +0 -27
  95. package/extensions/diagnostics-otel/src/service.test.ts +0 -290
  96. package/extensions/diagnostics-otel/src/service.ts +0 -666
  97. package/extensions/diagnostics-otel/symi.plugin.json +0 -8
  98. package/extensions/google-antigravity-auth/README.md +0 -24
  99. package/extensions/google-antigravity-auth/index.ts +0 -424
  100. package/extensions/google-antigravity-auth/node_modules/.bin/symi +0 -21
  101. package/extensions/google-antigravity-auth/package.json +0 -15
  102. package/extensions/google-antigravity-auth/symi.plugin.json +0 -9
  103. package/extensions/google-gemini-cli-auth/README.md +0 -35
  104. package/extensions/google-gemini-cli-auth/index.ts +0 -75
  105. package/extensions/google-gemini-cli-auth/node_modules/.bin/symi +0 -21
  106. package/extensions/google-gemini-cli-auth/oauth.test.ts +0 -162
  107. package/extensions/google-gemini-cli-auth/oauth.ts +0 -636
  108. package/extensions/google-gemini-cli-auth/package.json +0 -15
  109. package/extensions/google-gemini-cli-auth/symi.plugin.json +0 -9
  110. package/extensions/learning-loop/index.ts +0 -159
  111. package/extensions/learning-loop/node_modules/.bin/symi +0 -21
  112. package/extensions/learning-loop/package.json +0 -18
  113. package/extensions/learning-loop/src/analytics/gateway-methods.ts +0 -230
  114. package/extensions/learning-loop/src/analytics/metrics-aggregator.ts +0 -153
  115. package/extensions/learning-loop/src/capture/run-tracker.ts +0 -181
  116. package/extensions/learning-loop/src/capture/serializer.ts +0 -74
  117. package/extensions/learning-loop/src/db.ts +0 -583
  118. package/extensions/learning-loop/src/feedback/explicit-feedback.ts +0 -58
  119. package/extensions/learning-loop/src/feedback/implicit-signals.ts +0 -89
  120. package/extensions/learning-loop/src/graph/edge-inference.ts +0 -189
  121. package/extensions/learning-loop/src/graph/graph-retrieval.ts +0 -144
  122. package/extensions/learning-loop/src/graph/graph-store.ts +0 -183
  123. package/extensions/learning-loop/src/hooks.ts +0 -244
  124. package/extensions/learning-loop/src/injection/cache.ts +0 -73
  125. package/extensions/learning-loop/src/injection/context-injector.ts +0 -104
  126. package/extensions/learning-loop/src/injection/prompt-builder.ts +0 -43
  127. package/extensions/learning-loop/src/learning/embedding-bridge.ts +0 -54
  128. package/extensions/learning-loop/src/learning/learning-extractor.ts +0 -217
  129. package/extensions/learning-loop/src/learning/learning-store.ts +0 -158
  130. package/extensions/learning-loop/src/learning/retrieval.ts +0 -87
  131. package/extensions/learning-loop/src/math/confidence-intervals.ts +0 -62
  132. package/extensions/learning-loop/src/math/ewma.ts +0 -51
  133. package/extensions/learning-loop/src/math/weighted-scorer.ts +0 -42
  134. package/extensions/learning-loop/src/schema.ts +0 -176
  135. package/extensions/learning-loop/src/scoring/normalization.ts +0 -32
  136. package/extensions/learning-loop/src/scoring/quality-engine.ts +0 -78
  137. package/extensions/learning-loop/src/scoring/signal-extractors.ts +0 -155
  138. package/extensions/learning-loop/src/test/context-injector.test.ts +0 -142
  139. package/extensions/learning-loop/src/test/fixes.test.ts +0 -1286
  140. package/extensions/learning-loop/src/test/graph.test.ts +0 -711
  141. package/extensions/learning-loop/src/test/integration.test.ts +0 -312
  142. package/extensions/learning-loop/src/test/learning-store.test.ts +0 -191
  143. package/extensions/learning-loop/src/test/math.test.ts +0 -148
  144. package/extensions/learning-loop/src/test/quality-engine.test.ts +0 -231
  145. package/extensions/learning-loop/src/test/run-tracker.test.ts +0 -143
  146. package/extensions/learning-loop/src/types.ts +0 -281
  147. package/extensions/learning-loop/symi.plugin.json +0 -46
  148. package/extensions/llm-task/README.md +0 -97
  149. package/extensions/llm-task/index.ts +0 -6
  150. package/extensions/llm-task/package.json +0 -12
  151. package/extensions/llm-task/src/llm-task-tool.test.ts +0 -138
  152. package/extensions/llm-task/src/llm-task-tool.ts +0 -249
  153. package/extensions/llm-task/symi.plugin.json +0 -21
  154. package/extensions/memory-lancedb/config.ts +0 -161
  155. package/extensions/memory-lancedb/index.test.ts +0 -330
  156. package/extensions/memory-lancedb/index.ts +0 -670
  157. package/extensions/memory-lancedb/node_modules/.bin/arrow2csv +0 -21
  158. package/extensions/memory-lancedb/node_modules/.bin/openai +0 -21
  159. package/extensions/memory-lancedb/node_modules/.bin/symi +0 -21
  160. package/extensions/memory-lancedb/package.json +0 -20
  161. package/extensions/memory-lancedb/symi.plugin.json +0 -71
  162. package/extensions/minimax-portal-auth/README.md +0 -33
  163. package/extensions/minimax-portal-auth/index.ts +0 -161
  164. package/extensions/minimax-portal-auth/node_modules/.bin/symi +0 -21
  165. package/extensions/minimax-portal-auth/oauth.ts +0 -247
  166. package/extensions/minimax-portal-auth/package.json +0 -15
  167. package/extensions/minimax-portal-auth/symi.plugin.json +0 -9
  168. package/extensions/model-equalizer/index.ts +0 -80
  169. package/extensions/model-equalizer/skills/model-equalizer/SKILL.md +0 -58
  170. package/extensions/model-equalizer/src/detection.ts +0 -62
  171. package/extensions/model-equalizer/src/enhancer.ts +0 -63
  172. package/extensions/model-equalizer/src/test/detection.test.ts +0 -218
  173. package/extensions/model-equalizer/src/test/enhancer.test.ts +0 -137
  174. package/extensions/model-equalizer/src/test/integration.test.ts +0 -185
  175. package/extensions/model-equalizer/src/types.ts +0 -24
  176. package/extensions/model-equalizer/symi.plugin.json +0 -12
  177. package/extensions/phone-control/index.ts +0 -421
  178. package/extensions/phone-control/symi.plugin.json +0 -10
  179. package/extensions/pipeline/README.md +0 -75
  180. package/extensions/pipeline/SKILL.md +0 -97
  181. package/extensions/pipeline/index.ts +0 -18
  182. package/extensions/pipeline/package.json +0 -11
  183. package/extensions/pipeline/src/pipeline-tool.test.ts +0 -345
  184. package/extensions/pipeline/src/pipeline-tool.ts +0 -266
  185. package/extensions/pipeline/src/windows-spawn.test.ts +0 -148
  186. package/extensions/pipeline/src/windows-spawn.ts +0 -193
  187. package/extensions/pipeline/symi.plugin.json +0 -10
  188. package/extensions/qwen-portal-auth/README.md +0 -24
  189. package/extensions/qwen-portal-auth/index.ts +0 -134
  190. package/extensions/qwen-portal-auth/oauth.ts +0 -190
  191. package/extensions/qwen-portal-auth/symi.plugin.json +0 -9
  192. package/extensions/talk-voice/index.ts +0 -150
  193. package/extensions/talk-voice/symi.plugin.json +0 -10
  194. package/extensions/thread-ownership/index.test.ts +0 -180
  195. package/extensions/thread-ownership/index.ts +0 -133
  196. package/extensions/thread-ownership/symi.plugin.json +0 -28
  197. package/skills/1password/SKILL.md +0 -71
  198. package/skills/1password/references/cli-examples.md +0 -29
  199. package/skills/1password/references/get-started.md +0 -17
  200. package/skills/apple-notes/SKILL.md +0 -78
  201. package/skills/apple-reminders/SKILL.md +0 -119
  202. package/skills/bear-notes/SKILL.md +0 -108
  203. package/skills/blogwatcher/SKILL.md +0 -70
  204. package/skills/blucli/SKILL.md +0 -48
  205. package/skills/bluebubbles/SKILL.md +0 -132
  206. package/skills/camsnap/SKILL.md +0 -46
  207. package/skills/canvas/SKILL.md +0 -204
  208. package/skills/connect-email/SKILL.md +0 -142
  209. package/skills/document-generation/SKILL.md +0 -83
  210. package/skills/eightctl/SKILL.md +0 -51
  211. package/skills/food-order/SKILL.md +0 -49
  212. package/skills/gemini/SKILL.md +0 -44
  213. package/skills/gh-issues/SKILL.md +0 -865
  214. package/skills/gifgrep/SKILL.md +0 -80
  215. package/skills/github/SKILL.md +0 -164
  216. package/skills/gog/SKILL.md +0 -117
  217. package/skills/goplaces/SKILL.md +0 -53
  218. package/skills/healthcheck/SKILL.md +0 -246
  219. package/skills/himalaya/SKILL.md +0 -258
  220. package/skills/himalaya/references/configuration.md +0 -184
  221. package/skills/himalaya/references/message-composition.md +0 -199
  222. package/skills/imsg/SKILL.md +0 -122
  223. package/skills/long-task/SKILL.md +0 -58
  224. package/skills/long-task/scripts/detach-task.sh +0 -187
  225. package/skills/nano-banana-pro/SKILL.md +0 -59
  226. package/skills/nano-banana-pro/scripts/generate_image.py +0 -184
  227. package/skills/nano-pdf/SKILL.md +0 -39
  228. package/skills/notion/SKILL.md +0 -173
  229. package/skills/obsidian/SKILL.md +0 -82
  230. package/skills/openai-image-gen/SKILL.md +0 -90
  231. package/skills/openai-image-gen/scripts/gen.py +0 -240
  232. package/skills/openai-whisper/SKILL.md +0 -39
  233. package/skills/openai-whisper-api/SKILL.md +0 -53
  234. package/skills/openai-whisper-api/scripts/transcribe.sh +0 -85
  235. package/skills/openhue/SKILL.md +0 -113
  236. package/skills/oracle/SKILL.md +0 -126
  237. package/skills/ordercli/SKILL.md +0 -79
  238. package/skills/peekaboo/SKILL.md +0 -191
  239. package/skills/reactions-extensive/SKILL.md +0 -30
  240. package/skills/reactions-minimal/SKILL.md +0 -31
  241. package/skills/safe-edit/SKILL.md +0 -51
  242. package/skills/sag/SKILL.md +0 -88
  243. package/skills/sherpa-onnx-tts/SKILL.md +0 -104
  244. package/skills/sherpa-onnx-tts/bin/sherpa-onnx-tts +0 -178
  245. package/skills/songsee/SKILL.md +0 -50
  246. package/skills/sonoscli/SKILL.md +0 -66
  247. package/skills/spotify-player/SKILL.md +0 -65
  248. package/skills/symihub/SKILL.md +0 -78
  249. package/skills/things-mac/SKILL.md +0 -87
  250. package/skills/tmux/SKILL.md +0 -153
  251. package/skills/tmux/scripts/find-sessions.sh +0 -112
  252. package/skills/tmux/scripts/wait-for-text.sh +0 -83
  253. package/skills/trello/SKILL.md +0 -96
  254. package/skills/video-frames/SKILL.md +0 -47
  255. package/skills/video-frames/scripts/frame.sh +0 -81
  256. package/skills/voice-call/SKILL.md +0 -46
  257. package/skills/wacli/SKILL.md +0 -73
  258. package/skills/weather/SKILL.md +0 -113
  259. package/skills/xurl/SKILL.md +0 -462
@@ -1,48 +0,0 @@
1
- ---
2
- name: blucli
3
- description: BluOS CLI (blu) for discovery, playback, grouping, and volume.
4
- homepage: https://blucli.sh
5
- metadata:
6
- {
7
- "symi":
8
- {
9
- "emoji": "🫐",
10
- "requires": { "bins": ["blu"] },
11
- "install":
12
- [
13
- {
14
- "id": "go",
15
- "kind": "go",
16
- "module": "github.com/steipete/blucli/cmd/blu@latest",
17
- "bins": ["blu"],
18
- "label": "Install blucli (go)",
19
- },
20
- ],
21
- },
22
- }
23
- triggers: [blucli]
24
- ---
25
-
26
- # blucli (blu)
27
-
28
- Use `blu` to control Bluesound/NAD players.
29
-
30
- Quick start
31
-
32
- - `blu devices` (pick target)
33
- - `blu --device <id> status`
34
- - `blu play|pause|stop`
35
- - `blu volume set 15`
36
-
37
- Target selection (in priority order)
38
-
39
- - `--device <id|name|alias>`
40
- - `BLU_DEVICE`
41
- - config default (if set)
42
-
43
- Common tasks
44
-
45
- - Grouping: `blu group status|add|remove`
46
- - TuneIn search/play: `blu tunein search "query"`, `blu tunein play "query"`
47
-
48
- Prefer `--json` for scripts. Confirm the target device before changing playback.
@@ -1,132 +0,0 @@
1
- ---
2
- name: bluebubbles
3
- description: Use when you need to send or manage iMessages via BlueBubbles (recommended iMessage integration). Calls go through the generic message tool with channel="bluebubbles".
4
- metadata: { "symi": { "emoji": "🫧", "requires": { "config": ["channels.bluebubbles"] } } }
5
- triggers: [bluebubbles]
6
- ---
7
-
8
- # BlueBubbles Actions
9
-
10
- ## Overview
11
-
12
- BlueBubbles is Symi’s recommended iMessage integration. Use the `message` tool with `channel: "bluebubbles"` to send messages and manage iMessage conversations: send texts and attachments, react (tapbacks), edit/unsend, reply in threads, and manage group participants/names/icons.
13
-
14
- ## Inputs to collect
15
-
16
- - `target` (prefer `chat_guid:...`; also `+15551234567` in E.164 or `user@example.com`)
17
- - `message` text for send/edit/reply
18
- - `messageId` for react/edit/unsend/reply
19
- - Attachment `path` for local files, or `buffer` + `filename` for base64
20
-
21
- If the user is vague ("text my mom"), ask for the recipient handle or chat guid and the exact message content.
22
-
23
- ## Actions
24
-
25
- ### Send a message
26
-
27
- ```json
28
- {
29
- "action": "send",
30
- "channel": "bluebubbles",
31
- "target": "+15551234567",
32
- "message": "hello from Symi"
33
- }
34
- ```
35
-
36
- ### React (tapback)
37
-
38
- ```json
39
- {
40
- "action": "react",
41
- "channel": "bluebubbles",
42
- "target": "+15551234567",
43
- "messageId": "<message-guid>",
44
- "emoji": "❤️"
45
- }
46
- ```
47
-
48
- ### Remove a reaction
49
-
50
- ```json
51
- {
52
- "action": "react",
53
- "channel": "bluebubbles",
54
- "target": "+15551234567",
55
- "messageId": "<message-guid>",
56
- "emoji": "❤️",
57
- "remove": true
58
- }
59
- ```
60
-
61
- ### Edit a previously sent message
62
-
63
- ```json
64
- {
65
- "action": "edit",
66
- "channel": "bluebubbles",
67
- "target": "+15551234567",
68
- "messageId": "<message-guid>",
69
- "message": "updated text"
70
- }
71
- ```
72
-
73
- ### Unsend a message
74
-
75
- ```json
76
- {
77
- "action": "unsend",
78
- "channel": "bluebubbles",
79
- "target": "+15551234567",
80
- "messageId": "<message-guid>"
81
- }
82
- ```
83
-
84
- ### Reply to a specific message
85
-
86
- ```json
87
- {
88
- "action": "reply",
89
- "channel": "bluebubbles",
90
- "target": "+15551234567",
91
- "replyTo": "<message-guid>",
92
- "message": "replying to that"
93
- }
94
- ```
95
-
96
- ### Send an attachment
97
-
98
- ```json
99
- {
100
- "action": "sendAttachment",
101
- "channel": "bluebubbles",
102
- "target": "+15551234567",
103
- "path": "/tmp/photo.jpg",
104
- "caption": "here you go"
105
- }
106
- ```
107
-
108
- ### Send with an iMessage effect
109
-
110
- ```json
111
- {
112
- "action": "sendWithEffect",
113
- "channel": "bluebubbles",
114
- "target": "+15551234567",
115
- "message": "big news",
116
- "effect": "balloons"
117
- }
118
- ```
119
-
120
- ## Notes
121
-
122
- - Requires gateway config `channels.bluebubbles` (serverUrl/password/webhookPath).
123
- - Prefer `chat_guid` targets when you have them (especially for group chats).
124
- - BlueBubbles supports rich actions, but some are macOS-version dependent (for example, edit may be broken on macOS 26 Tahoe).
125
- - The gateway may expose both short and full message ids; full ids are more durable across restarts.
126
- - Developer reference for the underlying plugin lives in `extensions/bluebubbles/README.md`.
127
-
128
- ## Ideas to try
129
-
130
- - React with a tapback to acknowledge a request.
131
- - Reply in-thread when a user references a specific message.
132
- - Send a file attachment with a short caption.
@@ -1,46 +0,0 @@
1
- ---
2
- name: camsnap
3
- description: Capture frames or clips from RTSP/ONVIF cameras.
4
- homepage: https://camsnap.ai
5
- metadata:
6
- {
7
- "symi":
8
- {
9
- "emoji": "📸",
10
- "requires": { "bins": ["camsnap"] },
11
- "install":
12
- [
13
- {
14
- "id": "brew",
15
- "kind": "brew",
16
- "formula": "steipete/tap/camsnap",
17
- "bins": ["camsnap"],
18
- "label": "Install camsnap (brew)",
19
- },
20
- ],
21
- },
22
- }
23
- triggers: [camsnap]
24
- ---
25
-
26
- # camsnap
27
-
28
- Use `camsnap` to grab snapshots, clips, or motion events from configured cameras.
29
-
30
- Setup
31
-
32
- - Config file: `~/.config/camsnap/config.yaml`
33
- - Add camera: `camsnap add --name kitchen --host 192.168.0.10 --user user --pass pass`
34
-
35
- Common commands
36
-
37
- - Discover: `camsnap discover --info`
38
- - Snapshot: `camsnap snap kitchen --out shot.jpg`
39
- - Clip: `camsnap clip kitchen --dur 5s --out clip.mp4`
40
- - Motion watch: `camsnap watch kitchen --threshold 0.2 --action '...'`
41
- - Doctor: `camsnap doctor --probe`
42
-
43
- Notes
44
-
45
- - Requires `ffmpeg` on PATH.
46
- - Prefer a short test capture before longer clips.
@@ -1,204 +0,0 @@
1
- ---
2
- name: canvas
3
- description: Display HTML content on connected Symi nodes (Mac app, iOS, Android). Use for games, visualizations, dashboards, generated HTML, and interactive demos.
4
- triggers: [canvas, display, html, visualize, dashboard, demo]
5
- ---
6
-
7
- # Canvas Skill
8
-
9
- Display HTML content on connected Symi nodes (Mac app, iOS, Android).
10
-
11
- ## Overview
12
-
13
- The canvas tool lets you present web content on any connected node's canvas view. Great for:
14
-
15
- - Displaying games, visualizations, dashboards
16
- - Showing generated HTML content
17
- - Interactive demos
18
-
19
- ## How It Works
20
-
21
- ### Architecture
22
-
23
- ```
24
- ┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
25
- │ Canvas Host │────▶│ Node Bridge │────▶│ Node App │
26
- │ (HTTP Server) │ │ (TCP Server) │ │ (Mac/iOS/ │
27
- │ Port 18793 │ │ Port 18790 │ │ Android) │
28
- └─────────────────┘ └──────────────────┘ └─────────────┘
29
- ```
30
-
31
- 1. **Canvas Host Server**: Serves static HTML/CSS/JS files from `canvasHost.root` directory
32
- 2. **Node Bridge**: Communicates canvas URLs to connected nodes
33
- 3. **Node Apps**: Render the content in a WebView
34
-
35
- ### Tailscale Integration
36
-
37
- The canvas host server binds based on `gateway.bind` setting:
38
-
39
- | Bind Mode | Server Binds To | Canvas URL Uses |
40
- | ---------- | ------------------- | -------------------------- |
41
- | `loopback` | 127.0.0.1 | localhost (local only) |
42
- | `lan` | LAN interface | LAN IP address |
43
- | `tailnet` | Tailscale interface | Tailscale hostname |
44
- | `auto` | Best available | Tailscale > LAN > loopback |
45
-
46
- **Key insight:** The `canvasHostHostForBridge` is derived from `bridgeHost`. When bound to Tailscale, nodes receive URLs like:
47
-
48
- ```
49
- http://<tailscale-hostname>:18793/__symi__/canvas/<file>.html
50
- ```
51
-
52
- This is why localhost URLs don't work - the node receives the Tailscale hostname from the bridge!
53
-
54
- ## Actions
55
-
56
- | Action | Description |
57
- | ---------- | ------------------------------------ |
58
- | `present` | Show canvas with optional target URL |
59
- | `hide` | Hide the canvas |
60
- | `navigate` | Navigate to a new URL |
61
- | `eval` | Execute JavaScript in the canvas |
62
- | `snapshot` | Capture screenshot of canvas |
63
-
64
- ## Configuration
65
-
66
- In `~/.symi/symi.json`:
67
-
68
- ```json
69
- {
70
- "canvasHost": {
71
- "enabled": true,
72
- "port": 18793,
73
- "root": "/Users/you/symi/canvas",
74
- "liveReload": true
75
- },
76
- "gateway": {
77
- "bind": "auto"
78
- }
79
- }
80
- ```
81
-
82
- ### Live Reload
83
-
84
- When `liveReload: true` (default), the canvas host:
85
-
86
- - Watches the root directory for changes (via chokidar)
87
- - Injects a WebSocket client into HTML files
88
- - Automatically reloads connected canvases when files change
89
-
90
- Great for development!
91
-
92
- ## Workflow
93
-
94
- ### 1. Create HTML content
95
-
96
- Place files in the canvas root directory (default `~/symi/canvas/`):
97
-
98
- ```bash
99
- cat > ~/symi/canvas/my-game.html << 'HTML'
100
- <!DOCTYPE html>
101
- <html>
102
- <head><title>My Game</title></head>
103
- <body>
104
- <h1>Hello Canvas!</h1>
105
- </body>
106
- </html>
107
- HTML
108
- ```
109
-
110
- ### 2. Find your canvas host URL
111
-
112
- Check how your gateway is bound:
113
-
114
- ```bash
115
- cat ~/.symi/symi.json | jq '.gateway.bind'
116
- ```
117
-
118
- Then construct the URL:
119
-
120
- - **loopback**: `http://127.0.0.1:18793/__symi__/canvas/<file>.html`
121
- - **lan/tailnet/auto**: `http://<hostname>:18793/__symi__/canvas/<file>.html`
122
-
123
- Find your Tailscale hostname:
124
-
125
- ```bash
126
- tailscale status --json | jq -r '.Self.DNSName' | sed 's/\.$//'
127
- ```
128
-
129
- ### 3. Find connected nodes
130
-
131
- ```bash
132
- symi nodes list
133
- ```
134
-
135
- Look for Mac/iOS/Android nodes with canvas capability.
136
-
137
- ### 4. Present content
138
-
139
- ```
140
- canvas action:present node:<node-id> target:<full-url>
141
- ```
142
-
143
- **Example:**
144
-
145
- ```
146
- canvas action:present node:mac-63599bc4-b54d-4392-9048-b97abd58343a target:http://peters-mac-studio-1.sheep-coho.ts.net:18793/__symi__/canvas/snake.html
147
- ```
148
-
149
- ### 5. Navigate, snapshot, or hide
150
-
151
- ```
152
- canvas action:navigate node:<node-id> url:<new-url>
153
- canvas action:snapshot node:<node-id>
154
- canvas action:hide node:<node-id>
155
- ```
156
-
157
- ## Debugging
158
-
159
- ### White screen / content not loading
160
-
161
- **Cause:** URL mismatch between server bind and node expectation.
162
-
163
- **Debug steps:**
164
-
165
- 1. Check server bind: `cat ~/.symi/symi.json | jq '.gateway.bind'`
166
- 2. Check what port canvas is on: `lsof -i :18793`
167
- 3. Test URL directly: `curl http://<hostname>:18793/__symi__/canvas/<file>.html`
168
-
169
- **Solution:** Use the full hostname matching your bind mode, not localhost.
170
-
171
- ### "node required" error
172
-
173
- Always specify `node:<node-id>` parameter.
174
-
175
- ### "node not connected" error
176
-
177
- Node is offline. Use `symi nodes list` to find online nodes.
178
-
179
- ### Content not updating
180
-
181
- If live reload isn't working:
182
-
183
- 1. Check `liveReload: true` in config
184
- 2. Ensure file is in the canvas root directory
185
- 3. Check for watcher errors in logs
186
-
187
- ## URL Path Structure
188
-
189
- The canvas host serves from `/__symi__/canvas/` prefix:
190
-
191
- ```
192
- http://<host>:18793/__symi__/canvas/index.html → ~/symi/canvas/index.html
193
- http://<host>:18793/__symi__/canvas/games/snake.html → ~/symi/canvas/games/snake.html
194
- ```
195
-
196
- The `/__symi__/canvas/` prefix is defined by `CANVAS_HOST_PATH` constant.
197
-
198
- ## Tips
199
-
200
- - Keep HTML self-contained (inline CSS/JS) for best results
201
- - Use the default index.html as a test page (has bridge diagnostics)
202
- - The canvas persists until you `hide` it or navigate away
203
- - Live reload makes development fast - just save and it updates!
204
- - A2UI JSON push is WIP - use HTML files for now
@@ -1,142 +0,0 @@
1
- ---
2
- name: connect-email
3
- description: Connect a user's email account (Outlook 365 or Gmail) to Symi. Use when the user asks to connect, link, set up, configure, or troubleshoot email — including any mention of "my email", "work email", "outlook", "gmail", "microsoft 365", "m365", "google workspace", or "inbox". Precheck for an existing connection, ask Gmail or Outlook, then walk through the matching setup. Covers the `/outlook login` device-code flow and the full Gmail `hooks.gmail` Pub/Sub setup.
4
- triggers: [connect-email, connect, email]
5
- ---
6
-
7
- # Connect Email
8
-
9
- Symi has two email integrations. **Do not invent steps. Do not claim email is unsupported.** Follow this skill end-to-end.
10
-
11
- | Integration | Use when | How to connect |
12
- | --------------- | ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
13
- | **Outlook 365** | Microsoft 365 work/school account, personal `outlook.com` / `hotmail.com` / `live.com` | User sends `/outlook login` in chat → Microsoft device-code flow → tools become available immediately |
14
- | **Gmail** | Google Workspace, personal Gmail — **and** the user wants incoming mail to auto-create Symi sessions | Google Cloud Pub/Sub + `hooks.gmail` config (multi-step setup) |
15
-
16
- ## Step 0 — Precheck the connection state
17
-
18
- Before asking "Gmail or Outlook?", check what's already connected so you don't ask a user who's already set up.
19
-
20
- 1. **Outlook.** Your current system prompt already contains a line starting with `[Outlook 365]`, injected by the `outlook` plugin's `before_prompt_build` hook. If it reads _"Connected as &lt;email&gt;"_, Outlook is active — acknowledge it ("I can see you're already connected as X") and ask whether the user wants to add a second account or do something else.
21
- 2. **Gmail.** Read `~/.symi/symi.json`. If it has a `hooks.gmail` block with at least `account` and `topic` set, Gmail is configured — again, acknowledge before offering to re-setup.
22
-
23
- If neither is connected — or the user explicitly wants a _different_ account than what's shown — continue.
24
-
25
- ## Step 1 — Ask the user: Gmail or Outlook?
26
-
27
- One plain question. Do not assume. The correct branch depends on which mail provider the user actually has.
28
-
29
- ## Step 2a — Outlook branch
30
-
31
- 1. Ask the user to **type `/outlook login` in chat themselves**. Chat commands are intercepted by the command dispatcher only when the user sends them — if you emit `/outlook login` as assistant text it is just text, not a command.
32
- 2. They will receive a reply with a Microsoft device-code URL (typically `https://login.microsoft.com/device`) and an 8-character code. The code expires in 15 minutes.
33
- 3. The user opens the URL in any browser, signs in with their Microsoft account, pastes the code, and approves access.
34
- 4. Tokens are persisted to `~/.symi/credentials/outlook.json` (mode 0600). From the next turn, the `before_prompt_build` hook will inject `[Outlook 365] Connected as <email>` into your system context automatically.
35
- 5. Ask the user to send `/outlook status` to confirm. On success these seven tools are live for your use:
36
- - `outlook_list` — list messages in a folder (`inbox`, `sentitems`, `drafts`, `archive`, `junkemail`), optional unread filter
37
- - `outlook_read` — fetch the full body of a message by id (auto-marks read)
38
- - `outlook_send` — new message with To/CC, subject, body
39
- - `outlook_reply` — reply or reply-all to an existing message id
40
- - `outlook_search` — keyword search across subject / body / sender
41
- - `outlook_folders` — list folders with unread / total counts
42
- - `outlook_move` — move a message to another folder
43
-
44
- ### Outlook limitations to tell the user up front
45
-
46
- - **Plain-text body only.** No HTML formatting, no attachments (send or receive).
47
- - **Pull-only.** Symi does not receive incoming Outlook mail as Symi conversations. The user has to ask ("any new mail?") and you call `outlook_list` / `outlook_search`. There is no push, webhook, or background poll for Outlook today — if the user wants inbound-triggered sessions, they need Gmail.
48
- - **No Graph API retry/backoff yet.** A transient Graph 5xx surfaces as an error on the first try; re-running the tool usually succeeds.
49
-
50
- ## Step 2b — Gmail branch (full walkthrough)
51
-
52
- Gmail is heavier because Google requires Pub/Sub for push notifications. End result: when new mail lands in the watched label, Google publishes to a Pub/Sub topic; a push subscription POSTs to Symi's webhook; Symi creates a `hook:gmail:<id>` session that you then reply into as the agent.
53
-
54
- ### Prerequisites — verify with the user before starting
55
-
56
- - [ ] Access to a **Google Cloud project** (they can create one free at [console.cloud.google.com](https://console.cloud.google.com)).
57
- - [ ] `gcloud` CLI installed. On macOS: `brew install --cask google-cloud-sdk`. On Linux: follow the [gcloud install guide](https://cloud.google.com/sdk/docs/install).
58
- - [ ] OAuth credentials for the Gmail API, managed via `gogcli`. See `skills/gog/SKILL.md` for gogcli setup (`gog auth credentials /path/to/client_secret.json` then `gog auth add <email> --services gmail`).
59
- - [ ] Symi's webhook endpoint reachable by Google's push service. Either the Symi host is publicly reachable, **or** Tailscale is configured (`tailscale.mode: "serve"` for private, `"funnel"` for public-over-Tailscale).
60
-
61
- If any prerequisite is missing, stop and help the user install/configure it before continuing.
62
-
63
- ### Step-by-step setup
64
-
65
- 1. **Authenticate gcloud** — `gcloud auth login` and `gcloud config set project <project-id>`.
66
- 2. **Set up gogcli OAuth** — follow the `gog` skill: `gog auth credentials /path/to/client_secret.json`, then `gog auth add <email> --services gmail`.
67
- 3. **Create the Pub/Sub topic**
68
- ```bash
69
- gcloud pubsub topics create symi-gmail
70
- ```
71
- 4. **Grant Gmail permission to publish to the topic**
72
- ```bash
73
- gcloud pubsub topics add-iam-policy-binding symi-gmail \
74
- --member=serviceAccount:gmail-api-push@system.gserviceaccount.com \
75
- --role=roles/pubsub.publisher
76
- ```
77
- 5. **Create the push subscription** (pointing at Symi's webhook URL)
78
- ```bash
79
- gcloud pubsub subscriptions create symi-gmail-sub \
80
- --topic=symi-gmail \
81
- --push-endpoint=<SYMI_WEBHOOK_URL>/gmail-pubsub
82
- ```
83
- 6. **Register the Gmail watch** — call `users.watch` on the Gmail API with the topic and the label to watch (normally `INBOX`). The helpers in `src/hooks/gmail-setup-utils.ts` can do this from the gateway; ask the user whether they want you to run the setup via the gateway CLI or execute the `curl` against the Gmail API manually.
84
- 7. **Add `hooks.gmail` to `~/.symi/symi.json`**:
85
-
86
- ```json
87
- {
88
- "hooks": {
89
- "gmail": {
90
- "account": "user@example.com",
91
- "label": "INBOX",
92
- "topic": "projects/<project-id>/topics/symi-gmail",
93
- "subscription": "projects/<project-id>/subscriptions/symi-gmail-sub",
94
- "pushToken": "<random-shared-secret>",
95
- "hookUrl": "http://127.0.0.1:8788/gmail-pubsub",
96
- "includeBody": true,
97
- "maxBytes": 20480,
98
- "renewEveryMinutes": 720,
99
- "serve": { "bind": "127.0.0.1", "port": 8788, "path": "/gmail-pubsub" },
100
- "tailscale": { "mode": "off" }
101
- }
102
- }
103
- }
104
- ```
105
-
106
- Key fields:
107
- - `account` — the Gmail address being watched.
108
- - `label` — Gmail label/folder to watch (default `INBOX`).
109
- - `topic` / `subscription` — fully-qualified Pub/Sub resource names (`projects/<id>/topics/<name>`).
110
- - `pushToken` — shared secret Symi checks on incoming push requests so spoofed pushes are rejected.
111
- - `hookUrl` — URL the push subscription POSTs to; must match `serve.bind` + `serve.port` + `serve.path`, or be a Tailscale serve/funnel exposing them.
112
- - `includeBody` — include the email body in the prompt. `maxBytes` truncates oversized emails.
113
- - `renewEveryMinutes` — Gmail `watch` expires after ~7 days; default 720 (12h) re-registers well ahead.
114
- - `tailscale.mode` — `"off"` if Symi is publicly reachable; `"serve"` for a private tailnet webhook; `"funnel"` for Tailscale-exposed public endpoint.
115
- - `allowUnsafeExternalContent` — leave unset. Setting `true` disables Symi's external-content safety wrapper on email content — only use after explicit user acknowledgement.
116
-
117
- 8. **Restart the gateway** so it picks up the new hook config:
118
- ```bash
119
- systemctl --user restart symi-gateway # Linux
120
- ```
121
- On macOS with the headless tmux wrapper, kill and relaunch the tmux session running `gateway-headless.sh`. Changes under `hooks.gmail` are not hot-reloaded today.
122
- 9. **Verify** — have the user send a test email to `account`. Expected chain: Google publishes to the topic → Pub/Sub pushes to Symi's `/gmail-pubsub` endpoint → a `hook:gmail:<id>` session is created → you reply to it in the normal Symi conversation flow. Confirm the user sees the test email arrive as a Symi session.
123
-
124
- ### Gmail limitations to tell the user
125
-
126
- - Inbound only works while the watch is live. If `renewEveryMinutes` lapses, the Pub/Sub subscription fails, or the IAM binding gets removed, mail stops arriving — the gateway doctor (`symi doctor`) has checks for this.
127
- - Outgoing mail uses the reply path of whichever session/agent handles the inbound — it is not a native SMTP sender tool like `outlook_send`. For "compose a new email from scratch" workflows, Outlook is simpler.
128
- - `maxBytes` truncates very large emails so the prompt stays usable — set higher if the user routinely needs full message bodies.
129
-
130
- ## Which should I recommend?
131
-
132
- - **Microsoft 365 / outlook.com / hotmail.com / live.com** → Outlook. Two-minute setup.
133
- - **Gmail or Google Workspace**, user wants to **send mail on demand only** → Gmail via the `gog` CLI tools (no Pub/Sub needed) is simpler than the full `hooks.gmail` flow — mention this option.
134
- - **Gmail or Google Workspace**, user wants **incoming mail to auto-create Symi sessions** → Full Gmail Pub/Sub setup (Step 2b).
135
- - User wants _both send and auto-receive in Outlook_ → not available today; push/poll for Outlook is not implemented.
136
-
137
- ## References
138
-
139
- - Gmail Pub/Sub concept doc: `docs/automation/gmail-pubsub.md`
140
- - gog (Google Workspace CLI): `skills/gog/SKILL.md`
141
- - Outlook plugin source: `extensions/outlook/` (tools in `src/tools.ts`, OAuth in `src/auth.ts`)
142
- - Config types: `src/config/types.hooks.ts` (`HooksGmailConfig`)
@@ -1,83 +0,0 @@
1
- ---
2
- name: document-generation
3
- description: Generate Word (.docx), Excel (.xlsx), PowerPoint (.pptx), and PDF documents via npm packages bundled with Symi. Write a short Node.js script (.cjs for CommonJS), execute via exec with host=gateway. Use when the user asks to create, generate, or export documents.
4
- triggers: [document, docx, xlsx, pptx, pdf, word, excel, powerpoint, generate, export, file]
5
- ---
6
-
7
- # Document Generation
8
-
9
- Symi ships with these npm packages bundled for document generation via the `exec` tool:
10
-
11
- - **`docx`** — create `.docx` (Word) files with headings, paragraphs, tables, images, styles.
12
- - **`exceljs`** — create `.xlsx` (Excel) files with sheets, formulas, formatting, charts.
13
- - **`pptxgenjs`** — create `.pptx` (PowerPoint) files with slides, layouts, text, images, charts.
14
- - **`pdfkit`** — create `.pdf` files with text, images, tables, vector graphics.
15
-
16
- These are real `dependencies` in Symi's `package.json` — they install with Symi automatically. No separate setup step.
17
-
18
- ## How to invoke
19
-
20
- When the user asks to **create, generate, or export** a document:
21
-
22
- 1. Write a short Node.js script. Save it with the `.cjs` extension so CommonJS `require()` resolves correctly even though Symi's own package is `"type": "module"`.
23
- 2. Execute via the `exec` tool with `host: "gateway"` — the packages are installed in Symi's own `node_modules`, which the gateway host can resolve from.
24
- 3. Write the output to a path the user can pick up — typically the workspace dir, `~/Downloads/`, or wherever the user named.
25
-
26
- ## Minimal example — Word doc
27
-
28
- ```js
29
- // gen.cjs
30
- const { Document, Packer, Paragraph, TextRun, HeadingLevel } = require("docx");
31
- const fs = require("fs");
32
-
33
- const doc = new Document({
34
- sections: [
35
- {
36
- children: [
37
- new Paragraph({ heading: HeadingLevel.TITLE, children: [new TextRun("Report")] }),
38
- new Paragraph({ children: [new TextRun("Body content here.")] }),
39
- ],
40
- },
41
- ],
42
- });
43
-
44
- Packer.toBuffer(doc).then((buf) => {
45
- fs.writeFileSync("/tmp/report.docx", buf);
46
- });
47
- ```
48
-
49
- Then `exec({ host: "gateway", command: "node gen.cjs" })`.
50
-
51
- The script must run on the gateway host because that's where Symi's `node_modules` live — the sandbox and remote nodes do NOT have these packages.
52
-
53
- ## Other formats
54
-
55
- ```js
56
- // xlsx
57
- const ExcelJS = require("exceljs");
58
- const wb = new ExcelJS.Workbook();
59
- const sheet = wb.addWorksheet("Sheet1");
60
- sheet.getCell("A1").value = "Hello";
61
- await wb.xlsx.writeFile("/tmp/out.xlsx");
62
-
63
- // pptx
64
- const PptxGenJS = require("pptxgenjs");
65
- const pptx = new PptxGenJS();
66
- const slide = pptx.addSlide();
67
- slide.addText("Hello", { x: 1, y: 1, w: 4, h: 1 });
68
- await pptx.writeFile({ fileName: "/tmp/out.pptx" });
69
-
70
- // pdf
71
- const PDFDocument = require("pdfkit");
72
- const fs = require("fs");
73
- const pdf = new PDFDocument();
74
- pdf.pipe(fs.createWriteStream("/tmp/out.pdf"));
75
- pdf.text("Hello");
76
- pdf.end();
77
- ```
78
-
79
- ## What NOT to do
80
-
81
- - **Don't use Pandoc, LibreOffice, or other CLI converters** unless the user specifically asks. The bundled npm packages are faster, more reliable, and don't require external binaries.
82
- - **Don't try to generate documents from the sandbox host** — the packages live in Symi's own `node_modules` on the gateway host.
83
- - **Don't dump 10KB of HTML and call it a "document"**. Use the structured generators above.