@tonbo/cli 0.1.2 → 0.3.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 +21 -21
- package/dist/bin/tonbo.js +389 -261
- package/package.json +7 -7
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,19 +136,19 @@ 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
|
|
143
143
|
});
|
|
144
144
|
if (prepared.status === "upload") {
|
|
145
145
|
if (!prepared.upload_url) throw new Error("Tonbo did not return a source upload URL.");
|
|
146
|
+
if (!prepared.upload_headers || prepared.upload_headers["if-none-match"] !== "*")
|
|
147
|
+
throw new Error("Missing immutable source upload headers.");
|
|
146
148
|
const uploaded = await this.fetcher(prepared.upload_url, {
|
|
149
|
+
redirect: "error",
|
|
147
150
|
method: "PUT",
|
|
148
|
-
headers:
|
|
149
|
-
"content-type": prepared.content_type ?? "application/vnd.tonbo.source+tar",
|
|
150
|
-
"x-upsert": "false"
|
|
151
|
-
},
|
|
151
|
+
headers: prepared.upload_headers,
|
|
152
152
|
body: new Blob([new Uint8Array(bundle.bytes)])
|
|
153
153
|
});
|
|
154
154
|
let uploadError = null;
|
|
@@ -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
|
}
|
|
@@ -779,6 +773,40 @@ async function openBrowser(url) {
|
|
|
779
773
|
return void await exec("xdg-open", [url]);
|
|
780
774
|
}
|
|
781
775
|
|
|
776
|
+
// src/commands.ts
|
|
777
|
+
import { DEFAULT_INFERENCE_MODEL as DEFAULT_INFERENCE_MODEL2 } from "@tonbo/agent-source-bundler/inference-default";
|
|
778
|
+
|
|
779
|
+
// ../../packages/agent-source-bundler/src/generated/contracts.ts
|
|
780
|
+
var sourceBundleContract = { "version": 1, "format": "tar-v1", "key_prefix": "agent-source-bundles", "content_type": "application/vnd.tonbo.source+tar", "max_bytes": 67108864 };
|
|
781
|
+
var piSessionContract = { "version": 1, "adapter": "pi-jsonl-v3", "format_version": 3, "durable_completion_timeout_seconds": 30, "session_directory": "/sessions", "path_template": "/sessions/{session_id}.jsonl", "preflight_command": ["/usr/local/bin/artifacts", "runtime", "session-preflight"] };
|
|
782
|
+
|
|
783
|
+
// ../../packages/agent-source-bundler/src/deployment-origin.ts
|
|
784
|
+
function deploymentOriginLabel(actor) {
|
|
785
|
+
return actor === "onboarding" ? "Setup guide" : "tonbo deploy";
|
|
786
|
+
}
|
|
787
|
+
function githubRepositoryUrl(remote) {
|
|
788
|
+
if (typeof remote !== "string" || !remote || /\s/.test(remote) || [...remote].some((character) => character.charCodeAt(0) < 32))
|
|
789
|
+
return null;
|
|
790
|
+
let path6;
|
|
791
|
+
const ssh = /^git@github\.com:([^?#]+)$/.exec(remote);
|
|
792
|
+
if (ssh) path6 = ssh[1];
|
|
793
|
+
else {
|
|
794
|
+
try {
|
|
795
|
+
const url = new URL(remote);
|
|
796
|
+
if (url.hostname !== "github.com" || url.port || !["https:", "ssh:"].includes(url.protocol))
|
|
797
|
+
return null;
|
|
798
|
+
path6 = url.pathname.slice(1);
|
|
799
|
+
} catch {
|
|
800
|
+
return null;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
path6 = path6.replace(/\/$/, "").replace(/\.git$/, "");
|
|
804
|
+
const parts = path6.split("/");
|
|
805
|
+
if (parts.length !== 2 || !/^[a-z\d](?:[a-z\d-]{0,38})$/i.test(parts[0]) || !/^[a-z\d._-]{1,100}$/i.test(parts[1]) || [".", ".."].includes(parts[1]))
|
|
806
|
+
return null;
|
|
807
|
+
return `https://github.com/${parts[0]}/${parts[1]}`;
|
|
808
|
+
}
|
|
809
|
+
|
|
782
810
|
// src/commands.ts
|
|
783
811
|
import path4 from "node:path";
|
|
784
812
|
|
|
@@ -879,10 +907,10 @@ var piAgentSchema = {
|
|
|
879
907
|
}
|
|
880
908
|
}
|
|
881
909
|
};
|
|
882
|
-
var
|
|
910
|
+
var agentServiceSchema = {
|
|
883
911
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
884
|
-
"$id": "https://contracts.tonbo.dev/agents/
|
|
885
|
-
"title": "Tonbo
|
|
912
|
+
"$id": "https://contracts.tonbo.dev/agents/agent-service-v1.schema.json",
|
|
913
|
+
"title": "Tonbo Agent application service v1",
|
|
886
914
|
"type": "object",
|
|
887
915
|
"additionalProperties": false,
|
|
888
916
|
"required": [
|
|
@@ -982,10 +1010,10 @@ var declarationSchema = {
|
|
|
982
1010
|
}
|
|
983
1011
|
},
|
|
984
1012
|
"service": {
|
|
985
|
-
"$ref": "https://contracts.tonbo.dev/agents/
|
|
1013
|
+
"$ref": "https://contracts.tonbo.dev/agents/agent-service-v1.schema.json"
|
|
986
1014
|
},
|
|
987
1015
|
"agent": {
|
|
988
|
-
"$ref": "https://contracts.tonbo.dev/agents/
|
|
1016
|
+
"$ref": "https://contracts.tonbo.dev/agents/agent-name-v1.json#/$defs/publicHostname"
|
|
989
1017
|
},
|
|
990
1018
|
"harness": {
|
|
991
1019
|
"$ref": "https://contracts.tonbo.dev/agents/pi-agent-v1.schema.json"
|
|
@@ -995,7 +1023,7 @@ var declarationSchema = {
|
|
|
995
1023
|
var deploymentSchema = {
|
|
996
1024
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
997
1025
|
"$id": "https://contracts.tonbo.dev/agents/managed-deployment-v1.schema.json",
|
|
998
|
-
"title": "Managed
|
|
1026
|
+
"title": "Managed Agent Deployment v1",
|
|
999
1027
|
"type": "object",
|
|
1000
1028
|
"additionalProperties": false,
|
|
1001
1029
|
"required": [
|
|
@@ -1049,36 +1077,16 @@ var deploymentSchema = {
|
|
|
1049
1077
|
}
|
|
1050
1078
|
},
|
|
1051
1079
|
"service": {
|
|
1052
|
-
"$ref": "https://contracts.tonbo.dev/agents/
|
|
1080
|
+
"$ref": "https://contracts.tonbo.dev/agents/agent-service-v1.schema.json"
|
|
1053
1081
|
}
|
|
1054
1082
|
}
|
|
1055
1083
|
};
|
|
1056
|
-
var
|
|
1057
|
-
"version": 1,
|
|
1058
|
-
"format": "tar-v1",
|
|
1059
|
-
"bucket": "agent-source-bundles",
|
|
1060
|
-
"content_type": "application/vnd.tonbo.source+tar",
|
|
1061
|
-
"max_bytes": 67108864
|
|
1062
|
-
};
|
|
1063
|
-
var piSessionContract = {
|
|
1064
|
-
"version": 1,
|
|
1065
|
-
"adapter": "pi-jsonl-v3",
|
|
1066
|
-
"format_version": 3,
|
|
1067
|
-
"durable_completion_timeout_seconds": 30,
|
|
1068
|
-
"session_directory": "/sessions",
|
|
1069
|
-
"path_template": "/sessions/{session_id}.jsonl",
|
|
1070
|
-
"preflight_command": [
|
|
1071
|
-
"/usr/local/bin/artifacts",
|
|
1072
|
-
"runtime",
|
|
1073
|
-
"session-preflight"
|
|
1074
|
-
]
|
|
1075
|
-
};
|
|
1076
|
-
var projectNameContract = {
|
|
1084
|
+
var agentNameContract = {
|
|
1077
1085
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
1078
|
-
"$id": "https://contracts.tonbo.dev/agents/
|
|
1086
|
+
"$id": "https://contracts.tonbo.dev/agents/agent-name-v1.json",
|
|
1079
1087
|
"x-tonbo-version": 1,
|
|
1080
1088
|
"x-tonbo-public-hostname-apex": "tonbo.sh",
|
|
1081
|
-
"x-tonbo-public-hostname-template": "<
|
|
1089
|
+
"x-tonbo-public-hostname-template": "<agent>-<organization>-<word>-<word>.tonbo.sh",
|
|
1082
1090
|
"$defs": {
|
|
1083
1091
|
"name": {
|
|
1084
1092
|
"type": "string",
|
|
@@ -1117,6 +1125,116 @@ var projectNameContract = {
|
|
|
1117
1125
|
"www.tonbo.sh"
|
|
1118
1126
|
]
|
|
1119
1127
|
}
|
|
1128
|
+
},
|
|
1129
|
+
"hostnameWord": {
|
|
1130
|
+
"type": "string",
|
|
1131
|
+
"enum": [
|
|
1132
|
+
"arch",
|
|
1133
|
+
"bark",
|
|
1134
|
+
"beam",
|
|
1135
|
+
"bell",
|
|
1136
|
+
"bird",
|
|
1137
|
+
"blue",
|
|
1138
|
+
"boat",
|
|
1139
|
+
"bold",
|
|
1140
|
+
"book",
|
|
1141
|
+
"calm",
|
|
1142
|
+
"cave",
|
|
1143
|
+
"clay",
|
|
1144
|
+
"coal",
|
|
1145
|
+
"cool",
|
|
1146
|
+
"cove",
|
|
1147
|
+
"dawn",
|
|
1148
|
+
"deer",
|
|
1149
|
+
"dove",
|
|
1150
|
+
"dune",
|
|
1151
|
+
"dusk",
|
|
1152
|
+
"echo",
|
|
1153
|
+
"fern",
|
|
1154
|
+
"fire",
|
|
1155
|
+
"flax",
|
|
1156
|
+
"flow",
|
|
1157
|
+
"foam",
|
|
1158
|
+
"fawn",
|
|
1159
|
+
"frog",
|
|
1160
|
+
"gate",
|
|
1161
|
+
"glow",
|
|
1162
|
+
"gold",
|
|
1163
|
+
"gray",
|
|
1164
|
+
"gulf",
|
|
1165
|
+
"hail",
|
|
1166
|
+
"halo",
|
|
1167
|
+
"hare",
|
|
1168
|
+
"hawk",
|
|
1169
|
+
"hill",
|
|
1170
|
+
"iris",
|
|
1171
|
+
"jade",
|
|
1172
|
+
"kite",
|
|
1173
|
+
"lake",
|
|
1174
|
+
"lamb",
|
|
1175
|
+
"leaf",
|
|
1176
|
+
"lime",
|
|
1177
|
+
"lion",
|
|
1178
|
+
"lobe",
|
|
1179
|
+
"loft",
|
|
1180
|
+
"loom",
|
|
1181
|
+
"luma",
|
|
1182
|
+
"lynx",
|
|
1183
|
+
"mint",
|
|
1184
|
+
"mist",
|
|
1185
|
+
"moon",
|
|
1186
|
+
"moss",
|
|
1187
|
+
"navy",
|
|
1188
|
+
"nest",
|
|
1189
|
+
"nova",
|
|
1190
|
+
"oak",
|
|
1191
|
+
"ocean",
|
|
1192
|
+
"onyx",
|
|
1193
|
+
"opal",
|
|
1194
|
+
"otter",
|
|
1195
|
+
"palm",
|
|
1196
|
+
"peak",
|
|
1197
|
+
"pine",
|
|
1198
|
+
"pond",
|
|
1199
|
+
"pool",
|
|
1200
|
+
"rain",
|
|
1201
|
+
"reed",
|
|
1202
|
+
"reef",
|
|
1203
|
+
"rice",
|
|
1204
|
+
"ring",
|
|
1205
|
+
"rise",
|
|
1206
|
+
"road",
|
|
1207
|
+
"rock",
|
|
1208
|
+
"rose",
|
|
1209
|
+
"ruby",
|
|
1210
|
+
"sage",
|
|
1211
|
+
"sand",
|
|
1212
|
+
"seal",
|
|
1213
|
+
"seed",
|
|
1214
|
+
"silk",
|
|
1215
|
+
"sky",
|
|
1216
|
+
"snow",
|
|
1217
|
+
"soft",
|
|
1218
|
+
"star",
|
|
1219
|
+
"stem",
|
|
1220
|
+
"stone",
|
|
1221
|
+
"surf",
|
|
1222
|
+
"swan",
|
|
1223
|
+
"teal",
|
|
1224
|
+
"tide",
|
|
1225
|
+
"tree",
|
|
1226
|
+
"vale",
|
|
1227
|
+
"vine",
|
|
1228
|
+
"wave",
|
|
1229
|
+
"west",
|
|
1230
|
+
"wind",
|
|
1231
|
+
"wing",
|
|
1232
|
+
"wolf",
|
|
1233
|
+
"wood",
|
|
1234
|
+
"wren",
|
|
1235
|
+
"yarn",
|
|
1236
|
+
"zest"
|
|
1237
|
+
]
|
|
1120
1238
|
}
|
|
1121
1239
|
}
|
|
1122
1240
|
};
|
|
@@ -1129,8 +1247,8 @@ ajv.addKeyword({ keyword: "x-tonbo-public-hostname-apex" });
|
|
|
1129
1247
|
ajv.addKeyword({ keyword: "x-tonbo-public-hostname-template" });
|
|
1130
1248
|
ajv.addSchema(kubernetesProfilesSchema);
|
|
1131
1249
|
ajv.addSchema(piAgentSchema);
|
|
1132
|
-
ajv.addSchema(
|
|
1133
|
-
ajv.addSchema(
|
|
1250
|
+
ajv.addSchema(agentNameContract);
|
|
1251
|
+
ajv.addSchema(agentServiceSchema);
|
|
1134
1252
|
var validateDeclaration = ajv.compile(declarationSchema);
|
|
1135
1253
|
var validateDeploymentSpec = ajv.compile(deploymentSchema);
|
|
1136
1254
|
function validationMessage(label, errors) {
|
|
@@ -1150,30 +1268,42 @@ function assertManagedDeploymentSpec(value) {
|
|
|
1150
1268
|
}
|
|
1151
1269
|
}
|
|
1152
1270
|
|
|
1271
|
+
// src/declaration.ts
|
|
1272
|
+
import { DEFAULT_INFERENCE_MODEL } from "@tonbo/agent-source-bundler/inference-default";
|
|
1273
|
+
|
|
1274
|
+
// ../../packages/agent-source-bundler/src/native-pi.ts
|
|
1275
|
+
import { stringify } from "smol-toml";
|
|
1276
|
+
function nativePiDeclaration(model) {
|
|
1277
|
+
return {
|
|
1278
|
+
version: 2,
|
|
1279
|
+
harness: { runtime: "pi", driver: { kind: "native" } },
|
|
1280
|
+
inference: { model }
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1153
1284
|
// src/declaration.ts
|
|
1154
1285
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1155
1286
|
import { lstat as lstat2, open, readFile as readFile2, rename, rm } from "node:fs/promises";
|
|
1156
1287
|
import path2 from "node:path";
|
|
1157
|
-
import { parse, stringify } from "smol-toml";
|
|
1288
|
+
import { parse, stringify as stringify2 } from "smol-toml";
|
|
1158
1289
|
var DECLARATION_FILENAME = ".tonbo";
|
|
1159
|
-
|
|
1160
|
-
|
|
1290
|
+
function createDeclaration(model = DEFAULT_INFERENCE_MODEL, driver = { kind: "native" }, buildCommand, agentHostname) {
|
|
1291
|
+
const base = nativePiDeclaration(model.trim());
|
|
1161
1292
|
return parseDeclaration({
|
|
1162
|
-
|
|
1163
|
-
...
|
|
1164
|
-
harness: {
|
|
1165
|
-
inference: { model: model.trim() },
|
|
1293
|
+
...base,
|
|
1294
|
+
...agentHostname ? { agent: agentHostname } : {},
|
|
1295
|
+
harness: { ...base.harness, driver },
|
|
1166
1296
|
...buildCommand ? { build: { command: buildCommand } } : {}
|
|
1167
1297
|
});
|
|
1168
1298
|
}
|
|
1169
1299
|
function renderDeclaration(declaration) {
|
|
1170
1300
|
return `# Tonbo Agent configuration.
|
|
1171
1301
|
# Edit this file directly or run \`tonbo init\` to reconfigure.
|
|
1172
|
-
${
|
|
1302
|
+
${stringify2(declaration)}`;
|
|
1173
1303
|
}
|
|
1174
|
-
async function
|
|
1304
|
+
async function bindDeclarationAgent(root, agentHostname) {
|
|
1175
1305
|
const declaration = await loadDeclaration(root);
|
|
1176
|
-
const bound = parseDeclaration({ ...declaration, agent:
|
|
1306
|
+
const bound = parseDeclaration({ ...declaration, agent: agentHostname });
|
|
1177
1307
|
await saveDeclaration(root, bound, true);
|
|
1178
1308
|
return bound;
|
|
1179
1309
|
}
|
|
@@ -1252,7 +1382,7 @@ function buildDeploymentSpec(declaration, source) {
|
|
|
1252
1382
|
return spec;
|
|
1253
1383
|
}
|
|
1254
1384
|
|
|
1255
|
-
// src/
|
|
1385
|
+
// ../../packages/agent-source-bundler/src/index.ts
|
|
1256
1386
|
import { createHash as createHash2 } from "node:crypto";
|
|
1257
1387
|
import { lstat as lstat3, readFile as readFile3, readdir } from "node:fs/promises";
|
|
1258
1388
|
import path3 from "node:path";
|
|
@@ -1303,7 +1433,7 @@ async function validatePackageSource(root, source) {
|
|
|
1303
1433
|
const resolved = path3.resolve(settingsDirectory, source);
|
|
1304
1434
|
const relative = path3.relative(root, resolved);
|
|
1305
1435
|
if (relative === ".." || relative.startsWith(`..${path3.sep}`) || path3.isAbsolute(relative)) {
|
|
1306
|
-
throw new Error(`Local PI package ${source} resolves outside the deployed
|
|
1436
|
+
throw new Error(`Local PI package ${source} resolves outside the deployed agent.`);
|
|
1307
1437
|
}
|
|
1308
1438
|
let metadata;
|
|
1309
1439
|
try {
|
|
@@ -1320,7 +1450,7 @@ async function validatePackageSource(root, source) {
|
|
|
1320
1450
|
return;
|
|
1321
1451
|
}
|
|
1322
1452
|
throw new Error(
|
|
1323
|
-
`PI package ${source} must use an exact npm version, a full Git commit, or a
|
|
1453
|
+
`PI package ${source} must use an exact npm version, a full Git commit, or a agent-local path.`
|
|
1324
1454
|
);
|
|
1325
1455
|
}
|
|
1326
1456
|
async function validatePiPackages(root) {
|
|
@@ -1498,31 +1628,31 @@ async function readDefaultSshPublicKeys(sshDirectory = join(homedir(), ".ssh"))
|
|
|
1498
1628
|
return keys;
|
|
1499
1629
|
}
|
|
1500
1630
|
|
|
1501
|
-
// src/
|
|
1502
|
-
var
|
|
1503
|
-
var
|
|
1504
|
-
var
|
|
1505
|
-
function
|
|
1506
|
-
const valid = name.length >=
|
|
1631
|
+
// src/agent-name.ts
|
|
1632
|
+
var AGENT_NAME_RULE = agentNameContract.$defs.name;
|
|
1633
|
+
var AGENT_NAME_PATTERN = new RegExp(AGENT_NAME_RULE.pattern);
|
|
1634
|
+
var RESERVED_AGENT_NAMES = new Set(AGENT_NAME_RULE.not.enum);
|
|
1635
|
+
function validateAgentName(name) {
|
|
1636
|
+
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
1637
|
if (!valid) {
|
|
1508
1638
|
throw new Error(
|
|
1509
|
-
`Agent name must be ${
|
|
1639
|
+
`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
1640
|
);
|
|
1511
1641
|
}
|
|
1512
1642
|
}
|
|
1513
|
-
function
|
|
1643
|
+
function agentNameSuggestion(value) {
|
|
1514
1644
|
let name = value.normalize("NFKD").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
|
|
1515
1645
|
if (!/^[a-z]/.test(name)) name = `agent-${name}`;
|
|
1516
|
-
name = name.slice(0,
|
|
1517
|
-
if (name.length <
|
|
1518
|
-
name = `${name || "agent"}-agent`.slice(0,
|
|
1646
|
+
name = name.slice(0, AGENT_NAME_RULE.maxLength).replace(/-+$/g, "");
|
|
1647
|
+
if (name.length < AGENT_NAME_RULE.minLength) {
|
|
1648
|
+
name = `${name || "agent"}-agent`.slice(0, AGENT_NAME_RULE.maxLength);
|
|
1519
1649
|
}
|
|
1520
|
-
if (
|
|
1650
|
+
if (RESERVED_AGENT_NAMES.has(name)) name = `${name}-agent`;
|
|
1521
1651
|
return name;
|
|
1522
1652
|
}
|
|
1523
1653
|
|
|
1524
1654
|
// src/commands.ts
|
|
1525
|
-
async function
|
|
1655
|
+
async function resolveAgent(deps) {
|
|
1526
1656
|
const root = await findDeclarationRoot(deps.cwd());
|
|
1527
1657
|
const declaration = await loadDeclaration(root);
|
|
1528
1658
|
if (!declaration.agent) {
|
|
@@ -1532,14 +1662,14 @@ async function resolveProject(deps) {
|
|
|
1532
1662
|
return {
|
|
1533
1663
|
declaration,
|
|
1534
1664
|
oauthToken,
|
|
1535
|
-
|
|
1665
|
+
agent: selectAgent(await deps.api.listAgents(oauthToken), declaration.agent),
|
|
1536
1666
|
root
|
|
1537
1667
|
};
|
|
1538
1668
|
}
|
|
1539
|
-
function
|
|
1669
|
+
function selectAgent(agents, selector) {
|
|
1540
1670
|
const normalized = selector.toLowerCase();
|
|
1541
|
-
const matches =
|
|
1542
|
-
(
|
|
1671
|
+
const matches = agents.filter(
|
|
1672
|
+
(agent) => agent.id === selector || agent.name === normalized || agent.publicHostname === normalized
|
|
1543
1673
|
);
|
|
1544
1674
|
if (matches.length === 0) throw new Error(`Agent ${selector} was not found in your account.`);
|
|
1545
1675
|
if (matches.length > 1)
|
|
@@ -1573,7 +1703,7 @@ async function initCommand(deps, options) {
|
|
|
1573
1703
|
throw new Error("No Harness-specific configuration found. Pass --harness pi.");
|
|
1574
1704
|
} else {
|
|
1575
1705
|
deps.output({
|
|
1576
|
-
message: "No Harness-specific configuration was found in this
|
|
1706
|
+
message: "No Harness-specific configuration was found in this agent."
|
|
1577
1707
|
});
|
|
1578
1708
|
harness = await selectHarness(deps);
|
|
1579
1709
|
}
|
|
@@ -1597,21 +1727,21 @@ async function initCommand(deps, options) {
|
|
|
1597
1727
|
entry = (await deps.prompt("PI SDK entry file [dist/agent.mjs]: ")).trim();
|
|
1598
1728
|
}
|
|
1599
1729
|
entry ||= "dist/agent.mjs";
|
|
1600
|
-
let model = options.model?.trim() || existingDeclaration?.inference.model ||
|
|
1730
|
+
let model = options.model?.trim() || existingDeclaration?.inference.model || DEFAULT_INFERENCE_MODEL2;
|
|
1601
1731
|
let buildCommand = driver === "command" ? options.buildCommand ?? existingDeclaration?.build?.command ?? ["npm", "run", "build"] : void 0;
|
|
1602
|
-
let
|
|
1732
|
+
let agentPlan = existingDeclaration?.agent ? {
|
|
1603
1733
|
kind: "existing",
|
|
1604
1734
|
publicHostname: existingDeclaration.agent
|
|
1605
1735
|
} : { kind: "later" };
|
|
1606
1736
|
if (interactive && !existingDeclaration?.agent) {
|
|
1607
|
-
|
|
1737
|
+
agentPlan = await selectInitAgent(deps, root);
|
|
1608
1738
|
}
|
|
1609
1739
|
if (interactive) {
|
|
1610
1740
|
while (true) {
|
|
1611
1741
|
deps.output({
|
|
1612
|
-
message: renderInitSummary(
|
|
1742
|
+
message: renderInitSummary(agentPlan, harness, driver, model, entry, buildCommand)
|
|
1613
1743
|
});
|
|
1614
|
-
const saveLabel = initSaveLabel(exists,
|
|
1744
|
+
const saveLabel = initSaveLabel(exists, agentPlan);
|
|
1615
1745
|
const action = await deps.select(`${saveLabel} with this configuration?`, [
|
|
1616
1746
|
{
|
|
1617
1747
|
name: saveLabel,
|
|
@@ -1633,9 +1763,9 @@ async function initCommand(deps, options) {
|
|
|
1633
1763
|
if (action === "save") break;
|
|
1634
1764
|
const setting = await deps.select("What would you like to change?", [
|
|
1635
1765
|
{
|
|
1636
|
-
description:
|
|
1637
|
-
name: "
|
|
1638
|
-
value: "
|
|
1766
|
+
description: initAgentSummary(agentPlan),
|
|
1767
|
+
name: "Agent",
|
|
1768
|
+
value: "agent"
|
|
1639
1769
|
},
|
|
1640
1770
|
{ description: harnessName(harness), name: "Harness", value: "harness" },
|
|
1641
1771
|
{
|
|
@@ -1647,7 +1777,7 @@ async function initCommand(deps, options) {
|
|
|
1647
1777
|
...driver === "command" ? [{ description: entry, name: "PI SDK entry file", value: "entry" }] : [],
|
|
1648
1778
|
{ name: "Back to review", value: "back" }
|
|
1649
1779
|
]);
|
|
1650
|
-
if (setting === "
|
|
1780
|
+
if (setting === "agent") agentPlan = await selectInitAgent(deps, root);
|
|
1651
1781
|
if (setting === "harness") harness = await selectHarness(deps);
|
|
1652
1782
|
if (setting === "driver") {
|
|
1653
1783
|
driver = await deps.select(
|
|
@@ -1672,22 +1802,18 @@ async function initCommand(deps, options) {
|
|
|
1672
1802
|
}
|
|
1673
1803
|
}
|
|
1674
1804
|
}
|
|
1675
|
-
let
|
|
1676
|
-
let
|
|
1677
|
-
if (
|
|
1805
|
+
let agentHostname = agentPlan.kind === "existing" ? agentPlan.publicHostname : void 0;
|
|
1806
|
+
let createdAgent;
|
|
1807
|
+
if (agentPlan.kind === "create") {
|
|
1678
1808
|
const oauthToken = await deps.auth.accessToken();
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
projectPlan.name,
|
|
1682
|
-
projectPlan.organizationId
|
|
1683
|
-
);
|
|
1684
|
-
projectHostname = createdProject.publicHostname;
|
|
1809
|
+
createdAgent = await deps.api.createAgent(oauthToken, agentPlan.name, agentPlan.organizationId);
|
|
1810
|
+
agentHostname = createdAgent.publicHostname;
|
|
1685
1811
|
}
|
|
1686
1812
|
const configuredDeclaration = createDeclaration(
|
|
1687
1813
|
model,
|
|
1688
1814
|
driver === "native" ? { kind: "native" } : { kind: "command", protocol: "pi-rpc-v1", command: ["node", entry] },
|
|
1689
1815
|
buildCommand,
|
|
1690
|
-
|
|
1816
|
+
agentHostname
|
|
1691
1817
|
);
|
|
1692
1818
|
const declaration = parseDeclaration({
|
|
1693
1819
|
...configuredDeclaration,
|
|
@@ -1700,9 +1826,9 @@ async function initCommand(deps, options) {
|
|
|
1700
1826
|
try {
|
|
1701
1827
|
await saveDeclaration(root, declaration, overwrite);
|
|
1702
1828
|
} catch (error) {
|
|
1703
|
-
if (
|
|
1829
|
+
if (createdAgent) {
|
|
1704
1830
|
throw new Error(
|
|
1705
|
-
`Created agent ${
|
|
1831
|
+
`Created agent ${createdAgent.name}, but could not write ${DECLARATION_FILENAME}. Fix the file and run \`tonbo agent use ${createdAgent.publicHostname}\`.`,
|
|
1706
1832
|
{ cause: error }
|
|
1707
1833
|
);
|
|
1708
1834
|
}
|
|
@@ -1711,28 +1837,28 @@ async function initCommand(deps, options) {
|
|
|
1711
1837
|
const driverSummary = driver === "command" ? `PI SDK app
|
|
1712
1838
|
Build: ${buildCommand?.join(" ")}
|
|
1713
1839
|
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 ${
|
|
1840
|
+
const agentSummary = createdAgent ? `Agent: ${createdAgent.name} (${createdAgent.publicHostname})
|
|
1841
|
+
Organization: ${createdAgent.organizationName}
|
|
1842
|
+
Application: https://${createdAgent.publicHostname}
|
|
1843
|
+
SSH after deploy: ssh ${createdAgent.sshDestination}` : agentPlan.kind === "existing" ? `Agent: ${agentPlan.name ? `${agentPlan.name} (${agentPlan.publicHostname})` : agentPlan.publicHostname}${agentPlan.organizationName ? `
|
|
1844
|
+
Organization: ${agentPlan.organizationName}` : ""}${agentPlan.sshDestination ? `
|
|
1845
|
+
SSH after deploy: ssh ${agentPlan.sshDestination}` : ""}` : "Next: tonbo agent create <name>";
|
|
1720
1846
|
deps.output({
|
|
1721
|
-
message: `${
|
|
1722
|
-
${
|
|
1847
|
+
message: `${createdAgent ? `Created agent ${createdAgent.name} and` : exists ? "Updated" : "Created"} ${DECLARATION_FILENAME}.
|
|
1848
|
+
${agentSummary}
|
|
1723
1849
|
Harness: PI
|
|
1724
|
-
Mode: ${driverSummary}${
|
|
1850
|
+
Mode: ${driverSummary}${agentPlan.kind === "later" ? "" : "\nNext: tonbo deploy"}`,
|
|
1725
1851
|
declaration,
|
|
1726
1852
|
path: path4.join(root, DECLARATION_FILENAME)
|
|
1727
1853
|
});
|
|
1728
1854
|
}
|
|
1729
|
-
function initSaveLabel(exists,
|
|
1730
|
-
if (
|
|
1855
|
+
function initSaveLabel(exists, agent) {
|
|
1856
|
+
if (agent.kind === "create") {
|
|
1731
1857
|
return exists ? `Create agent and replace ${DECLARATION_FILENAME}` : `Create agent and ${DECLARATION_FILENAME}`;
|
|
1732
1858
|
}
|
|
1733
1859
|
return exists ? `Replace ${DECLARATION_FILENAME}` : `Create ${DECLARATION_FILENAME}`;
|
|
1734
1860
|
}
|
|
1735
|
-
async function
|
|
1861
|
+
async function selectInitAgent(deps, root) {
|
|
1736
1862
|
for (; ; ) {
|
|
1737
1863
|
const action = await deps.select("How should this directory connect to Tonbo?", [
|
|
1738
1864
|
{
|
|
@@ -1754,12 +1880,12 @@ async function selectInitProject(deps, root) {
|
|
|
1754
1880
|
if (action === "later") return { kind: "later" };
|
|
1755
1881
|
if (action === "create") {
|
|
1756
1882
|
const organization = await selectInitOrganization(deps);
|
|
1757
|
-
const defaultName =
|
|
1883
|
+
const defaultName = agentNameSuggestion(path4.basename(root)) || "tonbo-agent";
|
|
1758
1884
|
for (; ; ) {
|
|
1759
1885
|
const answer = (await deps.prompt(`Agent name [${defaultName}]: `)).trim().toLowerCase();
|
|
1760
1886
|
const name = answer || defaultName;
|
|
1761
1887
|
try {
|
|
1762
|
-
|
|
1888
|
+
validateAgentName(name);
|
|
1763
1889
|
return {
|
|
1764
1890
|
kind: "create",
|
|
1765
1891
|
name,
|
|
@@ -1774,36 +1900,36 @@ async function selectInitProject(deps, root) {
|
|
|
1774
1900
|
}
|
|
1775
1901
|
}
|
|
1776
1902
|
const oauthToken = await deps.auth.accessToken();
|
|
1777
|
-
const
|
|
1778
|
-
(
|
|
1903
|
+
const agents = (await deps.api.listAgents(oauthToken)).filter(
|
|
1904
|
+
(agent2) => agent2.status === "active"
|
|
1779
1905
|
);
|
|
1780
|
-
if (
|
|
1906
|
+
if (agents.length === 0) {
|
|
1781
1907
|
deps.output({ message: "No active agents are available. Create a new agent instead." });
|
|
1782
1908
|
continue;
|
|
1783
1909
|
}
|
|
1784
1910
|
const id = await deps.select(
|
|
1785
1911
|
"Which agent should this directory use?",
|
|
1786
|
-
|
|
1787
|
-
name:
|
|
1788
|
-
description: `Organization: ${
|
|
1789
|
-
value:
|
|
1912
|
+
agents.map((agent2) => ({
|
|
1913
|
+
name: agent2.name,
|
|
1914
|
+
description: `Organization: ${agent2.organizationName} \xB7 ${agent2.publicHostname} \xB7 ${agent2.id}`,
|
|
1915
|
+
value: agent2.id
|
|
1790
1916
|
}))
|
|
1791
1917
|
);
|
|
1792
|
-
const
|
|
1918
|
+
const agent = selectAgent(agents, id);
|
|
1793
1919
|
return {
|
|
1794
1920
|
kind: "existing",
|
|
1795
|
-
name:
|
|
1796
|
-
organizationName:
|
|
1797
|
-
publicHostname:
|
|
1798
|
-
sshDestination:
|
|
1921
|
+
name: agent.name,
|
|
1922
|
+
organizationName: agent.organizationName,
|
|
1923
|
+
publicHostname: agent.publicHostname,
|
|
1924
|
+
sshDestination: agent.sshDestination
|
|
1799
1925
|
};
|
|
1800
1926
|
}
|
|
1801
1927
|
}
|
|
1802
|
-
function
|
|
1803
|
-
if (
|
|
1804
|
-
return `Create ${
|
|
1805
|
-
if (
|
|
1806
|
-
return
|
|
1928
|
+
function initAgentSummary(agent) {
|
|
1929
|
+
if (agent.kind === "create")
|
|
1930
|
+
return `Create ${agent.name}${agent.organizationName ? ` in ${agent.organizationName}` : ""} (permanent address assigned after creation)`;
|
|
1931
|
+
if (agent.kind === "existing") {
|
|
1932
|
+
return agent.name ? `${agent.name}${agent.organizationName ? ` in ${agent.organizationName}` : ""} (${agent.publicHostname})` : `Keep ${agent.publicHostname}`;
|
|
1807
1933
|
}
|
|
1808
1934
|
return "Set up later";
|
|
1809
1935
|
}
|
|
@@ -1813,7 +1939,7 @@ async function selectInitOrganization(deps) {
|
|
|
1813
1939
|
(organization) => organization.role !== "member"
|
|
1814
1940
|
);
|
|
1815
1941
|
if (organizations.length === 0)
|
|
1816
|
-
throw new Error("No organization lets you create a
|
|
1942
|
+
throw new Error("No organization lets you create a agent. Ask an admin or owner.");
|
|
1817
1943
|
if (organizations.length === 1) return organizations[0];
|
|
1818
1944
|
const id = await deps.select(
|
|
1819
1945
|
"Which organization should own the new agent?",
|
|
@@ -1839,11 +1965,11 @@ function harnessName(harness) {
|
|
|
1839
1965
|
const supported = supportedHarnesses.find((candidate) => candidate.id === harness);
|
|
1840
1966
|
return supported?.name ?? harness;
|
|
1841
1967
|
}
|
|
1842
|
-
function renderInitSummary(
|
|
1968
|
+
function renderInitSummary(agent, harness, driver, model, entry, buildCommand) {
|
|
1843
1969
|
const lines = [
|
|
1844
1970
|
"",
|
|
1845
1971
|
"Tonbo Agent configuration:",
|
|
1846
|
-
` Agent: ${
|
|
1972
|
+
` Agent: ${initAgentSummary(agent)}`,
|
|
1847
1973
|
` Harness: ${harnessName(harness)}`,
|
|
1848
1974
|
` Execution: ${driver === "native" ? "PI CLI (`pi`)" : "PI SDK app"}`,
|
|
1849
1975
|
` Model: ${model}`
|
|
@@ -1862,7 +1988,7 @@ function piTargetChoices(settingsFound) {
|
|
|
1862
1988
|
value: "command"
|
|
1863
1989
|
},
|
|
1864
1990
|
native: {
|
|
1865
|
-
description: settingsFound ? "Run pi directly and load the detected
|
|
1991
|
+
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
1992
|
name: "PI CLI (`pi`)",
|
|
1867
1993
|
value: "native"
|
|
1868
1994
|
}
|
|
@@ -1928,31 +2054,31 @@ async function sshKeyRemoveCommand(deps, fingerprint) {
|
|
|
1928
2054
|
const key = await deps.api.revokeSshKey(oauthToken, fingerprint);
|
|
1929
2055
|
deps.output({ message: `Revoked SSH key ${key.fingerprint}.`, key });
|
|
1930
2056
|
}
|
|
1931
|
-
async function
|
|
2057
|
+
async function agentUseCommand(deps, selector, force = false) {
|
|
1932
2058
|
const root = await findDeclarationRoot(deps.cwd());
|
|
1933
2059
|
const declaration = await loadDeclaration(root);
|
|
1934
2060
|
const oauthToken = await deps.auth.accessToken();
|
|
1935
|
-
const
|
|
1936
|
-
const
|
|
1937
|
-
if (declaration.agent ===
|
|
2061
|
+
const agents = await deps.api.listAgents(oauthToken);
|
|
2062
|
+
const agent = selectAgent(agents, selector);
|
|
2063
|
+
if (declaration.agent === agent.publicHostname) {
|
|
1938
2064
|
deps.output({
|
|
1939
|
-
message: `.tonbo already uses agent ${
|
|
1940
|
-
agent
|
|
2065
|
+
message: `.tonbo already uses agent ${agent.publicHostname}.`,
|
|
2066
|
+
agent
|
|
1941
2067
|
});
|
|
1942
2068
|
return;
|
|
1943
2069
|
}
|
|
1944
2070
|
if (declaration.agent && !force) {
|
|
1945
|
-
const current =
|
|
2071
|
+
const current = agents.find((candidate) => candidate.publicHostname === declaration.agent);
|
|
1946
2072
|
const currentLabel = current?.publicHostname ?? declaration.agent;
|
|
1947
2073
|
if (!deps.interactive()) {
|
|
1948
2074
|
throw new Error(
|
|
1949
|
-
`.tonbo already uses agent ${currentLabel}. Pass --force to rebind it to ${
|
|
2075
|
+
`.tonbo already uses agent ${currentLabel}. Pass --force to rebind it to ${agent.publicHostname}.`
|
|
1950
2076
|
);
|
|
1951
2077
|
}
|
|
1952
2078
|
const action = await deps.select(
|
|
1953
|
-
`Rebind .tonbo from ${currentLabel} to ${
|
|
2079
|
+
`Rebind .tonbo from ${currentLabel} to ${agent.publicHostname}?`,
|
|
1954
2080
|
[
|
|
1955
|
-
{ name: `Rebind to ${
|
|
2081
|
+
{ name: `Rebind to ${agent.publicHostname}`, value: "rebind" },
|
|
1956
2082
|
{ name: "Cancel", value: "cancel" }
|
|
1957
2083
|
]
|
|
1958
2084
|
);
|
|
@@ -1961,10 +2087,10 @@ async function projectUseCommand(deps, selector, force = false) {
|
|
|
1961
2087
|
return;
|
|
1962
2088
|
}
|
|
1963
2089
|
}
|
|
1964
|
-
await
|
|
1965
|
-
deps.output({ message: `Bound .tonbo to agent ${
|
|
2090
|
+
await bindDeclarationAgent(root, agent.publicHostname);
|
|
2091
|
+
deps.output({ message: `Bound .tonbo to agent ${agent.publicHostname}.`, agent });
|
|
1966
2092
|
}
|
|
1967
|
-
async function
|
|
2093
|
+
async function agentCreateCommand(deps, name) {
|
|
1968
2094
|
const root = await findDeclarationRoot(deps.cwd());
|
|
1969
2095
|
const declaration = await loadDeclaration(root);
|
|
1970
2096
|
if (declaration.agent) {
|
|
@@ -1973,28 +2099,28 @@ async function projectCreateCommand(deps, name) {
|
|
|
1973
2099
|
);
|
|
1974
2100
|
}
|
|
1975
2101
|
const oauthToken = await deps.auth.accessToken();
|
|
1976
|
-
|
|
1977
|
-
const
|
|
2102
|
+
validateAgentName(name);
|
|
2103
|
+
const agent = await deps.api.createAgent(oauthToken, name);
|
|
1978
2104
|
try {
|
|
1979
|
-
await
|
|
2105
|
+
await bindDeclarationAgent(root, agent.publicHostname);
|
|
1980
2106
|
} catch (error) {
|
|
1981
2107
|
throw new Error(
|
|
1982
|
-
`Created agent ${
|
|
2108
|
+
`Created agent ${agent.name}, but could not bind ${DECLARATION_FILENAME}. Fix the file and run \`tonbo agent use ${agent.publicHostname}\`.`,
|
|
1983
2109
|
{ cause: error }
|
|
1984
2110
|
);
|
|
1985
2111
|
}
|
|
1986
|
-
deps.output({ message: `Created agent ${
|
|
2112
|
+
deps.output({ message: `Created agent ${agent.name} and bound it in .tonbo.`, agent });
|
|
1987
2113
|
}
|
|
1988
2114
|
async function deployCommand(deps, options = { promote: true }) {
|
|
1989
|
-
const { declaration, oauthToken,
|
|
2115
|
+
const { declaration, oauthToken, agent, root } = await resolveAgent(deps);
|
|
1990
2116
|
if (declaration.build) await runBuildCommand(root, declaration.build.command);
|
|
1991
2117
|
const source = await buildSourceBundle(root);
|
|
1992
2118
|
const origin = { actor: "cli", git: await deps.gitProvenance(root) };
|
|
1993
|
-
const managementToken = await deps.api.exchangeManagementToken(oauthToken,
|
|
2119
|
+
const managementToken = await deps.api.exchangeManagementToken(oauthToken, agent.id);
|
|
1994
2120
|
const result = await deps.api.deploy({
|
|
1995
2121
|
bundle: source,
|
|
1996
2122
|
origin,
|
|
1997
|
-
|
|
2123
|
+
agentId: agent.id,
|
|
1998
2124
|
promote: options.promote,
|
|
1999
2125
|
spec: buildDeploymentSpec(declaration, source),
|
|
2000
2126
|
token: managementToken
|
|
@@ -2002,12 +2128,12 @@ async function deployCommand(deps, options = { promote: true }) {
|
|
|
2002
2128
|
const { deployment } = result;
|
|
2003
2129
|
const shortId = shortDeploymentId(deployment.id);
|
|
2004
2130
|
const lines = [
|
|
2005
|
-
options.promote ? `Deployed agent ${
|
|
2006
|
-
`Organization: ${
|
|
2131
|
+
options.promote ? `Deployed agent ${agent.name}.` : `Created Deployment ${deploymentName(deployment)} (${shortId}) for agent ${agent.name} without promoting it.`,
|
|
2132
|
+
`Organization: ${agent.organizationName}`,
|
|
2007
2133
|
`Deployment: ${deploymentName(deployment)} (${shortId})`,
|
|
2008
2134
|
`Source: ${deploymentSourceLabel(deployment)}`,
|
|
2009
2135
|
`Production: ${options.promote ? productionStateLabel(deployment) : `not promoted; run \`tonbo deployments promote ${shortId}\` to move Production here`}`,
|
|
2010
|
-
`Application: https://${
|
|
2136
|
+
`Application: https://${agent.publicHostname}`
|
|
2011
2137
|
];
|
|
2012
2138
|
if (result.unchanged) {
|
|
2013
2139
|
lines.push(
|
|
@@ -2023,49 +2149,49 @@ async function deployCommand(deps, options = { promote: true }) {
|
|
|
2023
2149
|
' tonbo run "<prompt>"',
|
|
2024
2150
|
"",
|
|
2025
2151
|
"Connect with SSH:",
|
|
2026
|
-
` ssh ${
|
|
2152
|
+
` ssh ${agent.sshDestination}`
|
|
2027
2153
|
);
|
|
2028
2154
|
}
|
|
2029
|
-
deps.output({ message: lines.join("\n"), agent
|
|
2155
|
+
deps.output({ message: lines.join("\n"), agent, ...result });
|
|
2030
2156
|
}
|
|
2031
2157
|
async function runCommand(deps, prompt, options) {
|
|
2032
|
-
const { oauthToken,
|
|
2033
|
-
const managementToken = await deps.api.exchangeManagementToken(oauthToken,
|
|
2158
|
+
const { oauthToken, agent } = await resolveAgent(deps);
|
|
2159
|
+
const managementToken = await deps.api.exchangeManagementToken(oauthToken, agent.id);
|
|
2034
2160
|
const result = await deps.api.run({
|
|
2035
|
-
|
|
2161
|
+
agentId: agent.id,
|
|
2036
2162
|
prompt,
|
|
2037
2163
|
sessionId: options.session,
|
|
2038
2164
|
token: managementToken
|
|
2039
2165
|
});
|
|
2040
2166
|
deps.output({
|
|
2041
2167
|
message: result.turn.assistant_text,
|
|
2042
|
-
agent
|
|
2168
|
+
agent,
|
|
2043
2169
|
...result
|
|
2044
2170
|
});
|
|
2045
2171
|
}
|
|
2046
2172
|
async function agentListCommand(deps) {
|
|
2047
|
-
const agents = await deps.api.
|
|
2173
|
+
const agents = await deps.api.listAgents(await deps.auth.accessToken());
|
|
2048
2174
|
deps.output({
|
|
2049
2175
|
message: agents.length ? agents.map((agent) => `${agent.name} \xB7 ${agent.publicHostname}`).join("\n") : "No agents are available.",
|
|
2050
2176
|
agents
|
|
2051
2177
|
});
|
|
2052
2178
|
}
|
|
2053
|
-
async function
|
|
2054
|
-
const {
|
|
2055
|
-
const production = await deps.api.getProduction(
|
|
2056
|
-
const deployment = production ? await deps.api.getDeployment(
|
|
2179
|
+
async function agentShowCommand(deps) {
|
|
2180
|
+
const { agent, token } = await agentManagement(deps);
|
|
2181
|
+
const production = await deps.api.getProduction(agent.id, token);
|
|
2182
|
+
const deployment = production ? await deps.api.getDeployment(agent.id, production.deployment_id, token) : null;
|
|
2057
2183
|
const lines = [
|
|
2058
|
-
`agent: ${
|
|
2059
|
-
`Organization: ${
|
|
2060
|
-
`application: https://${
|
|
2061
|
-
`ssh: ssh ${
|
|
2184
|
+
`agent: ${agent.name}`,
|
|
2185
|
+
`Organization: ${agent.organizationName}`,
|
|
2186
|
+
`application: https://${agent.publicHostname}`,
|
|
2187
|
+
`ssh: ssh ${agent.sshDestination}`
|
|
2062
2188
|
];
|
|
2063
2189
|
if (deployment) {
|
|
2064
2190
|
lines.push(
|
|
2065
2191
|
`production: ${deploymentName(deployment)} (${shortDeploymentId(deployment.id)}) ${productionStateLabel(deployment)}`
|
|
2066
2192
|
);
|
|
2067
2193
|
}
|
|
2068
|
-
deps.output({ message: lines.join("\n"), agent
|
|
2194
|
+
deps.output({ message: lines.join("\n"), agent, production, deployment });
|
|
2069
2195
|
}
|
|
2070
2196
|
function shortDeploymentId(id) {
|
|
2071
2197
|
return id.replace(/-/g, "").slice(0, 8);
|
|
@@ -2076,7 +2202,7 @@ function deploymentName(deployment) {
|
|
|
2076
2202
|
}
|
|
2077
2203
|
function deploymentSourceLabel(deployment) {
|
|
2078
2204
|
const git = deployment.origin.git;
|
|
2079
|
-
if (!git) return
|
|
2205
|
+
if (!git) return deploymentOriginLabel(deployment.origin.actor);
|
|
2080
2206
|
const sha = git.commit_sha.slice(0, 7);
|
|
2081
2207
|
return git.ref ? `${sha} \xB7 ${git.ref}` : sha;
|
|
2082
2208
|
}
|
|
@@ -2092,7 +2218,7 @@ function selectDeployment(deployments, prefix) {
|
|
|
2092
2218
|
const matches = deployments.filter(
|
|
2093
2219
|
(deployment) => deployment.id.replace(/-/g, "").startsWith(normalized)
|
|
2094
2220
|
);
|
|
2095
|
-
if (matches.length === 0) throw new Error(`Deployment ${prefix} was not found in this
|
|
2221
|
+
if (matches.length === 0) throw new Error(`Deployment ${prefix} was not found in this agent.`);
|
|
2096
2222
|
if (matches.length > 1) {
|
|
2097
2223
|
throw new Error(
|
|
2098
2224
|
`Deployment id ${prefix} is ambiguous; it matches ${matches.map((deployment) => shortDeploymentId(deployment.id)).join(", ")}. Use more characters.`
|
|
@@ -2118,10 +2244,10 @@ function productionMarker(state) {
|
|
|
2118
2244
|
return "";
|
|
2119
2245
|
}
|
|
2120
2246
|
async function deploymentsListCommand(deps) {
|
|
2121
|
-
const {
|
|
2247
|
+
const { agent, token } = await agentManagement(deps);
|
|
2122
2248
|
const [deployments, production] = await Promise.all([
|
|
2123
|
-
deps.api.listDeployments(
|
|
2124
|
-
deps.api.getProduction(
|
|
2249
|
+
deps.api.listDeployments(agent.id, token),
|
|
2250
|
+
deps.api.getProduction(agent.id, token)
|
|
2125
2251
|
]);
|
|
2126
2252
|
const message = deployments.length ? renderTable(
|
|
2127
2253
|
["ID", "NAME", "STATUS", "PRODUCTION", "SOURCE", "CREATED", "BY"],
|
|
@@ -2134,18 +2260,18 @@ async function deploymentsListCommand(deps) {
|
|
|
2134
2260
|
formatTimestamp(deployment.created_at),
|
|
2135
2261
|
deployment.created_by_user_id ? shortDeploymentId(deployment.created_by_user_id) : ""
|
|
2136
2262
|
])
|
|
2137
|
-
) : `No Deployments exist for agent ${
|
|
2138
|
-
deps.output({ message, agent
|
|
2263
|
+
) : `No Deployments exist for agent ${agent.name}. Run \`tonbo deploy\` to create one.`;
|
|
2264
|
+
deps.output({ message, agent, production, deployments });
|
|
2139
2265
|
}
|
|
2140
2266
|
async function deploymentsShowCommand(deps, prefix) {
|
|
2141
|
-
const {
|
|
2142
|
-
const deployment = selectDeployment(await deps.api.listDeployments(
|
|
2143
|
-
const rollouts = (await deps.api.listRollouts(
|
|
2267
|
+
const { agent, token } = await agentManagement(deps);
|
|
2268
|
+
const deployment = selectDeployment(await deps.api.listDeployments(agent.id, token), prefix);
|
|
2269
|
+
const rollouts = (await deps.api.listRollouts(agent.id, token)).filter(
|
|
2144
2270
|
(rollout) => rollout.deployment_id === deployment.id || rollout.previous_deployment_id === deployment.id
|
|
2145
2271
|
);
|
|
2146
2272
|
deps.output({
|
|
2147
2273
|
message: renderDeploymentDetail(deployment, rollouts),
|
|
2148
|
-
agent
|
|
2274
|
+
agent,
|
|
2149
2275
|
deployment,
|
|
2150
2276
|
rollouts
|
|
2151
2277
|
});
|
|
@@ -2186,43 +2312,43 @@ function renderDeploymentDetail(deployment, rollouts) {
|
|
|
2186
2312
|
return lines.join("\n");
|
|
2187
2313
|
}
|
|
2188
2314
|
async function deploymentsPromoteCommand(deps, prefix) {
|
|
2189
|
-
const {
|
|
2190
|
-
const target = selectDeployment(await deps.api.listDeployments(
|
|
2191
|
-
const result = await moveProduction(deps,
|
|
2315
|
+
const { agent, token } = await agentManagement(deps);
|
|
2316
|
+
const target = selectDeployment(await deps.api.listDeployments(agent.id, token), prefix);
|
|
2317
|
+
const result = await moveProduction(deps, agent.id, target, token);
|
|
2192
2318
|
deps.output({
|
|
2193
|
-
message: describeProductionMove(
|
|
2194
|
-
agent
|
|
2319
|
+
message: describeProductionMove(agent, result, "Promoted"),
|
|
2320
|
+
agent,
|
|
2195
2321
|
...result
|
|
2196
2322
|
});
|
|
2197
2323
|
}
|
|
2198
2324
|
async function deploymentsRollbackCommand(deps, prefix) {
|
|
2199
|
-
const {
|
|
2325
|
+
const { agent, token } = await agentManagement(deps);
|
|
2200
2326
|
let target;
|
|
2201
2327
|
if (prefix !== void 0) {
|
|
2202
|
-
target = selectDeployment(await deps.api.listDeployments(
|
|
2328
|
+
target = selectDeployment(await deps.api.listDeployments(agent.id, token), prefix);
|
|
2203
2329
|
} else {
|
|
2204
|
-
const rollouts = await deps.api.listRollouts(
|
|
2330
|
+
const rollouts = await deps.api.listRollouts(agent.id, token);
|
|
2205
2331
|
const previousId = rollouts.find(
|
|
2206
2332
|
(rollout) => rollout.previous_deployment_id !== null
|
|
2207
2333
|
)?.previous_deployment_id;
|
|
2208
2334
|
if (!previousId) {
|
|
2209
2335
|
throw new Error(
|
|
2210
|
-
`Agent ${
|
|
2336
|
+
`Agent ${agent.name} has no previous Production Deployment to roll back to. Name one with \`tonbo deployments rollback <id>\`.`
|
|
2211
2337
|
);
|
|
2212
2338
|
}
|
|
2213
|
-
target = await deps.api.getDeployment(
|
|
2339
|
+
target = await deps.api.getDeployment(agent.id, previousId, token);
|
|
2214
2340
|
}
|
|
2215
|
-
const result = await moveProduction(deps,
|
|
2341
|
+
const result = await moveProduction(deps, agent.id, target, token);
|
|
2216
2342
|
deps.output({
|
|
2217
|
-
message: describeProductionMove(
|
|
2218
|
-
agent
|
|
2343
|
+
message: describeProductionMove(agent, result, "Rolled back"),
|
|
2344
|
+
agent,
|
|
2219
2345
|
...result
|
|
2220
2346
|
});
|
|
2221
2347
|
}
|
|
2222
|
-
async function moveProduction(deps,
|
|
2223
|
-
const previous = await deps.api.getProduction(
|
|
2348
|
+
async function moveProduction(deps, agentId, target, token) {
|
|
2349
|
+
const previous = await deps.api.getProduction(agentId, token);
|
|
2224
2350
|
const production = await deps.api.putProduction(
|
|
2225
|
-
|
|
2351
|
+
agentId,
|
|
2226
2352
|
{
|
|
2227
2353
|
deployment_id: target.id,
|
|
2228
2354
|
desired_state: "running",
|
|
@@ -2231,49 +2357,49 @@ async function moveProduction(deps, projectId, target, token) {
|
|
|
2231
2357
|
token
|
|
2232
2358
|
);
|
|
2233
2359
|
return {
|
|
2234
|
-
deployment: await deps.api.getDeployment(
|
|
2360
|
+
deployment: await deps.api.getDeployment(agentId, target.id, token),
|
|
2235
2361
|
previous,
|
|
2236
2362
|
production
|
|
2237
2363
|
};
|
|
2238
2364
|
}
|
|
2239
|
-
function describeProductionMove(
|
|
2365
|
+
function describeProductionMove(agent, move, verb) {
|
|
2240
2366
|
const { deployment, previous } = move;
|
|
2241
2367
|
const name = `${deploymentName(deployment)} (${shortDeploymentId(deployment.id)})`;
|
|
2242
|
-
const headline = previous?.deployment_id === deployment.id ? `Deployment ${name} is already Production for agent ${
|
|
2368
|
+
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)})` : ""}.`;
|
|
2243
2369
|
return `${headline}
|
|
2244
2370
|
Production: ${productionStateLabel(deployment)}
|
|
2245
|
-
Application: https://${
|
|
2371
|
+
Application: https://${agent.publicHostname}`;
|
|
2246
2372
|
}
|
|
2247
|
-
async function
|
|
2248
|
-
const { oauthToken,
|
|
2373
|
+
async function agentManagement(deps) {
|
|
2374
|
+
const { oauthToken, agent } = await resolveAgent(deps);
|
|
2249
2375
|
return {
|
|
2250
|
-
|
|
2251
|
-
token: await deps.api.exchangeManagementToken(oauthToken,
|
|
2376
|
+
agent,
|
|
2377
|
+
token: await deps.api.exchangeManagementToken(oauthToken, agent.id)
|
|
2252
2378
|
};
|
|
2253
2379
|
}
|
|
2254
2380
|
async function secretListCommand(deps) {
|
|
2255
|
-
const {
|
|
2256
|
-
const secrets = await deps.api.
|
|
2381
|
+
const { agent, token } = await agentManagement(deps);
|
|
2382
|
+
const secrets = await deps.api.listAgentSecrets(agent.id, token);
|
|
2257
2383
|
deps.output({
|
|
2258
2384
|
message: secrets.length ? secrets.map((secret) => secret.name).join("\n") : "No agent secrets are configured.",
|
|
2259
|
-
agent
|
|
2385
|
+
agent,
|
|
2260
2386
|
secrets
|
|
2261
2387
|
});
|
|
2262
2388
|
}
|
|
2263
2389
|
async function secretSetCommand(deps, name, options) {
|
|
2264
2390
|
const value = await deps.secretValue(name, options.fromEnv);
|
|
2265
|
-
const {
|
|
2266
|
-
const secret = await deps.api.
|
|
2391
|
+
const { agent, token } = await agentManagement(deps);
|
|
2392
|
+
const secret = await deps.api.setAgentSecret(agent.id, name, value, token);
|
|
2267
2393
|
deps.output({
|
|
2268
2394
|
message: `Set agent secret ${secret.name}. Redeploy to replace the active runtime with this value.`,
|
|
2269
|
-
agent
|
|
2395
|
+
agent,
|
|
2270
2396
|
secret
|
|
2271
2397
|
});
|
|
2272
2398
|
}
|
|
2273
2399
|
async function secretRemoveCommand(deps, name) {
|
|
2274
|
-
const {
|
|
2275
|
-
await deps.api.
|
|
2276
|
-
deps.output({ message: `Removed agent secret ${name}.`, agent
|
|
2400
|
+
const { agent, token } = await agentManagement(deps);
|
|
2401
|
+
await deps.api.deleteAgentSecret(agent.id, name, token);
|
|
2402
|
+
deps.output({ message: `Removed agent secret ${name}.`, agent, name });
|
|
2277
2403
|
}
|
|
2278
2404
|
|
|
2279
2405
|
// src/credentials.ts
|
|
@@ -2379,15 +2505,17 @@ var AUTHOR_MAX_LENGTH = 255;
|
|
|
2379
2505
|
async function collectGitProvenance(root, run = defaultGitRunner) {
|
|
2380
2506
|
const commitSha = await read(run, ["rev-parse", "HEAD"], root);
|
|
2381
2507
|
if (!commitSha || !COMMIT_SHA.test(commitSha)) return null;
|
|
2382
|
-
const [ref, subject, author, status] = await Promise.all([
|
|
2508
|
+
const [ref, subject, author, status, remote] = await Promise.all([
|
|
2383
2509
|
read(run, ["rev-parse", "--abbrev-ref", "HEAD"], root),
|
|
2384
2510
|
read(run, ["log", "-1", "--format=%s"], root),
|
|
2385
2511
|
read(run, ["log", "-1", "--format=%an"], root),
|
|
2386
2512
|
// Only the deployed tree matters: changes elsewhere in the repository do
|
|
2387
2513
|
// not alter the uploaded bundle.
|
|
2388
|
-
read(run, ["status", "--porcelain", "--", "."], root)
|
|
2514
|
+
read(run, ["status", "--porcelain", "--", "."], root),
|
|
2515
|
+
read(run, ["remote", "get-url", "origin"], root)
|
|
2389
2516
|
]);
|
|
2390
2517
|
return {
|
|
2518
|
+
...githubRepositoryUrl(remote) ? { repository_url: githubRepositoryUrl(remote) } : {},
|
|
2391
2519
|
commit_sha: commitSha,
|
|
2392
2520
|
ref: ref === "HEAD" ? null : clamp(ref, REF_MAX_LENGTH),
|
|
2393
2521
|
subject: clamp(subject, SUBJECT_MAX_LENGTH),
|
|
@@ -2593,18 +2721,18 @@ function createProgram(dependencies = createDependencies) {
|
|
|
2593
2721
|
})
|
|
2594
2722
|
);
|
|
2595
2723
|
});
|
|
2596
|
-
const
|
|
2597
|
-
|
|
2598
|
-
|
|
2724
|
+
const agent = program.command("agent").description("manage the Agent bound to this source directory");
|
|
2725
|
+
agent.command("create <name>").description("create an agent and bind this directory to it").action(async (name) => agentCreateCommand(dependencies(jsonOutput(program)), name));
|
|
2726
|
+
agent.command("list").description("list the agents available to your account").action(async () => agentListCommand(dependencies(jsonOutput(program))));
|
|
2599
2727
|
const sshKey = program.command("ssh-key").description("manage public keys used by native agent SSH");
|
|
2600
2728
|
sshKey.command("add <public-key>").description("register an OpenSSH public key with the current Tonbo account").action(async (path6) => sshKeyAddCommand(dependencies(jsonOutput(program)), path6));
|
|
2601
2729
|
sshKey.command("remove <fingerprint>").description("revoke an SSH public key from the current Tonbo account").action(
|
|
2602
2730
|
async (fingerprint) => sshKeyRemoveCommand(dependencies(jsonOutput(program)), fingerprint)
|
|
2603
2731
|
);
|
|
2604
|
-
|
|
2605
|
-
async (selector, options) =>
|
|
2732
|
+
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(
|
|
2733
|
+
async (selector, options) => agentUseCommand(dependencies(jsonOutput(program)), selector, options.force === true)
|
|
2606
2734
|
);
|
|
2607
|
-
|
|
2735
|
+
agent.command("show").description("show the agent bound to this directory and its connection addresses").action(async () => agentShowCommand(dependencies(jsonOutput(program))));
|
|
2608
2736
|
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(
|
|
2609
2737
|
async (options) => deployCommand(dependencies(jsonOutput(program)), { promote: options.promote })
|
|
2610
2738
|
);
|