@botharness/core 1.3.0 → 1.3.1
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/CHANGELOG.md +28 -2
- package/CHANGELOG.zh.md +28 -2
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +425 -5
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,11 +6,37 @@ Unreleased entries arrive as [change fragments](changes/README.md) and are folde
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
-
No changes since 1.3.
|
|
9
|
+
No changes since 1.3.1 yet.
|
|
10
|
+
|
|
11
|
+
## [1.3.1] - 2026-10-14
|
|
12
|
+
|
|
13
|
+
Entering `deepseekbot` on the DSH Plugins page installs it in one step again, now on DSH 0.2.0-rc.2 only; the `deepseekbot` CLI can configure a Host, operate a Bot's Browser and Computer and manage token-based IM apps; the Bot Browser panel streams page changes live; and a development build of the mobile app shows the Roster and native PersonaBot DMs.
|
|
14
|
+
|
|
15
|
+
### Breaking Changes
|
|
16
|
+
|
|
17
|
+
- DeepSeekBot 1.3.1 needs exactly DSH 0.2.0-rc.2, the only release its bundled Computer runtime works with. DSH 0.2.0-rc.1 and the 0.2.1 alphas refuse it at installation with a message naming DeepSeekBot; upgrade DSH to 0.2.0-rc.2 first, or stay on DeepSeekBot 1.3.0 ([#1672](https://github.com/BotHarness/DeepSeekBot/pull/1672)).
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- The getting-started checklist adds "See how it works in DSH", a short walkthrough from the Bot's Sessions tab into the same session in DSH and back to the Bot DM ([#1496](https://github.com/BotHarness/DeepSeekBot/issues/1496)).
|
|
22
|
+
- Added a development build of the DeepSeekBot mobile app that connects to the Human's Host over Tailscale HTTPS and shows the Roster of PersonaBots and Channels with live state, and recorded why it reads live state over DSH's Remote stream socket in [ADR-0200](docs/adr/0200-the-mobile-app-reads-live-state-over-the-dsh-remote-stream-socket.md) ([#1650](https://github.com/BotHarness/DeepSeekBot/issues/1650)).
|
|
23
|
+
- Added a native PersonaBot DM to the mobile app's development build: tap a Bot in the Roster to read the DM from an on-device cache and then the Host, page back through older history, send with visible sending, sent and not-sent states (a retry never sends twice), watch the reply stream in, and resume after the app returns from the background without lost or repeated messages; replies render as selectable Markdown in system dark mode and zh/en ([#1651](https://github.com/BotHarness/DeepSeekBot/issues/1651)).
|
|
24
|
+
- The `deepseekbot` CLI can now configure what the Web UI configures, so an agent can set up and test a Host on its own: `browser-access-set`, `computer-access-set`, `access` and `access-set` (full access needs `--acknowledge-risk`), `settings` and `settings-set`, `workspace-create` and `group-create` ([#1677](https://github.com/BotHarness/DeepSeekBot/issues/1677)).
|
|
25
|
+
- The `deepseekbot` CLI can now operate a Bot's Browser and the Computer: `browser-open`, `browser-frame` (save the current picture to a file), `browser-takeover`, `browser-stop`, `computer-start`, `computer-status` and `computer-stop`. `grants` now also works against a live Host ([#1677](https://github.com/BotHarness/DeepSeekBot/issues/1677)).
|
|
26
|
+
- The `deepseekbot` CLI can now add, inspect and remove token-based IM apps (Discord, Slack, QQ, DingTalk, WeCom, Matrix), bind and unbind them to a Bot, show each binding's reception, and connect an external group conversation to a Channel: `im-app-add`, `im-app-status`, `im-app-remove`, `im-bind`, `im-unbind`, `im-status`, `channel-connect` and `channel-ingests`. Credentials are read from stdin only and never printed ([#1677](https://github.com/BotHarness/DeepSeekBot/issues/1677)).
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- The Bot Browser panel in the Host web page now follows the page as it changes instead of refreshing once per second. Chrome pushes a new picture whenever the page repaints, so typing, scrolling and clicking show up in about a quarter of a second, and a page that does not change costs nothing. Runtimes that cannot stream keep the once-per-second picture ([#1636](https://github.com/BotHarness/DeepSeekBot/issues/1636)).
|
|
10
31
|
|
|
11
32
|
### Documentation
|
|
12
33
|
|
|
13
|
-
- Documented how a paired phone keeps reaching the Host across restarts: a device gateway on the same `dsh web` listener verifies a per-device credential, forwards to the native carriers, and revokes one device at once, including its open streams; runtime behavior is unchanged ([ADR-0199](
|
|
34
|
+
- Documented how a paired phone keeps reaching the Host across restarts: a device gateway on the same `dsh web` listener verifies a per-device credential, forwards to the native carriers, and revokes one device at once, including its open streams; runtime behavior is unchanged ([ADR-0199](docs/adr/0199-paired-devices-reach-the-host-through-a-same-listener-device-gateway.md), [#1649](https://github.com/BotHarness/DeepSeekBot/issues/1649)).
|
|
35
|
+
- The GitHub README and installation guides now install `deepseekbot@1.3.0` and list its highlights, and the npm package README states the DSH 0.2.0 minimum ([#1667](https://github.com/BotHarness/DeepSeekBot/pull/1667)).
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- Entering `deepseekbot` on the DSH Plugins page installs it in one step again; 1.3.0 first stopped to ask for `agent-browser` build approval. The Browser trial driver's `agent-browser` is now an optional add-on. DeepSeekBot now declares that it needs DSH 0.2.0-rc.2, the only release its bundled Computer runtime works with, so other DSH versions get a clear refusal naming DeepSeekBot. Every release is now installed into a fresh Profile before it is published ([#1672](https://github.com/BotHarness/DeepSeekBot/pull/1672)).
|
|
14
40
|
|
|
15
41
|
## [1.3.0] - 2026-10-14
|
|
16
42
|
|
package/CHANGELOG.zh.md
CHANGED
|
@@ -6,11 +6,37 @@
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
-
1.3.
|
|
9
|
+
1.3.1 之后暂无变化。
|
|
10
|
+
|
|
11
|
+
## [1.3.1] - 2026-10-14
|
|
12
|
+
|
|
13
|
+
在 DSH 插件页输入 `deepseekbot` 重新一步装好,现在只支持 DSH 0.2.0-rc.2;`deepseekbot` 命令行可以配置 Host、操作 Bot 的浏览器和 Computer、管理基于 token 的 IM 应用;Bot 浏览器面板随页面变化实时刷新;手机 App 开发版可以查看 Bot 列表并使用原生 PersonaBot 私信。
|
|
14
|
+
|
|
15
|
+
### Breaking Changes
|
|
16
|
+
|
|
17
|
+
- DeepSeekBot 1.3.1 只支持 DSH 0.2.0-rc.2(随包的 Computer 运行时只兼容这个版本)。DSH 0.2.0-rc.1 和 0.2.1 alpha 安装时会拒绝它,并给出点名 DeepSeekBot 的提示;请先把 DSH 升级到 0.2.0-rc.2,或继续使用 DeepSeekBot 1.3.0([#1672](https://github.com/BotHarness/DeepSeekBot/pull/1672))。
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- 上手清单新增「看看它在 DSH 里怎么干活」:一段小导览,从 Bot 的「会话」标签跳到 DSH 里的同一个会话,再回到 Bot 私聊([#1496](https://github.com/BotHarness/DeepSeekBot/issues/1496))。
|
|
22
|
+
- 新增 DeepSeekBot 手机 App 的开发版:通过 Tailscale HTTPS 连接用户自己的 Host,显示 PersonaBot 与频道列表及其实时状态;并在 [ADR-0200](docs/adr/0200-the-mobile-app-reads-live-state-over-the-dsh-remote-stream-socket.md) 记录了它通过 DSH Remote stream socket 读取实时状态的原因([#1650](https://github.com/BotHarness/DeepSeekBot/issues/1650))。
|
|
23
|
+
- 手机 App 开发版新增原生 PersonaBot 私信:在列表中点开 Bot,先显示设备上的缓存再与 Host 对齐,可向上翻看更早的历史;发送时可见"发送中/已送达/未发送",重试不会重复发送;Bot 回复实时流入;App 从后台回来后续接,不丢失也不重复消息;回复以可选中的 Markdown 显示,跟随系统深色模式与中英文([#1651](https://github.com/BotHarness/DeepSeekBot/issues/1651))。
|
|
24
|
+
- `deepseekbot` 命令行现在可以完成 Web 界面里的这些配置,Agent 能独立搭建和测试 Host:`browser-access-set`、`computer-access-set`、`access` 与 `access-set`(完全访问需加 `--acknowledge-risk`)、`settings` 与 `settings-set`、`workspace-create` 和 `group-create`([#1677](https://github.com/BotHarness/DeepSeekBot/issues/1677))。
|
|
25
|
+
- `deepseekbot` 命令行现在可以操作 Bot 的浏览器和 Computer:`browser-open`、`browser-frame`(把当前画面保存到文件)、`browser-takeover`、`browser-stop`、`computer-start`、`computer-status` 和 `computer-stop`。`grants` 现在也支持在线 Host([#1677](https://github.com/BotHarness/DeepSeekBot/issues/1677))。
|
|
26
|
+
- `deepseekbot` 命令行现在可以添加、查看和移除基于 token 的 IM 应用(Discord、Slack、QQ、钉钉、企业微信、Matrix),把它们绑定到 Bot 或解绑,查看每个绑定的接收状态,并把外部群聊接入 Channel:`im-app-add`、`im-app-status`、`im-app-remove`、`im-bind`、`im-unbind`、`im-status`、`channel-connect` 和 `channel-ingests`。凭证只从标准输入读取,绝不打印([#1677](https://github.com/BotHarness/DeepSeekBot/issues/1677))。
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Host 网页里的 Bot 浏览器面板现在随页面变化实时刷新,不再每秒刷新一次。页面每次重绘时 Chrome 都会推送新画面,所以输入、滚动和点击大约四分之一秒就能看到,页面不变时不产生额外开销。无法推流的运行方式仍保持每秒一张画面([#1636](https://github.com/BotHarness/DeepSeekBot/issues/1636))。
|
|
10
31
|
|
|
11
32
|
### Documentation
|
|
12
33
|
|
|
13
|
-
- 记录配对手机如何在 Host 重启后继续访问:同一 `dsh web` 监听器上的设备网关校验每台设备的凭据,转发到原生通道,并可立即撤销单台设备及其已打开的数据流;运行时行为不变([ADR-0199](
|
|
34
|
+
- 记录配对手机如何在 Host 重启后继续访问:同一 `dsh web` 监听器上的设备网关校验每台设备的凭据,转发到原生通道,并可立即撤销单台设备及其已打开的数据流;运行时行为不变([ADR-0199](docs/adr/0199-paired-devices-reach-the-host-through-a-same-listener-device-gateway.md),[#1649](https://github.com/BotHarness/DeepSeekBot/issues/1649))。
|
|
35
|
+
- GitHub README 和安装指南改为安装 `deepseekbot@1.3.0` 并列出它的亮点,npm 包说明写明最低需要 DSH 0.2.0([#1667](https://github.com/BotHarness/DeepSeekBot/pull/1667))。
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- 在 DSH 插件页输入 `deepseekbot` 重新一步装好;1.3.0 会先停下来要求批准 `agent-browser` 构建。Browser 试用驱动用的 `agent-browser` 改为可选加装。DeepSeekBot 现在明确声明需要 DSH 0.2.0-rc.2(随包的 Computer 运行时只兼容这个版本),其他 DSH 版本会直接看到点名 DeepSeekBot 的不兼容提示。每次发版前都会先在全新 Profile 里实装验证([#1672](https://github.com/BotHarness/DeepSeekBot/pull/1672))。
|
|
14
40
|
|
|
15
41
|
## [1.3.0] - 2026-10-14
|
|
16
42
|
|
package/dist/index.d.mts
CHANGED
|
@@ -15188,7 +15188,7 @@ interface BotCreateFailure {
|
|
|
15188
15188
|
name: string;
|
|
15189
15189
|
};
|
|
15190
15190
|
}
|
|
15191
|
-
export declare const BOT_CREATE_HELP = "deepseekbot: create PersonaBots without clicking\n\nUsage:\n deepseekbot create --name <name> [--persona <text> | --persona-stdin]\n [--description <text>] [--role <tag> ...]\n [--preset <model-preset-id>] [--home <dsh-home>]\n deepseekbot create --name <name> --from-git <url | owner/repo> [...]\n deepseekbot create [--name <name>] --from-zip <file> [...]\n deepseekbot create [--name <name>] --from-dir <directory> [...]\n deepseekbot list [--home <dsh-home>]\n deepseekbot show <id> [--home <dsh-home>]\n deepseekbot model-presets [--home <dsh-home>]\n deepseekbot model-preset-create --name <preset> --orchestrator-provider <p> --orchestrator-model <m>\n [--orchestrator-effort <e>] --assignment-provider <p>\n --assignment-model <m> [--assignment-effort <e>] [--home <dsh-home>]\n deepseekbot model-preset-apply <id> --preset <preset-id> [--home <dsh-home>]\n deepseekbot model-plan <id> [--home <dsh-home>]\n deepseekbot pause <id> [--home <dsh-home>]\n deepseekbot resume <id> [--home <dsh-home>]\n deepseekbot update <id> [--name <name>] [--description <text>] [--role <tag> ...] [--home <dsh-home>]\n deepseekbot human-name-set (--name <text> | --clear) [--home <dsh-home>]\n deepseekbot channel-human-name-set <channel> (--nickname <text> | --clear) [--home <dsh-home>]\n deepseekbot memory-snapshot <id> [--home <dsh-home>]\n deepseekbot memory-file <id> --path <file> [--home <dsh-home>]\n deepseekbot memory-history <id> [--limit <n>] [--home <dsh-home>]\n deepseekbot memory-diff <id> --sha <commit> [--home <dsh-home>]\n deepseekbot memory-save <id> --path <file> (--body <text> | --body-stdin)\n [--expected-head <sha>] [--edit-id <id>] [--home <dsh-home>]\n deepseekbot channels [--home <dsh-home>]\n deepseekbot channel-messages <channel> [--limit <n>] [--before <id>] [--home <dsh-home>]\n deepseekbot grants <id> [--home <dsh-home>]\n deepseekbot grant-revoke <id> --grant <grant> [--home <dsh-home>]\n deepseekbot grant-write-set <id> --grant <grant> (--enabled | --disabled) [--home <dsh-home>]\n deepseekbot schedules <id> [--home <dsh-home>]\n deepseekbot schedule-create <id> --title <t> --prompt <p> (--every <s> | --daily <HH:MM> |\n --weekly <HH:MM> --weekdays <0-6,..> | --once-date <d> --once-time <t> |\n --cron <expr>) [--timezone <tz>] [--enabled|--disabled] [--locked] [--home <dsh-home>]\n deepseekbot schedule-update <id> --sid <schedule> [--title ...] [--prompt ...] [trigger ...] [--home <dsh-home>]\n deepseekbot schedule-delete <id> --sid <schedule> [--home <dsh-home>]\n deepseekbot schedule-history <id> --sid <schedule> [--home <dsh-home>]\n deepseekbot schedule-run-now <id> --sid <schedule> [--home <dsh-home>]\n deepseekbot schedule-preview (--every <s> | --daily ... | ...) [--home <dsh-home>]\n deepseekbot pairings <id> [--home <dsh-home>]\n deepseekbot secret-put <NAME> [--home <dsh-home>] secret value arrives on stdin only\n deepseekbot secret-list [--home <dsh-home>] names, sources, writability; never values\n deepseekbot secret-unset <NAME> [--home <dsh-home>]\n deepseekbot search <words> find commands by words\n deepseekbot send <id> (--body <text> | --body-stdin) [--message-id human-<UUID>] [--timeout <seconds>]\n deepseekbot send-status <id> --message-id <Human-message-id>\n deepseekbot tool-approval-status <channel> --message-id <card-id>\n deepseekbot tool-approval-decide <channel> --message-id <card-id> --outcome allowed-once|rejected\n deepseekbot user-question-status <channel> --message-id <card-id>\n deepseekbot user-question-answer <channel> --message-id <card-id> --answer-stdin\n deepseekbot ask-secret-status <channel> --message-id <card-id>\n deepseekbot ask-secret-submit <channel> --message-id <card-id> --values-stdin\n deepseekbot ask-secret-cancel <channel> --message-id <card-id>\n deepseekbot ask-secret-account-status <channel> --message-id <configured-card-id>\n deepseekbot ask-secret-replace <channel> --message-id <configured-card-id>\n deepseekbot ask-secret-revoke <channel> --message-id <configured-card-id>\n deepseekbot ask-secret-accounts <id>\n deepseekbot release-info [--since <version>]\n deepseekbot workspace-options\n deepseekbot grant-create <id> --workspace <workspace-id>\n deepseekbot bot-attention <id> [--limit <1-100>] [--cursor <source-id>] [--state <state>]\n deepseekbot bot-sessions <id> [--limit <1-100>]\n deepseekbot bot-activity <id> [--limit <1-100>]\n deepseekbot bot-delete-preview <id>\n deepseekbot bot-delete-confirm <id> --confirmation-stdin\n deepseekbot bot-delete-retry <id>\n deepseekbot im-apps\n deepseekbot im-authorize <feishu|weixin>\n deepseekbot im-credentials <attempt-id> --credentials-stdin\n deepseekbot im-verify <attempt-id> --verification-stdin\n deepseekbot pairing-status <attempt-id> [--wait] [--timeout <seconds>]\n deepseekbot im-cancel <attempt-id>\n deepseekbot --help | deepseekbot create --help\n\nOutput:\n stdout carries JSON, pretty by default; --compact condenses it to one line\n for agents. Human-readable lines go to stderr only.\n\nSources (exactly one per create):\n (none) blank bot; --preset applies a model preset, otherwise the model\n step reports no-model-yet and activation stays a Settings task\n --from-git clone a Git repository as Memory (HTTPS, SSH, or owner/repo\n shorthand for https://github.com/owner/repo.git)\n --from-zip import a bot bundle zip (same shape as the bot-zip export)\n --from-dir import a bot bundle directory (same shape as the bot-zip export;\n a .git subtree is skipped as files but packed as history)\n\nMachine contract:\n stdout carries exactly one JSON document. Success exits 0 with the bot id,\n DM channel, data directory, per-step statuses, and next actions. Failure\n exits non-zero with {\"error\": {\"code\", \"message\"}} using a stable code\n (usage, secret-in-argv, bad-zip, bad-bundle, bad-ref, unknown-preset,\n unknown-bot, unknown-channel, duplicate-preset, git-not-found, git-clone-failed, git-clone-timeout,\n memory-unavailable, lease-unavailable, bad-credentials, invalid-input). Human-readable lines go to stderr only.\n Stop the Host before offline database commands: the writer lease is exclusive.\n\nLive Host:\n Require --host <loopback-origin|tailnet-HTTPS-origin> or DEEPSEEKBOT_HOST.\n Auth: DEEPSEEKBOT_HOST_TOKEN or --token-file <private-file>, never a token in argv.\n Login is fresh per call; cookies stay in memory. --timeout defaults to 120 seconds (max 600).\n send returns its receipt and exact committed replies. Inspect send-status on timeout\n before retrying; send never automatically resubmits. channel-messages also accepts --host.\n create (blank, Git, Zip and directory), list/show, model-presets/create/apply/plan\n and channels use the Host when --host or DEEPSEEKBOT_HOST is set. Do not combine\n --home with an online target; failures never fall back to local database access.\n Zip/directory uploads contain local bytes; Git cloning uses the Host environment.\n Creation errors retain known bot/steps; outcome unknown means inspect before retrying.\n Bot diagnostics are bounded summaries, not full execution logs or live streams.\n Deletion requires preview then stdin {\"token\":\"<preview.token>\"}; Memory is retained.\n Question stdin: {\"answers\":[{\"id\":\"question-id\",\"selected\":[\"option\"],\"custom\":\"optional\"}]}\n Private input stdin: {\"field_id\":\"confidential value\",...}; only declared fields.\n Private input commands return status/acceptance only and never retry a submission.\n\nModels:\n model-presets lists the profile presets; model-preset-create mints one from\n explicit provider/model routes; model-preset-apply and model-plan read and\n write through the Host when selected, otherwise directly offline. Routes are shape-checked plus\n provider-existence-checked offline; catalog liveness and readiness\n inspection stay Host-side, and model-plan reports readiness deferred.\n\nMemory:\n memory-snapshot/file/history/diff read the bot memory store; memory-save\n writes one file and commits it. --expected-head defaults to the current\n HEAD (pass it explicitly for compare-and-swap); --edit-id defaults to a\n fresh UUID and makes repeat submissions idempotent. Conflicts, missing\n files, and bad shas fail coded (memory-conflict, not-found,\n memory-unknown-commit).\n\nIdentity:\n The bot name is a label. Every create mints a new bot id, so reusing a name\n always returns a second bot and still exits 0.\n\nSecrets:\n Never pass secrets as arguments: any --api-key/--token/--secret/--password\n style flag is a hard error (secret-in-argv). Model keys and tokens travel in\n the environment (for example DEEPSEEK_API_KEY, GITHUB_TOKEN) or a stdin pipe\n (--persona-stdin); result JSON never contains secret values.\n\nHome:\n --home points at the target DSH_HOME (a fresh directory works with zero\n clicks); without it, DSH_HOME from the environment is used.\n";
|
|
15191
|
+
export declare const BOT_CREATE_HELP = "deepseekbot: create PersonaBots without clicking\n\nUsage:\n deepseekbot create --name <name> [--persona <text> | --persona-stdin]\n [--description <text>] [--role <tag> ...]\n [--preset <model-preset-id>] [--home <dsh-home>]\n deepseekbot create --name <name> --from-git <url | owner/repo> [...]\n deepseekbot create [--name <name>] --from-zip <file> [...]\n deepseekbot create [--name <name>] --from-dir <directory> [...]\n deepseekbot list [--home <dsh-home>]\n deepseekbot show <id> [--home <dsh-home>]\n deepseekbot model-presets [--home <dsh-home>]\n deepseekbot model-preset-create --name <preset> --orchestrator-provider <p> --orchestrator-model <m>\n [--orchestrator-effort <e>] --assignment-provider <p>\n --assignment-model <m> [--assignment-effort <e>] [--home <dsh-home>]\n deepseekbot model-preset-apply <id> --preset <preset-id> [--home <dsh-home>]\n deepseekbot model-plan <id> [--home <dsh-home>]\n deepseekbot pause <id> [--home <dsh-home>]\n deepseekbot resume <id> [--home <dsh-home>]\n deepseekbot update <id> [--name <name>] [--description <text>] [--role <tag> ...] [--home <dsh-home>]\n deepseekbot human-name-set (--name <text> | --clear) [--home <dsh-home>]\n deepseekbot channel-human-name-set <channel> (--nickname <text> | --clear) [--home <dsh-home>]\n deepseekbot memory-snapshot <id> [--home <dsh-home>]\n deepseekbot memory-file <id> --path <file> [--home <dsh-home>]\n deepseekbot memory-history <id> [--limit <n>] [--home <dsh-home>]\n deepseekbot memory-diff <id> --sha <commit> [--home <dsh-home>]\n deepseekbot memory-save <id> --path <file> (--body <text> | --body-stdin)\n [--expected-head <sha>] [--edit-id <id>] [--home <dsh-home>]\n deepseekbot channels [--home <dsh-home>]\n deepseekbot channel-messages <channel> [--limit <n>] [--before <id>] [--home <dsh-home>]\n deepseekbot grants <id> [--home <dsh-home>]\n deepseekbot grant-revoke <id> --grant <grant> [--home <dsh-home>]\n deepseekbot grant-write-set <id> --grant <grant> (--enabled | --disabled) [--home <dsh-home>]\n deepseekbot schedules <id> [--home <dsh-home>]\n deepseekbot schedule-create <id> --title <t> --prompt <p> (--every <s> | --daily <HH:MM> |\n --weekly <HH:MM> --weekdays <0-6,..> | --once-date <d> --once-time <t> |\n --cron <expr>) [--timezone <tz>] [--enabled|--disabled] [--locked] [--home <dsh-home>]\n deepseekbot schedule-update <id> --sid <schedule> [--title ...] [--prompt ...] [trigger ...] [--home <dsh-home>]\n deepseekbot schedule-delete <id> --sid <schedule> [--home <dsh-home>]\n deepseekbot schedule-history <id> --sid <schedule> [--home <dsh-home>]\n deepseekbot schedule-run-now <id> --sid <schedule> [--home <dsh-home>]\n deepseekbot schedule-preview (--every <s> | --daily ... | ...) [--home <dsh-home>]\n deepseekbot pairings <id> [--home <dsh-home>]\n deepseekbot secret-put <NAME> [--home <dsh-home>] secret value arrives on stdin only\n deepseekbot secret-list [--home <dsh-home>] names, sources, writability; never values\n deepseekbot secret-unset <NAME> [--home <dsh-home>]\n deepseekbot search <words> find commands by words\n deepseekbot send <id> (--body <text> | --body-stdin) [--message-id human-<UUID>] [--timeout <seconds>]\n deepseekbot send-status <id> --message-id <Human-message-id>\n deepseekbot tool-approval-status <channel> --message-id <card-id>\n deepseekbot tool-approval-decide <channel> --message-id <card-id> --outcome allowed-once|rejected\n deepseekbot user-question-status <channel> --message-id <card-id>\n deepseekbot user-question-answer <channel> --message-id <card-id> --answer-stdin\n deepseekbot ask-secret-status <channel> --message-id <card-id>\n deepseekbot ask-secret-submit <channel> --message-id <card-id> --values-stdin\n deepseekbot ask-secret-cancel <channel> --message-id <card-id>\n deepseekbot ask-secret-account-status <channel> --message-id <configured-card-id>\n deepseekbot ask-secret-replace <channel> --message-id <configured-card-id>\n deepseekbot ask-secret-revoke <channel> --message-id <configured-card-id>\n deepseekbot ask-secret-accounts <id>\n deepseekbot release-info [--since <version>]\n deepseekbot workspace-options\n deepseekbot grant-create <id> --workspace <workspace-id>\n deepseekbot browser-access-set <id> (--enabled | --disabled)\n deepseekbot computer-access-set <id> (--enabled | --disabled)\n deepseekbot access <id>\n deepseekbot access-set <id> --mode workspace-write|danger-full-access [--scope chat|assignments] [--acknowledge-risk]\n deepseekbot settings <namespace>\n deepseekbot settings-set <namespace> --patch-stdin\n deepseekbot workspace-create <absolute-path>\n deepseekbot group-create <name> --member <bot-id> [--member <bot-id> ...]\n deepseekbot im-app-add <discord|slack|qq|dingtalk|wecom|matrix> --credentials-stdin\n deepseekbot im-app-status <platform>\n deepseekbot im-app-remove <platform> --app <app-id>\n deepseekbot im-bind <id> --app <app-id>\n deepseekbot im-unbind <id> --app <app-id>\n deepseekbot im-status <id>\n deepseekbot channel-connect <channel> --bot <id> --conversation <external-id> [--app <app-id>]\n deepseekbot channel-ingests <channel>\n deepseekbot browser-open <id>\n deepseekbot browser-frame <id> --out <absolute-file>\n deepseekbot browser-takeover <id> (--enabled | --disabled)\n deepseekbot browser-stop <id>\n deepseekbot computer-start [--target local|container]\n deepseekbot computer-status\n deepseekbot computer-stop\n deepseekbot bot-attention <id> [--limit <1-100>] [--cursor <source-id>] [--state <state>]\n deepseekbot bot-sessions <id> [--limit <1-100>]\n deepseekbot bot-activity <id> [--limit <1-100>]\n deepseekbot bot-delete-preview <id>\n deepseekbot bot-delete-confirm <id> --confirmation-stdin\n deepseekbot bot-delete-retry <id>\n deepseekbot im-apps\n deepseekbot im-authorize <feishu|weixin>\n deepseekbot im-credentials <attempt-id> --credentials-stdin\n deepseekbot im-verify <attempt-id> --verification-stdin\n deepseekbot pairing-status <attempt-id> [--wait] [--timeout <seconds>]\n deepseekbot im-cancel <attempt-id>\n deepseekbot --help | deepseekbot create --help\n\nOutput:\n stdout carries JSON, pretty by default; --compact condenses it to one line\n for agents. Human-readable lines go to stderr only.\n\nSources (exactly one per create):\n (none) blank bot; --preset applies a model preset, otherwise the model\n step reports no-model-yet and activation stays a Settings task\n --from-git clone a Git repository as Memory (HTTPS, SSH, or owner/repo\n shorthand for https://github.com/owner/repo.git)\n --from-zip import a bot bundle zip (same shape as the bot-zip export)\n --from-dir import a bot bundle directory (same shape as the bot-zip export;\n a .git subtree is skipped as files but packed as history)\n\nMachine contract:\n stdout carries exactly one JSON document. Success exits 0 with the bot id,\n DM channel, data directory, per-step statuses, and next actions. Failure\n exits non-zero with {\"error\": {\"code\", \"message\"}} using a stable code\n (usage, secret-in-argv, bad-zip, bad-bundle, bad-ref, unknown-preset,\n unknown-bot, unknown-channel, duplicate-preset, git-not-found, git-clone-failed, git-clone-timeout,\n memory-unavailable, lease-unavailable, bad-credentials, invalid-input). Human-readable lines go to stderr only.\n Stop the Host before offline database commands: the writer lease is exclusive.\n\nLive Host:\n Require --host <loopback-origin|tailnet-HTTPS-origin> or DEEPSEEKBOT_HOST.\n Auth: DEEPSEEKBOT_HOST_TOKEN or --token-file <private-file>, never a token in argv.\n Login is fresh per call; cookies stay in memory. --timeout defaults to 120 seconds (max 600).\n send returns its receipt and exact committed replies. Inspect send-status on timeout\n before retrying; send never automatically resubmits. channel-messages also accepts --host.\n create (blank, Git, Zip and directory), list/show, model-presets/create/apply/plan\n and channels use the Host when --host or DEEPSEEKBOT_HOST is set. Do not combine\n --home with an online target; failures never fall back to local database access.\n Zip/directory uploads contain local bytes; Git cloning uses the Host environment.\n Creation errors retain known bot/steps; outcome unknown means inspect before retrying.\n Bot diagnostics are bounded summaries, not full execution logs or live streams.\n Deletion requires preview then stdin {\"token\":\"<preview.token>\"}; Memory is retained.\n Question stdin: {\"answers\":[{\"id\":\"question-id\",\"selected\":[\"option\"],\"custom\":\"optional\"}]}\n Private input stdin: {\"field_id\":\"confidential value\",...}; only declared fields.\n Private input commands return status/acceptance only and never retry a submission.\n\nModels:\n model-presets lists the profile presets; model-preset-create mints one from\n explicit provider/model routes; model-preset-apply and model-plan read and\n write through the Host when selected, otherwise directly offline. Routes are shape-checked plus\n provider-existence-checked offline; catalog liveness and readiness\n inspection stay Host-side, and model-plan reports readiness deferred.\n\nMemory:\n memory-snapshot/file/history/diff read the bot memory store; memory-save\n writes one file and commits it. --expected-head defaults to the current\n HEAD (pass it explicitly for compare-and-swap); --edit-id defaults to a\n fresh UUID and makes repeat submissions idempotent. Conflicts, missing\n files, and bad shas fail coded (memory-conflict, not-found,\n memory-unknown-commit).\n\nIdentity:\n The bot name is a label. Every create mints a new bot id, so reusing a name\n always returns a second bot and still exits 0.\n\nSecrets:\n Never pass secrets as arguments: any --api-key/--token/--secret/--password\n style flag is a hard error (secret-in-argv). Model keys and tokens travel in\n the environment (for example DEEPSEEK_API_KEY, GITHUB_TOKEN) or a stdin pipe\n (--persona-stdin); result JSON never contains secret values.\n\nHome:\n --home points at the target DSH_HOME (a fresh directory works with zero\n clicks); without it, DSH_HOME from the environment is used.\n";
|
|
15192
15192
|
export declare function runBotCreateCli(argv: readonly string[], io: BotCreateCliIo): Promise<number>;
|
|
15193
15193
|
//#endregion
|
|
15194
15194
|
//#region packages/core/src/bots/slug.d.ts
|
package/dist/index.mjs
CHANGED
|
@@ -108865,8 +108865,58 @@ const LIVE_OPTIONS = {
|
|
|
108865
108865
|
"answer-stdin": { type: "boolean" },
|
|
108866
108866
|
"values-stdin": { type: "boolean" },
|
|
108867
108867
|
since: { type: "string" },
|
|
108868
|
-
workspace: { type: "string" }
|
|
108868
|
+
workspace: { type: "string" },
|
|
108869
|
+
mode: { type: "string" },
|
|
108870
|
+
scope: { type: "string" },
|
|
108871
|
+
"acknowledge-risk": { type: "boolean" },
|
|
108872
|
+
"patch-stdin": { type: "boolean" },
|
|
108873
|
+
member: {
|
|
108874
|
+
type: "string",
|
|
108875
|
+
multiple: true
|
|
108876
|
+
},
|
|
108877
|
+
app: { type: "string" },
|
|
108878
|
+
bot: { type: "string" },
|
|
108879
|
+
conversation: { type: "string" },
|
|
108880
|
+
out: { type: "string" },
|
|
108881
|
+
target: { type: "string" }
|
|
108869
108882
|
};
|
|
108883
|
+
const DEVICE_COMMANDS = [
|
|
108884
|
+
"browser-open",
|
|
108885
|
+
"browser-frame",
|
|
108886
|
+
"browser-takeover",
|
|
108887
|
+
"browser-stop",
|
|
108888
|
+
"computer-start",
|
|
108889
|
+
"computer-status",
|
|
108890
|
+
"computer-stop"
|
|
108891
|
+
];
|
|
108892
|
+
const IM_CONTROL_COMMANDS = [
|
|
108893
|
+
"im-app-add",
|
|
108894
|
+
"im-app-status",
|
|
108895
|
+
"im-app-remove",
|
|
108896
|
+
"im-bind",
|
|
108897
|
+
"im-unbind",
|
|
108898
|
+
"im-status",
|
|
108899
|
+
"channel-connect",
|
|
108900
|
+
"channel-ingests"
|
|
108901
|
+
];
|
|
108902
|
+
const TOKEN_IM_PLATFORMS = /* @__PURE__ */ new Set([
|
|
108903
|
+
"discord",
|
|
108904
|
+
"slack",
|
|
108905
|
+
"qq",
|
|
108906
|
+
"dingtalk",
|
|
108907
|
+
"wecom",
|
|
108908
|
+
"matrix"
|
|
108909
|
+
]);
|
|
108910
|
+
const CONTROL_COMMANDS = [
|
|
108911
|
+
"browser-access-set",
|
|
108912
|
+
"computer-access-set",
|
|
108913
|
+
"access",
|
|
108914
|
+
"access-set",
|
|
108915
|
+
"settings",
|
|
108916
|
+
"settings-set",
|
|
108917
|
+
"workspace-create",
|
|
108918
|
+
"group-create"
|
|
108919
|
+
];
|
|
108870
108920
|
const LIVE_COMMANDS = [
|
|
108871
108921
|
...IM_COMMANDS,
|
|
108872
108922
|
{
|
|
@@ -108932,6 +108982,98 @@ const LIVE_COMMANDS = [
|
|
|
108932
108982
|
{
|
|
108933
108983
|
command: "grant-create",
|
|
108934
108984
|
description: "authorize a Bot workspace through the live Host"
|
|
108985
|
+
},
|
|
108986
|
+
{
|
|
108987
|
+
command: "browser-access-set",
|
|
108988
|
+
description: "turn a Bot's Browser Access on or off through the live Host"
|
|
108989
|
+
},
|
|
108990
|
+
{
|
|
108991
|
+
command: "computer-access-set",
|
|
108992
|
+
description: "turn a Bot's Computer Access on or off through the live Host"
|
|
108993
|
+
},
|
|
108994
|
+
{
|
|
108995
|
+
command: "access",
|
|
108996
|
+
description: "read a Bot's access preset (workspace write or full access)"
|
|
108997
|
+
},
|
|
108998
|
+
{
|
|
108999
|
+
command: "access-set",
|
|
109000
|
+
description: "set a Bot's access preset; full access needs --acknowledge-risk"
|
|
109001
|
+
},
|
|
109002
|
+
{
|
|
109003
|
+
command: "settings",
|
|
109004
|
+
description: "read one live Host settings namespace"
|
|
109005
|
+
},
|
|
109006
|
+
{
|
|
109007
|
+
command: "settings-set",
|
|
109008
|
+
description: "patch one live Host settings namespace with JSON from stdin"
|
|
109009
|
+
},
|
|
109010
|
+
{
|
|
109011
|
+
command: "workspace-create",
|
|
109012
|
+
description: "register an absolute folder as a Host workspace"
|
|
109013
|
+
},
|
|
109014
|
+
{
|
|
109015
|
+
command: "group-create",
|
|
109016
|
+
description: "create a group Channel with one or more Bots"
|
|
109017
|
+
},
|
|
109018
|
+
{
|
|
109019
|
+
command: "im-app-add",
|
|
109020
|
+
description: "add a token-based IM app (Discord, Slack, …) with credentials JSON from stdin"
|
|
109021
|
+
},
|
|
109022
|
+
{
|
|
109023
|
+
command: "im-app-status",
|
|
109024
|
+
description: "read a token IM platform's apps and connection state"
|
|
109025
|
+
},
|
|
109026
|
+
{
|
|
109027
|
+
command: "im-app-remove",
|
|
109028
|
+
description: "remove a token IM app from the Provider"
|
|
109029
|
+
},
|
|
109030
|
+
{
|
|
109031
|
+
command: "im-bind",
|
|
109032
|
+
description: "bind a connected IM app to a Bot"
|
|
109033
|
+
},
|
|
109034
|
+
{
|
|
109035
|
+
command: "im-unbind",
|
|
109036
|
+
description: "unbind an IM app from a Bot"
|
|
109037
|
+
},
|
|
109038
|
+
{
|
|
109039
|
+
command: "im-status",
|
|
109040
|
+
description: "read a Bot's IM apps and bindings with reception state"
|
|
109041
|
+
},
|
|
109042
|
+
{
|
|
109043
|
+
command: "channel-connect",
|
|
109044
|
+
description: "connect an external group conversation to a Channel through a Bot's binding"
|
|
109045
|
+
},
|
|
109046
|
+
{
|
|
109047
|
+
command: "channel-ingests",
|
|
109048
|
+
description: "list a Channel's connected external conversations"
|
|
109049
|
+
},
|
|
109050
|
+
{
|
|
109051
|
+
command: "browser-open",
|
|
109052
|
+
description: "start a Bot's Browser and open its tab for viewing"
|
|
109053
|
+
},
|
|
109054
|
+
{
|
|
109055
|
+
command: "browser-frame",
|
|
109056
|
+
description: "save the current picture of a Bot's Browser to a file"
|
|
109057
|
+
},
|
|
109058
|
+
{
|
|
109059
|
+
command: "browser-takeover",
|
|
109060
|
+
description: "pause a Bot's Browser for Human input (--enabled) or hand it back (--disabled)"
|
|
109061
|
+
},
|
|
109062
|
+
{
|
|
109063
|
+
command: "browser-stop",
|
|
109064
|
+
description: "stop a Bot's Browser"
|
|
109065
|
+
},
|
|
109066
|
+
{
|
|
109067
|
+
command: "computer-start",
|
|
109068
|
+
description: "start the profile Computer with explicit authorization"
|
|
109069
|
+
},
|
|
109070
|
+
{
|
|
109071
|
+
command: "computer-status",
|
|
109072
|
+
description: "read the profile Computer target and state"
|
|
109073
|
+
},
|
|
109074
|
+
{
|
|
109075
|
+
command: "computer-stop",
|
|
109076
|
+
description: "stop the profile Computer"
|
|
108935
109077
|
}
|
|
108936
109078
|
];
|
|
108937
109079
|
function usage$1(message) {
|
|
@@ -109011,7 +109153,7 @@ async function connectLiveHost(values, io, signal) {
|
|
|
109011
109153
|
};
|
|
109012
109154
|
const scrub = (value) => value.replaceAll(secret, "[redacted]").replaceAll(encodeURIComponent(secret), "[redacted]");
|
|
109013
109155
|
const rpc = async (method, args) => {
|
|
109014
|
-
const name = method
|
|
109156
|
+
const name = method.includes("/") ? method : `botharness/${method}`;
|
|
109015
109157
|
const response = await request(new URL(`/api/${name}`, base), {
|
|
109016
109158
|
method: "POST",
|
|
109017
109159
|
headers: {
|
|
@@ -109022,7 +109164,7 @@ async function connectLiveHost(values, io, signal) {
|
|
|
109022
109164
|
type: "client-request",
|
|
109023
109165
|
rpcId: randomUUID(),
|
|
109024
109166
|
method: name,
|
|
109025
|
-
payload: method
|
|
109167
|
+
payload: method.startsWith("dsh-im/") ? args : { args }
|
|
109026
109168
|
})
|
|
109027
109169
|
});
|
|
109028
109170
|
const payload = await readResponse(response);
|
|
@@ -109070,9 +109212,40 @@ async function connectLiveHost(values, io, signal) {
|
|
|
109070
109212
|
}
|
|
109071
109213
|
return payload;
|
|
109072
109214
|
};
|
|
109215
|
+
const http = async (path, body) => {
|
|
109216
|
+
const response = await request(new URL(path, base), {
|
|
109217
|
+
method: body === void 0 ? "GET" : "POST",
|
|
109218
|
+
headers: {
|
|
109219
|
+
cookie,
|
|
109220
|
+
...body === void 0 ? {} : { "content-type": "application/json" }
|
|
109221
|
+
},
|
|
109222
|
+
...body === void 0 ? {} : { body: JSON.stringify(body) }
|
|
109223
|
+
});
|
|
109224
|
+
let bytes;
|
|
109225
|
+
try {
|
|
109226
|
+
bytes = new Uint8Array(await response.arrayBuffer());
|
|
109227
|
+
} catch {
|
|
109228
|
+
throw new CliLiveError("host-unreachable", "The Host response was interrupted before the command deadline.");
|
|
109229
|
+
}
|
|
109230
|
+
if (response.status === 401 || response.status === 403) throw new CliLiveError("host-unauthorized", "Host login was refused; refresh its launch token.");
|
|
109231
|
+
if (!response.ok) {
|
|
109232
|
+
let detail = {};
|
|
109233
|
+
try {
|
|
109234
|
+
detail = record(JSON.parse(new TextDecoder().decode(bytes)));
|
|
109235
|
+
} catch {
|
|
109236
|
+
detail = { error: new TextDecoder().decode(bytes).slice(0, 200) };
|
|
109237
|
+
}
|
|
109238
|
+
throw new CliLiveError(typeof detail["code"] === "string" ? scrub(detail["code"]) : "host-refused", scrub(typeof detail["error"] === "string" && detail["error"] !== "" ? detail["error"] : `The Host refused ${path} with HTTP ${String(response.status)}.`));
|
|
109239
|
+
}
|
|
109240
|
+
return {
|
|
109241
|
+
response,
|
|
109242
|
+
bytes
|
|
109243
|
+
};
|
|
109244
|
+
};
|
|
109073
109245
|
return {
|
|
109074
109246
|
rpc,
|
|
109075
|
-
importBotZip
|
|
109247
|
+
importBotZip,
|
|
109248
|
+
http
|
|
109076
109249
|
};
|
|
109077
109250
|
}
|
|
109078
109251
|
async function runLiveCli(command, rest, values, io) {
|
|
@@ -109086,6 +109259,13 @@ async function runLiveCli(command, rest, values, io) {
|
|
|
109086
109259
|
throw error;
|
|
109087
109260
|
}
|
|
109088
109261
|
}
|
|
109262
|
+
if (DEVICE_COMMANDS.some((entry) => entry === command)) return runDeviceCommand(command, rest, values, io);
|
|
109263
|
+
if (command === "grants") {
|
|
109264
|
+
if (rest.length !== 1 || !rest[0]?.trim()) usage$1("grants needs exactly one bot id.");
|
|
109265
|
+
return (await connectLiveHost(values, io, AbortSignal.timeout(timeoutMs(values.timeout)))).rpc("grants", { slug: rest[0].trim() });
|
|
109266
|
+
}
|
|
109267
|
+
if (IM_CONTROL_COMMANDS.some((entry) => entry === command)) return runImControlCommand(command, rest, values, io);
|
|
109268
|
+
if (CONTROL_COMMANDS.some((entry) => entry === command)) return runControlCommand(command, rest, values, io);
|
|
109089
109269
|
const noTarget = command === "release-info" || command === "workspace-options";
|
|
109090
109270
|
if (rest.length !== (noTarget ? 0 : 1)) usage$1(`${command} ${noTarget ? "takes no positional arguments" : "needs exactly one target id"}.`);
|
|
109091
109271
|
const target = rest[0]?.trim() ?? "";
|
|
@@ -109307,6 +109487,223 @@ async function runLiveCli(command, rest, values, io) {
|
|
|
109307
109487
|
});
|
|
109308
109488
|
return rpc("releaseInfo", values.since === void 0 ? {} : { since: values.since });
|
|
109309
109489
|
}
|
|
109490
|
+
async function runControlCommand(command, rest, values, io) {
|
|
109491
|
+
if (rest.length !== 1 || !rest[0]?.trim()) usage$1(`${command} needs exactly one target.`);
|
|
109492
|
+
const target = rest[0].trim();
|
|
109493
|
+
let patch;
|
|
109494
|
+
if (command === "browser-access-set" || command === "computer-access-set") {
|
|
109495
|
+
if (values.enabled === values.disabled) usage$1(`${command} needs exactly one of --enabled or --disabled.`);
|
|
109496
|
+
}
|
|
109497
|
+
if (command === "access-set") {
|
|
109498
|
+
if (values.mode !== "workspace-write" && values.mode !== "danger-full-access") usage$1("--mode must be workspace-write or danger-full-access.");
|
|
109499
|
+
if (values.scope !== void 0 && values.scope !== "chat" && values.scope !== "assignments") usage$1("--scope must be chat or assignments.");
|
|
109500
|
+
if (values.mode === "danger-full-access" && values["acknowledge-risk"] !== true) usage$1("Full access lets the Bot run any command and use the Browser and Computer without asking. Pass --acknowledge-risk to confirm.");
|
|
109501
|
+
}
|
|
109502
|
+
if (command === "settings-set") {
|
|
109503
|
+
if (values["patch-stdin"] !== true) usage$1("settings-set requires --patch-stdin.");
|
|
109504
|
+
try {
|
|
109505
|
+
const parsed = JSON.parse(await (io.readStdin?.() ?? Promise.resolve("")));
|
|
109506
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("not an object");
|
|
109507
|
+
patch = parsed;
|
|
109508
|
+
} catch {
|
|
109509
|
+
usage$1("--patch-stdin must be a JSON object of settings fields.");
|
|
109510
|
+
}
|
|
109511
|
+
}
|
|
109512
|
+
if (command === "workspace-create" && !target.startsWith("/") && !/^[A-Za-z]:[\\/]/u.test(target)) usage$1("workspace-create needs an absolute folder path.");
|
|
109513
|
+
const members = (values.member ?? []).map((member) => member.trim()).filter(Boolean);
|
|
109514
|
+
if (command === "group-create" && members.length === 0) usage$1("group-create needs at least one --member <bot-id>.");
|
|
109515
|
+
const { rpc } = await connectLiveHost(values, io, AbortSignal.timeout(timeoutMs(values.timeout)));
|
|
109516
|
+
if (command === "browser-access-set" || command === "computer-access-set") return rpc(command === "browser-access-set" ? "browserAccessSet" : "computerAccessSet", {
|
|
109517
|
+
slug: target,
|
|
109518
|
+
enabled: values.enabled === true
|
|
109519
|
+
});
|
|
109520
|
+
if (command === "access") return rpc("assignmentAccessGet", { slug: target });
|
|
109521
|
+
if (command === "access-set") return rpc("assignmentAccessSet", {
|
|
109522
|
+
slug: target,
|
|
109523
|
+
mode: values.mode,
|
|
109524
|
+
...values.scope === void 0 ? {} : { scope: values.scope },
|
|
109525
|
+
...values.mode === "danger-full-access" ? { acknowledgeRisk: true } : {}
|
|
109526
|
+
});
|
|
109527
|
+
if (command === "workspace-create") return rpc("workspace/create", { request: { path: target } });
|
|
109528
|
+
if (command === "group-create") return rpc("channelCreate", {
|
|
109529
|
+
name: target,
|
|
109530
|
+
members
|
|
109531
|
+
});
|
|
109532
|
+
const described = record(await rpc("settings/describe", {}));
|
|
109533
|
+
const found = (Array.isArray(described["namespaces"]) ? described["namespaces"] : []).map(record).find((entry) => entry["ns"] === target);
|
|
109534
|
+
if (found === void 0) throw new CliLiveError("unknown-settings", `The Host has no settings namespace ${target}.`);
|
|
109535
|
+
if (command === "settings") return {
|
|
109536
|
+
ns: target,
|
|
109537
|
+
revision: found["revision"],
|
|
109538
|
+
value: found["value"]
|
|
109539
|
+
};
|
|
109540
|
+
const updated = record(await rpc("settings/update", {
|
|
109541
|
+
ns: target,
|
|
109542
|
+
patch,
|
|
109543
|
+
expectedRevision: found["revision"]
|
|
109544
|
+
}));
|
|
109545
|
+
return {
|
|
109546
|
+
ns: target,
|
|
109547
|
+
revision: updated["revision"],
|
|
109548
|
+
value: updated["value"]
|
|
109549
|
+
};
|
|
109550
|
+
}
|
|
109551
|
+
function appSummary(value) {
|
|
109552
|
+
const app = record(value);
|
|
109553
|
+
const bot = record(app["bot"]);
|
|
109554
|
+
const error = app["error"] === null || app["error"] === void 0 ? null : record(app["error"]);
|
|
109555
|
+
return {
|
|
109556
|
+
id: app["botId"],
|
|
109557
|
+
name: bot["name"] ?? app["name"],
|
|
109558
|
+
state: app["state"],
|
|
109559
|
+
connected: app["connected"] === true,
|
|
109560
|
+
error: error === null ? null : {
|
|
109561
|
+
code: error["code"],
|
|
109562
|
+
message: error["message"]
|
|
109563
|
+
},
|
|
109564
|
+
...app["stats"] === void 0 ? {} : { stats: app["stats"] }
|
|
109565
|
+
};
|
|
109566
|
+
}
|
|
109567
|
+
function connectionSummary(platform, value) {
|
|
109568
|
+
const status = record(value);
|
|
109569
|
+
return {
|
|
109570
|
+
platform,
|
|
109571
|
+
state: status["state"],
|
|
109572
|
+
apps: (Array.isArray(status["bots"]) ? status["bots"] : []).map(appSummary)
|
|
109573
|
+
};
|
|
109574
|
+
}
|
|
109575
|
+
async function runImControlCommand(command, rest, values, io) {
|
|
109576
|
+
if (rest.length !== 1 || !rest[0]?.trim()) usage$1(`${command} needs exactly one target.`);
|
|
109577
|
+
const target = rest[0].trim();
|
|
109578
|
+
const app = values.app?.trim() ?? "";
|
|
109579
|
+
if ((command === "im-app-add" || command === "im-app-status" || command === "im-app-remove") && !TOKEN_IM_PLATFORMS.has(target)) usage$1(`${command} supports ${[...TOKEN_IM_PLATFORMS].join(", ")}; use im-authorize for feishu or weixin.`);
|
|
109580
|
+
let credentials;
|
|
109581
|
+
if (command === "im-app-add") {
|
|
109582
|
+
if (values["credentials-stdin"] !== true) usage$1("im-app-add requires --credentials-stdin.");
|
|
109583
|
+
try {
|
|
109584
|
+
const parsed = JSON.parse(await (io.readStdin?.() ?? Promise.resolve("")));
|
|
109585
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("not an object");
|
|
109586
|
+
credentials = parsed;
|
|
109587
|
+
} catch {
|
|
109588
|
+
usage$1("--credentials-stdin must be a JSON object, for example {\"token\":\"…\"}.");
|
|
109589
|
+
}
|
|
109590
|
+
}
|
|
109591
|
+
if ((command === "im-app-remove" || command === "im-bind" || command === "im-unbind") && !app) usage$1(`${command} requires --app <app-id>.`);
|
|
109592
|
+
if (command === "channel-connect" && (!values.bot?.trim() || !values.conversation?.trim())) usage$1("channel-connect requires --bot <bot-id> and --conversation <external-id>.");
|
|
109593
|
+
const { rpc } = await connectLiveHost(values, io, AbortSignal.timeout(timeoutMs(values.timeout)));
|
|
109594
|
+
const provider = (method, payload) => rpc(`dsh-im/${target}`, {
|
|
109595
|
+
method,
|
|
109596
|
+
payload
|
|
109597
|
+
});
|
|
109598
|
+
if (command === "im-app-add") return connectionSummary(target, await provider("bot.bind-credentials", credentials));
|
|
109599
|
+
if (command === "im-app-status") return connectionSummary(target, await provider("connection.status", {}));
|
|
109600
|
+
if (command === "im-app-remove") return connectionSummary(target, await provider("bot.delete", {
|
|
109601
|
+
botId: app,
|
|
109602
|
+
confirm: true
|
|
109603
|
+
}));
|
|
109604
|
+
if (command === "channel-ingests") return rpc("channelIngests", { channelId: target });
|
|
109605
|
+
if (command === "im-bind") {
|
|
109606
|
+
const apps = record(await rpc("messagingApps", {}));
|
|
109607
|
+
const found = (Array.isArray(apps["apps"]) ? apps["apps"] : []).map(record).find((entry) => entry["ref"] === app);
|
|
109608
|
+
if (found === void 0) throw new CliLiveError("unknown-app", `The Host has no connected IM app ${app}.`);
|
|
109609
|
+
return rpc("messagingIdentity", {
|
|
109610
|
+
slug: target,
|
|
109611
|
+
input: {
|
|
109612
|
+
kind: "bind",
|
|
109613
|
+
providerId: found["providerId"],
|
|
109614
|
+
accountRef: found["ref"],
|
|
109615
|
+
fingerprint: found["fingerprint"]
|
|
109616
|
+
}
|
|
109617
|
+
});
|
|
109618
|
+
}
|
|
109619
|
+
const slug = command === "channel-connect" ? values.bot.trim() : target;
|
|
109620
|
+
const snapshot = record(await rpc("messagingSnapshot", { slug }));
|
|
109621
|
+
const identities = (Array.isArray(snapshot["identities"]) ? snapshot["identities"] : []).map(record);
|
|
109622
|
+
if (command === "im-status") return {
|
|
109623
|
+
apps: (Array.isArray(snapshot["accounts"]) ? snapshot["accounts"] : []).map(record).map((entry) => ({
|
|
109624
|
+
ref: entry["ref"],
|
|
109625
|
+
platform: entry["platform"],
|
|
109626
|
+
name: entry["name"],
|
|
109627
|
+
connected: entry["connected"] === true
|
|
109628
|
+
})),
|
|
109629
|
+
bindings: identities.map((entry) => ({
|
|
109630
|
+
id: entry["id"],
|
|
109631
|
+
app: entry["accountRef"],
|
|
109632
|
+
platform: entry["platform"],
|
|
109633
|
+
name: entry["name"],
|
|
109634
|
+
enabled: entry["enabled"],
|
|
109635
|
+
availability: entry["availability"],
|
|
109636
|
+
reception: entry["reception"],
|
|
109637
|
+
conversations: entry["grantCount"]
|
|
109638
|
+
}))
|
|
109639
|
+
};
|
|
109640
|
+
const binding = identities.find((entry) => command === "channel-connect" ? app === "" || entry["accountRef"] === app : entry["accountRef"] === app);
|
|
109641
|
+
if (binding === void 0) throw new CliLiveError("unknown-binding", command === "channel-connect" ? `Bot ${slug} has no IM app binding to connect through.` : `Bot ${slug} is not bound to IM app ${app}.`);
|
|
109642
|
+
if (command === "im-unbind") return rpc("messagingIdentity", {
|
|
109643
|
+
slug,
|
|
109644
|
+
input: {
|
|
109645
|
+
kind: "unbind",
|
|
109646
|
+
id: binding["id"],
|
|
109647
|
+
expectedRevision: binding["revision"]
|
|
109648
|
+
}
|
|
109649
|
+
});
|
|
109650
|
+
return rpc("channelIngest", {
|
|
109651
|
+
channelId: target,
|
|
109652
|
+
input: {
|
|
109653
|
+
kind: "add",
|
|
109654
|
+
bindingId: binding["id"],
|
|
109655
|
+
conversation: {
|
|
109656
|
+
kind: "group",
|
|
109657
|
+
id: values.conversation.trim()
|
|
109658
|
+
}
|
|
109659
|
+
}
|
|
109660
|
+
});
|
|
109661
|
+
}
|
|
109662
|
+
async function runDeviceCommand(command, rest, values, io) {
|
|
109663
|
+
const computer = command.startsWith("computer-");
|
|
109664
|
+
if (computer ? rest.length !== 0 : rest.length !== 1 || !rest[0]?.trim()) usage$1(computer ? `${command} takes no positional arguments.` : `${command} needs exactly one bot id.`);
|
|
109665
|
+
const slug = rest[0]?.trim() ?? "";
|
|
109666
|
+
if (command === "browser-frame") {
|
|
109667
|
+
const out = values.out ?? "";
|
|
109668
|
+
if (!out.startsWith("/") && !/^[A-Za-z]:[\\/]/u.test(out)) usage$1("browser-frame requires --out <absolute-file>.");
|
|
109669
|
+
}
|
|
109670
|
+
if (command === "browser-takeover" && values.enabled === values.disabled) usage$1("browser-takeover needs exactly one of --enabled or --disabled.");
|
|
109671
|
+
if (command === "computer-start" && values.target !== void 0 && values.target !== "local" && values.target !== "container") usage$1("--target must be local or container.");
|
|
109672
|
+
const { http } = await connectLiveHost(values, io, AbortSignal.timeout(timeoutMs(values.timeout)));
|
|
109673
|
+
const json = async (path, body) => {
|
|
109674
|
+
const { bytes } = await http(path, body);
|
|
109675
|
+
try {
|
|
109676
|
+
return JSON.parse(new TextDecoder().decode(bytes));
|
|
109677
|
+
} catch {
|
|
109678
|
+
throw new CliLiveError("host-protocol-error", `The Host returned no JSON for ${path}.`);
|
|
109679
|
+
}
|
|
109680
|
+
};
|
|
109681
|
+
if (command === "browser-open") return json("/api/browser/open", { slug });
|
|
109682
|
+
if (command === "browser-takeover") return json("/api/browser/takeover", {
|
|
109683
|
+
slug,
|
|
109684
|
+
active: values.enabled === true
|
|
109685
|
+
});
|
|
109686
|
+
if (command === "browser-stop") return json("/api/browser/stop", { slug });
|
|
109687
|
+
if (command === "computer-start") return json("/api/computer/start", {
|
|
109688
|
+
authorize: true,
|
|
109689
|
+
...values.target === void 0 ? {} : { target: values.target }
|
|
109690
|
+
});
|
|
109691
|
+
if (command === "computer-status") return json("/api/computer/status");
|
|
109692
|
+
if (command === "computer-stop") return json("/api/computer/stop", { authorize: true });
|
|
109693
|
+
const { response, bytes } = await http(`/botharness-browser/viewer/local/frame?slug=${encodeURIComponent(slug)}`);
|
|
109694
|
+
writeFileSync(values.out, bytes, { mode: 384 });
|
|
109695
|
+
const width = Number(response.headers.get("x-viewport-width"));
|
|
109696
|
+
const height = Number(response.headers.get("x-viewport-height"));
|
|
109697
|
+
return {
|
|
109698
|
+
path: values.out,
|
|
109699
|
+
bytes: bytes.byteLength,
|
|
109700
|
+
mimeType: response.headers.get("content-type"),
|
|
109701
|
+
...width > 0 && height > 0 ? { viewport: {
|
|
109702
|
+
width,
|
|
109703
|
+
height
|
|
109704
|
+
} } : {}
|
|
109705
|
+
};
|
|
109706
|
+
}
|
|
109310
109707
|
//#endregion
|
|
109311
109708
|
//#region packages/core/src/bots/cli-management.ts
|
|
109312
109709
|
const ONLINE_MANAGEMENT_COMMANDS = [
|
|
@@ -109606,6 +110003,29 @@ Usage:
|
|
|
109606
110003
|
deepseekbot release-info [--since <version>]
|
|
109607
110004
|
deepseekbot workspace-options
|
|
109608
110005
|
deepseekbot grant-create <id> --workspace <workspace-id>
|
|
110006
|
+
deepseekbot browser-access-set <id> (--enabled | --disabled)
|
|
110007
|
+
deepseekbot computer-access-set <id> (--enabled | --disabled)
|
|
110008
|
+
deepseekbot access <id>
|
|
110009
|
+
deepseekbot access-set <id> --mode workspace-write|danger-full-access [--scope chat|assignments] [--acknowledge-risk]
|
|
110010
|
+
deepseekbot settings <namespace>
|
|
110011
|
+
deepseekbot settings-set <namespace> --patch-stdin
|
|
110012
|
+
deepseekbot workspace-create <absolute-path>
|
|
110013
|
+
deepseekbot group-create <name> --member <bot-id> [--member <bot-id> ...]
|
|
110014
|
+
deepseekbot im-app-add <discord|slack|qq|dingtalk|wecom|matrix> --credentials-stdin
|
|
110015
|
+
deepseekbot im-app-status <platform>
|
|
110016
|
+
deepseekbot im-app-remove <platform> --app <app-id>
|
|
110017
|
+
deepseekbot im-bind <id> --app <app-id>
|
|
110018
|
+
deepseekbot im-unbind <id> --app <app-id>
|
|
110019
|
+
deepseekbot im-status <id>
|
|
110020
|
+
deepseekbot channel-connect <channel> --bot <id> --conversation <external-id> [--app <app-id>]
|
|
110021
|
+
deepseekbot channel-ingests <channel>
|
|
110022
|
+
deepseekbot browser-open <id>
|
|
110023
|
+
deepseekbot browser-frame <id> --out <absolute-file>
|
|
110024
|
+
deepseekbot browser-takeover <id> (--enabled | --disabled)
|
|
110025
|
+
deepseekbot browser-stop <id>
|
|
110026
|
+
deepseekbot computer-start [--target local|container]
|
|
110027
|
+
deepseekbot computer-status
|
|
110028
|
+
deepseekbot computer-stop
|
|
109609
110029
|
deepseekbot bot-attention <id> [--limit <1-100>] [--cursor <source-id>] [--state <state>]
|
|
109610
110030
|
deepseekbot bot-sessions <id> [--limit <1-100>]
|
|
109611
110031
|
deepseekbot bot-activity <id> [--limit <1-100>]
|
|
@@ -111506,7 +111926,7 @@ async function runBotCreateCli(argv, io) {
|
|
|
111506
111926
|
out.stdout(JSON.stringify(await runOnlineManagement(command, rest, values, io, input, bundle), null, 2));
|
|
111507
111927
|
return 0;
|
|
111508
111928
|
}
|
|
111509
|
-
if (LIVE_COMMANDS.some((entry) => entry.command === command) || command === "channel-messages" && (values.host !== void 0 || io.env["DEEPSEEKBOT_HOST"] !== void 0)) {
|
|
111929
|
+
if (LIVE_COMMANDS.some((entry) => entry.command === command) || (command === "channel-messages" || command === "grants") && (values.host !== void 0 || io.env["DEEPSEEKBOT_HOST"] !== void 0)) {
|
|
111510
111930
|
if (values.home !== void 0) throw usageError("Do not combine --home and an online Host target.");
|
|
111511
111931
|
out.stdout(JSON.stringify(await runLiveCli(command, rest, values, io), null, 2));
|
|
111512
111932
|
return 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@botharness/core",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "BotHarness core: PersonaBot registry, memory, and state",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"@deepseek-ai/dsh-tools": ">=0.2.0-0"
|
|
26
26
|
},
|
|
27
27
|
"engines": {
|
|
28
|
-
"dsh": "
|
|
28
|
+
"dsh": "0.2.0-rc.2",
|
|
29
29
|
"node": ">=22.16"
|
|
30
30
|
},
|
|
31
31
|
"dsh": {
|