@tonbo/cli 0.3.2 → 0.3.4

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 CHANGED
@@ -207,7 +207,8 @@ Machines belong to an Account independently of Agents. Allocate in `us-east-1`,
207
207
 
208
208
  ```console
209
209
  tonbo --json region list
210
- tonbo --json machine allocate --account <account-uuid> --region us-east-1
210
+ tonbo --json machine allocate --account <account-uuid> --region us-east-1 --request-id <request-uuid>
211
+ tonbo --json machine allocation <request-uuid> --account <account-uuid>
211
212
  tonbo --json machine list --account <account-uuid>
212
213
  tonbo --json machine bind <machine-uuid> --agent <agent-uuid>
213
214
  tonbo --json machine show <machine-uuid>
@@ -219,3 +220,5 @@ tonbo --json machine release <machine-uuid>
219
220
  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.
220
221
 
221
222
  `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.
223
+
224
+ Persist the allocation request UUID before sending when automating. Reuse it with the same account and region after a lost response; lookup returns the original Machine even after release. Omitting `--request-id` generates one, included in the success output or retry error.
package/dist/bin/tonbo.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/app.ts
4
+ import { randomUUID as randomUUID5 } from "node:crypto";
4
5
  import { Command } from "commander";
5
6
  import { readFileSync } from "node:fs";
6
7
 
@@ -523,6 +524,9 @@ var TonboApi = class {
523
524
  this.accountOrigin = accountOrigin;
524
525
  this.managementOrigin = managementOrigin;
525
526
  }
