@tonbo/cli 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -22
- package/dist/bin/tonbo.js +227 -226
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,11 +12,11 @@ The CLI deploys an Agent directly from a local directory without requiring Git o
|
|
|
12
12
|
```console
|
|
13
13
|
tonbo login
|
|
14
14
|
tonbo init
|
|
15
|
-
No Harness-specific configuration was found in this
|
|
15
|
+
No Harness-specific configuration was found in this agent.
|
|
16
16
|
? Which supported Harness should Tonbo use?
|
|
17
|
-
❯ PI (`pi`) Run this
|
|
17
|
+
❯ PI (`pi`) Run this agent with the pi command.
|
|
18
18
|
? How should this Agent connect to Tonbo?
|
|
19
|
-
❯ Create a new
|
|
19
|
+
❯ Create a new agent Create it only after you confirm the complete configuration.
|
|
20
20
|
Use an existing agent
|
|
21
21
|
Set up later
|
|
22
22
|
? Agent name [my-agent]:
|
|
@@ -26,11 +26,11 @@ Tonbo Agent configuration:
|
|
|
26
26
|
Harness: PI
|
|
27
27
|
Execution: PI CLI (`pi`)
|
|
28
28
|
Model: claude-sonnet-4-5
|
|
29
|
-
? Create
|
|
30
|
-
❯ Create
|
|
31
|
-
Edit configuration Change the
|
|
29
|
+
? Create agent and .tonbo with this configuration?
|
|
30
|
+
❯ Create agent and .tonbo
|
|
31
|
+
Edit configuration Change the agent, Harness, execution mode, model, or SDK entrypoint.
|
|
32
32
|
Cancel
|
|
33
|
-
Created
|
|
33
|
+
Created agent my-agent and .tonbo.
|
|
34
34
|
Agent: my-agent (my-agent-my-account.tonbo.sh)
|
|
35
35
|
Organization: my-account
|
|
36
36
|
Application: https://my-agent-my-account.tonbo.sh
|
|
@@ -39,12 +39,12 @@ Mode: PI CLI
|
|
|
39
39
|
Next: tonbo deploy
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
Use the arrow keys and Enter to select a supported Harness and either create or select the one Agent owned by this source tree. Agent creation and file writes wait until the final review; cancelling leaves both local and cloud state unchanged. `AGENTS.md` is Harness-neutral and never identifies PI by itself. When `.pi/settings.json` exists, the shared source inspector identifies the PI Harness and reports its workspace-local packages. A regular PI
|
|
42
|
+
Use the arrow keys and Enter to select a supported Harness and either create or select the one Agent owned by this source tree. Agent creation and file writes wait until the final review; cancelling leaves both local and cloud state unchanged. `AGENTS.md` is Harness-neutral and never identifies PI by itself. When `.pi/settings.json` exists, the shared source inspector identifies the PI Harness and reports its workspace-local packages. A regular PI agent defaults to the native PI CLI; choose `Edit configuration` only when changing the Agent, execution mode, model, or another setting. Unsupported and planned Harnesses are not shown.
|
|
43
43
|
|
|
44
44
|
The generated `.tonbo` is TOML:
|
|
45
45
|
|
|
46
46
|
```toml
|
|
47
|
-
# Tonbo Agent
|
|
47
|
+
# Tonbo Agent configuration.
|
|
48
48
|
# Edit this file directly or run `tonbo init` to reconfigure.
|
|
49
49
|
version = 2
|
|
50
50
|
agent = "my-agent-my-account.tonbo.sh"
|
|
@@ -59,14 +59,14 @@ kind = "native"
|
|
|
59
59
|
model = "claude-sonnet-4-5"
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
The immutable full Agent hostname is the only durable Agent authority in the source tree; it is not a credential. This keeps Agents with the same name in different Organizations unambiguous. `deploy`, `run`, `ssh` and `secret` refuse an unbound declaration. `tonbo agent use <name-hostname-or-id>` accepts any convenient selector, resolves it through the signed-in account and writes the selected `
|
|
62
|
+
The immutable full Agent hostname is the only durable Agent authority in the source tree; it is not a credential. This keeps Agents with the same name in different Organizations unambiguous. `deploy`, `run`, `ssh` and `secret` refuse an unbound declaration. `tonbo agent use <name-hostname-or-id>` accepts any convenient selector, resolves it through the signed-in account and writes the selected `agent-organization.tonbo.sh` hostname into `.tonbo`; rebinding an existing declaration requires interactive confirmation or `--force`. UUIDs remain internal API identities and are never committed by the CLI.
|
|
63
63
|
|
|
64
|
-
The default inference model is written without prompting. Override it explicitly with `--model`. Headless initialization makes Harness and driver choices explicit and may bind the resulting declaration with `
|
|
64
|
+
The default inference model is written without prompting. Override it explicitly with `--model`. Headless initialization makes Harness and driver choices explicit and may bind the resulting declaration with `agent use`:
|
|
65
65
|
|
|
66
66
|
```console
|
|
67
67
|
tonbo init --harness pi --driver native
|
|
68
68
|
tonbo init --harness pi --driver command --agent-entry dist/agent.mjs
|
|
69
|
-
tonbo agent use my-
|
|
69
|
+
tonbo agent use my-agent-my-account.tonbo.sh
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
After interactive initialization, deploy the current contents:
|
|
@@ -88,12 +88,12 @@ Every `tonbo deploy` creates one immutable Deployment: the uploaded source, the
|
|
|
88
88
|
|
|
89
89
|
```console
|
|
90
90
|
$ tonbo deploy
|
|
91
|
-
Deployed
|
|
91
|
+
Deployed agent my-agent.
|
|
92
92
|
Organization: my-account
|
|
93
93
|
Deployment: Fix greeting (0f3a7c21)
|
|
94
94
|
Source: 0123456 · main
|
|
95
95
|
Production: promoting (generation 4)
|
|
96
|
-
Application: https://my-
|
|
96
|
+
Application: https://my-agent-my-account.tonbo.sh
|
|
97
97
|
|
|
98
98
|
Agent process: starts with the first turn and stays warm while the runtime is active.
|
|
99
99
|
|
|
@@ -101,7 +101,7 @@ Start the Agent:
|
|
|
101
101
|
tonbo run "<prompt>"
|
|
102
102
|
|
|
103
103
|
Connect with SSH:
|
|
104
|
-
ssh my-
|
|
104
|
+
ssh my-agent.my-account@tonbo.sh
|
|
105
105
|
```
|
|
106
106
|
|
|
107
107
|
When the new Deployment has the same contents as the Production Deployment it replaces, `deploy` says so; the record is still created. `tonbo deploy --no-promote` creates the Deployment without moving Production, for example to stage a build and promote it later:
|
|
@@ -120,7 +120,7 @@ tonbo deployments rollback 9b8c1d22 # or to a named one
|
|
|
120
120
|
Use `tonbo agent show` to rediscover the username, application address and SSH command later. `tonbo login` registers public keys found at the conventional `~/.ssh/id_ed25519.pub`, `id_ecdsa.pub` and `id_rsa.pub` paths. Register a key at another path with `tonbo ssh-key add <path.pub>` and revoke a lost or retired key with `tonbo ssh-key remove SHA256:...`. The CLI is not part of the SSH connection; ordinary OpenSSH, `scp` and `rsync` connect directly:
|
|
121
121
|
|
|
122
122
|
```console
|
|
123
|
-
ssh my-
|
|
123
|
+
ssh my-agent.my-account@tonbo.sh
|
|
124
124
|
```
|
|
125
125
|
|
|
126
126
|
The SSH user name is the Agent qualified by its username; the signed public key identifies the Tonbo user and is checked against membership and IAM at connection time. The CLI resolves the Agent name in `.tonbo` and prints the server-authoritative destination, so users never need to remember or assemble a username. The shell and HTTP turns may coexist on the singleton runtime, allowing the shell to observe the Agent while it works. Session writer fencing protects durable conversation history; ordinary workspace files retain Linux process concurrency semantics.
|
|
@@ -131,11 +131,11 @@ The platform injects scoped inference access and owns PI Session capture. Do not
|
|
|
131
131
|
|
|
132
132
|
Deploy snapshots regular files under `.tonbo`, excluding `.git`, `node_modules`, PI package caches, local environment files and patterns in `.tonboignore`. Coding tools start in the persistent Artifacts workspace, while `AGENTS.md` and trusted `.pi` extensions, skills, prompts and settings load from the Deployment's uploaded source.
|
|
133
133
|
|
|
134
|
-
## Supported PI
|
|
134
|
+
## Supported PI agent shapes
|
|
135
135
|
|
|
136
136
|
### 1. Agent-local Agent
|
|
137
137
|
|
|
138
|
-
Keep the normal PI
|
|
138
|
+
Keep the normal PI agent layout. Tonbo runs the same `AGENTS.md`, `.pi/APPEND_SYSTEM.md`, `.pi/settings.json`, `.pi/extensions`, `.pi/skills` and `.pi/prompts` that local `pi` loads:
|
|
139
139
|
|
|
140
140
|
```console
|
|
141
141
|
pi
|
|
@@ -144,9 +144,9 @@ tonbo deploy
|
|
|
144
144
|
tonbo run "Start the task."
|
|
145
145
|
```
|
|
146
146
|
|
|
147
|
-
### 2. PI Package
|
|
147
|
+
### 2. PI Package agent
|
|
148
148
|
|
|
149
|
-
Use PI's
|
|
149
|
+
Use PI's agent-local package declaration as the single source of truth. A package is a PI resource, not another Tonbo execution mode: `tonbo init` recognizes `.pi/settings.json`, then the native PI driver loads the packages. Install an immutable package reference locally, verify it with PI, and deploy the agent; Tonbo excludes `.pi/npm` and `.pi/git` caches and restores packages inside the isolated runtime:
|
|
150
150
|
|
|
151
151
|
```console
|
|
152
152
|
pi install -l npm:@acme/my-pi-agent@1.2.3
|
|
@@ -196,7 +196,25 @@ Node.js 22 or newer is required. Human OAuth tokens are stored at `${XDG_CONFIG_
|
|
|
196
196
|
- [Design documentation map](../../docs/design/README.md)
|
|
197
197
|
- [Agent source inspection and onboarding](../../docs/design/agent-source-onboarding.md)
|
|
198
198
|
- [Shared source inspector](../../packages/agent-source-inspector/README.md)
|
|
199
|
-
- [Agent resource model](../../docs/design/
|
|
200
|
-
- [Agent Deployments, Production and Rollouts](../../docs/design/
|
|
199
|
+
- [Agent resource model](../../docs/design/agent-resource-model.md)
|
|
200
|
+
- [Agent Deployments, Production and Rollouts](../../docs/design/agent-deployments.md)
|
|
201
201
|
- [ADR-0008: Tonbo CLI onboarding and credential boundaries](../../apps/user-center/docs/decisions/adr-0008-tonbo-cli-onboarding.md)
|
|
202
202
|
- [Management API v1](../../contracts/management/openapi-v1.yaml)
|
|
203
|
+
|
|
204
|
+
## Machines
|
|
205
|
+
|
|
206
|
+
Machines belong to an Account independently of Agents. Allocate in `us-east-1`, bind an available Machine from Agent detail or the CLI, inspect measured metrics, and release it when no longer needed. Allocation starts no compute. The platform handles idle pause/resume automatically; there are no customer power controls or arbitrary Machine command/upload APIs.
|
|
207
|
+
|
|
208
|
+
```console
|
|
209
|
+
tonbo --json machine allocate --account <account-uuid> --region us-east-1
|
|
210
|
+
tonbo --json machine list --account <account-uuid>
|
|
211
|
+
tonbo --json machine bind <machine-uuid> --agent <agent-uuid>
|
|
212
|
+
tonbo --json machine show <machine-uuid>
|
|
213
|
+
tonbo --json machine metrics <machine-uuid>
|
|
214
|
+
tonbo --json machine unbind <machine-uuid>
|
|
215
|
+
tonbo --json machine release <machine-uuid>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Only one active Agent binding per Machine and one Machine per Agent are currently supported. Unbinding drains execution and completes physical deletion, final usage settlement and storage credential revocation before the Machine becomes available. Wait for `available: true` in `machine show` before rebinding. Cross-Agent reassignment resets rootfs; the destination mounts its own Agent Workspace and Sessions. Hidden pause/resume within the same Agent retains rootfs and process state. Release also drains an existing binding automatically; it does not delete Agent files or Sessions.
|
|
219
|
+
|
|
220
|
+
`tonbo agent list` lists accessible Agents without requiring a local `.tonbo` file. JSON command output uses `agent` for one Agent and `agents` for the list; there is no duplicate `agent` output alias.
|
package/dist/bin/tonbo.js
CHANGED
|
@@ -49,14 +49,14 @@ var TonboApi = class {
|
|
|
49
49
|
...body === void 0 ? {} : { body: JSON.stringify(body) }
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
|
-
|
|
52
|
+
listAgents(oauthToken) {
|
|
53
53
|
return requestJson(
|
|
54
54
|
this.fetcher,
|
|
55
55
|
`${this.accountOrigin}/api/cli/agents`,
|
|
56
56
|
{
|
|
57
57
|
headers: { authorization: `Bearer ${oauthToken}` }
|
|
58
58
|
}
|
|
59
|
-
).then((body) => body.
|
|
59
|
+
).then((body) => body.agents);
|
|
60
60
|
}
|
|
61
61
|
listOrganizations(oauthToken) {
|
|
62
62
|
return requestJson(
|
|
@@ -67,7 +67,7 @@ var TonboApi = class {
|
|
|
67
67
|
}
|
|
68
68
|
).then((body) => body.organizations);
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
createAgent(oauthToken, name, organizationId) {
|
|
71
71
|
return requestJson(
|
|
72
72
|
this.fetcher,
|
|
73
73
|
`${this.accountOrigin}/api/cli/agents`,
|
|
@@ -79,7 +79,7 @@ var TonboApi = class {
|
|
|
79
79
|
},
|
|
80
80
|
body: JSON.stringify(organizationId ? { name, orgId: organizationId } : { name })
|
|
81
81
|
}
|
|
82
|
-
).then((body) => body.
|
|
82
|
+
).then((body) => body.agent);
|
|
83
83
|
}
|
|
84
84
|
registerSshKey(oauthToken, key) {
|
|
85
85
|
return requestJson(
|
|
@@ -113,10 +113,10 @@ var TonboApi = class {
|
|
|
113
113
|
}
|
|
114
114
|
).then((body) => body.key);
|
|
115
115
|
}
|
|
116
|
-
exchangeManagementToken(oauthToken,
|
|
116
|
+
exchangeManagementToken(oauthToken, agentId) {
|
|
117
117
|
return requestJson(
|
|
118
118
|
this.fetcher,
|
|
119
|
-
`${this.accountOrigin}/api/cli/agents/${
|
|
119
|
+
`${this.accountOrigin}/api/cli/agents/${agentId}/token`,
|
|
120
120
|
{
|
|
121
121
|
method: "POST",
|
|
122
122
|
headers: { authorization: `Bearer ${oauthToken}` }
|
|
@@ -126,7 +126,7 @@ var TonboApi = class {
|
|
|
126
126
|
async deploy({
|
|
127
127
|
bundle,
|
|
128
128
|
origin,
|
|
129
|
-
|
|
129
|
+
agentId,
|
|
130
130
|
promote,
|
|
131
131
|
spec,
|
|
132
132
|
token
|
|
@@ -136,7 +136,7 @@ var TonboApi = class {
|
|
|
136
136
|
sha256: bundle.sha256,
|
|
137
137
|
size_bytes: bundle.size_bytes
|
|
138
138
|
};
|
|
139
|
-
const bundlesPath = `/v1/agents/${
|
|
139
|
+
const bundlesPath = `/v1/agents/${agentId}/source-bundles`;
|
|
140
140
|
const prepared = await this.management("PUT", `${bundlesPath}/${bundle.sha256}`, token, {
|
|
141
141
|
format: descriptor.format,
|
|
142
142
|
size_bytes: descriptor.size_bytes
|
|
@@ -166,14 +166,14 @@ var TonboApi = class {
|
|
|
166
166
|
throw error;
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
|
-
let deployment = await this.createDeployment(
|
|
170
|
-
const previous = await this.getProduction(
|
|
171
|
-
const previousDeployment = previous ? await this.getDeployment(
|
|
169
|
+
let deployment = await this.createDeployment(agentId, { spec, origin }, token);
|
|
170
|
+
const previous = await this.getProduction(agentId, token);
|
|
171
|
+
const previousDeployment = previous ? await this.getDeployment(agentId, previous.deployment_id, token) : null;
|
|
172
172
|
const unchanged = previousDeployment?.spec_sha256 === deployment.spec_sha256;
|
|
173
173
|
let production = previous;
|
|
174
174
|
if (promote) {
|
|
175
175
|
production = await this.putProduction(
|
|
176
|
-
|
|
176
|
+
agentId,
|
|
177
177
|
{
|
|
178
178
|
deployment_id: deployment.id,
|
|
179
179
|
desired_state: "running",
|
|
@@ -181,32 +181,32 @@ var TonboApi = class {
|
|
|
181
181
|
},
|
|
182
182
|
token
|
|
183
183
|
);
|
|
184
|
-
deployment = await this.getDeployment(
|
|
184
|
+
deployment = await this.getDeployment(agentId, deployment.id, token);
|
|
185
185
|
}
|
|
186
186
|
return { deployment, production, unchanged };
|
|
187
187
|
}
|
|
188
|
-
createDeployment(
|
|
188
|
+
createDeployment(agentId, body, token) {
|
|
189
189
|
return this.management(
|
|
190
190
|
"POST",
|
|
191
|
-
`/v1/agents/${
|
|
191
|
+
`/v1/agents/${agentId}/deployments`,
|
|
192
192
|
token,
|
|
193
193
|
body
|
|
194
194
|
).then((response) => response.data);
|
|
195
195
|
}
|
|
196
|
-
listDeployments(
|
|
197
|
-
return this.managementList(`/v1/agents/${
|
|
196
|
+
listDeployments(agentId, token) {
|
|
197
|
+
return this.managementList(`/v1/agents/${agentId}/deployments`, token);
|
|
198
198
|
}
|
|
199
|
-
getDeployment(
|
|
199
|
+
getDeployment(agentId, deploymentId, token) {
|
|
200
200
|
return this.management(
|
|
201
201
|
"GET",
|
|
202
|
-
`/v1/agents/${
|
|
202
|
+
`/v1/agents/${agentId}/deployments/${deploymentId}`,
|
|
203
203
|
token
|
|
204
204
|
).then((response) => response.data);
|
|
205
205
|
}
|
|
206
|
-
getProduction(
|
|
206
|
+
getProduction(agentId, token) {
|
|
207
207
|
return this.management(
|
|
208
208
|
"GET",
|
|
209
|
-
`/v1/agents/${
|
|
209
|
+
`/v1/agents/${agentId}/production`,
|
|
210
210
|
token
|
|
211
211
|
).then(
|
|
212
212
|
(response) => response.data,
|
|
@@ -216,44 +216,38 @@ var TonboApi = class {
|
|
|
216
216
|
}
|
|
217
217
|
);
|
|
218
218
|
}
|
|
219
|
-
putProduction(
|
|
219
|
+
putProduction(agentId, body, token) {
|
|
220
220
|
return this.management(
|
|
221
221
|
"PUT",
|
|
222
|
-
`/v1/agents/${
|
|
222
|
+
`/v1/agents/${agentId}/production`,
|
|
223
223
|
token,
|
|
224
224
|
body
|
|
225
225
|
).then((response) => response.data);
|
|
226
226
|
}
|
|
227
|
-
listRollouts(
|
|
228
|
-
return this.managementList(
|
|
229
|
-
`/v1/agents/${projectId}/production/rollouts`,
|
|
230
|
-
token
|
|
231
|
-
);
|
|
227
|
+
listRollouts(agentId, token) {
|
|
228
|
+
return this.managementList(`/v1/agents/${agentId}/production/rollouts`, token);
|
|
232
229
|
}
|
|
233
230
|
async run({
|
|
234
|
-
|
|
231
|
+
agentId,
|
|
235
232
|
prompt,
|
|
236
233
|
sessionId,
|
|
237
234
|
token,
|
|
238
235
|
turnId = randomUUID()
|
|
239
236
|
}) {
|
|
240
|
-
const
|
|
241
|
-
const production = await this.getProduction(
|
|
237
|
+
const agentPath = `/v1/agents/${agentId}`;
|
|
238
|
+
const production = await this.getProduction(agentId, token);
|
|
242
239
|
if (!production)
|
|
243
240
|
throw new Error("Agent has no Production Deployment; run `tonbo deploy` first.");
|
|
244
241
|
if (production.observed_state !== "running")
|
|
245
242
|
throw new Error(
|
|
246
243
|
`Production is ${production.observed_state}${production.last_error ? ` (${production.last_error})` : ""}; wait for it to be running.`
|
|
247
244
|
);
|
|
248
|
-
const session = sessionId ? { id: sessionId } : (await this.management(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
token,
|
|
252
|
-
{ deployment_id: production.deployment_id }
|
|
253
|
-
)).data;
|
|
245
|
+
const session = sessionId ? { id: sessionId } : (await this.management("POST", `${agentPath}/sessions`, token, {
|
|
246
|
+
deployment_id: production.deployment_id
|
|
247
|
+
})).data;
|
|
254
248
|
const turn = (await this.management(
|
|
255
249
|
"POST",
|
|
256
|
-
`${
|
|
250
|
+
`${agentPath}/sessions/${session.id}/turns`,
|
|
257
251
|
token,
|
|
258
252
|
{ prompt },
|
|
259
253
|
turnId
|
|
@@ -262,36 +256,36 @@ var TonboApi = class {
|
|
|
262
256
|
}
|
|
263
257
|
turnEvents({
|
|
264
258
|
after = 0,
|
|
265
|
-
|
|
259
|
+
agentId,
|
|
266
260
|
sessionId,
|
|
267
261
|
token,
|
|
268
262
|
turnId
|
|
269
263
|
}) {
|
|
270
264
|
return this.management(
|
|
271
265
|
"GET",
|
|
272
|
-
`/v1/agents/${
|
|
266
|
+
`/v1/agents/${agentId}/sessions/${sessionId}/turns/${turnId}/events?after=${after}`,
|
|
273
267
|
token
|
|
274
268
|
);
|
|
275
269
|
}
|
|
276
|
-
|
|
270
|
+
listAgentSecrets(agentId, token) {
|
|
277
271
|
return this.management(
|
|
278
272
|
"GET",
|
|
279
|
-
`/v1/agents/${
|
|
273
|
+
`/v1/agents/${agentId}/secrets`,
|
|
280
274
|
token
|
|
281
275
|
).then((body) => body.data);
|
|
282
276
|
}
|
|
283
|
-
|
|
277
|
+
setAgentSecret(agentId, name, value, token) {
|
|
284
278
|
return this.management(
|
|
285
279
|
"PUT",
|
|
286
|
-
`/v1/agents/${
|
|
280
|
+
`/v1/agents/${agentId}/secrets/${encodeURIComponent(name)}`,
|
|
287
281
|
token,
|
|
288
282
|
{ value }
|
|
289
283
|
);
|
|
290
284
|
}
|
|
291
|
-
|
|
285
|
+
deleteAgentSecret(agentId, name, token) {
|
|
292
286
|
return this.management(
|
|
293
287
|
"DELETE",
|
|
294
|
-
`/v1/agents/${
|
|
288
|
+
`/v1/agents/${agentId}/secrets/${encodeURIComponent(name)}`,
|
|
295
289
|
token
|
|
296
290
|
);
|
|
297
291
|
}
|
|
@@ -879,10 +873,10 @@ var piAgentSchema = {
|
|
|
879
873
|
}
|
|
880
874
|
}
|
|
881
875
|
};
|
|
882
|
-
var
|
|
876
|
+
var agentServiceSchema = {
|
|
883
877
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
884
|
-
"$id": "https://contracts.tonbo.dev/agents/
|
|
885
|
-
"title": "Tonbo
|
|
878
|
+
"$id": "https://contracts.tonbo.dev/agents/agent-service-v1.schema.json",
|
|
879
|
+
"title": "Tonbo Agent application service v1",
|
|
886
880
|
"type": "object",
|
|
887
881
|
"additionalProperties": false,
|
|
888
882
|
"required": [
|
|
@@ -982,10 +976,10 @@ var declarationSchema = {
|
|
|
982
976
|
}
|
|
983
977
|
},
|
|
984
978
|
"service": {
|
|
985
|
-
"$ref": "https://contracts.tonbo.dev/agents/
|
|
979
|
+
"$ref": "https://contracts.tonbo.dev/agents/agent-service-v1.schema.json"
|
|
986
980
|
},
|
|
987
981
|
"agent": {
|
|
988
|
-
"$ref": "https://contracts.tonbo.dev/agents/
|
|
982
|
+
"$ref": "https://contracts.tonbo.dev/agents/agent-name-v1.json#/$defs/publicHostname"
|
|
989
983
|
},
|
|
990
984
|
"harness": {
|
|
991
985
|
"$ref": "https://contracts.tonbo.dev/agents/pi-agent-v1.schema.json"
|
|
@@ -995,7 +989,7 @@ var declarationSchema = {
|
|
|
995
989
|
var deploymentSchema = {
|
|
996
990
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
997
991
|
"$id": "https://contracts.tonbo.dev/agents/managed-deployment-v1.schema.json",
|
|
998
|
-
"title": "Managed
|
|
992
|
+
"title": "Managed Agent Deployment v1",
|
|
999
993
|
"type": "object",
|
|
1000
994
|
"additionalProperties": false,
|
|
1001
995
|
"required": [
|
|
@@ -1049,7 +1043,7 @@ var deploymentSchema = {
|
|
|
1049
1043
|
}
|
|
1050
1044
|
},
|
|
1051
1045
|
"service": {
|
|
1052
|
-
"$ref": "https://contracts.tonbo.dev/agents/
|
|
1046
|
+
"$ref": "https://contracts.tonbo.dev/agents/agent-service-v1.schema.json"
|
|
1053
1047
|
}
|
|
1054
1048
|
}
|
|
1055
1049
|
};
|
|
@@ -1073,12 +1067,12 @@ var piSessionContract = {
|
|
|
1073
1067
|
"session-preflight"
|
|
1074
1068
|
]
|
|
1075
1069
|
};
|
|
1076
|
-
var
|
|
1070
|
+
var agentNameContract = {
|
|
1077
1071
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
1078
|
-
"$id": "https://contracts.tonbo.dev/agents/
|
|
1072
|
+
"$id": "https://contracts.tonbo.dev/agents/agent-name-v1.json",
|
|
1079
1073
|
"x-tonbo-version": 1,
|
|
1080
1074
|
"x-tonbo-public-hostname-apex": "tonbo.sh",
|
|
1081
|
-
"x-tonbo-public-hostname-template": "<
|
|
1075
|
+
"x-tonbo-public-hostname-template": "<agent>-<organization>.tonbo.sh",
|
|
1082
1076
|
"$defs": {
|
|
1083
1077
|
"name": {
|
|
1084
1078
|
"type": "string",
|
|
@@ -1129,8 +1123,8 @@ ajv.addKeyword({ keyword: "x-tonbo-public-hostname-apex" });
|
|
|
1129
1123
|
ajv.addKeyword({ keyword: "x-tonbo-public-hostname-template" });
|
|
1130
1124
|
ajv.addSchema(kubernetesProfilesSchema);
|
|
1131
1125
|
ajv.addSchema(piAgentSchema);
|
|
1132
|
-
ajv.addSchema(
|
|
1133
|
-
ajv.addSchema(
|
|
1126
|
+
ajv.addSchema(agentNameContract);
|
|
1127
|
+
ajv.addSchema(agentServiceSchema);
|
|
1134
1128
|
var validateDeclaration = ajv.compile(declarationSchema);
|
|
1135
1129
|
var validateDeploymentSpec = ajv.compile(deploymentSchema);
|
|
1136
1130
|
function validationMessage(label, errors) {
|
|
@@ -1157,10 +1151,10 @@ import path2 from "node:path";
|
|
|
1157
1151
|
import { parse, stringify } from "smol-toml";
|
|
1158
1152
|
var DECLARATION_FILENAME = ".tonbo";
|
|
1159
1153
|
var DEFAULT_INFERENCE_MODEL = "claude-sonnet-4-5";
|
|
1160
|
-
function createDeclaration(model = DEFAULT_INFERENCE_MODEL, driver = { kind: "native" }, buildCommand,
|
|
1154
|
+
function createDeclaration(model = DEFAULT_INFERENCE_MODEL, driver = { kind: "native" }, buildCommand, agentHostname) {
|
|
1161
1155
|
return parseDeclaration({
|
|
1162
1156
|
version: 2,
|
|
1163
|
-
...
|
|
1157
|
+
...agentHostname ? { agent: agentHostname } : {},
|
|
1164
1158
|
harness: { runtime: "pi", driver },
|
|
1165
1159
|
inference: { model: model.trim() },
|
|
1166
1160
|
...buildCommand ? { build: { command: buildCommand } } : {}
|
|
@@ -1171,9 +1165,9 @@ function renderDeclaration(declaration) {
|
|
|
1171
1165
|
# Edit this file directly or run \`tonbo init\` to reconfigure.
|
|
1172
1166
|
${stringify(declaration)}`;
|
|
1173
1167
|
}
|
|
1174
|
-
async function
|
|
1168
|
+
async function bindDeclarationAgent(root, agentHostname) {
|
|
1175
1169
|
const declaration = await loadDeclaration(root);
|
|
1176
|
-
const bound = parseDeclaration({ ...declaration, agent:
|
|
1170
|
+
const bound = parseDeclaration({ ...declaration, agent: agentHostname });
|
|
1177
1171
|
await saveDeclaration(root, bound, true);
|
|
1178
1172
|
return bound;
|
|
1179
1173
|
}
|
|
@@ -1303,7 +1297,7 @@ async function validatePackageSource(root, source) {
|
|
|
1303
1297
|
const resolved = path3.resolve(settingsDirectory, source);
|
|
1304
1298
|
const relative = path3.relative(root, resolved);
|
|
1305
1299
|
if (relative === ".." || relative.startsWith(`..${path3.sep}`) || path3.isAbsolute(relative)) {
|
|
1306
|
-
throw new Error(`Local PI package ${source} resolves outside the deployed
|
|
1300
|
+
throw new Error(`Local PI package ${source} resolves outside the deployed agent.`);
|
|
1307
1301
|
}
|
|
1308
1302
|
let metadata;
|
|
1309
1303
|
try {
|
|
@@ -1320,7 +1314,7 @@ async function validatePackageSource(root, source) {
|
|
|
1320
1314
|
return;
|
|
1321
1315
|
}
|
|
1322
1316
|
throw new Error(
|
|
1323
|
-
`PI package ${source} must use an exact npm version, a full Git commit, or a
|
|
1317
|
+
`PI package ${source} must use an exact npm version, a full Git commit, or a agent-local path.`
|
|
1324
1318
|
);
|
|
1325
1319
|
}
|
|
1326
1320
|
async function validatePiPackages(root) {
|
|
@@ -1498,31 +1492,31 @@ async function readDefaultSshPublicKeys(sshDirectory = join(homedir(), ".ssh"))
|
|
|
1498
1492
|
return keys;
|
|
1499
1493
|
}
|
|
1500
1494
|
|
|
1501
|
-
// src/
|
|
1502
|
-
var
|
|
1503
|
-
var
|
|
1504
|
-
var
|
|
1505
|
-
function
|
|
1506
|
-
const valid = name.length >=
|
|
1495
|
+
// src/agent-name.ts
|
|
1496
|
+
var AGENT_NAME_RULE = agentNameContract.$defs.name;
|
|
1497
|
+
var AGENT_NAME_PATTERN = new RegExp(AGENT_NAME_RULE.pattern);
|
|
1498
|
+
var RESERVED_AGENT_NAMES = new Set(AGENT_NAME_RULE.not.enum);
|
|
1499
|
+
function validateAgentName(name) {
|
|
1500
|
+
const valid = name.length >= AGENT_NAME_RULE.minLength && name.length <= AGENT_NAME_RULE.maxLength && AGENT_NAME_PATTERN.test(name) && !RESERVED_AGENT_NAMES.has(name);
|
|
1507
1501
|
if (!valid) {
|
|
1508
1502
|
throw new Error(
|
|
1509
|
-
`Agent name must be ${
|
|
1503
|
+
`Agent name must be ${AGENT_NAME_RULE.minLength}-${AGENT_NAME_RULE.maxLength} lowercase letters, digits, or single hyphens, start with a letter, and not be reserved.`
|
|
1510
1504
|
);
|
|
1511
1505
|
}
|
|
1512
1506
|
}
|
|
1513
|
-
function
|
|
1507
|
+
function agentNameSuggestion(value) {
|
|
1514
1508
|
let name = value.normalize("NFKD").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
|
|
1515
1509
|
if (!/^[a-z]/.test(name)) name = `agent-${name}`;
|
|
1516
|
-
name = name.slice(0,
|
|
1517
|
-
if (name.length <
|
|
1518
|
-
name = `${name || "agent"}-agent`.slice(0,
|
|
1510
|
+
name = name.slice(0, AGENT_NAME_RULE.maxLength).replace(/-+$/g, "");
|
|
1511
|
+
if (name.length < AGENT_NAME_RULE.minLength) {
|
|
1512
|
+
name = `${name || "agent"}-agent`.slice(0, AGENT_NAME_RULE.maxLength);
|
|
1519
1513
|
}
|
|
1520
|
-
if (
|
|
1514
|
+
if (RESERVED_AGENT_NAMES.has(name)) name = `${name}-agent`;
|
|
1521
1515
|
return name;
|
|
1522
1516
|
}
|
|
1523
1517
|
|
|
1524
1518
|
// src/commands.ts
|
|
1525
|
-
async function
|
|
1519
|
+
async function resolveAgent(deps) {
|
|
1526
1520
|
const root = await findDeclarationRoot(deps.cwd());
|
|
1527
1521
|
const declaration = await loadDeclaration(root);
|
|
1528
1522
|
if (!declaration.agent) {
|
|
@@ -1532,14 +1526,14 @@ async function resolveProject(deps) {
|
|
|
1532
1526
|
return {
|
|
1533
1527
|
declaration,
|
|
1534
1528
|
oauthToken,
|
|
1535
|
-
|
|
1529
|
+
agent: selectAgent(await deps.api.listAgents(oauthToken), declaration.agent),
|
|
1536
1530
|
root
|
|
1537
1531
|
};
|
|
1538
1532
|
}
|
|
1539
|
-
function
|
|
1533
|
+
function selectAgent(agents, selector) {
|
|
1540
1534
|
const normalized = selector.toLowerCase();
|
|
1541
|
-
const matches =
|
|
1542
|
-
(
|
|
1535
|
+
const matches = agents.filter(
|
|
1536
|
+
(agent) => agent.id === selector || agent.name === normalized || agent.publicHostname === normalized
|
|
1543
1537
|
);
|
|
1544
1538
|
if (matches.length === 0) throw new Error(`Agent ${selector} was not found in your account.`);
|
|
1545
1539
|
if (matches.length > 1)
|
|
@@ -1573,7 +1567,7 @@ async function initCommand(deps, options) {
|
|
|
1573
1567
|
throw new Error("No Harness-specific configuration found. Pass --harness pi.");
|
|
1574
1568
|
} else {
|
|
1575
1569
|
deps.output({
|
|
1576
|
-
message: "No Harness-specific configuration was found in this
|
|
1570
|
+
message: "No Harness-specific configuration was found in this agent."
|
|
1577
1571
|
});
|
|
1578
1572
|
harness = await selectHarness(deps);
|
|
1579
1573
|
}
|
|
@@ -1599,19 +1593,19 @@ async function initCommand(deps, options) {
|
|
|
1599
1593
|
entry ||= "dist/agent.mjs";
|
|
1600
1594
|
let model = options.model?.trim() || existingDeclaration?.inference.model || DEFAULT_INFERENCE_MODEL;
|
|
1601
1595
|
let buildCommand = driver === "command" ? options.buildCommand ?? existingDeclaration?.build?.command ?? ["npm", "run", "build"] : void 0;
|
|
1602
|
-
let
|
|
1596
|
+
let agentPlan = existingDeclaration?.agent ? {
|
|
1603
1597
|
kind: "existing",
|
|
1604
1598
|
publicHostname: existingDeclaration.agent
|
|
1605
1599
|
} : { kind: "later" };
|
|
1606
1600
|
if (interactive && !existingDeclaration?.agent) {
|
|
1607
|
-
|
|
1601
|
+
agentPlan = await selectInitAgent(deps, root);
|
|
1608
1602
|
}
|
|
1609
1603
|
if (interactive) {
|
|
1610
1604
|
while (true) {
|
|
1611
1605
|
deps.output({
|
|
1612
|
-
message: renderInitSummary(
|
|
1606
|
+
message: renderInitSummary(agentPlan, harness, driver, model, entry, buildCommand)
|
|
1613
1607
|
});
|
|
1614
|
-
const saveLabel = initSaveLabel(exists,
|
|
1608
|
+
const saveLabel = initSaveLabel(exists, agentPlan);
|
|
1615
1609
|
const action = await deps.select(`${saveLabel} with this configuration?`, [
|
|
1616
1610
|
{
|
|
1617
1611
|
name: saveLabel,
|
|
@@ -1633,9 +1627,9 @@ async function initCommand(deps, options) {
|
|
|
1633
1627
|
if (action === "save") break;
|
|
1634
1628
|
const setting = await deps.select("What would you like to change?", [
|
|
1635
1629
|
{
|
|
1636
|
-
description:
|
|
1637
|
-
name: "
|
|
1638
|
-
value: "
|
|
1630
|
+
description: initAgentSummary(agentPlan),
|
|
1631
|
+
name: "Agent",
|
|
1632
|
+
value: "agent"
|
|
1639
1633
|
},
|
|
1640
1634
|
{ description: harnessName(harness), name: "Harness", value: "harness" },
|
|
1641
1635
|
{
|
|
@@ -1647,7 +1641,7 @@ async function initCommand(deps, options) {
|
|
|
1647
1641
|
...driver === "command" ? [{ description: entry, name: "PI SDK entry file", value: "entry" }] : [],
|
|
1648
1642
|
{ name: "Back to review", value: "back" }
|
|
1649
1643
|
]);
|
|
1650
|
-
if (setting === "
|
|
1644
|
+
if (setting === "agent") agentPlan = await selectInitAgent(deps, root);
|
|
1651
1645
|
if (setting === "harness") harness = await selectHarness(deps);
|
|
1652
1646
|
if (setting === "driver") {
|
|
1653
1647
|
driver = await deps.select(
|
|
@@ -1672,22 +1666,18 @@ async function initCommand(deps, options) {
|
|
|
1672
1666
|
}
|
|
1673
1667
|
}
|
|
1674
1668
|
}
|
|
1675
|
-
let
|
|
1676
|
-
let
|
|
1677
|
-
if (
|
|
1669
|
+
let agentHostname = agentPlan.kind === "existing" ? agentPlan.publicHostname : void 0;
|
|
1670
|
+
let createdAgent;
|
|
1671
|
+
if (agentPlan.kind === "create") {
|
|
1678
1672
|
const oauthToken = await deps.auth.accessToken();
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
projectPlan.name,
|
|
1682
|
-
projectPlan.organizationId
|
|
1683
|
-
);
|
|
1684
|
-
projectHostname = createdProject.publicHostname;
|
|
1673
|
+
createdAgent = await deps.api.createAgent(oauthToken, agentPlan.name, agentPlan.organizationId);
|
|
1674
|
+
agentHostname = createdAgent.publicHostname;
|
|
1685
1675
|
}
|
|
1686
1676
|
const configuredDeclaration = createDeclaration(
|
|
1687
1677
|
model,
|
|
1688
1678
|
driver === "native" ? { kind: "native" } : { kind: "command", protocol: "pi-rpc-v1", command: ["node", entry] },
|
|
1689
1679
|
buildCommand,
|
|
1690
|
-
|
|
1680
|
+
agentHostname
|
|
1691
1681
|
);
|
|
1692
1682
|
const declaration = parseDeclaration({
|
|
1693
1683
|
...configuredDeclaration,
|
|
@@ -1700,9 +1690,9 @@ async function initCommand(deps, options) {
|
|
|
1700
1690
|
try {
|
|
1701
1691
|
await saveDeclaration(root, declaration, overwrite);
|
|
1702
1692
|
} catch (error) {
|
|
1703
|
-
if (
|
|
1693
|
+
if (createdAgent) {
|
|
1704
1694
|
throw new Error(
|
|
1705
|
-
`Created agent ${
|
|
1695
|
+
`Created agent ${createdAgent.name}, but could not write ${DECLARATION_FILENAME}. Fix the file and run \`tonbo agent use ${createdAgent.publicHostname}\`.`,
|
|
1706
1696
|
{ cause: error }
|
|
1707
1697
|
);
|
|
1708
1698
|
}
|
|
@@ -1711,30 +1701,30 @@ async function initCommand(deps, options) {
|
|
|
1711
1701
|
const driverSummary = driver === "command" ? `PI SDK app
|
|
1712
1702
|
Build: ${buildCommand?.join(" ")}
|
|
1713
1703
|
Entrypoint: node ${entry}` : inspection.pi.packageCount > 0 ? `PI CLI with ${inspection.pi.packageCount} package${inspection.pi.packageCount === 1 ? "" : "s"}` : "PI CLI";
|
|
1714
|
-
const
|
|
1715
|
-
Organization: ${
|
|
1716
|
-
Application: https://${
|
|
1717
|
-
SSH after deploy: ssh ${
|
|
1718
|
-
Organization: ${
|
|
1719
|
-
SSH after deploy: ssh ${
|
|
1704
|
+
const agentSummary = createdAgent ? `Agent: ${createdAgent.name} (${createdAgent.publicHostname})
|
|
1705
|
+
Organization: ${createdAgent.organizationName}
|
|
1706
|
+
Application: https://${createdAgent.publicHostname}
|
|
1707
|
+
SSH after deploy: ssh ${createdAgent.sshDestination}` : agentPlan.kind === "existing" ? `Agent: ${agentPlan.name ? `${agentPlan.name} (${agentPlan.publicHostname})` : agentPlan.publicHostname}${agentPlan.organizationName ? `
|
|
1708
|
+
Organization: ${agentPlan.organizationName}` : ""}${agentPlan.sshDestination ? `
|
|
1709
|
+
SSH after deploy: ssh ${agentPlan.sshDestination}` : ""}` : "Next: tonbo agent create <name>";
|
|
1720
1710
|
deps.output({
|
|
1721
|
-
message: `${
|
|
1722
|
-
${
|
|
1711
|
+
message: `${createdAgent ? `Created agent ${createdAgent.name} and` : exists ? "Updated" : "Created"} ${DECLARATION_FILENAME}.
|
|
1712
|
+
${agentSummary}
|
|
1723
1713
|
Harness: PI
|
|
1724
|
-
Mode: ${driverSummary}${
|
|
1714
|
+
Mode: ${driverSummary}${agentPlan.kind === "later" ? "" : "\nNext: tonbo deploy"}`,
|
|
1725
1715
|
declaration,
|
|
1726
1716
|
path: path4.join(root, DECLARATION_FILENAME)
|
|
1727
1717
|
});
|
|
1728
1718
|
}
|
|
1729
|
-
function initSaveLabel(exists,
|
|
1730
|
-
if (
|
|
1719
|
+
function initSaveLabel(exists, agent) {
|
|
1720
|
+
if (agent.kind === "create") {
|
|
1731
1721
|
return exists ? `Create agent and replace ${DECLARATION_FILENAME}` : `Create agent and ${DECLARATION_FILENAME}`;
|
|
1732
1722
|
}
|
|
1733
1723
|
return exists ? `Replace ${DECLARATION_FILENAME}` : `Create ${DECLARATION_FILENAME}`;
|
|
1734
1724
|
}
|
|
1735
|
-
async function
|
|
1725
|
+
async function selectInitAgent(deps, root) {
|
|
1736
1726
|
for (; ; ) {
|
|
1737
|
-
const action = await deps.select("How should this
|
|
1727
|
+
const action = await deps.select("How should this directory connect to Tonbo?", [
|
|
1738
1728
|
{
|
|
1739
1729
|
description: "Create it only after you confirm the complete configuration.",
|
|
1740
1730
|
name: "Create a new agent",
|
|
@@ -1754,12 +1744,12 @@ async function selectInitProject(deps, root) {
|
|
|
1754
1744
|
if (action === "later") return { kind: "later" };
|
|
1755
1745
|
if (action === "create") {
|
|
1756
1746
|
const organization = await selectInitOrganization(deps);
|
|
1757
|
-
const defaultName =
|
|
1747
|
+
const defaultName = agentNameSuggestion(path4.basename(root)) || "tonbo-agent";
|
|
1758
1748
|
for (; ; ) {
|
|
1759
1749
|
const answer = (await deps.prompt(`Agent name [${defaultName}]: `)).trim().toLowerCase();
|
|
1760
1750
|
const name = answer || defaultName;
|
|
1761
1751
|
try {
|
|
1762
|
-
|
|
1752
|
+
validateAgentName(name);
|
|
1763
1753
|
return {
|
|
1764
1754
|
kind: "create",
|
|
1765
1755
|
name,
|
|
@@ -1774,36 +1764,36 @@ async function selectInitProject(deps, root) {
|
|
|
1774
1764
|
}
|
|
1775
1765
|
}
|
|
1776
1766
|
const oauthToken = await deps.auth.accessToken();
|
|
1777
|
-
const
|
|
1778
|
-
(
|
|
1767
|
+
const agents = (await deps.api.listAgents(oauthToken)).filter(
|
|
1768
|
+
(agent2) => agent2.status === "active"
|
|
1779
1769
|
);
|
|
1780
|
-
if (
|
|
1770
|
+
if (agents.length === 0) {
|
|
1781
1771
|
deps.output({ message: "No active agents are available. Create a new agent instead." });
|
|
1782
1772
|
continue;
|
|
1783
1773
|
}
|
|
1784
1774
|
const id = await deps.select(
|
|
1785
|
-
"Which agent should this
|
|
1786
|
-
|
|
1787
|
-
name:
|
|
1788
|
-
description: `Organization: ${
|
|
1789
|
-
value:
|
|
1775
|
+
"Which agent should this directory use?",
|
|
1776
|
+
agents.map((agent2) => ({
|
|
1777
|
+
name: agent2.name,
|
|
1778
|
+
description: `Organization: ${agent2.organizationName} \xB7 ${agent2.publicHostname} \xB7 ${agent2.id}`,
|
|
1779
|
+
value: agent2.id
|
|
1790
1780
|
}))
|
|
1791
1781
|
);
|
|
1792
|
-
const
|
|
1782
|
+
const agent = selectAgent(agents, id);
|
|
1793
1783
|
return {
|
|
1794
1784
|
kind: "existing",
|
|
1795
|
-
name:
|
|
1796
|
-
organizationName:
|
|
1797
|
-
publicHostname:
|
|
1798
|
-
sshDestination:
|
|
1785
|
+
name: agent.name,
|
|
1786
|
+
organizationName: agent.organizationName,
|
|
1787
|
+
publicHostname: agent.publicHostname,
|
|
1788
|
+
sshDestination: agent.sshDestination
|
|
1799
1789
|
};
|
|
1800
1790
|
}
|
|
1801
1791
|
}
|
|
1802
|
-
function
|
|
1803
|
-
if (
|
|
1804
|
-
return `Create ${
|
|
1805
|
-
if (
|
|
1806
|
-
return
|
|
1792
|
+
function initAgentSummary(agent) {
|
|
1793
|
+
if (agent.kind === "create")
|
|
1794
|
+
return `Create ${agent.name}${agent.organizationName ? ` in ${agent.organizationName}` : ""} (permanent address assigned after creation)`;
|
|
1795
|
+
if (agent.kind === "existing") {
|
|
1796
|
+
return agent.name ? `${agent.name}${agent.organizationName ? ` in ${agent.organizationName}` : ""} (${agent.publicHostname})` : `Keep ${agent.publicHostname}`;
|
|
1807
1797
|
}
|
|
1808
1798
|
return "Set up later";
|
|
1809
1799
|
}
|
|
@@ -1813,7 +1803,7 @@ async function selectInitOrganization(deps) {
|
|
|
1813
1803
|
(organization) => organization.role !== "member"
|
|
1814
1804
|
);
|
|
1815
1805
|
if (organizations.length === 0)
|
|
1816
|
-
throw new Error("No organization lets you create a
|
|
1806
|
+
throw new Error("No organization lets you create a agent. Ask an admin or owner.");
|
|
1817
1807
|
if (organizations.length === 1) return organizations[0];
|
|
1818
1808
|
const id = await deps.select(
|
|
1819
1809
|
"Which organization should own the new agent?",
|
|
@@ -1839,11 +1829,11 @@ function harnessName(harness) {
|
|
|
1839
1829
|
const supported = supportedHarnesses.find((candidate) => candidate.id === harness);
|
|
1840
1830
|
return supported?.name ?? harness;
|
|
1841
1831
|
}
|
|
1842
|
-
function renderInitSummary(
|
|
1832
|
+
function renderInitSummary(agent, harness, driver, model, entry, buildCommand) {
|
|
1843
1833
|
const lines = [
|
|
1844
1834
|
"",
|
|
1845
1835
|
"Tonbo Agent configuration:",
|
|
1846
|
-
` Agent: ${
|
|
1836
|
+
` Agent: ${initAgentSummary(agent)}`,
|
|
1847
1837
|
` Harness: ${harnessName(harness)}`,
|
|
1848
1838
|
` Execution: ${driver === "native" ? "PI CLI (`pi`)" : "PI SDK app"}`,
|
|
1849
1839
|
` Model: ${model}`
|
|
@@ -1862,7 +1852,7 @@ function piTargetChoices(settingsFound) {
|
|
|
1862
1852
|
value: "command"
|
|
1863
1853
|
},
|
|
1864
1854
|
native: {
|
|
1865
|
-
description: settingsFound ? "Run pi directly and load the detected
|
|
1855
|
+
description: settingsFound ? "Run pi directly and load the detected agent-local PI configuration." : "Run pi directly with AGENTS.md and optional agent-local .pi resources.",
|
|
1866
1856
|
name: "PI CLI (`pi`)",
|
|
1867
1857
|
value: "native"
|
|
1868
1858
|
}
|
|
@@ -1928,28 +1918,31 @@ async function sshKeyRemoveCommand(deps, fingerprint) {
|
|
|
1928
1918
|
const key = await deps.api.revokeSshKey(oauthToken, fingerprint);
|
|
1929
1919
|
deps.output({ message: `Revoked SSH key ${key.fingerprint}.`, key });
|
|
1930
1920
|
}
|
|
1931
|
-
async function
|
|
1921
|
+
async function agentUseCommand(deps, selector, force = false) {
|
|
1932
1922
|
const root = await findDeclarationRoot(deps.cwd());
|
|
1933
1923
|
const declaration = await loadDeclaration(root);
|
|
1934
1924
|
const oauthToken = await deps.auth.accessToken();
|
|
1935
|
-
const
|
|
1936
|
-
const
|
|
1937
|
-
if (declaration.agent ===
|
|
1938
|
-
deps.output({
|
|
1925
|
+
const agents = await deps.api.listAgents(oauthToken);
|
|
1926
|
+
const agent = selectAgent(agents, selector);
|
|
1927
|
+
if (declaration.agent === agent.publicHostname) {
|
|
1928
|
+
deps.output({
|
|
1929
|
+
message: `.tonbo already uses agent ${agent.publicHostname}.`,
|
|
1930
|
+
agent
|
|
1931
|
+
});
|
|
1939
1932
|
return;
|
|
1940
1933
|
}
|
|
1941
1934
|
if (declaration.agent && !force) {
|
|
1942
|
-
const current =
|
|
1935
|
+
const current = agents.find((candidate) => candidate.publicHostname === declaration.agent);
|
|
1943
1936
|
const currentLabel = current?.publicHostname ?? declaration.agent;
|
|
1944
1937
|
if (!deps.interactive()) {
|
|
1945
1938
|
throw new Error(
|
|
1946
|
-
`.tonbo already uses agent ${currentLabel}. Pass --force to rebind it to ${
|
|
1939
|
+
`.tonbo already uses agent ${currentLabel}. Pass --force to rebind it to ${agent.publicHostname}.`
|
|
1947
1940
|
);
|
|
1948
1941
|
}
|
|
1949
1942
|
const action = await deps.select(
|
|
1950
|
-
`Rebind .tonbo from ${currentLabel} to ${
|
|
1943
|
+
`Rebind .tonbo from ${currentLabel} to ${agent.publicHostname}?`,
|
|
1951
1944
|
[
|
|
1952
|
-
{ name: `Rebind to ${
|
|
1945
|
+
{ name: `Rebind to ${agent.publicHostname}`, value: "rebind" },
|
|
1953
1946
|
{ name: "Cancel", value: "cancel" }
|
|
1954
1947
|
]
|
|
1955
1948
|
);
|
|
@@ -1958,10 +1951,10 @@ async function projectUseCommand(deps, selector, force = false) {
|
|
|
1958
1951
|
return;
|
|
1959
1952
|
}
|
|
1960
1953
|
}
|
|
1961
|
-
await
|
|
1962
|
-
deps.output({ message: `Bound .tonbo to agent ${
|
|
1954
|
+
await bindDeclarationAgent(root, agent.publicHostname);
|
|
1955
|
+
deps.output({ message: `Bound .tonbo to agent ${agent.publicHostname}.`, agent });
|
|
1963
1956
|
}
|
|
1964
|
-
async function
|
|
1957
|
+
async function agentCreateCommand(deps, name) {
|
|
1965
1958
|
const root = await findDeclarationRoot(deps.cwd());
|
|
1966
1959
|
const declaration = await loadDeclaration(root);
|
|
1967
1960
|
if (declaration.agent) {
|
|
@@ -1970,28 +1963,28 @@ async function projectCreateCommand(deps, name) {
|
|
|
1970
1963
|
);
|
|
1971
1964
|
}
|
|
1972
1965
|
const oauthToken = await deps.auth.accessToken();
|
|
1973
|
-
|
|
1974
|
-
const
|
|
1966
|
+
validateAgentName(name);
|
|
1967
|
+
const agent = await deps.api.createAgent(oauthToken, name);
|
|
1975
1968
|
try {
|
|
1976
|
-
await
|
|
1969
|
+
await bindDeclarationAgent(root, agent.publicHostname);
|
|
1977
1970
|
} catch (error) {
|
|
1978
1971
|
throw new Error(
|
|
1979
|
-
`Created agent ${
|
|
1972
|
+
`Created agent ${agent.name}, but could not bind ${DECLARATION_FILENAME}. Fix the file and run \`tonbo agent use ${agent.publicHostname}\`.`,
|
|
1980
1973
|
{ cause: error }
|
|
1981
1974
|
);
|
|
1982
1975
|
}
|
|
1983
|
-
deps.output({ message: `Created agent ${
|
|
1976
|
+
deps.output({ message: `Created agent ${agent.name} and bound it in .tonbo.`, agent });
|
|
1984
1977
|
}
|
|
1985
1978
|
async function deployCommand(deps, options = { promote: true }) {
|
|
1986
|
-
const { declaration, oauthToken,
|
|
1979
|
+
const { declaration, oauthToken, agent, root } = await resolveAgent(deps);
|
|
1987
1980
|
if (declaration.build) await runBuildCommand(root, declaration.build.command);
|
|
1988
1981
|
const source = await buildSourceBundle(root);
|
|
1989
1982
|
const origin = { actor: "cli", git: await deps.gitProvenance(root) };
|
|
1990
|
-
const managementToken = await deps.api.exchangeManagementToken(oauthToken,
|
|
1983
|
+
const managementToken = await deps.api.exchangeManagementToken(oauthToken, agent.id);
|
|
1991
1984
|
const result = await deps.api.deploy({
|
|
1992
1985
|
bundle: source,
|
|
1993
1986
|
origin,
|
|
1994
|
-
|
|
1987
|
+
agentId: agent.id,
|
|
1995
1988
|
promote: options.promote,
|
|
1996
1989
|
spec: buildDeploymentSpec(declaration, source),
|
|
1997
1990
|
token: managementToken
|
|
@@ -1999,12 +1992,12 @@ async function deployCommand(deps, options = { promote: true }) {
|
|
|
1999
1992
|
const { deployment } = result;
|
|
2000
1993
|
const shortId = shortDeploymentId(deployment.id);
|
|
2001
1994
|
const lines = [
|
|
2002
|
-
options.promote ? `Deployed agent ${
|
|
2003
|
-
`Organization: ${
|
|
1995
|
+
options.promote ? `Deployed agent ${agent.name}.` : `Created Deployment ${deploymentName(deployment)} (${shortId}) for agent ${agent.name} without promoting it.`,
|
|
1996
|
+
`Organization: ${agent.organizationName}`,
|
|
2004
1997
|
`Deployment: ${deploymentName(deployment)} (${shortId})`,
|
|
2005
1998
|
`Source: ${deploymentSourceLabel(deployment)}`,
|
|
2006
1999
|
`Production: ${options.promote ? productionStateLabel(deployment) : `not promoted; run \`tonbo deployments promote ${shortId}\` to move Production here`}`,
|
|
2007
|
-
`Application: https://${
|
|
2000
|
+
`Application: https://${agent.publicHostname}`
|
|
2008
2001
|
];
|
|
2009
2002
|
if (result.unchanged) {
|
|
2010
2003
|
lines.push(
|
|
@@ -2020,42 +2013,49 @@ async function deployCommand(deps, options = { promote: true }) {
|
|
|
2020
2013
|
' tonbo run "<prompt>"',
|
|
2021
2014
|
"",
|
|
2022
2015
|
"Connect with SSH:",
|
|
2023
|
-
` ssh ${
|
|
2016
|
+
` ssh ${agent.sshDestination}`
|
|
2024
2017
|
);
|
|
2025
2018
|
}
|
|
2026
|
-
deps.output({ message: lines.join("\n"),
|
|
2019
|
+
deps.output({ message: lines.join("\n"), agent, ...result });
|
|
2027
2020
|
}
|
|
2028
2021
|
async function runCommand(deps, prompt, options) {
|
|
2029
|
-
const { oauthToken,
|
|
2030
|
-
const managementToken = await deps.api.exchangeManagementToken(oauthToken,
|
|
2022
|
+
const { oauthToken, agent } = await resolveAgent(deps);
|
|
2023
|
+
const managementToken = await deps.api.exchangeManagementToken(oauthToken, agent.id);
|
|
2031
2024
|
const result = await deps.api.run({
|
|
2032
|
-
|
|
2025
|
+
agentId: agent.id,
|
|
2033
2026
|
prompt,
|
|
2034
2027
|
sessionId: options.session,
|
|
2035
2028
|
token: managementToken
|
|
2036
2029
|
});
|
|
2037
2030
|
deps.output({
|
|
2038
2031
|
message: result.turn.assistant_text,
|
|
2039
|
-
|
|
2032
|
+
agent,
|
|
2040
2033
|
...result
|
|
2041
2034
|
});
|
|
2042
2035
|
}
|
|
2043
|
-
async function
|
|
2044
|
-
const
|
|
2045
|
-
|
|
2046
|
-
|
|
2036
|
+
async function agentListCommand(deps) {
|
|
2037
|
+
const agents = await deps.api.listAgents(await deps.auth.accessToken());
|
|
2038
|
+
deps.output({
|
|
2039
|
+
message: agents.length ? agents.map((agent) => `${agent.name} \xB7 ${agent.publicHostname}`).join("\n") : "No agents are available.",
|
|
2040
|
+
agents
|
|
2041
|
+
});
|
|
2042
|
+
}
|
|
2043
|
+
async function agentShowCommand(deps) {
|
|
2044
|
+
const { agent, token } = await agentManagement(deps);
|
|
2045
|
+
const production = await deps.api.getProduction(agent.id, token);
|
|
2046
|
+
const deployment = production ? await deps.api.getDeployment(agent.id, production.deployment_id, token) : null;
|
|
2047
2047
|
const lines = [
|
|
2048
|
-
`agent: ${
|
|
2049
|
-
`Organization: ${
|
|
2050
|
-
`application: https://${
|
|
2051
|
-
`ssh: ssh ${
|
|
2048
|
+
`agent: ${agent.name}`,
|
|
2049
|
+
`Organization: ${agent.organizationName}`,
|
|
2050
|
+
`application: https://${agent.publicHostname}`,
|
|
2051
|
+
`ssh: ssh ${agent.sshDestination}`
|
|
2052
2052
|
];
|
|
2053
2053
|
if (deployment) {
|
|
2054
2054
|
lines.push(
|
|
2055
2055
|
`production: ${deploymentName(deployment)} (${shortDeploymentId(deployment.id)}) ${productionStateLabel(deployment)}`
|
|
2056
2056
|
);
|
|
2057
2057
|
}
|
|
2058
|
-
deps.output({ message: lines.join("\n"),
|
|
2058
|
+
deps.output({ message: lines.join("\n"), agent, production, deployment });
|
|
2059
2059
|
}
|
|
2060
2060
|
function shortDeploymentId(id) {
|
|
2061
2061
|
return id.replace(/-/g, "").slice(0, 8);
|
|
@@ -2082,7 +2082,7 @@ function selectDeployment(deployments, prefix) {
|
|
|
2082
2082
|
const matches = deployments.filter(
|
|
2083
2083
|
(deployment) => deployment.id.replace(/-/g, "").startsWith(normalized)
|
|
2084
2084
|
);
|
|
2085
|
-
if (matches.length === 0) throw new Error(`Deployment ${prefix} was not found in this
|
|
2085
|
+
if (matches.length === 0) throw new Error(`Deployment ${prefix} was not found in this agent.`);
|
|
2086
2086
|
if (matches.length > 1) {
|
|
2087
2087
|
throw new Error(
|
|
2088
2088
|
`Deployment id ${prefix} is ambiguous; it matches ${matches.map((deployment) => shortDeploymentId(deployment.id)).join(", ")}. Use more characters.`
|
|
@@ -2108,10 +2108,10 @@ function productionMarker(state) {
|
|
|
2108
2108
|
return "";
|
|
2109
2109
|
}
|
|
2110
2110
|
async function deploymentsListCommand(deps) {
|
|
2111
|
-
const {
|
|
2111
|
+
const { agent, token } = await agentManagement(deps);
|
|
2112
2112
|
const [deployments, production] = await Promise.all([
|
|
2113
|
-
deps.api.listDeployments(
|
|
2114
|
-
deps.api.getProduction(
|
|
2113
|
+
deps.api.listDeployments(agent.id, token),
|
|
2114
|
+
deps.api.getProduction(agent.id, token)
|
|
2115
2115
|
]);
|
|
2116
2116
|
const message = deployments.length ? renderTable(
|
|
2117
2117
|
["ID", "NAME", "STATUS", "PRODUCTION", "SOURCE", "CREATED", "BY"],
|
|
@@ -2124,18 +2124,18 @@ async function deploymentsListCommand(deps) {
|
|
|
2124
2124
|
formatTimestamp(deployment.created_at),
|
|
2125
2125
|
deployment.created_by_user_id ? shortDeploymentId(deployment.created_by_user_id) : ""
|
|
2126
2126
|
])
|
|
2127
|
-
) : `No Deployments exist for agent ${
|
|
2128
|
-
deps.output({ message,
|
|
2127
|
+
) : `No Deployments exist for agent ${agent.name}. Run \`tonbo deploy\` to create one.`;
|
|
2128
|
+
deps.output({ message, agent, production, deployments });
|
|
2129
2129
|
}
|
|
2130
2130
|
async function deploymentsShowCommand(deps, prefix) {
|
|
2131
|
-
const {
|
|
2132
|
-
const deployment = selectDeployment(await deps.api.listDeployments(
|
|
2133
|
-
const rollouts = (await deps.api.listRollouts(
|
|
2131
|
+
const { agent, token } = await agentManagement(deps);
|
|
2132
|
+
const deployment = selectDeployment(await deps.api.listDeployments(agent.id, token), prefix);
|
|
2133
|
+
const rollouts = (await deps.api.listRollouts(agent.id, token)).filter(
|
|
2134
2134
|
(rollout) => rollout.deployment_id === deployment.id || rollout.previous_deployment_id === deployment.id
|
|
2135
2135
|
);
|
|
2136
2136
|
deps.output({
|
|
2137
2137
|
message: renderDeploymentDetail(deployment, rollouts),
|
|
2138
|
-
|
|
2138
|
+
agent,
|
|
2139
2139
|
deployment,
|
|
2140
2140
|
rollouts
|
|
2141
2141
|
});
|
|
@@ -2176,43 +2176,43 @@ function renderDeploymentDetail(deployment, rollouts) {
|
|
|
2176
2176
|
return lines.join("\n");
|
|
2177
2177
|
}
|
|
2178
2178
|
async function deploymentsPromoteCommand(deps, prefix) {
|
|
2179
|
-
const {
|
|
2180
|
-
const target = selectDeployment(await deps.api.listDeployments(
|
|
2181
|
-
const result = await moveProduction(deps,
|
|
2179
|
+
const { agent, token } = await agentManagement(deps);
|
|
2180
|
+
const target = selectDeployment(await deps.api.listDeployments(agent.id, token), prefix);
|
|
2181
|
+
const result = await moveProduction(deps, agent.id, target, token);
|
|
2182
2182
|
deps.output({
|
|
2183
|
-
message: describeProductionMove(
|
|
2184
|
-
|
|
2183
|
+
message: describeProductionMove(agent, result, "Promoted"),
|
|
2184
|
+
agent,
|
|
2185
2185
|
...result
|
|
2186
2186
|
});
|
|
2187
2187
|
}
|
|
2188
2188
|
async function deploymentsRollbackCommand(deps, prefix) {
|
|
2189
|
-
const {
|
|
2189
|
+
const { agent, token } = await agentManagement(deps);
|
|
2190
2190
|
let target;
|
|
2191
2191
|
if (prefix !== void 0) {
|
|
2192
|
-
target = selectDeployment(await deps.api.listDeployments(
|
|
2192
|
+
target = selectDeployment(await deps.api.listDeployments(agent.id, token), prefix);
|
|
2193
2193
|
} else {
|
|
2194
|
-
const rollouts = await deps.api.listRollouts(
|
|
2194
|
+
const rollouts = await deps.api.listRollouts(agent.id, token);
|
|
2195
2195
|
const previousId = rollouts.find(
|
|
2196
2196
|
(rollout) => rollout.previous_deployment_id !== null
|
|
2197
2197
|
)?.previous_deployment_id;
|
|
2198
2198
|
if (!previousId) {
|
|
2199
2199
|
throw new Error(
|
|
2200
|
-
`Agent ${
|
|
2200
|
+
`Agent ${agent.name} has no previous Production Deployment to roll back to. Name one with \`tonbo deployments rollback <id>\`.`
|
|
2201
2201
|
);
|
|
2202
2202
|
}
|
|
2203
|
-
target = await deps.api.getDeployment(
|
|
2203
|
+
target = await deps.api.getDeployment(agent.id, previousId, token);
|
|
2204
2204
|
}
|
|
2205
|
-
const result = await moveProduction(deps,
|
|
2205
|
+
const result = await moveProduction(deps, agent.id, target, token);
|
|
2206
2206
|
deps.output({
|
|
2207
|
-
message: describeProductionMove(
|
|
2208
|
-
|
|
2207
|
+
message: describeProductionMove(agent, result, "Rolled back"),
|
|
2208
|
+
agent,
|
|
2209
2209
|
...result
|
|
2210
2210
|
});
|
|
2211
2211
|
}
|
|
2212
|
-
async function moveProduction(deps,
|
|
2213
|
-
const previous = await deps.api.getProduction(
|
|
2212
|
+
async function moveProduction(deps, agentId, target, token) {
|
|
2213
|
+
const previous = await deps.api.getProduction(agentId, token);
|
|
2214
2214
|
const production = await deps.api.putProduction(
|
|
2215
|
-
|
|
2215
|
+
agentId,
|
|
2216
2216
|
{
|
|
2217
2217
|
deployment_id: target.id,
|
|
2218
2218
|
desired_state: "running",
|
|
@@ -2221,49 +2221,49 @@ async function moveProduction(deps, projectId, target, token) {
|
|
|
2221
2221
|
token
|
|
2222
2222
|
);
|
|
2223
2223
|
return {
|
|
2224
|
-
deployment: await deps.api.getDeployment(
|
|
2224
|
+
deployment: await deps.api.getDeployment(agentId, target.id, token),
|
|
2225
2225
|
previous,
|
|
2226
2226
|
production
|
|
2227
2227
|
};
|
|
2228
2228
|
}
|
|
2229
|
-
function describeProductionMove(
|
|
2229
|
+
function describeProductionMove(agent, move, verb) {
|
|
2230
2230
|
const { deployment, previous } = move;
|
|
2231
2231
|
const name = `${deploymentName(deployment)} (${shortDeploymentId(deployment.id)})`;
|
|
2232
|
-
const headline = previous?.deployment_id === deployment.id ? `Deployment ${name} is already Production for agent ${
|
|
2232
|
+
const headline = previous?.deployment_id === deployment.id ? `Deployment ${name} is already Production for agent ${agent.name}; requested it to be running.` : `${verb} ${name} to Production for agent ${agent.name}${previous ? ` (from ${shortDeploymentId(previous.deployment_id)})` : ""}.`;
|
|
2233
2233
|
return `${headline}
|
|
2234
2234
|
Production: ${productionStateLabel(deployment)}
|
|
2235
|
-
Application: https://${
|
|
2235
|
+
Application: https://${agent.publicHostname}`;
|
|
2236
2236
|
}
|
|
2237
|
-
async function
|
|
2238
|
-
const { oauthToken,
|
|
2237
|
+
async function agentManagement(deps) {
|
|
2238
|
+
const { oauthToken, agent } = await resolveAgent(deps);
|
|
2239
2239
|
return {
|
|
2240
|
-
|
|
2241
|
-
token: await deps.api.exchangeManagementToken(oauthToken,
|
|
2240
|
+
agent,
|
|
2241
|
+
token: await deps.api.exchangeManagementToken(oauthToken, agent.id)
|
|
2242
2242
|
};
|
|
2243
2243
|
}
|
|
2244
2244
|
async function secretListCommand(deps) {
|
|
2245
|
-
const {
|
|
2246
|
-
const secrets = await deps.api.
|
|
2245
|
+
const { agent, token } = await agentManagement(deps);
|
|
2246
|
+
const secrets = await deps.api.listAgentSecrets(agent.id, token);
|
|
2247
2247
|
deps.output({
|
|
2248
2248
|
message: secrets.length ? secrets.map((secret) => secret.name).join("\n") : "No agent secrets are configured.",
|
|
2249
|
-
|
|
2249
|
+
agent,
|
|
2250
2250
|
secrets
|
|
2251
2251
|
});
|
|
2252
2252
|
}
|
|
2253
2253
|
async function secretSetCommand(deps, name, options) {
|
|
2254
2254
|
const value = await deps.secretValue(name, options.fromEnv);
|
|
2255
|
-
const {
|
|
2256
|
-
const secret = await deps.api.
|
|
2255
|
+
const { agent, token } = await agentManagement(deps);
|
|
2256
|
+
const secret = await deps.api.setAgentSecret(agent.id, name, value, token);
|
|
2257
2257
|
deps.output({
|
|
2258
2258
|
message: `Set agent secret ${secret.name}. Redeploy to replace the active runtime with this value.`,
|
|
2259
|
-
|
|
2259
|
+
agent,
|
|
2260
2260
|
secret
|
|
2261
2261
|
});
|
|
2262
2262
|
}
|
|
2263
2263
|
async function secretRemoveCommand(deps, name) {
|
|
2264
|
-
const {
|
|
2265
|
-
await deps.api.
|
|
2266
|
-
deps.output({ message: `Removed agent secret ${name}.`,
|
|
2264
|
+
const { agent, token } = await agentManagement(deps);
|
|
2265
|
+
await deps.api.deleteAgentSecret(agent.id, name, token);
|
|
2266
|
+
deps.output({ message: `Removed agent secret ${name}.`, agent, name });
|
|
2267
2267
|
}
|
|
2268
2268
|
|
|
2269
2269
|
// src/credentials.ts
|
|
@@ -2523,7 +2523,7 @@ function jsonOutput(program) {
|
|
|
2523
2523
|
return program.opts().json === true;
|
|
2524
2524
|
}
|
|
2525
2525
|
function createProgram(dependencies = createDependencies) {
|
|
2526
|
-
const program = new Command().name("tonbo").description("Deploy a persistent Agent
|
|
2526
|
+
const program = new Command().name("tonbo").description("Deploy a persistent Agent to Tonbo.").version(packageVersion.version).option("--json", "print machine-readable JSON");
|
|
2527
2527
|
program.command("init").description("interactively create a Tonbo Agent declaration in this directory").option("--harness <harness>", "Agent Harness (currently pi)").option("--model <model>", "inference model").option("--driver <driver>", "PI driver: native or command").option("--agent-entry <file>", "Node entry file for the command driver").option("--build-command <argv...>", "build command argv for the command driver").option("--force", `replace an existing ${DECLARATION_FILENAME}`).action(
|
|
2528
2528
|
async (options) => initCommand(dependencies(jsonOutput(program)), options)
|
|
2529
2529
|
);
|
|
@@ -2583,17 +2583,18 @@ function createProgram(dependencies = createDependencies) {
|
|
|
2583
2583
|
})
|
|
2584
2584
|
);
|
|
2585
2585
|
});
|
|
2586
|
-
const
|
|
2587
|
-
|
|
2586
|
+
const agent = program.command("agent").description("manage the Agent bound to this source directory");
|
|
2587
|
+
agent.command("create <name>").description("create an agent and bind this directory to it").action(async (name) => agentCreateCommand(dependencies(jsonOutput(program)), name));
|
|
2588
|
+
agent.command("list").description("list the agents available to your account").action(async () => agentListCommand(dependencies(jsonOutput(program))));
|
|
2588
2589
|
const sshKey = program.command("ssh-key").description("manage public keys used by native agent SSH");
|
|
2589
2590
|
sshKey.command("add <public-key>").description("register an OpenSSH public key with the current Tonbo account").action(async (path6) => sshKeyAddCommand(dependencies(jsonOutput(program)), path6));
|
|
2590
2591
|
sshKey.command("remove <fingerprint>").description("revoke an SSH public key from the current Tonbo account").action(
|
|
2591
2592
|
async (fingerprint) => sshKeyRemoveCommand(dependencies(jsonOutput(program)), fingerprint)
|
|
2592
2593
|
);
|
|
2593
|
-
|
|
2594
|
-
async (selector, options) =>
|
|
2594
|
+
agent.command("use <agent>").description("write the agent hostname into this directory's .tonbo declaration").option("--force", "replace an existing agent binding without confirmation").action(
|
|
2595
|
+
async (selector, options) => agentUseCommand(dependencies(jsonOutput(program)), selector, options.force === true)
|
|
2595
2596
|
);
|
|
2596
|
-
|
|
2597
|
+
agent.command("show").description("show the agent bound to this directory and its connection addresses").action(async () => agentShowCommand(dependencies(jsonOutput(program))));
|
|
2597
2598
|
program.command("deploy").description("upload this directory as a new Deployment and promote it to Production").option("--no-promote", "create the Deployment without moving Production").action(
|
|
2598
2599
|
async (options) => deployCommand(dependencies(jsonOutput(program)), { promote: options.promote })
|
|
2599
2600
|
);
|