@otto-assistant/bridge 0.4.92
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.
- package/bin.js +2 -0
- package/dist/agent-model.e2e.test.js +755 -0
- package/dist/ai-tool-to-genai.js +233 -0
- package/dist/ai-tool-to-genai.test.js +267 -0
- package/dist/ai-tool.js +6 -0
- package/dist/anthropic-auth-plugin.js +728 -0
- package/dist/anthropic-auth-plugin.test.js +125 -0
- package/dist/anthropic-auth-state.js +231 -0
- package/dist/bin.js +90 -0
- package/dist/channel-management.js +227 -0
- package/dist/cli-parsing.test.js +137 -0
- package/dist/cli-send-thread.e2e.test.js +356 -0
- package/dist/cli.js +3276 -0
- package/dist/commands/abort.js +65 -0
- package/dist/commands/action-buttons.js +245 -0
- package/dist/commands/add-project.js +113 -0
- package/dist/commands/agent.js +335 -0
- package/dist/commands/ask-question.js +274 -0
- package/dist/commands/btw.js +116 -0
- package/dist/commands/compact.js +120 -0
- package/dist/commands/context-usage.js +140 -0
- package/dist/commands/create-new-project.js +130 -0
- package/dist/commands/diff.js +63 -0
- package/dist/commands/file-upload.js +275 -0
- package/dist/commands/fork.js +220 -0
- package/dist/commands/gemini-apikey.js +70 -0
- package/dist/commands/login.js +885 -0
- package/dist/commands/mcp.js +239 -0
- package/dist/commands/memory-snapshot.js +24 -0
- package/dist/commands/mention-mode.js +44 -0
- package/dist/commands/merge-worktree.js +159 -0
- package/dist/commands/model-variant.js +364 -0
- package/dist/commands/model.js +776 -0
- package/dist/commands/new-worktree.js +366 -0
- package/dist/commands/paginated-select.js +57 -0
- package/dist/commands/permissions.js +274 -0
- package/dist/commands/queue.js +206 -0
- package/dist/commands/remove-project.js +115 -0
- package/dist/commands/restart-opencode-server.js +127 -0
- package/dist/commands/resume.js +149 -0
- package/dist/commands/run-command.js +79 -0
- package/dist/commands/screenshare.js +303 -0
- package/dist/commands/screenshare.test.js +20 -0
- package/dist/commands/session-id.js +78 -0
- package/dist/commands/session.js +176 -0
- package/dist/commands/share.js +80 -0
- package/dist/commands/tasks.js +205 -0
- package/dist/commands/types.js +2 -0
- package/dist/commands/undo-redo.js +305 -0
- package/dist/commands/unset-model.js +138 -0
- package/dist/commands/upgrade.js +42 -0
- package/dist/commands/user-command.js +155 -0
- package/dist/commands/verbosity.js +125 -0
- package/dist/commands/worktree-settings.js +43 -0
- package/dist/commands/worktrees.js +410 -0
- package/dist/condense-memory.js +33 -0
- package/dist/config.js +94 -0
- package/dist/context-awareness-plugin.js +363 -0
- package/dist/context-awareness-plugin.test.js +124 -0
- package/dist/critique-utils.js +95 -0
- package/dist/database.js +1310 -0
- package/dist/db.js +251 -0
- package/dist/db.test.js +138 -0
- package/dist/debounce-timeout.js +28 -0
- package/dist/debounced-process-flush.js +77 -0
- package/dist/discord-bot.js +1008 -0
- package/dist/discord-command-registration.js +524 -0
- package/dist/discord-urls.js +81 -0
- package/dist/discord-utils.js +591 -0
- package/dist/discord-utils.test.js +134 -0
- package/dist/errors.js +157 -0
- package/dist/escape-backticks.test.js +429 -0
- package/dist/event-stream-real-capture.e2e.test.js +533 -0
- package/dist/eventsource-parser.test.js +327 -0
- package/dist/exec-async.js +26 -0
- package/dist/external-opencode-sync.js +480 -0
- package/dist/format-tables.js +302 -0
- package/dist/format-tables.test.js +308 -0
- package/dist/forum-sync/config.js +79 -0
- package/dist/forum-sync/discord-operations.js +154 -0
- package/dist/forum-sync/index.js +5 -0
- package/dist/forum-sync/markdown.js +113 -0
- package/dist/forum-sync/sync-to-discord.js +417 -0
- package/dist/forum-sync/sync-to-files.js +190 -0
- package/dist/forum-sync/types.js +53 -0
- package/dist/forum-sync/watchers.js +307 -0
- package/dist/gateway-proxy-reconnect.e2e.test.js +394 -0
- package/dist/gateway-proxy.e2e.test.js +483 -0
- package/dist/genai-worker-wrapper.js +111 -0
- package/dist/genai-worker.js +311 -0
- package/dist/genai.js +232 -0
- package/dist/generated/browser.js +17 -0
- package/dist/generated/client.js +37 -0
- package/dist/generated/commonInputTypes.js +10 -0
- package/dist/generated/enums.js +52 -0
- package/dist/generated/internal/class.js +49 -0
- package/dist/generated/internal/prismaNamespace.js +253 -0
- package/dist/generated/internal/prismaNamespaceBrowser.js +223 -0
- package/dist/generated/models/bot_api_keys.js +1 -0
- package/dist/generated/models/bot_tokens.js +1 -0
- package/dist/generated/models/channel_agents.js +1 -0
- package/dist/generated/models/channel_directories.js +1 -0
- package/dist/generated/models/channel_mention_mode.js +1 -0
- package/dist/generated/models/channel_models.js +1 -0
- package/dist/generated/models/channel_verbosity.js +1 -0
- package/dist/generated/models/channel_worktrees.js +1 -0
- package/dist/generated/models/forum_sync_configs.js +1 -0
- package/dist/generated/models/global_models.js +1 -0
- package/dist/generated/models/ipc_requests.js +1 -0
- package/dist/generated/models/part_messages.js +1 -0
- package/dist/generated/models/scheduled_tasks.js +1 -0
- package/dist/generated/models/session_agents.js +1 -0
- package/dist/generated/models/session_events.js +1 -0
- package/dist/generated/models/session_models.js +1 -0
- package/dist/generated/models/session_start_sources.js +1 -0
- package/dist/generated/models/thread_sessions.js +1 -0
- package/dist/generated/models/thread_worktrees.js +1 -0
- package/dist/generated/models.js +1 -0
- package/dist/heap-monitor.js +122 -0
- package/dist/hrana-server.js +263 -0
- package/dist/hrana-server.test.js +370 -0
- package/dist/html-actions.js +123 -0
- package/dist/html-actions.test.js +70 -0
- package/dist/html-components.js +117 -0
- package/dist/html-components.test.js +34 -0
- package/dist/image-optimizer-plugin.js +153 -0
- package/dist/image-utils.js +112 -0
- package/dist/interaction-handler.js +397 -0
- package/dist/ipc-polling.js +252 -0
- package/dist/ipc-tools-plugin.js +193 -0
- package/dist/kimaki-digital-twin.e2e.test.js +161 -0
- package/dist/kimaki-opencode-plugin-loading.e2e.test.js +87 -0
- package/dist/kimaki-opencode-plugin.js +17 -0
- package/dist/kimaki-opencode-plugin.test.js +98 -0
- package/dist/limit-heading-depth.js +25 -0
- package/dist/limit-heading-depth.test.js +105 -0
- package/dist/logger.js +165 -0
- package/dist/markdown.js +342 -0
- package/dist/markdown.test.js +257 -0
- package/dist/message-finish-field.e2e.test.js +165 -0
- package/dist/message-formatting.js +413 -0
- package/dist/message-formatting.test.js +73 -0
- package/dist/message-preprocessing.js +330 -0
- package/dist/onboarding-tutorial.js +172 -0
- package/dist/onboarding-welcome.js +37 -0
- package/dist/openai-realtime.js +224 -0
- package/dist/opencode-command-detection.js +65 -0
- package/dist/opencode-command-detection.test.js +240 -0
- package/dist/opencode-command.js +129 -0
- package/dist/opencode-command.test.js +48 -0
- package/dist/opencode-interrupt-plugin.js +361 -0
- package/dist/opencode-interrupt-plugin.test.js +458 -0
- package/dist/opencode.js +861 -0
- package/dist/otto/branding.js +22 -0
- package/dist/otto/index.js +21 -0
- package/dist/parse-permission-rules.test.js +117 -0
- package/dist/patch-text-parser.js +97 -0
- package/dist/plugin-logger.js +59 -0
- package/dist/privacy-sanitizer.js +105 -0
- package/dist/queue-advanced-abort.e2e.test.js +293 -0
- package/dist/queue-advanced-action-buttons.e2e.test.js +206 -0
- package/dist/queue-advanced-e2e-setup.js +786 -0
- package/dist/queue-advanced-footer.e2e.test.js +472 -0
- package/dist/queue-advanced-model-switch.e2e.test.js +299 -0
- package/dist/queue-advanced-permissions-typing.e2e.test.js +180 -0
- package/dist/queue-advanced-question.e2e.test.js +261 -0
- package/dist/queue-advanced-typing-interrupt.e2e.test.js +114 -0
- package/dist/queue-advanced-typing.e2e.test.js +153 -0
- package/dist/queue-drain-after-interactive-ui.e2e.test.js +119 -0
- package/dist/queue-interrupt-drain.e2e.test.js +135 -0
- package/dist/queue-question-select-drain.e2e.test.js +120 -0
- package/dist/runtime-idle-sweeper.js +52 -0
- package/dist/runtime-lifecycle.e2e.test.js +508 -0
- package/dist/sentry.js +23 -0
- package/dist/session-handler/agent-utils.js +67 -0
- package/dist/session-handler/event-stream-state.js +420 -0
- package/dist/session-handler/event-stream-state.test.js +563 -0
- package/dist/session-handler/model-utils.js +124 -0
- package/dist/session-handler/opencode-session-event-log.js +94 -0
- package/dist/session-handler/thread-runtime-state.js +104 -0
- package/dist/session-handler/thread-session-runtime.js +3258 -0
- package/dist/session-handler.js +9 -0
- package/dist/session-search.js +100 -0
- package/dist/session-search.test.js +40 -0
- package/dist/session-title-rename.test.js +80 -0
- package/dist/startup-service.js +153 -0
- package/dist/startup-time.e2e.test.js +296 -0
- package/dist/store.js +17 -0
- package/dist/system-message.js +613 -0
- package/dist/system-message.test.js +602 -0
- package/dist/task-runner.js +295 -0
- package/dist/task-schedule.js +209 -0
- package/dist/task-schedule.test.js +71 -0
- package/dist/test-utils.js +299 -0
- package/dist/thinking-utils.js +35 -0
- package/dist/thread-message-queue.e2e.test.js +999 -0
- package/dist/tools.js +357 -0
- package/dist/undo-redo.e2e.test.js +161 -0
- package/dist/unnest-code-blocks.js +146 -0
- package/dist/unnest-code-blocks.test.js +673 -0
- package/dist/upgrade.js +114 -0
- package/dist/utils.js +144 -0
- package/dist/voice-attachment.js +34 -0
- package/dist/voice-handler.js +646 -0
- package/dist/voice-message.e2e.test.js +1021 -0
- package/dist/voice.js +447 -0
- package/dist/voice.test.js +235 -0
- package/dist/wait-session.js +94 -0
- package/dist/websockify.js +69 -0
- package/dist/worker-types.js +4 -0
- package/dist/worktree-lifecycle.e2e.test.js +308 -0
- package/dist/worktree-utils.js +3 -0
- package/dist/worktrees.js +929 -0
- package/dist/worktrees.test.js +189 -0
- package/dist/xml.js +92 -0
- package/dist/xml.test.js +32 -0
- package/package.json +98 -0
- package/schema.prisma +295 -0
- package/skills/batch/SKILL.md +87 -0
- package/skills/critique/SKILL.md +112 -0
- package/skills/egaki/SKILL.md +100 -0
- package/skills/errore/SKILL.md +647 -0
- package/skills/event-sourcing-state/SKILL.md +252 -0
- package/skills/gitchamber/SKILL.md +93 -0
- package/skills/goke/SKILL.md +644 -0
- package/skills/jitter/EDITOR.md +219 -0
- package/skills/jitter/EXPORT-INTERNALS.md +309 -0
- package/skills/jitter/SKILL.md +158 -0
- package/skills/jitter/jitter-clipboard.json +1042 -0
- package/skills/jitter/package.json +14 -0
- package/skills/jitter/tsconfig.json +15 -0
- package/skills/jitter/utils/actions.ts +212 -0
- package/skills/jitter/utils/export.ts +114 -0
- package/skills/jitter/utils/index.ts +141 -0
- package/skills/jitter/utils/snapshot.ts +154 -0
- package/skills/jitter/utils/traverse.ts +246 -0
- package/skills/jitter/utils/types.ts +279 -0
- package/skills/jitter/utils/wait.ts +133 -0
- package/skills/lintcn/SKILL.md +873 -0
- package/skills/new-skill/SKILL.md +211 -0
- package/skills/npm-package/SKILL.md +239 -0
- package/skills/playwriter/SKILL.md +35 -0
- package/skills/proxyman/SKILL.md +215 -0
- package/skills/security-review/SKILL.md +208 -0
- package/skills/simplify/SKILL.md +58 -0
- package/skills/spiceflow/SKILL.md +14 -0
- package/skills/termcast/SKILL.md +945 -0
- package/skills/tuistory/SKILL.md +250 -0
- package/skills/usecomputer/SKILL.md +264 -0
- package/skills/x-articles/SKILL.md +554 -0
- package/skills/zele/SKILL.md +112 -0
- package/skills/zustand-centralized-state/SKILL.md +1004 -0
- package/src/agent-model.e2e.test.ts +976 -0
- package/src/ai-tool-to-genai.test.ts +296 -0
- package/src/ai-tool-to-genai.ts +283 -0
- package/src/ai-tool.ts +39 -0
- package/src/anthropic-auth-plugin.test.ts +159 -0
- package/src/anthropic-auth-plugin.ts +861 -0
- package/src/anthropic-auth-state.ts +282 -0
- package/src/bin.ts +111 -0
- package/src/channel-management.ts +334 -0
- package/src/cli-parsing.test.ts +195 -0
- package/src/cli-send-thread.e2e.test.ts +464 -0
- package/src/cli.ts +4581 -0
- package/src/commands/abort.ts +89 -0
- package/src/commands/action-buttons.ts +364 -0
- package/src/commands/add-project.ts +149 -0
- package/src/commands/agent.ts +473 -0
- package/src/commands/ask-question.ts +390 -0
- package/src/commands/btw.ts +164 -0
- package/src/commands/compact.ts +157 -0
- package/src/commands/context-usage.ts +199 -0
- package/src/commands/create-new-project.ts +190 -0
- package/src/commands/diff.ts +91 -0
- package/src/commands/file-upload.ts +389 -0
- package/src/commands/fork.ts +321 -0
- package/src/commands/gemini-apikey.ts +104 -0
- package/src/commands/login.ts +1173 -0
- package/src/commands/mcp.ts +307 -0
- package/src/commands/memory-snapshot.ts +30 -0
- package/src/commands/mention-mode.ts +68 -0
- package/src/commands/merge-worktree.ts +223 -0
- package/src/commands/model-variant.ts +483 -0
- package/src/commands/model.ts +1053 -0
- package/src/commands/new-worktree.ts +510 -0
- package/src/commands/paginated-select.ts +81 -0
- package/src/commands/permissions.ts +397 -0
- package/src/commands/queue.ts +271 -0
- package/src/commands/remove-project.ts +155 -0
- package/src/commands/restart-opencode-server.ts +162 -0
- package/src/commands/resume.ts +230 -0
- package/src/commands/run-command.ts +123 -0
- package/src/commands/screenshare.test.ts +30 -0
- package/src/commands/screenshare.ts +366 -0
- package/src/commands/session-id.ts +109 -0
- package/src/commands/session.ts +227 -0
- package/src/commands/share.ts +106 -0
- package/src/commands/tasks.ts +293 -0
- package/src/commands/types.ts +25 -0
- package/src/commands/undo-redo.ts +386 -0
- package/src/commands/unset-model.ts +173 -0
- package/src/commands/upgrade.ts +52 -0
- package/src/commands/user-command.ts +198 -0
- package/src/commands/verbosity.ts +173 -0
- package/src/commands/worktree-settings.ts +70 -0
- package/src/commands/worktrees.ts +552 -0
- package/src/condense-memory.ts +36 -0
- package/src/config.ts +111 -0
- package/src/context-awareness-plugin.test.ts +142 -0
- package/src/context-awareness-plugin.ts +510 -0
- package/src/critique-utils.ts +139 -0
- package/src/database.ts +1876 -0
- package/src/db.test.ts +162 -0
- package/src/db.ts +286 -0
- package/src/debounce-timeout.ts +43 -0
- package/src/debounced-process-flush.ts +104 -0
- package/src/discord-bot.ts +1330 -0
- package/src/discord-command-registration.ts +693 -0
- package/src/discord-urls.ts +88 -0
- package/src/discord-utils.test.ts +153 -0
- package/src/discord-utils.ts +800 -0
- package/src/errors.ts +201 -0
- package/src/escape-backticks.test.ts +469 -0
- package/src/event-stream-real-capture.e2e.test.ts +692 -0
- package/src/eventsource-parser.test.ts +351 -0
- package/src/exec-async.ts +35 -0
- package/src/external-opencode-sync.ts +685 -0
- package/src/format-tables.test.ts +335 -0
- package/src/format-tables.ts +445 -0
- package/src/forum-sync/config.ts +92 -0
- package/src/forum-sync/discord-operations.ts +241 -0
- package/src/forum-sync/index.ts +9 -0
- package/src/forum-sync/markdown.ts +172 -0
- package/src/forum-sync/sync-to-discord.ts +595 -0
- package/src/forum-sync/sync-to-files.ts +294 -0
- package/src/forum-sync/types.ts +175 -0
- package/src/forum-sync/watchers.ts +454 -0
- package/src/gateway-proxy-reconnect.e2e.test.ts +523 -0
- package/src/gateway-proxy.e2e.test.ts +640 -0
- package/src/genai-worker-wrapper.ts +164 -0
- package/src/genai-worker.ts +386 -0
- package/src/genai.ts +321 -0
- package/src/generated/browser.ts +114 -0
- package/src/generated/client.ts +138 -0
- package/src/generated/commonInputTypes.ts +736 -0
- package/src/generated/enums.ts +88 -0
- package/src/generated/internal/class.ts +384 -0
- package/src/generated/internal/prismaNamespace.ts +2386 -0
- package/src/generated/internal/prismaNamespaceBrowser.ts +326 -0
- package/src/generated/models/bot_api_keys.ts +1288 -0
- package/src/generated/models/bot_tokens.ts +1656 -0
- package/src/generated/models/channel_agents.ts +1256 -0
- package/src/generated/models/channel_directories.ts +1859 -0
- package/src/generated/models/channel_mention_mode.ts +1300 -0
- package/src/generated/models/channel_models.ts +1288 -0
- package/src/generated/models/channel_verbosity.ts +1228 -0
- package/src/generated/models/channel_worktrees.ts +1300 -0
- package/src/generated/models/forum_sync_configs.ts +1452 -0
- package/src/generated/models/global_models.ts +1288 -0
- package/src/generated/models/ipc_requests.ts +1485 -0
- package/src/generated/models/part_messages.ts +1302 -0
- package/src/generated/models/scheduled_tasks.ts +2320 -0
- package/src/generated/models/session_agents.ts +1086 -0
- package/src/generated/models/session_events.ts +1439 -0
- package/src/generated/models/session_models.ts +1114 -0
- package/src/generated/models/session_start_sources.ts +1408 -0
- package/src/generated/models/thread_sessions.ts +1781 -0
- package/src/generated/models/thread_worktrees.ts +1356 -0
- package/src/generated/models.ts +30 -0
- package/src/heap-monitor.ts +152 -0
- package/src/hrana-server.test.ts +434 -0
- package/src/hrana-server.ts +314 -0
- package/src/html-actions.test.ts +87 -0
- package/src/html-actions.ts +174 -0
- package/src/html-components.test.ts +38 -0
- package/src/html-components.ts +181 -0
- package/src/image-optimizer-plugin.ts +194 -0
- package/src/image-utils.ts +149 -0
- package/src/interaction-handler.ts +576 -0
- package/src/ipc-polling.ts +326 -0
- package/src/ipc-tools-plugin.ts +236 -0
- package/src/kimaki-digital-twin.e2e.test.ts +199 -0
- package/src/kimaki-opencode-plugin-loading.e2e.test.ts +109 -0
- package/src/kimaki-opencode-plugin.test.ts +108 -0
- package/src/kimaki-opencode-plugin.ts +18 -0
- package/src/limit-heading-depth.test.ts +116 -0
- package/src/limit-heading-depth.ts +26 -0
- package/src/logger.ts +208 -0
- package/src/markdown.test.ts +308 -0
- package/src/markdown.ts +410 -0
- package/src/message-finish-field.e2e.test.ts +192 -0
- package/src/message-formatting.test.ts +81 -0
- package/src/message-formatting.ts +533 -0
- package/src/message-preprocessing.ts +455 -0
- package/src/onboarding-tutorial.ts +176 -0
- package/src/onboarding-welcome.ts +49 -0
- package/src/openai-realtime.ts +358 -0
- package/src/opencode-command-detection.test.ts +307 -0
- package/src/opencode-command-detection.ts +76 -0
- package/src/opencode-command.test.ts +70 -0
- package/src/opencode-command.ts +188 -0
- package/src/opencode-interrupt-plugin.test.ts +677 -0
- package/src/opencode-interrupt-plugin.ts +477 -0
- package/src/opencode.ts +1110 -0
- package/src/otto/branding.ts +23 -0
- package/src/otto/index.ts +22 -0
- package/src/parse-permission-rules.test.ts +127 -0
- package/src/patch-text-parser.ts +107 -0
- package/src/plugin-logger.ts +68 -0
- package/src/privacy-sanitizer.ts +142 -0
- package/src/queue-advanced-abort.e2e.test.ts +382 -0
- package/src/queue-advanced-action-buttons.e2e.test.ts +268 -0
- package/src/queue-advanced-e2e-setup.ts +873 -0
- package/src/queue-advanced-footer.e2e.test.ts +576 -0
- package/src/queue-advanced-model-switch.e2e.test.ts +383 -0
- package/src/queue-advanced-permissions-typing.e2e.test.ts +245 -0
- package/src/queue-advanced-question.e2e.test.ts +316 -0
- package/src/queue-advanced-typing-interrupt.e2e.test.ts +146 -0
- package/src/queue-advanced-typing.e2e.test.ts +199 -0
- package/src/queue-drain-after-interactive-ui.e2e.test.ts +151 -0
- package/src/queue-interrupt-drain.e2e.test.ts +166 -0
- package/src/queue-question-select-drain.e2e.test.ts +152 -0
- package/src/runtime-idle-sweeper.ts +76 -0
- package/src/runtime-lifecycle.e2e.test.ts +641 -0
- package/src/schema.sql +173 -0
- package/src/sentry.ts +26 -0
- package/src/session-handler/agent-utils.ts +97 -0
- package/src/session-handler/event-stream-fixtures/real-session-action-buttons.jsonl +45 -0
- package/src/session-handler/event-stream-fixtures/real-session-footer-suppressed-on-pre-idle-interrupt.jsonl +40 -0
- package/src/session-handler/event-stream-fixtures/real-session-permission-external-file.jsonl +23 -0
- package/src/session-handler/event-stream-fixtures/real-session-task-normal.jsonl +22 -0
- package/src/session-handler/event-stream-fixtures/real-session-task-three-parallel-sleeps.jsonl +277 -0
- package/src/session-handler/event-stream-fixtures/real-session-task-user-interruption.jsonl +46 -0
- package/src/session-handler/event-stream-fixtures/session-abort-after-idle-race.jsonl +21 -0
- package/src/session-handler/event-stream-fixtures/session-concurrent-messages-serialized.jsonl +56 -0
- package/src/session-handler/event-stream-fixtures/session-explicit-abort.jsonl +44 -0
- package/src/session-handler/event-stream-fixtures/session-normal-completion.jsonl +29 -0
- package/src/session-handler/event-stream-fixtures/session-tool-call-noisy-stream.jsonl +29 -0
- package/src/session-handler/event-stream-fixtures/session-two-completions-same-session.jsonl +50 -0
- package/src/session-handler/event-stream-fixtures/session-user-interruption.jsonl +59 -0
- package/src/session-handler/event-stream-fixtures/session-voice-queued-followup.jsonl +52 -0
- package/src/session-handler/event-stream-state.test.ts +645 -0
- package/src/session-handler/event-stream-state.ts +608 -0
- package/src/session-handler/model-utils.ts +183 -0
- package/src/session-handler/opencode-session-event-log.ts +130 -0
- package/src/session-handler/thread-runtime-state.ts +212 -0
- package/src/session-handler/thread-session-runtime.ts +4281 -0
- package/src/session-handler.ts +15 -0
- package/src/session-search.test.ts +50 -0
- package/src/session-search.ts +148 -0
- package/src/session-title-rename.test.ts +112 -0
- package/src/startup-service.ts +200 -0
- package/src/startup-time.e2e.test.ts +373 -0
- package/src/store.ts +122 -0
- package/src/system-message.test.ts +612 -0
- package/src/system-message.ts +723 -0
- package/src/task-runner.ts +421 -0
- package/src/task-schedule.test.ts +84 -0
- package/src/task-schedule.ts +311 -0
- package/src/test-utils.ts +435 -0
- package/src/thinking-utils.ts +61 -0
- package/src/thread-message-queue.e2e.test.ts +1219 -0
- package/src/tools.ts +430 -0
- package/src/undici.d.ts +12 -0
- package/src/undo-redo.e2e.test.ts +209 -0
- package/src/unnest-code-blocks.test.ts +713 -0
- package/src/unnest-code-blocks.ts +185 -0
- package/src/upgrade.ts +127 -0
- package/src/utils.ts +212 -0
- package/src/voice-attachment.ts +51 -0
- package/src/voice-handler.ts +908 -0
- package/src/voice-message.e2e.test.ts +1255 -0
- package/src/voice.test.ts +281 -0
- package/src/voice.ts +627 -0
- package/src/wait-session.ts +147 -0
- package/src/websockify.ts +101 -0
- package/src/worker-types.ts +64 -0
- package/src/worktree-lifecycle.e2e.test.ts +391 -0
- package/src/worktree-utils.ts +4 -0
- package/src/worktrees.test.ts +223 -0
- package/src/worktrees.ts +1294 -0
- package/src/xml.test.ts +38 -0
- package/src/xml.ts +121 -0
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: x-articles
|
|
3
|
+
description: >
|
|
4
|
+
Edit x.com (Twitter) long-form article drafts reliably. Use this for
|
|
5
|
+
markdown imports, bulk formatting, code blocks, headings, lists, and
|
|
6
|
+
repeated inline styling. Inspect and validate with Playwriter, but prefer
|
|
7
|
+
x.com (Twitter) article GraphQL mutations for deterministic updates.
|
|
8
|
+
version: 0.1.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<!-- Skill for editing x.com (Twitter) article drafts through Playwriter plus content-state mutations. -->
|
|
12
|
+
|
|
13
|
+
Use this skill when editing long-form article drafts on `x.com/compose/articles`
|
|
14
|
+
(Twitter Articles).
|
|
15
|
+
|
|
16
|
+
## Read Playwriter First
|
|
17
|
+
|
|
18
|
+
Before using this skill, read the `playwriter` skill and run:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
playwriter skill
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This skill assumes Playwriter is already set up and connected to the user's
|
|
25
|
+
existing Chrome session.
|
|
26
|
+
|
|
27
|
+
Read the full output. Do not pipe it through `head`, `tail`, or other
|
|
28
|
+
truncation commands.
|
|
29
|
+
|
|
30
|
+
## Core idea
|
|
31
|
+
|
|
32
|
+
Use Playwriter for three things:
|
|
33
|
+
|
|
34
|
+
1. connect to the already-open x.com (Twitter) article draft
|
|
35
|
+
2. inspect the editor and capture one real network mutation
|
|
36
|
+
3. validate the final rendered result after updates
|
|
37
|
+
|
|
38
|
+
For anything bigger than a tiny tweak, do **not** rely on manual typing inside
|
|
39
|
+
the editor. Generate the article `content_state` locally and send the same
|
|
40
|
+
GraphQL mutation x.com (Twitter) already uses.
|
|
41
|
+
|
|
42
|
+
## Editor model
|
|
43
|
+
|
|
44
|
+
The article body is represented as a `content_state` object with two main
|
|
45
|
+
parts:
|
|
46
|
+
|
|
47
|
+
- `blocks`: ordered content blocks
|
|
48
|
+
- `entity_map`: supporting entities, especially code blocks
|
|
49
|
+
|
|
50
|
+
Important block types:
|
|
51
|
+
|
|
52
|
+
- `unstyled` — normal paragraph
|
|
53
|
+
- `header-two` — section subheading
|
|
54
|
+
- `ordered-list-item` — numbered list item
|
|
55
|
+
- `atomic` — embedded block like a markdown code block
|
|
56
|
+
|
|
57
|
+
Important entity type:
|
|
58
|
+
|
|
59
|
+
- `MARKDOWN` — used for code blocks, with the markdown fence stored in
|
|
60
|
+
`entity_map[*].value.data.markdown`
|
|
61
|
+
|
|
62
|
+
Longer example `content_state`:
|
|
63
|
+
|
|
64
|
+
````json
|
|
65
|
+
{
|
|
66
|
+
"blocks": [
|
|
67
|
+
{
|
|
68
|
+
"key": "k0",
|
|
69
|
+
"text": "event sourcing for application state",
|
|
70
|
+
"type": "header-two",
|
|
71
|
+
"data": {},
|
|
72
|
+
"entity_ranges": [],
|
|
73
|
+
"inline_style_ranges": []
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"key": "k1",
|
|
77
|
+
"text": "your clanker loves state",
|
|
78
|
+
"type": "unstyled",
|
|
79
|
+
"data": {},
|
|
80
|
+
"entity_ranges": [],
|
|
81
|
+
"inline_style_ranges": [
|
|
82
|
+
{ "offset": 19, "length": 5, "style": "Bold" }
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"key": "k2",
|
|
87
|
+
"text": "doubles your final app state",
|
|
88
|
+
"type": "ordered-list-item",
|
|
89
|
+
"data": {},
|
|
90
|
+
"entity_ranges": [],
|
|
91
|
+
"inline_style_ranges": []
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"key": "k3",
|
|
95
|
+
"text": "doubles your bugs",
|
|
96
|
+
"type": "ordered-list-item",
|
|
97
|
+
"data": {},
|
|
98
|
+
"entity_ranges": [],
|
|
99
|
+
"inline_style_ranges": []
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"key": "k4",
|
|
103
|
+
"text": " ",
|
|
104
|
+
"type": "atomic",
|
|
105
|
+
"data": {},
|
|
106
|
+
"entity_ranges": [
|
|
107
|
+
{ "key": 0, "offset": 0, "length": 1 }
|
|
108
|
+
],
|
|
109
|
+
"inline_style_ranges": []
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"key": "k5",
|
|
113
|
+
"text": "if you can derive it, don't store it.",
|
|
114
|
+
"type": "unstyled",
|
|
115
|
+
"data": {},
|
|
116
|
+
"entity_ranges": [],
|
|
117
|
+
"inline_style_ranges": [
|
|
118
|
+
{ "offset": 7, "length": 6, "style": "Bold" }
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
"entity_map": [
|
|
123
|
+
{
|
|
124
|
+
"key": "0",
|
|
125
|
+
"value": {
|
|
126
|
+
"type": "MARKDOWN",
|
|
127
|
+
"mutability": "Mutable",
|
|
128
|
+
"data": {
|
|
129
|
+
"markdown": "```typescript\nfunction shouldShowFooter() {\n return true\n}\n```"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
````
|
|
136
|
+
|
|
137
|
+
This is the minimum mental model:
|
|
138
|
+
|
|
139
|
+
- `blocks` is the article in order
|
|
140
|
+
- each paragraph, heading, and list item is a separate block
|
|
141
|
+
- code blocks are `atomic` blocks that point into `entity_map`
|
|
142
|
+
- inline bold lives in `inline_style_ranges`
|
|
143
|
+
|
|
144
|
+
## Recommended workflow
|
|
145
|
+
|
|
146
|
+
### 1. Open or locate the draft
|
|
147
|
+
|
|
148
|
+
Find the existing article editor page in the connected browser. The URL format
|
|
149
|
+
is:
|
|
150
|
+
|
|
151
|
+
```text
|
|
152
|
+
https://x.com/compose/articles/edit/<article_id>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Always parse and keep the numeric `article_id`. The content mutation needs it.
|
|
156
|
+
|
|
157
|
+
Example Playwriter check:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
playwriter session new
|
|
161
|
+
playwriter -s 1 -e '
|
|
162
|
+
state.page = context.pages().find((p) => {
|
|
163
|
+
return p.url().includes("/compose/articles/edit/")
|
|
164
|
+
})
|
|
165
|
+
if (!state.page) {
|
|
166
|
+
throw new Error("No article editor page found")
|
|
167
|
+
}
|
|
168
|
+
console.log(state.page.url())
|
|
169
|
+
'
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### 2. Explore with small manual edits first
|
|
173
|
+
|
|
174
|
+
Use the UI to learn how the editor reacts before doing bulk updates. Good
|
|
175
|
+
exploration tasks:
|
|
176
|
+
|
|
177
|
+
- add one paragraph
|
|
178
|
+
- convert one block to `Sottotitolo`
|
|
179
|
+
- insert one code block
|
|
180
|
+
- bold one word in one paragraph
|
|
181
|
+
|
|
182
|
+
After each change, inspect the rendered HTML with `getCleanHTML()`.
|
|
183
|
+
|
|
184
|
+
Example validation command:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
playwriter -s 1 -e '
|
|
188
|
+
state.page = context.pages().find((p) => {
|
|
189
|
+
return p.url().includes("/compose/articles/edit/")
|
|
190
|
+
})
|
|
191
|
+
console.log(
|
|
192
|
+
await getCleanHTML({
|
|
193
|
+
locator: state.page.locator("[data-testid=\"composer\"]"),
|
|
194
|
+
showDiffSinceLastCall: false,
|
|
195
|
+
}),
|
|
196
|
+
)
|
|
197
|
+
'
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### 3. Capture real network traffic
|
|
201
|
+
|
|
202
|
+
Watch GraphQL requests while making one tiny manual change. This gives you the
|
|
203
|
+
exact mutation names and payload shapes used by the current x.com (Twitter)
|
|
204
|
+
editor.
|
|
205
|
+
|
|
206
|
+
The two important mutations found in this session were:
|
|
207
|
+
|
|
208
|
+
- `ArticleEntityUpdateTitle`
|
|
209
|
+
- `ArticleEntityUpdateContent`
|
|
210
|
+
|
|
211
|
+
The content mutation URL looked like:
|
|
212
|
+
|
|
213
|
+
```text
|
|
214
|
+
https://x.com/i/api/graphql/<queryId>/ArticleEntityUpdateContent
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
The exact `queryId` can change over time. Do not hardcode it blindly without
|
|
218
|
+
first confirming it from a real request in the current session.
|
|
219
|
+
|
|
220
|
+
Example request logger:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
playwriter -s 1 -e '
|
|
224
|
+
state.page = context.pages().find((p) => {
|
|
225
|
+
return p.url().includes("/compose/articles/edit/")
|
|
226
|
+
})
|
|
227
|
+
state.requests = []
|
|
228
|
+
state.page.removeAllListeners("request")
|
|
229
|
+
state.page.on("request", (req) => {
|
|
230
|
+
if (req.url().includes("ArticleEntity") || req.url().includes("graphql")) {
|
|
231
|
+
state.requests.push({
|
|
232
|
+
url: req.url(),
|
|
233
|
+
method: req.method(),
|
|
234
|
+
postData: req.postData(),
|
|
235
|
+
})
|
|
236
|
+
}
|
|
237
|
+
})
|
|
238
|
+
console.log(
|
|
239
|
+
"Ready: now make one tiny manual edit in the page, then rerun this command to inspect state.requests",
|
|
240
|
+
)
|
|
241
|
+
'
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### 4. Use direct content updates for bulk work
|
|
245
|
+
|
|
246
|
+
Once you know the current mutation shape, generate the full `content_state`
|
|
247
|
+
locally and send the content update directly.
|
|
248
|
+
|
|
249
|
+
This is the reliable path for:
|
|
250
|
+
|
|
251
|
+
- full markdown import
|
|
252
|
+
- replacing large sections
|
|
253
|
+
- converting paragraphs to ordered lists
|
|
254
|
+
- adding one bold keyword per paragraph
|
|
255
|
+
- fixing code block languages
|
|
256
|
+
|
|
257
|
+
Concrete pattern:
|
|
258
|
+
|
|
259
|
+
1. build `content_state` in a local JSON file
|
|
260
|
+
2. read `ct0` from `document.cookie`
|
|
261
|
+
3. send `ArticleEntityUpdateContent` with the same `queryId` and feature flags
|
|
262
|
+
4. reload the page
|
|
263
|
+
|
|
264
|
+
### 5. Reload and validate
|
|
265
|
+
|
|
266
|
+
After every direct mutation:
|
|
267
|
+
|
|
268
|
+
1. reload the article editor page
|
|
269
|
+
2. inspect `getCleanHTML()`
|
|
270
|
+
3. search for expected headings, list items, bold splits, and code labels
|
|
271
|
+
|
|
272
|
+
Do not trust the visual editor alone.
|
|
273
|
+
|
|
274
|
+
Example reload + search:
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
playwriter -s 1 -e '
|
|
278
|
+
state.page = context.pages().find((p) => {
|
|
279
|
+
return p.url().includes("/compose/articles/edit/")
|
|
280
|
+
})
|
|
281
|
+
await state.page.reload({ waitUntil: "domcontentloaded" })
|
|
282
|
+
await waitForPageLoad({ page: state.page, timeout: 8000 })
|
|
283
|
+
console.log(
|
|
284
|
+
await getCleanHTML({
|
|
285
|
+
locator: state.page.locator("[data-testid=\"composer\"]"),
|
|
286
|
+
search: /debugging with event streams|typescript|ordered-list-item/i,
|
|
287
|
+
showDiffSinceLastCall: false,
|
|
288
|
+
}),
|
|
289
|
+
)
|
|
290
|
+
'
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Block type cheatsheet
|
|
294
|
+
|
|
295
|
+
### Paragraphs
|
|
296
|
+
|
|
297
|
+
Use:
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"type": "unstyled",
|
|
302
|
+
"text": "your paragraph text"
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Subheadings
|
|
307
|
+
|
|
308
|
+
Use:
|
|
309
|
+
|
|
310
|
+
```json
|
|
311
|
+
{
|
|
312
|
+
"type": "header-two",
|
|
313
|
+
"text": "debugging with event streams"
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Numbered lists
|
|
318
|
+
|
|
319
|
+
Each item is its own block:
|
|
320
|
+
|
|
321
|
+
```json
|
|
322
|
+
{
|
|
323
|
+
"type": "ordered-list-item",
|
|
324
|
+
"text": "doubles your bug surface"
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Code blocks
|
|
329
|
+
|
|
330
|
+
Code blocks are not plain text blocks. They are:
|
|
331
|
+
|
|
332
|
+
1. one `atomic` block in `blocks`
|
|
333
|
+
2. one `MARKDOWN` entity in `entity_map`
|
|
334
|
+
|
|
335
|
+
The atomic block points to the entity with `entity_ranges`.
|
|
336
|
+
|
|
337
|
+
The entity markdown should include the full fence, for example:
|
|
338
|
+
|
|
339
|
+
````text
|
|
340
|
+
```typescript
|
|
341
|
+
const x = 1
|
|
342
|
+
```
|
|
343
|
+
````
|
|
344
|
+
|
|
345
|
+
If you want the visible language label to say `typescript`, the stored fence
|
|
346
|
+
must be ` ```typescript `, not ` ```ts `.
|
|
347
|
+
|
|
348
|
+
## Inline styles
|
|
349
|
+
|
|
350
|
+
Bold text is represented with `inlineStyleRanges` inside a block.
|
|
351
|
+
|
|
352
|
+
Important session learning:
|
|
353
|
+
|
|
354
|
+
- the style name is `Bold`
|
|
355
|
+
- not `BOLD`
|
|
356
|
+
|
|
357
|
+
Example:
|
|
358
|
+
|
|
359
|
+
```json
|
|
360
|
+
{
|
|
361
|
+
"text": "your clanker loves state",
|
|
362
|
+
"inlineStyleRanges": [
|
|
363
|
+
{ "offset": 19, "length": 5, "style": "Bold" }
|
|
364
|
+
]
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
Always calculate offsets against the raw block text exactly as stored.
|
|
369
|
+
|
|
370
|
+
## Known UI pitfalls
|
|
371
|
+
|
|
372
|
+
The manual editor flow has several traps:
|
|
373
|
+
|
|
374
|
+
### Heading inheritance
|
|
375
|
+
|
|
376
|
+
After creating a heading, pressing `Enter` once can keep the next block in the
|
|
377
|
+
same heading style. To reset to a paragraph, press `Enter` again.
|
|
378
|
+
|
|
379
|
+
### Post-code-block cursor placement
|
|
380
|
+
|
|
381
|
+
Typing after a code block is unreliable. The editor can:
|
|
382
|
+
|
|
383
|
+
- append text to the wrong block
|
|
384
|
+
- split text unexpectedly
|
|
385
|
+
- create stray headings
|
|
386
|
+
- leave part of a sentence in one block and the rest in another
|
|
387
|
+
|
|
388
|
+
For anything more than a tiny manual tweak, use direct content updates instead.
|
|
389
|
+
|
|
390
|
+
### Visual feedback is incomplete
|
|
391
|
+
|
|
392
|
+
The editor can look correct while the underlying block structure is wrong.
|
|
393
|
+
Always inspect the HTML or mutation payload.
|
|
394
|
+
|
|
395
|
+
### Playwriter sessions can reset
|
|
396
|
+
|
|
397
|
+
If the relay server restarts or the extension reconnects, Playwriter sessions
|
|
398
|
+
can disappear. If that happens, create a new Playwriter session and reattach to
|
|
399
|
+
the already-open article page.
|
|
400
|
+
|
|
401
|
+
Recovery command:
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
playwriter session new
|
|
405
|
+
playwriter -s 1 -e '
|
|
406
|
+
state.page = context.pages().find((p) => {
|
|
407
|
+
return p.url().includes("/compose/articles/edit/")
|
|
408
|
+
})
|
|
409
|
+
if (!state.page) {
|
|
410
|
+
throw new Error("No article editor page found")
|
|
411
|
+
}
|
|
412
|
+
console.log(state.page.url())
|
|
413
|
+
'
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
## Auth and request details
|
|
417
|
+
|
|
418
|
+
Direct content updates need proper auth headers. In this session, the direct
|
|
419
|
+
`fetch()` worked only after including:
|
|
420
|
+
|
|
421
|
+
- the X bearer token
|
|
422
|
+
- `x-csrf-token` from the `ct0` cookie
|
|
423
|
+
- the standard X active-user/auth/client-language headers
|
|
424
|
+
|
|
425
|
+
If you get `403`, inspect the successful browser request and match its headers.
|
|
426
|
+
|
|
427
|
+
In this session, the direct fetch succeeded only after matching:
|
|
428
|
+
|
|
429
|
+
- bearer token
|
|
430
|
+
- `x-csrf-token`
|
|
431
|
+
- `x-twitter-active-user`
|
|
432
|
+
- `x-twitter-auth-type`
|
|
433
|
+
- `x-twitter-client-language`
|
|
434
|
+
|
|
435
|
+
## Validation checklist
|
|
436
|
+
|
|
437
|
+
After updating an article, verify all of these:
|
|
438
|
+
|
|
439
|
+
1. correct title in the title field
|
|
440
|
+
2. headings appear as `header-two`
|
|
441
|
+
3. ordered lists appear as `ordered-list-item`
|
|
442
|
+
4. code blocks render as `markdown-code-block`
|
|
443
|
+
5. code block language labels say what you expect, for example `typescript`
|
|
444
|
+
6. bold keywords are split into separate styled spans in the HTML
|
|
445
|
+
7. no stray empty headings or broken split paragraphs remain
|
|
446
|
+
|
|
447
|
+
## Useful recipes
|
|
448
|
+
|
|
449
|
+
### Import a markdown article
|
|
450
|
+
|
|
451
|
+
1. parse the markdown locally
|
|
452
|
+
2. map paragraphs to `unstyled`
|
|
453
|
+
3. map `##` headings to `header-two`
|
|
454
|
+
4. map numbered list items to `ordered-list-item`
|
|
455
|
+
5. map fenced code blocks to `atomic` + `MARKDOWN` entities
|
|
456
|
+
6. send `ArticleEntityUpdateContent`
|
|
457
|
+
7. reload and validate
|
|
458
|
+
|
|
459
|
+
The fastest implementation is usually:
|
|
460
|
+
|
|
461
|
+
1. generate `./tmp/x-article-content-state.json`
|
|
462
|
+
2. read it from a Playwriter command with `fs.readFileSync`
|
|
463
|
+
3. push it with the direct content mutation
|
|
464
|
+
|
|
465
|
+
### Bold one keyword per paragraph
|
|
466
|
+
|
|
467
|
+
1. choose one keyword per paragraph
|
|
468
|
+
2. compute exact `offset` and `length`
|
|
469
|
+
3. add `inlineStyleRanges` with style `Bold`
|
|
470
|
+
4. push the updated `content_state`
|
|
471
|
+
5. reload and verify the HTML splits around the bold span
|
|
472
|
+
|
|
473
|
+
### Fix code language labels
|
|
474
|
+
|
|
475
|
+
Update the markdown entity fences. Example:
|
|
476
|
+
|
|
477
|
+
- bad: ` ```ts `
|
|
478
|
+
- good: ` ```typescript `
|
|
479
|
+
|
|
480
|
+
Then resend the full `content_state` and reload the editor.
|
|
481
|
+
|
|
482
|
+
## Minimal bulk update example
|
|
483
|
+
|
|
484
|
+
Use this pattern when you already have the right `queryId` and payload shape:
|
|
485
|
+
|
|
486
|
+
```bash
|
|
487
|
+
playwriter -s 1 -e '
|
|
488
|
+
const fs = require("node:fs")
|
|
489
|
+
state.page = context.pages().find((p) => {
|
|
490
|
+
return p.url().includes("/compose/articles/edit/")
|
|
491
|
+
})
|
|
492
|
+
const articleId = state.page.url().match(/edit\/(\d+)/)?.[1]
|
|
493
|
+
const contentState = JSON.parse(
|
|
494
|
+
fs.readFileSync("./tmp/x-article-content-state.json", "utf8"),
|
|
495
|
+
)
|
|
496
|
+
const csrfToken = await state.page.evaluate(() => {
|
|
497
|
+
return document.cookie
|
|
498
|
+
.split("; ")
|
|
499
|
+
.find((x) => x.startsWith("ct0="))
|
|
500
|
+
?.slice(4) || ""
|
|
501
|
+
})
|
|
502
|
+
const payload = {
|
|
503
|
+
variables: {
|
|
504
|
+
content_state: contentState,
|
|
505
|
+
article_entity: articleId,
|
|
506
|
+
},
|
|
507
|
+
features: {
|
|
508
|
+
profile_label_improvements_pcf_label_in_post_enabled: true,
|
|
509
|
+
responsive_web_profile_redirect_enabled: false,
|
|
510
|
+
rweb_tipjar_consumption_enabled: false,
|
|
511
|
+
verified_phone_label_enabled: false,
|
|
512
|
+
responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
|
|
513
|
+
responsive_web_graphql_timeline_navigation_enabled: true,
|
|
514
|
+
},
|
|
515
|
+
queryId: "<capture-from-real-request>",
|
|
516
|
+
}
|
|
517
|
+
const response = await state.page.evaluate(async ({ payload, csrfToken }) => {
|
|
518
|
+
const res = await fetch(
|
|
519
|
+
`https://x.com/i/api/graphql/${payload.queryId}/ArticleEntityUpdateContent`,
|
|
520
|
+
{
|
|
521
|
+
method: "POST",
|
|
522
|
+
credentials: "include",
|
|
523
|
+
headers: {
|
|
524
|
+
authorization: "<capture-from-real-request>",
|
|
525
|
+
"content-type": "application/json",
|
|
526
|
+
"x-csrf-token": csrfToken,
|
|
527
|
+
"x-twitter-active-user": "yes",
|
|
528
|
+
"x-twitter-auth-type": "OAuth2Session",
|
|
529
|
+
"x-twitter-client-language": "it",
|
|
530
|
+
},
|
|
531
|
+
body: JSON.stringify(payload),
|
|
532
|
+
},
|
|
533
|
+
)
|
|
534
|
+
return { status: res.status, text: await res.text() }
|
|
535
|
+
}, { payload, csrfToken })
|
|
536
|
+
console.log(response.status)
|
|
537
|
+
console.log(response.text.slice(0, 1000))
|
|
538
|
+
'
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
Replace the bearer token and `queryId` with values captured from a successful
|
|
542
|
+
browser request in the current session.
|
|
543
|
+
|
|
544
|
+
## Default strategy
|
|
545
|
+
|
|
546
|
+
Use this default unless the task is tiny:
|
|
547
|
+
|
|
548
|
+
1. inspect the current draft in the browser
|
|
549
|
+
2. capture one real content mutation from X
|
|
550
|
+
3. generate the final `content_state` locally
|
|
551
|
+
4. update the draft with the same mutation shape
|
|
552
|
+
5. validate the result in the live editor HTML
|
|
553
|
+
|
|
554
|
+
That is the fastest path and the most likely to work in one shot.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zele
|
|
3
|
+
description: >
|
|
4
|
+
Control Gmail and Google Calendar via CLI. Read, search, send, reply, and forward
|
|
5
|
+
emails. Create, update, and delete calendar events. Manage drafts, labels, and attachments.
|
|
6
|
+
Supports multiple Google accounts. Use this skill whenever the user asks to check email,
|
|
7
|
+
send messages, schedule meetings, or manage their calendar.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# zele — Gmail & Google Calendar CLI
|
|
11
|
+
|
|
12
|
+
A multi-account Gmail and Google Calendar client. Output is YAML, pipe-friendly.
|
|
13
|
+
|
|
14
|
+
## Setup
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# install (requires bun)
|
|
18
|
+
bun install -g zele
|
|
19
|
+
|
|
20
|
+
# show connected accounts
|
|
21
|
+
zele whoami
|
|
22
|
+
|
|
23
|
+
# authenticate (opens browser, supports multiple accounts)
|
|
24
|
+
zele login
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Remote/headless login:** `zele login` is interactive — it prints an authorization URL and waits for a redirect URL to be pasted back. In agent/headless environments, run it inside tmux so the process persists:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# start login in a tmux session
|
|
31
|
+
tmux new-session -d -s zele-login 'zele login'
|
|
32
|
+
|
|
33
|
+
# read the authorization URL from tmux output
|
|
34
|
+
tmux capture-pane -t zele-login -p
|
|
35
|
+
|
|
36
|
+
# after the user completes consent in their browser, paste the redirect URL
|
|
37
|
+
tmux send-keys -t zele-login 'http://localhost:...?code=...' Enter
|
|
38
|
+
|
|
39
|
+
# verify login succeeded
|
|
40
|
+
tmux capture-pane -t zele-login -p
|
|
41
|
+
tmux kill-session -t zele-login
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Important
|
|
45
|
+
|
|
46
|
+
**Always run `zele --help` before using.** The help output is the source of truth for all commands, options, and syntax. Run `zele <command> --help` for subcommand details (e.g. `zele mail send --help`). NEVER use head to truncate the output. read it fully.
|
|
47
|
+
|
|
48
|
+
Running `zele` with no subcommand launches a human-friendly TUI for browsing email. **Agents should not use the TUI** — always use the CLI subcommands (`zele mail list`, `zele cal events`, etc.) which output structured YAML.
|
|
49
|
+
|
|
50
|
+
## Capabilities
|
|
51
|
+
|
|
52
|
+
- **Mail:** list, search, read, send, reply, forward, star, archive, trash, label, watch for new emails, manage filters
|
|
53
|
+
- **Drafts:** list, create, get, send, delete
|
|
54
|
+
- **Calendar:** list calendars, list/search events, create/update/delete events, RSVP, free/busy
|
|
55
|
+
- **Labels:** list, create, delete, unread counts
|
|
56
|
+
- **Attachments:** list per thread, download
|
|
57
|
+
- **Multi-account:** all commands support `--account <email>` to filter; list/search merge across accounts
|
|
58
|
+
|
|
59
|
+
## Account discovery
|
|
60
|
+
|
|
61
|
+
When the user asks to check emails **for a specific account** (e.g. "check my work email", "what's new on my personal Gmail?"), always run `zele whoami` first to list the connected accounts and find the exact email address to pass to `--account`. Never guess the email — use the output of `zele whoami` to pick the right one.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# list connected accounts
|
|
65
|
+
zele whoami
|
|
66
|
+
|
|
67
|
+
# then use the email from the output
|
|
68
|
+
zele mail list --account user@work.com
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Examples
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# list inbox
|
|
75
|
+
zele mail list
|
|
76
|
+
|
|
77
|
+
# list only unread emails
|
|
78
|
+
zele mail list --filter "is:unread"
|
|
79
|
+
|
|
80
|
+
# list unread emails with attachments in inbox
|
|
81
|
+
zele mail list --filter "is:unread has:attachment"
|
|
82
|
+
|
|
83
|
+
# combine filter with folder
|
|
84
|
+
zele mail list --filter "from:github" --folder sent
|
|
85
|
+
|
|
86
|
+
# search mail
|
|
87
|
+
zele mail search "from:github subject:review"
|
|
88
|
+
|
|
89
|
+
# read a thread (thread IDs come from list/search output)
|
|
90
|
+
zele mail read <threadId>
|
|
91
|
+
|
|
92
|
+
# send an email with attachment
|
|
93
|
+
zele mail send --to alice@example.com --subject "Report" --body "See attached" --attach report.pdf
|
|
94
|
+
|
|
95
|
+
# reply all
|
|
96
|
+
zele mail reply <threadId> --body "Thanks!" --all
|
|
97
|
+
|
|
98
|
+
# watch inbox for new mail (polls every 15s)
|
|
99
|
+
zele mail watch
|
|
100
|
+
|
|
101
|
+
# today's calendar events across all accounts
|
|
102
|
+
zele cal events --today --all
|
|
103
|
+
|
|
104
|
+
# create a meeting with Google Meet
|
|
105
|
+
zele cal create --summary "Standup" --from tomorrow --to +30m --meet --attendees bob@example.com
|
|
106
|
+
|
|
107
|
+
# check free/busy
|
|
108
|
+
zele cal freebusy --from today --to +8h
|
|
109
|
+
|
|
110
|
+
# list Gmail filters
|
|
111
|
+
zele mail filter list
|
|
112
|
+
```
|