527
+ cancelActivities(agentId, token, body) {
528
+ return this.management("POST", `/v1/agents/${agentId}/production/cancellations`, token, body);
529
+ }
526
530
  async listRegions(oauthToken) {
527
531
  return parseRegionDiscovery(
528
532
  await requestJson(this.fetcher, `${this.accountOrigin}/api/cli/regions`, {
@@ -558,7 +562,7 @@ var TonboApi = class {
558
562
  }
559
563
  ).then((body) => body.organizations);
560
564
  }
561
- createAgent(oauthToken, name, organizationId) {
565
+ createAgent(oauthToken, name, organizationId, requestId = randomUUID()) {
562
566
  return requestJson(
563
567
  this.fetcher,
564
568
  `${this.accountOrigin}/api/cli/agents`,
@@ -568,9 +572,28 @@ var TonboApi = class {
568
572
  authorization: `Bearer ${oauthToken}`,
569
573
  "content-type": "application/json"
570
574
  },
571
- body: JSON.stringify(organizationId ? { name, orgId: organizationId } : { name })
575
+ body: JSON.stringify({
576
+ name,
577
+ requestId,
578
+ ...organizationId ? { orgId: organizationId } : {}
579
+ })
572
580
  }
573
- ).then((body) => body.agent);
581
+ ).then((body) => body.agent).catch((cause) => {
582
+ if (cause instanceof Error && "status" in cause && typeof cause.status === "number" && cause.status >= 400 && cause.status < 500)
583
+ throw cause;
584
+ throw new Error(
585
+ `Agent creation was not confirmed. Query \`tonbo agent creation ${requestId}${organizationId ? ` --account ${organizationId}` : ""}\` or retry creation with --request-id ${requestId} and the same name and account.`,
586
+ { cause }
587
+ );
588
+ });
589
+ }
590
+ readAgentCreationRequest(oauthToken, requestId, accountId) {
591
+ const account = accountId ? `?account=${encodeURIComponent(accountId)}` : "";
592
+ return requestJson(
593
+ this.fetcher,
594
+ `${this.accountOrigin}/api/cli/agents/creations/${encodeURIComponent(requestId)}${account}`,
595
+ { headers: { authorization: `Bearer ${oauthToken}` } }
596
+ );
574
597
  }
575
598
  registerSshKey(oauthToken, key) {
576
599
  return requestJson(
@@ -1256,8 +1279,9 @@ async function openBrowser(url) {
1256
1279
 
1257
1280
  // ../../packages/agent-source-bundler/dist/src/generated/contracts.js
1258
1281
  var inferenceDefault = { "model": "claude-sonnet-5" };
1259
- var sourceBundleContract = { "version": 1, "format": "tar-v1", "key_prefix": "agent-source-bundles", "content_type": "application/vnd.tonbo.source+tar", "max_bytes": 67108864 };
1282
+ var sourceBundleContract = { "version": 1, "format": "tar-v1", "key_prefix": "agent-source-bundles", "content_type": "application/vnd.tonbo.source+tar", "max_bytes": 67108864, "runtime_cache_directories": [".pi/npm", ".pi/git"] };
1260
1283
  var piSessionContract = { "version": 1, "adapter": "pi-jsonl-v3", "format_version": 3, "durable_completion_timeout_seconds": 30, "turn_claim_seconds": 30, "turn_claim_renew_seconds": 10, "session_directory": "/sessions", "path_template": "/sessions/{session_id}.jsonl", "preflight_command": ["/usr/local/bin/artifacts", "runtime", "session-preflight"] };
1284
+ var piSlackTemplate = { "repository": "tonbo-io/pi-slack-agent", "source_directory": "examples/pi-slack-agent", "service": { "command": ["node", "slack/server.mjs"], "secrets": ["SLACK_SIGNING_SECRET", "SLACK_BOT_TOKEN", "SLACK_BOT_USER_ID", "SLACK_TEAM_ID", "TONBO_AGENT_API_KEY", "TONBO_AGENT_API_ORIGIN", "TONBO_IAM_ORIGIN"] } };
1261
1285
 
1262
1286
  // ../../packages/agent-source-bundler/dist/src/inference-default.js
1263
1287
  var DEFAULT_INFERENCE_MODEL = inferenceDefault.model;
@@ -1292,6 +1316,7 @@ function githubRepositoryUrl(remote) {
1292
1316
 
1293
1317
  // src/commands.ts
1294
1318
  import path4 from "node:path";
1319
+ import { randomUUID as randomUUID3 } from "node:crypto";
1295
1320
 
1296
1321
  // src/build.ts
1297
1322
  import { spawn } from "node:child_process";
@@ -1320,11 +1345,13 @@ async function runBuildCommand(root, command) {
1320
1345
 
1321
1346
  // ../../packages/agent-source-bundler/dist/src/native-pi.js
1322
1347
  import { stringify } from "smol-toml";
1323
- function nativePiDeclaration(model) {
1348
+ var PI_SLACK_TEMPLATE_SERVICE = piSlackTemplate.service;
1349
+ function nativePiDeclaration(model, service) {
1324
1350
  return {
1325
1351
  version: 2,
1326
1352
  harness: { runtime: "pi", driver: { kind: "native" } },
1327
- inference: { model }
1353
+ inference: { model },
1354
+ ...service ? { service } : {}
1328
1355
  };
1329
1356
  }
1330
1357
 
@@ -1446,8 +1473,7 @@ var DEFAULT_IGNORES = [
1446
1473
  "!.env.example",
1447
1474
  ".tonbo-cache/",
1448
1475
  ".tonbo-system/",
1449
- ".pi/npm/",
1450
- ".pi/git/"
1476
+ ...sourceBundleContract.runtime_cache_directories.map((directory) => `${directory}/`)
1451
1477
  ];
1452
1478
  var EXACT_NPM_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
1453
1479
  var GIT_COMMIT = /^[0-9a-f]{40}$/i;
@@ -2129,7 +2155,7 @@ async function agentUseCommand(deps, selector, force = false) {
2129
2155
  await bindDeclarationAgent(root, agent.publicHostname);
2130
2156
  deps.output({ message: `Bound .tonbo to agent ${agent.publicHostname}.`, agent });
2131
2157
  }
2132
- async function agentCreateCommand(deps, name) {
2158
+ async function agentCreateCommand(deps, name, options = {}) {
2133
2159
  const root = await findDeclarationRoot(deps.cwd());
2134
2160
  const declaration = await loadDeclaration(root);
2135
2161
  if (declaration.agent) {
@@ -2139,7 +2165,8 @@ async function agentCreateCommand(deps, name) {
2139
2165
  }
2140
2166
  const oauthToken = await deps.auth.accessToken();
2141
2167
  validateAgentName(name);
2142
- const agent = await deps.api.createAgent(oauthToken, name);
2168
+ const requestId = options.requestId ?? randomUUID3();
2169
+ const agent = await deps.api.createAgent(oauthToken, name, options.account, requestId);
2143
2170
  try {
2144
2171
  await bindDeclarationAgent(root, agent.publicHostname);
2145
2172
  } catch (error) {
@@ -2148,7 +2175,11 @@ async function agentCreateCommand(deps, name) {
2148
2175
  { cause: error }
2149
2176
  );
2150
2177
  }
2151
- deps.output({ message: `Created agent ${agent.name} and bound it in .tonbo.`, agent });
2178
+ deps.output({
2179
+ message: `Created agent ${agent.name} and bound it in .tonbo.`,
2180
+ agent,
2181
+ requestId
2182
+ });
2152
2183
  }
2153
2184
  async function deployCommand(deps, options = { promote: true }) {
2154
2185
  const { declaration, oauthToken, agent, root } = await resolveAgent(deps);
@@ -2440,9 +2471,23 @@ async function secretRemoveCommand(deps, name) {
2440
2471
  await deps.api.deleteAgentSecret(agent.id, name, token);
2441
2472
  deps.output({ message: `Removed agent secret ${name}.`, agent, name });
2442
2473
  }
2474
+ async function cancelActivitiesCommand(deps, options) {
2475
+ const { oauthToken, agent } = await resolveAgent(deps);
2476
+ const token = await deps.api.exchangeManagementToken(oauthToken, agent.id);
2477
+ const result = await deps.api.cancelActivities(agent.id, token, {
2478
+ sandbox_id: options.sandbox,
2479
+ runtime_session_id: options.runtimeSession,
2480
+ work_keys: options.workKey,
2481
+ reason: options.reason
2482
+ });
2483
+ deps.output({
2484
+ message: "Activity cancellation requested. Physical retirement and application checkpoint cancellation must still be verified.",
2485
+ cancellation: result
2486
+ });
2487
+ }
2443
2488
 
2444
2489
  // src/credentials.ts
2445
- import { randomUUID as randomUUID3 } from "node:crypto";
2490
+ import { randomUUID as randomUUID4 } from "node:crypto";
2446
2491
  import { chmod, lstat as lstat4, mkdir, open as open2, readFile as readFile5, rename as rename2, rm as rm2 } from "node:fs/promises";
2447
2492
  import os from "node:os";
2448
2493
  import path5 from "node:path";
@@ -2473,7 +2518,7 @@ var FileCredentialStore = class {
2473
2518
  await assertExistingDestinationIsSafe(this.filename);
2474
2519
  const temporary = path5.join(
2475
2520
  directory,
2476
- `.${path5.basename(this.filename)}.${process.pid}.${randomUUID3()}.tmp`
2521
+ `.${path5.basename(this.filename)}.${process.pid}.${randomUUID4()}.tmp`
2477
2522
  );
2478
2523
  let handle = null;
2479
2524
  try {
@@ -2719,13 +2764,33 @@ function createProgram(dependencies = createDependencies) {
2719
2764
  )
2720
2765
  );
2721
2766
  });
2722
- machine.command("allocate").requiredOption("--account <account>", "owning account UUID").requiredOption("--region <region>", "compute region (see tonbo region list)").action(async (options) => {
2767
+ machine.command("allocate").requiredOption("--account <account>", "owning account UUID").requiredOption("--region <region>", "compute region (see tonbo region list)").option(
2768
+ "--request-id <uuid>",
2769
+ "stable allocation request UUID; reuse it after an uncertain response"
2770
+ ).action(async (options) => {
2723
2771
  const deps = dependencies(jsonOutput(program));
2724
- deps.output(
2725
- await deps.api.machineRequest(await deps.auth.accessToken(), "", "POST", {
2772
+ const requestId = options.requestId ?? randomUUID5();
2773
+ try {
2774
+ const result = await deps.api.machineRequest(await deps.auth.accessToken(), "", "POST", {
2726
2775
  accountId: options.account,
2727
- region: options.region
2728
- })
2776
+ region: options.region,
2777
+ requestId
2778
+ });
2779
+ deps.output({ ...result, requestId });
2780
+ } catch (cause) {
2781
+ throw new Error(
2782
+ `Machine allocation was not confirmed. Retry with --request-id ${requestId} and the same account and region.`,
2783
+ { cause }
2784
+ );
2785
+ }
2786
+ });
2787
+ machine.command("allocation <requestId>").description("query the original Machine for an allocation request").requiredOption("--account <account>", "owning account UUID").action(async (requestId, options) => {
2788
+ const deps = dependencies(jsonOutput(program));
2789
+ deps.output(
2790
+ await deps.api.machineRequest(
2791
+ await deps.auth.accessToken(),
2792
+ `/allocations/${encodeURIComponent(requestId)}?account=${encodeURIComponent(options.account)}`
2793
+ )
2729
2794
  );
2730
2795
  });
2731
2796
  for (const operation of ["show", "metrics", "release"]) {
@@ -2765,7 +2830,19 @@ function createProgram(dependencies = createDependencies) {
2765
2830
  );
2766
2831
  });
2767
2832
  const agent = program.command("agent").description("manage the Agent bound to this source directory");
2768
- agent.command("create <name>").description("create an agent and bind this directory to it").action(async (name) => agentCreateCommand(dependencies(jsonOutput(program)), name));
2833
+ agent.command("create <name>").description("create an agent and bind this directory to it").option("--account <account>", "owning account UUID; defaults to your personal account").option("--request-id <uuid>", "reuse the same creation UUID after an uncertain response").action(
2834
+ async (name, options) => agentCreateCommand(dependencies(jsonOutput(program)), name, options)
2835
+ );
2836
+ agent.command("creation <requestId>").description("query the original Agent for a creation request").option("--account <account>", "owning account UUID; defaults to your personal account").action(async (requestId, options) => {
2837
+ const deps = dependencies(jsonOutput(program));
2838
+ deps.output(
2839
+ await deps.api.readAgentCreationRequest(
2840
+ await deps.auth.accessToken(),
2841
+ requestId,
2842
+ options.account
2843
+ )
2844
+ );
2845
+ });
2769
2846
  agent.command("list").description("list the agents available to your account").action(async () => agentListCommand(dependencies(jsonOutput(program))));
2770
2847
  const sshKey = program.command("ssh-key").description("manage public keys used by native agent SSH");
2771
2848
  sshKey.command("add <public-key>").description("register an OpenSSH public key with the current Tonbo account").action(async (path6) => sshKeyAddCommand(dependencies(jsonOutput(program)), path6));
@@ -2787,6 +2864,9 @@ function createProgram(dependencies = createDependencies) {
2787
2864
  deployments.command("promote <deployment>").description("move Production to a Deployment by id prefix").action(
2788
2865
  async (prefix) => deploymentsPromoteCommand(dependencies(jsonOutput(program)), prefix)
2789
2866
  );
2867
+ deployments.command("cancel-activities").description("cancel selected named work on an exact retired runtime").requiredOption("--sandbox <id>", "retired Sandbox UUID").requiredOption("--runtime-session <id>", "exact activation UUID").requiredOption("--work-key <keys...>", "named work keys to cancel").requiredOption("--reason <reason>", "reason retained in the cancellation audit").action(
2868
+ async (options) => cancelActivitiesCommand(dependencies(jsonOutput(program)), options)
2869
+ );
2790
2870
  deployments.command("rollback [deployment]").description("move Production back to the previous Production Deployment, or to a named one").action(
2791
2871
  async (prefix) => deploymentsRollbackCommand(dependencies(jsonOutput(program)), prefix)
2792
2872
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tonbo/cli",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Deploy persistent Agents and manage their Machines from the command line.",
5
5
  "homepage": "https://tonbo.dev",
6
6
  "bugs": {