@divergeai/cli 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +168 -0
  2. package/dist/diverge.mjs +9342 -0
  3. package/package.json +34 -0
package/README.md ADDED
@@ -0,0 +1,168 @@
1
+ # diverge
2
+
3
+ The Divergent platform CLI: sign in, point coding agents at the platform's
4
+ model gateway, and call platform tools as yourself.
5
+
6
+ ```bash
7
+ npm install -g @divergeai/cli
8
+ diverge login # opens the browser; choose your organization
9
+ diverge agents setup claude # Claude Code on the gateway
10
+ diverge agents run dsh -- --profile web --port 3131 --no-open
11
+ diverge tools list --organization-id <id>
12
+ ```
13
+
14
+ Node 22 or later. Linux and macOS are supported; Windows is best-effort.
15
+
16
+ ## Sign in
17
+
18
+ `diverge login` runs the browser sign-in (authorization code with PKCE on a
19
+ loopback listener). A login serves exactly one organization: without
20
+ `--organization-id` the platform asks you to choose. The login is cached in
21
+ `~/.diverge/token.json` (mode 0600), refreshes itself, and `diverge logout`
22
+ revokes it on the platform before forgetting it.
23
+
24
+ - `diverge status` shows the deployment, the organization, the scopes, and a
25
+ `Tool execution:` line naming who a tool call would run as: you, or a
26
+ delegated `DIVERGENT_USER_ID` through service credentials. Read it before
27
+ granting anything.
28
+ - The CLI names the account it acts as — `Signed in as:` on `login` and
29
+ `status`, `Acting as:` before every `tools` request — because a token
30
+ names only a user id and the wrong account is invisible without the email:
31
+ use the operator account for operator work, and the break-glass account
32
+ only knowingly.
33
+ - A login is for one account. `diverge login --as <email>` tells the platform
34
+ which one; it signs that account in instead of reusing whatever session the
35
+ browser holds and issues nothing to any other. Without `--as` the account
36
+ of the last login is kept, so switching accounts is always explicit.
37
+ - A login also remembers its scopes: without `--scopes` the next login asks
38
+ for the same ones, so a routine re-login never narrows what the CLI may do.
39
+ A token carries only what was asked at sign-in; the Forge scopes
40
+ (`forge:read forge:write`) are what the Forge lanes need.
41
+ - `diverge token` prints a fresh access token and nothing else. It is the
42
+ credential helper Claude Code and Codex call, so an agent never holds a
43
+ long-lived key.
44
+ - `diverge login --api-url <url>` targets another deployment and remembers it;
45
+ `DIVERGENT_API_URL` overrides per shell.
46
+
47
+ ## Coding agents
48
+
49
+ `diverge agents setup <agent>` writes the configuration one agent needs to use
50
+ the gateway, keeping everything else in the file:
51
+
52
+ | agent | what is written |
53
+ | --- | --- |
54
+ | `claude` | `~/.claude/settings.json` (or `./.claude/settings.local.json` with `--scope project`): the gateway base URL and `diverge token` as the key helper; `--project-id` adds the project header, `--model-discovery` lets Claude Code list the gateway's models |
55
+ | `opencode` | `~/.config/opencode/opencode.json`: a `divergent-<surface>` provider reading `DIVERGENT_TOKEN` |
56
+ | `codex` | `~/.codex/config.toml`: a `divergent` provider block with `diverge token` as its auth command; `--print` shows it instead |
57
+ | `dsh` | `$DSH_HOME/divergent-<surface>.yml`: a patch overlay with the models the gateway serves, dsh's DeepSeek adapter, telemetry upload and search turned off, and the platform's web search served over `diverge tools bridge` (row `mcp-divergent`) |
58
+
59
+ `diverge agents run dsh` launches DeepSeek Harness on the overlay and keeps
60
+ its token fresh in dsh's own credentials file until it exits. With nothing
61
+ else it starts dsh's web UI and prints the URL; `diverge agents run dsh --
62
+ "<task>"` answers one task headless and exits; anything after `--` goes to
63
+ dsh as given.
64
+
65
+ `diverge agents status` shows the organization, the scopes, and the models
66
+ each surface serves. `diverge agents rotate` keeps a token file fresh
67
+ (default `~/.divergent/token`, every 600 seconds; `--once` writes and exits)
68
+ for a client that reads a file or an environment substitution rather than
69
+ running a helper.
70
+
71
+ ### Web search
72
+
73
+ Claude Code and Codex search through their model vendor's own server tool,
74
+ which the gateway forwards unchanged when the organization's upstream is
75
+ Anthropic or OpenAI. Every other model, dsh included, searches through the
76
+ platform's `search_online` tool: one key on the platform, metered and audited
77
+ per organization. The dsh overlay wires it in as `mcp__divergent__search_online`
78
+ (dsh's DeepSeek-backed `web_search` needs a vendor key and is off; `web_fetch`
79
+ stays). In dsh's web profile the shipped agent presets still list `web_search`,
80
+ where a patch cannot reach; it fails loudly and the model uses the bridge's
81
+ tool listed beside it. To give Claude Code or Codex the same tool for a
82
+ non-native upstream, register the bridge once:
83
+
84
+ ```bash
85
+ claude mcp add --scope user divergent -- diverge tools bridge --tools search_online
86
+ ```
87
+
88
+ ```toml
89
+ # ~/.codex/config.toml
90
+ [mcp_servers.divergent]
91
+ command = "diverge"
92
+ args = ["tools", "bridge", "--tools", "search_online"]
93
+ ```
94
+
95
+ ## Platform tools
96
+
97
+ Every command takes the scope it acts in: `--organization-id` (or
98
+ `DIVERGENT_ORG_ID`, else the organization the login was issued to) and, for a
99
+ resource inside a project, `--project-id` (or `DIVERGENT_PROJECT_ID`).
100
+
101
+ ```bash
102
+ diverge tools list --filter rbac # find candidates
103
+ diverge tools describe rbac_assign_role # one schema, on demand
104
+ diverge tools call rbac_list_roles --args-json '{}'
105
+ ```
106
+
107
+ A write that needs a human is not refused: it is persisted as a pending
108
+ invocation, decided in the platform's approval inbox, and read back here by
109
+ id. Branch on the exit code, never on text:
110
+
111
+ | code | meaning |
112
+ | --- | --- |
113
+ | 0 | executed, or approved and executed |
114
+ | 3 | pending: hand over the link, read it back later with `diverge tools invocation <id>` (add `--wait` to stay attached) |
115
+ | 4 | rejected, with the operator's reason |
116
+ | 5 | expired: raise a fresh call, the old id is final |
117
+ | 1 | failure, including approved-but-failed |
118
+
119
+ The CLI raises and reads; it never decides. `diverge tools bridge` serves the
120
+ same catalog as a stdio MCP server for clients without a shell, with
121
+ `divergent_invocation_status` for the read-back. `--tools <name,name>` publishes
122
+ and accepts only those tools: the whole catalog is tens of thousands of tokens
123
+ on every request, so a harness gets the tools it was given and nothing else.
124
+
125
+ ## Tenant plugins
126
+
127
+ A tenant plugin is a plugin whose code runs in your organization's own
128
+ runtime and never inside the platform: the platform knows it through a
129
+ manifest (`@divergent/plugin-contract`), enables it per organization, and
130
+ reaches it through your bridge binding. See
131
+ [RFC: Tenant Plugins](../../docs/rfcs/rfc-tenant-plugins.md).
132
+
133
+ ```bash
134
+ diverge plugins validate manifest.json # dry run: every problem the platform would refuse, at once
135
+ diverge plugins publish manifest.json # forge_plugins:create in the login's organization
136
+ diverge plugins list
137
+ diverge plugins retire <pluginId> # the descriptor and its tools go; installations stay
138
+ ```
139
+
140
+ Tools are pinned to the manifest: a change is a new version published again.
141
+ After publishing, enable the plugin with `forge_set_plugin_state` (or the
142
+ Forge Routing page) and bind the runtime with a bridge binding whose
143
+ `runtimeType` equals the manifest's `runtime.type`; declare `networkPolicy`
144
+ on the binding when the endpoint is a tailnet or LAN address.
145
+
146
+ ## Updating
147
+
148
+ `diverge update` installs the latest release with the npm that installed it.
149
+
150
+ ## Environment
151
+
152
+ | variable | effect |
153
+ | --- | --- |
154
+ | `DIVERGENT_API_URL` | the deployment for this shell |
155
+ | `DIVERGE_HOME` | state directory, default `~/.diverge` |
156
+ | `DIVERGENT_ORG_ID`, `DIVERGENT_PROJECT_ID` | tool scope when the flags are omitted; without either, the login's organization |
157
+ | `DIVERGENT_CLIENT_ID`, `DIVERGENT_CLIENT_SECRET`, `DIVERGENT_USER_ID` | unattended tool execution with service credentials, delegated to that person |
158
+ | `DSH_HOME` | where dsh keeps its files, default `~/.dsh` |
159
+
160
+ ## Security
161
+
162
+ Public OAuth client with PKCE; a sign-in carries the scopes your roles back
163
+ at the organization and omits the rest (`diverge login` says which); access tokens live one hour; refresh tokens
164
+ rotate and a replay revokes the session; the cache is written with mode 0600
165
+ from its first byte; `logout` revokes on the platform; every request carries
166
+ `User-Agent: diverge/<version>`, which the platform records as the calling
167
+ client. The package carries no secret. Released from GitHub Actions by npm
168
+ trusted publishing, so no publish token exists.