@tonbo/cli 0.0.4 → 0.0.6
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 +78 -19
- package/dist/src/app.js +14 -1
- package/dist/src/build.d.ts +1 -0
- package/dist/src/build.js +21 -0
- package/dist/src/commands.d.ts +9 -0
- package/dist/src/commands.js +64 -1
- package/dist/src/contracts.js +3 -2
- package/dist/src/declaration.d.ts +8 -2
- package/dist/src/declaration.js +72 -7
- package/dist/src/generated/contracts.d.ts +73 -40
- package/dist/src/generated/contracts.js +87 -52
- package/dist/src/prompt.d.ts +1 -0
- package/dist/src/prompt.js +10 -0
- package/dist/src/source.d.ts +1 -0
- package/dist/src/source.js +70 -0
- package/dist/src/types.d.ts +15 -13
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -7,25 +7,32 @@ npm install --global @tonbo/cli
|
|
|
7
7
|
tonbo --version
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
The V1 CLI deploys a PI-based Agent directly from a local directory without requiring Git or Tonbo calls in the Agent source.
|
|
11
|
-
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
10
|
+
The V1 CLI deploys a PI-based Agent directly from a local directory without requiring Git or Tonbo calls in the Agent source. Initialize the current directory interactively:
|
|
11
|
+
|
|
12
|
+
```console
|
|
13
|
+
tonbo init
|
|
14
|
+
? Agent shape: [1] PI project [2] PI Package project [3] PI SDK app [1]:
|
|
15
|
+
? Inference model [claude-sonnet-4-5]:
|
|
16
|
+
Created .tonbo.
|
|
17
|
+
Next: tonbo project create <slug>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The generated `.tonbo` is TOML:
|
|
21
|
+
|
|
22
|
+
```toml
|
|
23
|
+
version = 1
|
|
24
|
+
|
|
25
|
+
[agent]
|
|
26
|
+
runtime = "pi"
|
|
27
|
+
|
|
28
|
+
[agent.driver]
|
|
29
|
+
kind = "native"
|
|
30
|
+
|
|
31
|
+
[inference]
|
|
32
|
+
model = "claude-sonnet-4-5"
|
|
26
33
|
```
|
|
27
34
|
|
|
28
|
-
Then authenticate, bind the directory and deploy its current contents:
|
|
35
|
+
For automation, use `tonbo init --model <model>`; an existing declaration requires interactive confirmation or `--force`. Then authenticate, bind the directory and deploy its current contents:
|
|
29
36
|
|
|
30
37
|
```console
|
|
31
38
|
tonbo login
|
|
@@ -56,9 +63,61 @@ ssh my-project@tonbo.sh
|
|
|
56
63
|
|
|
57
64
|
The SSH username selects the globally unique Project; the signed public key identifies the Tonbo user and is checked against Project membership and IAM at connection time. 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.
|
|
58
65
|
|
|
59
|
-
The platform injects scoped inference access and
|
|
66
|
+
The platform injects scoped inference access and owns PI Session capture. Do not put a model-provider API key or a Tonbo credential in `.tonbo`.
|
|
67
|
+
|
|
68
|
+
Deploy snapshots regular files under `.tonbo`, excluding `.git`, `node_modules`, PI package caches, local environment files and patterns in `.tonboignore`. Equal contents produce the same revision. Coding tools start in the persistent Artifacts workspace, while `AGENTS.md` and trusted `.pi` extensions, skills, prompts and settings load from the uploaded revision.
|
|
69
|
+
|
|
70
|
+
## Supported PI project shapes
|
|
71
|
+
|
|
72
|
+
### 1. Project-local Agent
|
|
73
|
+
|
|
74
|
+
Keep the normal PI project 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:
|
|
75
|
+
|
|
76
|
+
```console
|
|
77
|
+
pi
|
|
78
|
+
tonbo init
|
|
79
|
+
tonbo project create my-agent
|
|
80
|
+
tonbo deploy
|
|
81
|
+
tonbo run "Start the task."
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 2. PI Package project
|
|
85
|
+
|
|
86
|
+
Use PI's project-local package declaration as the single source of truth. Install an immutable package reference locally, verify it with PI, and deploy the project; Tonbo excludes `.pi/npm` and `.pi/git` caches and restores packages inside the isolated runtime:
|
|
87
|
+
|
|
88
|
+
```console
|
|
89
|
+
pi install -l npm:@acme/my-pi-agent@1.2.3
|
|
90
|
+
pi
|
|
91
|
+
tonbo init
|
|
92
|
+
tonbo deploy
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Remote npm packages must use an exact version. Git packages must use a full 40-character commit, for example `git:github.com/acme/my-pi-agent@0123456789abcdef0123456789abcdef01234567`. Movable tags, branches, ranges and `latest` fail deployment.
|
|
96
|
+
|
|
97
|
+
### 3. PI SDK app
|
|
98
|
+
|
|
99
|
+
Choose `PI SDK app` in `tonbo init`, or declare a command driver directly:
|
|
100
|
+
|
|
101
|
+
```toml
|
|
102
|
+
version = 1
|
|
103
|
+
|
|
104
|
+
[agent]
|
|
105
|
+
runtime = "pi"
|
|
106
|
+
secrets = ["GITHUB_TOKEN"]
|
|
107
|
+
|
|
108
|
+
[agent.driver]
|
|
109
|
+
kind = "command"
|
|
110
|
+
protocol = "pi-rpc-v1"
|
|
111
|
+
command = ["node", "dist/agent.mjs"]
|
|
112
|
+
|
|
113
|
+
[build]
|
|
114
|
+
command = ["npm", "run", "build"]
|
|
115
|
+
|
|
116
|
+
[inference]
|
|
117
|
+
model = "claude-sonnet-4-5"
|
|
118
|
+
```
|
|
60
119
|
|
|
61
|
-
|
|
120
|
+
`tonbo deploy` runs the build command before creating the source snapshot. The entrypoint must keep stdout exclusively for PI RPC and send logs to stderr. It receives `PI_CODING_AGENT_MODULE`, `PI_CODING_AGENT_DIR`, `PI_CODING_AGENT_SESSION_DIR`, `TONBO_PI_SESSION_FILE`, `TONBO_PI_SESSION_ID`, `TONBO_PI_PROVIDER`, `TONBO_PI_MODEL` and `TONBO_PI_RPC_PROTOCOL`. Load the platform PI SDK from `PI_CODING_AGENT_MODULE`, open `TONBO_PI_SESSION_FILE`, create an `AgentSessionRuntime`, and pass it to `runRpcMode()`. This lets the app customize tools and runtime services without forking PI while preserving Tonbo's durable Session and Turn protocol.
|
|
62
121
|
|
|
63
122
|
For a non-interactive invocation, provide a current Tonbo CLI OAuth access token and an explicit Project:
|
|
64
123
|
|
package/dist/src/app.js
CHANGED
|
@@ -2,10 +2,12 @@ import { Command } from "commander";
|
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { TonboApi } from "./api.js";
|
|
4
4
|
import { AuthClient } from "./auth.js";
|
|
5
|
-
import { deployCommand, runCommand, loginCommand, projectCreateCommand, projectUseCommand, sshCommand, sshKeyAddCommand, sshKeyRemoveCommand, secretListCommand, secretRemoveCommand, secretSetCommand, } from "./commands.js";
|
|
5
|
+
import { deployCommand, initCommand, runCommand, loginCommand, projectCreateCommand, projectUseCommand, sshCommand, sshKeyAddCommand, sshKeyRemoveCommand, secretListCommand, secretRemoveCommand, secretSetCommand, } from "./commands.js";
|
|
6
6
|
import { FileConfigStore } from "./config.js";
|
|
7
7
|
import { FileCredentialStore } from "./credentials.js";
|
|
8
|
+
import { DECLARATION_FILENAME } from "./declaration.js";
|
|
8
9
|
import { silentProgress, TerminalProgress } from "./progress.js";
|
|
10
|
+
import { terminalPrompt } from "./prompt.js";
|
|
9
11
|
import { readDefaultSshPublicKeys } from "./ssh-key.js";
|
|
10
12
|
const packageVersion = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8"));
|
|
11
13
|
export function createDependencies(json = false) {
|
|
@@ -17,6 +19,7 @@ export function createDependencies(json = false) {
|
|
|
17
19
|
config: new FileConfigStore(),
|
|
18
20
|
cwd: () => process.cwd(),
|
|
19
21
|
defaultSshPublicKeys: readDefaultSshPublicKeys,
|
|
22
|
+
interactive: () => !json && process.stdin.isTTY === true && process.stderr.isTTY === true,
|
|
20
23
|
output: (value) => {
|
|
21
24
|
if (json)
|
|
22
25
|
console.log(JSON.stringify(value));
|
|
@@ -24,6 +27,7 @@ export function createDependencies(json = false) {
|
|
|
24
27
|
console.log(value.message ?? value);
|
|
25
28
|
},
|
|
26
29
|
progress: json ? silentProgress : new TerminalProgress(process.stderr),
|
|
30
|
+
prompt: terminalPrompt,
|
|
27
31
|
secretValue: async (name, fromEnvironment) => {
|
|
28
32
|
const environmentName = fromEnvironment ?? name;
|
|
29
33
|
const value = process.env[environmentName];
|
|
@@ -39,6 +43,15 @@ export function createProgram(dependencies = createDependencies) {
|
|
|
39
43
|
.description("Deploy a persistent Project Agent to Tonbo.")
|
|
40
44
|
.version(packageVersion.version)
|
|
41
45
|
.option("--json", "print machine-readable JSON");
|
|
46
|
+
program
|
|
47
|
+
.command("init")
|
|
48
|
+
.description("interactively create a Tonbo Agent declaration in this directory")
|
|
49
|
+
.option("--model <model>", "inference model")
|
|
50
|
+
.option("--driver <driver>", "PI driver: native or command")
|
|
51
|
+
.option("--agent-entry <file>", "Node entry file for the command driver")
|
|
52
|
+
.option("--build-command <argv...>", "build command argv for the command driver")
|
|
53
|
+
.option("--force", `replace an existing ${DECLARATION_FILENAME}`)
|
|
54
|
+
.action(async (options) => initCommand(dependencies(program.opts().json), options));
|
|
42
55
|
program
|
|
43
56
|
.command("login")
|
|
44
57
|
.description("sign in through the browser and store the session in the user config")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runBuildCommand(root: string, command: string[]): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
export async function runBuildCommand(root, command) {
|
|
3
|
+
if (command.length === 0)
|
|
4
|
+
throw new Error("Build command must not be empty.");
|
|
5
|
+
await new Promise((resolve, reject) => {
|
|
6
|
+
const child = spawn(command[0], command.slice(1), {
|
|
7
|
+
cwd: root,
|
|
8
|
+
env: process.env,
|
|
9
|
+
// Command output is progress, not the CLI result. Keep stdout available
|
|
10
|
+
// for the single JSON document emitted by `tonbo --json deploy`.
|
|
11
|
+
stdio: ["inherit", process.stderr, process.stderr],
|
|
12
|
+
});
|
|
13
|
+
child.once("error", reject);
|
|
14
|
+
child.once("exit", (code, signal) => {
|
|
15
|
+
if (code === 0)
|
|
16
|
+
resolve();
|
|
17
|
+
else
|
|
18
|
+
reject(new Error(`Build command failed${signal ? ` with signal ${signal}` : ` with status ${code}`}.`));
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
package/dist/src/commands.d.ts
CHANGED
|
@@ -10,8 +10,10 @@ export interface CommandDependencies {
|
|
|
10
10
|
config: ConfigStore;
|
|
11
11
|
cwd: () => string;
|
|
12
12
|
defaultSshPublicKeys: typeof readDefaultSshPublicKeys;
|
|
13
|
+
interactive: () => boolean;
|
|
13
14
|
output: (value: unknown) => void;
|
|
14
15
|
progress: ProgressReporter;
|
|
16
|
+
prompt: (question: string) => Promise<string>;
|
|
15
17
|
executable?: () => string;
|
|
16
18
|
secretValue: (name: string, fromEnvironment?: string) => Promise<string>;
|
|
17
19
|
}
|
|
@@ -20,6 +22,13 @@ export declare function resolveProject(deps: CommandDependencies, selector?: str
|
|
|
20
22
|
project: ProjectSummary;
|
|
21
23
|
}>;
|
|
22
24
|
export declare function selectProject(projects: ProjectSummary[], selector: string): ProjectSummary;
|
|
25
|
+
export declare function initCommand(deps: CommandDependencies, options: {
|
|
26
|
+
agentEntry?: string;
|
|
27
|
+
buildCommand?: string[];
|
|
28
|
+
driver?: "native" | "command";
|
|
29
|
+
force?: boolean;
|
|
30
|
+
model?: string;
|
|
31
|
+
}): Promise<void>;
|
|
23
32
|
export declare function loginCommand(deps: CommandDependencies): Promise<void>;
|
|
24
33
|
export declare function sshKeyAddCommand(deps: CommandDependencies, path: string): Promise<void>;
|
|
25
34
|
export declare function sshKeyRemoveCommand(deps: CommandDependencies, fingerprint: string): Promise<void>;
|
package/dist/src/commands.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { runBuildCommand } from "./build.js";
|
|
3
|
+
import { buildRevision, createDeclaration, declarationExists, DECLARATION_FILENAME, DEFAULT_INFERENCE_MODEL, loadDeclaration, saveDeclaration, } from "./declaration.js";
|
|
2
4
|
import { buildSourceBundle, findDeclarationRoot } from "./source.js";
|
|
3
5
|
import { readSshPublicKey } from "./ssh-key.js";
|
|
4
6
|
import { launchProjectSsh } from "./ssh.js";
|
|
@@ -36,6 +38,65 @@ export function selectProject(projects, selector) {
|
|
|
36
38
|
throw new Error(`Project ${selector} is not active.`);
|
|
37
39
|
return matches[0];
|
|
38
40
|
}
|
|
41
|
+
export async function initCommand(deps, options) {
|
|
42
|
+
const root = deps.cwd();
|
|
43
|
+
const exists = await declarationExists(root);
|
|
44
|
+
let overwrite = options.force === true;
|
|
45
|
+
if (exists && !overwrite) {
|
|
46
|
+
if (!deps.interactive())
|
|
47
|
+
throw new Error(`${DECLARATION_FILENAME} already exists. Pass --force to replace it.`);
|
|
48
|
+
const answer = (await deps.prompt(`Replace existing ${DECLARATION_FILENAME}? [y/N] `))
|
|
49
|
+
.trim()
|
|
50
|
+
.toLowerCase();
|
|
51
|
+
if (answer !== "y" && answer !== "yes") {
|
|
52
|
+
deps.output({ message: `Kept existing ${DECLARATION_FILENAME}.` });
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
overwrite = true;
|
|
56
|
+
}
|
|
57
|
+
let driver = options.driver;
|
|
58
|
+
if (driver !== undefined && driver !== "native" && driver !== "command") {
|
|
59
|
+
throw new Error("--driver must be native or command.");
|
|
60
|
+
}
|
|
61
|
+
let shape = driver === "command" ? "sdk" : "project";
|
|
62
|
+
if (!driver && deps.interactive()) {
|
|
63
|
+
const answer = (await deps.prompt("Agent shape: [1] PI project [2] PI Package project [3] PI SDK app [1]: ")).trim();
|
|
64
|
+
if (!["", "1", "2", "3"].includes(answer)) {
|
|
65
|
+
throw new Error("Agent shape must be 1, 2, or 3.");
|
|
66
|
+
}
|
|
67
|
+
shape = answer === "2" ? "package" : answer === "3" ? "sdk" : "project";
|
|
68
|
+
driver = shape === "sdk" ? "command" : "native";
|
|
69
|
+
}
|
|
70
|
+
driver ||= "native";
|
|
71
|
+
if (driver === "native" && (options.agentEntry || options.buildCommand)) {
|
|
72
|
+
throw new Error("--agent-entry and --build-command require --driver command.");
|
|
73
|
+
}
|
|
74
|
+
let entry = options.agentEntry?.trim();
|
|
75
|
+
if (driver === "command" && !entry && deps.interactive()) {
|
|
76
|
+
entry = (await deps.prompt("PI SDK entry file [dist/agent.mjs]: ")).trim();
|
|
77
|
+
}
|
|
78
|
+
entry ||= "dist/agent.mjs";
|
|
79
|
+
let model = options.model?.trim();
|
|
80
|
+
if (!model && deps.interactive()) {
|
|
81
|
+
model = (await deps.prompt(`Inference model [${DEFAULT_INFERENCE_MODEL}]: `)).trim();
|
|
82
|
+
}
|
|
83
|
+
model ||= DEFAULT_INFERENCE_MODEL;
|
|
84
|
+
const buildCommand = driver === "command" ? (options.buildCommand ?? ["npm", "run", "build"]) : undefined;
|
|
85
|
+
const declaration = createDeclaration(model, driver === "native"
|
|
86
|
+
? { kind: "native" }
|
|
87
|
+
: { kind: "command", protocol: "pi-rpc-v1", command: ["node", entry] }, buildCommand);
|
|
88
|
+
await saveDeclaration(root, declaration, overwrite);
|
|
89
|
+
const shapeHint = shape === "package"
|
|
90
|
+
? "\nPI packages stay in .pi/settings.json; use exact npm versions or Git commits."
|
|
91
|
+
: shape === "sdk"
|
|
92
|
+
? `\nBuild: ${buildCommand?.join(" ")}\nEntrypoint: node ${entry}`
|
|
93
|
+
: "";
|
|
94
|
+
deps.output({
|
|
95
|
+
message: `${exists ? "Updated" : "Created"} ${DECLARATION_FILENAME}.${shapeHint}\nNext: tonbo project create <slug>`,
|
|
96
|
+
declaration,
|
|
97
|
+
path: path.join(root, DECLARATION_FILENAME),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
39
100
|
export async function loginCommand(deps) {
|
|
40
101
|
try {
|
|
41
102
|
const tokens = await deps.auth.login((event) => reportLoginProgress(deps.progress, event));
|
|
@@ -121,6 +182,8 @@ export async function projectCreateCommand(deps, slug, name) {
|
|
|
121
182
|
export async function deployCommand(deps, selector) {
|
|
122
183
|
const root = await findDeclarationRoot(deps.cwd());
|
|
123
184
|
const declaration = await loadDeclaration(root);
|
|
185
|
+
if (declaration.build)
|
|
186
|
+
await runBuildCommand(root, declaration.build.command);
|
|
124
187
|
const source = await buildSourceBundle(root);
|
|
125
188
|
const oauthToken = await deps.auth.accessToken();
|
|
126
189
|
let binding = await deps.config.getBinding(root);
|
package/dist/src/contracts.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
2
|
-
import { declarationSchema, kubernetesProfilesSchema, projectServiceSchema, revisionSchema, } from "./generated/contracts.js";
|
|
2
|
+
import { declarationSchema, kubernetesProfilesSchema, piAgentSchema, projectServiceSchema, revisionSchema, } from "./generated/contracts.js";
|
|
3
3
|
const ajv = new Ajv2020({ allErrors: true, useDefaults: true });
|
|
4
4
|
ajv.addKeyword({ keyword: "x-tonbo-profiles" });
|
|
5
5
|
ajv.addSchema(kubernetesProfilesSchema);
|
|
6
|
+
ajv.addSchema(piAgentSchema);
|
|
6
7
|
ajv.addSchema(projectServiceSchema);
|
|
7
8
|
const validateDeclaration = ajv.compile(declarationSchema);
|
|
8
9
|
const validateRevision = ajv.compile(revisionSchema);
|
|
@@ -13,7 +14,7 @@ function validationMessage(label, errors) {
|
|
|
13
14
|
export function parseDeclaration(value) {
|
|
14
15
|
const candidate = structuredClone(value);
|
|
15
16
|
if (!validateDeclaration(candidate)) {
|
|
16
|
-
throw new Error(validationMessage(".tonbo", validateDeclaration.errors));
|
|
17
|
+
throw new Error(validationMessage(".tonbo TOML", validateDeclaration.errors));
|
|
17
18
|
}
|
|
18
19
|
return candidate;
|
|
19
20
|
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { parseDeclaration } from "./contracts.js";
|
|
2
|
-
import type { ManagedRevisionSpec, SourceBundle } from "./types.js";
|
|
3
|
-
export declare
|
|
2
|
+
import type { ManagedRevisionSpec, SourceBundle, TonboDeclaration } from "./types.js";
|
|
3
|
+
export declare const DECLARATION_FILENAME = ".tonbo";
|
|
4
|
+
export declare const DEFAULT_INFERENCE_MODEL = "claude-sonnet-4-5";
|
|
5
|
+
export declare function createDeclaration(model?: string, driver?: TonboDeclaration["agent"]["driver"], buildCommand?: string[]): TonboDeclaration;
|
|
6
|
+
export declare function renderDeclaration(declaration: TonboDeclaration): string;
|
|
7
|
+
export declare function declarationExists(root: string): Promise<boolean>;
|
|
8
|
+
export declare function saveDeclaration(root: string, declaration: TonboDeclaration, overwrite: boolean): Promise<void>;
|
|
9
|
+
export declare function loadDeclaration(declarationRoot: string): Promise<TonboDeclaration>;
|
|
4
10
|
export declare function buildRevision(declaration: ReturnType<typeof parseDeclaration>, source: SourceBundle): ManagedRevisionSpec;
|
package/dist/src/declaration.js
CHANGED
|
@@ -1,30 +1,95 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { lstat, open, readFile, rename, rm } from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
4
|
+
import { parse, stringify } from "smol-toml";
|
|
3
5
|
import { assertManagedRevision, parseDeclaration } from "./contracts.js";
|
|
6
|
+
export const DECLARATION_FILENAME = ".tonbo";
|
|
7
|
+
export const DEFAULT_INFERENCE_MODEL = "claude-sonnet-4-5";
|
|
8
|
+
export function createDeclaration(model = DEFAULT_INFERENCE_MODEL, driver = { kind: "native" }, buildCommand) {
|
|
9
|
+
return parseDeclaration({
|
|
10
|
+
version: 1,
|
|
11
|
+
agent: { runtime: "pi", driver },
|
|
12
|
+
inference: { model: model.trim() },
|
|
13
|
+
...(buildCommand ? { build: { command: buildCommand } } : {}),
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
export function renderDeclaration(declaration) {
|
|
17
|
+
return `# Tonbo Project Agent configuration.\n${stringify(declaration)}`;
|
|
18
|
+
}
|
|
19
|
+
export async function declarationExists(root) {
|
|
20
|
+
const filename = path.join(root, DECLARATION_FILENAME);
|
|
21
|
+
try {
|
|
22
|
+
const metadata = await lstat(filename);
|
|
23
|
+
if (metadata.isSymbolicLink() || !metadata.isFile())
|
|
24
|
+
throw new Error(`${filename} must be a regular file.`);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (error.code === "ENOENT")
|
|
29
|
+
return false;
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export async function saveDeclaration(root, declaration, overwrite) {
|
|
34
|
+
const filename = path.join(root, DECLARATION_FILENAME);
|
|
35
|
+
const exists = await declarationExists(root);
|
|
36
|
+
if (exists && !overwrite)
|
|
37
|
+
throw new Error(`${DECLARATION_FILENAME} already exists. Pass --force to replace it.`);
|
|
38
|
+
const contents = renderDeclaration(declaration);
|
|
39
|
+
if (!overwrite) {
|
|
40
|
+
const handle = await open(filename, "wx", 0o644).catch((error) => {
|
|
41
|
+
if (error.code === "EEXIST")
|
|
42
|
+
throw new Error(`${DECLARATION_FILENAME} already exists. Pass --force to replace it.`);
|
|
43
|
+
throw error;
|
|
44
|
+
});
|
|
45
|
+
try {
|
|
46
|
+
await handle.writeFile(contents, "utf8");
|
|
47
|
+
await handle.sync();
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
await handle.close();
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const temporary = path.join(root, `.${DECLARATION_FILENAME}.${process.pid}.${randomUUID()}.tmp`);
|
|
55
|
+
let handle;
|
|
56
|
+
try {
|
|
57
|
+
handle = await open(temporary, "wx", 0o644);
|
|
58
|
+
await handle.writeFile(contents, "utf8");
|
|
59
|
+
await handle.sync();
|
|
60
|
+
await handle.close();
|
|
61
|
+
handle = undefined;
|
|
62
|
+
await rename(temporary, filename);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
await handle?.close().catch(() => undefined);
|
|
66
|
+
await rm(temporary, { force: true }).catch(() => undefined);
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
4
70
|
export async function loadDeclaration(declarationRoot) {
|
|
5
|
-
const filename = path.join(declarationRoot,
|
|
71
|
+
const filename = path.join(declarationRoot, DECLARATION_FILENAME);
|
|
6
72
|
let parsed;
|
|
7
73
|
try {
|
|
8
|
-
parsed =
|
|
74
|
+
parsed = parse(await readFile(filename, "utf8"));
|
|
9
75
|
}
|
|
10
76
|
catch (error) {
|
|
11
77
|
if (error.code === "ENOENT")
|
|
12
|
-
throw new Error(`No
|
|
13
|
-
throw new Error(`Could not read ${filename} as
|
|
78
|
+
throw new Error(`No ${DECLARATION_FILENAME} declaration found at ${filename}.`);
|
|
79
|
+
throw new Error(`Could not read ${filename} as TOML.`, { cause: error });
|
|
14
80
|
}
|
|
15
81
|
return parseDeclaration(parsed);
|
|
16
82
|
}
|
|
17
83
|
export function buildRevision(declaration, source) {
|
|
18
84
|
const spec = {
|
|
19
85
|
version: 1,
|
|
20
|
-
|
|
86
|
+
agent: declaration.agent,
|
|
21
87
|
source: {
|
|
22
88
|
format: source.format,
|
|
23
89
|
sha256: source.sha256,
|
|
24
90
|
size_bytes: source.size_bytes,
|
|
25
91
|
},
|
|
26
92
|
inference: declaration.inference,
|
|
27
|
-
session_capture: { adapter: declaration.session_capture.adapter },
|
|
28
93
|
...(declaration.service ? { service: declaration.service } : {}),
|
|
29
94
|
};
|
|
30
95
|
assertManagedRevision(spec);
|
|
@@ -1,3 +1,59 @@
|
|
|
1
|
+
export declare const piAgentSchema: {
|
|
2
|
+
readonly $schema: "https://json-schema.org/draft/2020-12/schema";
|
|
3
|
+
readonly $id: "https://contracts.tonbo.dev/agents/pi-agent-v1.schema.json";
|
|
4
|
+
readonly title: "PI Agent v1";
|
|
5
|
+
readonly type: "object";
|
|
6
|
+
readonly additionalProperties: false;
|
|
7
|
+
readonly required: readonly ["runtime", "driver"];
|
|
8
|
+
readonly properties: {
|
|
9
|
+
readonly runtime: {
|
|
10
|
+
readonly const: "pi";
|
|
11
|
+
};
|
|
12
|
+
readonly secrets: {
|
|
13
|
+
readonly type: "array";
|
|
14
|
+
readonly maxItems: 32;
|
|
15
|
+
readonly uniqueItems: true;
|
|
16
|
+
readonly items: {
|
|
17
|
+
readonly type: "string";
|
|
18
|
+
readonly pattern: "^[A-Z_][A-Z0-9_]{0,127}$";
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
readonly driver: {
|
|
22
|
+
readonly oneOf: readonly [{
|
|
23
|
+
readonly type: "object";
|
|
24
|
+
readonly additionalProperties: false;
|
|
25
|
+
readonly required: readonly ["kind"];
|
|
26
|
+
readonly properties: {
|
|
27
|
+
readonly kind: {
|
|
28
|
+
readonly const: "native";
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
}, {
|
|
32
|
+
readonly type: "object";
|
|
33
|
+
readonly additionalProperties: false;
|
|
34
|
+
readonly required: readonly ["kind", "protocol", "command"];
|
|
35
|
+
readonly properties: {
|
|
36
|
+
readonly kind: {
|
|
37
|
+
readonly const: "command";
|
|
38
|
+
};
|
|
39
|
+
readonly protocol: {
|
|
40
|
+
readonly const: "pi-rpc-v1";
|
|
41
|
+
};
|
|
42
|
+
readonly command: {
|
|
43
|
+
readonly type: "array";
|
|
44
|
+
readonly minItems: 1;
|
|
45
|
+
readonly maxItems: 64;
|
|
46
|
+
readonly items: {
|
|
47
|
+
readonly type: "string";
|
|
48
|
+
readonly minLength: 1;
|
|
49
|
+
readonly maxLength: 1024;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
}];
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
};
|
|
1
57
|
export declare const projectServiceSchema: {
|
|
2
58
|
readonly $schema: "https://json-schema.org/draft/2020-12/schema";
|
|
3
59
|
readonly $id: "https://contracts.tonbo.dev/agents/project-service-v1.schema.json";
|
|
@@ -52,23 +108,13 @@ export declare const declarationSchema: {
|
|
|
52
108
|
readonly title: "Tonbo Project Agent declaration v1";
|
|
53
109
|
readonly type: "object";
|
|
54
110
|
readonly additionalProperties: false;
|
|
55
|
-
readonly required: readonly ["version", "
|
|
111
|
+
readonly required: readonly ["version", "agent"];
|
|
56
112
|
readonly properties: {
|
|
57
113
|
readonly version: {
|
|
58
114
|
readonly const: 1;
|
|
59
115
|
};
|
|
60
|
-
readonly
|
|
61
|
-
readonly
|
|
62
|
-
readonly additionalProperties: false;
|
|
63
|
-
readonly required: readonly ["mode", "runtime"];
|
|
64
|
-
readonly properties: {
|
|
65
|
-
readonly mode: {
|
|
66
|
-
readonly const: "managed";
|
|
67
|
-
};
|
|
68
|
-
readonly runtime: {
|
|
69
|
-
readonly const: "pi";
|
|
70
|
-
};
|
|
71
|
-
};
|
|
116
|
+
readonly agent: {
|
|
117
|
+
readonly $ref: "https://contracts.tonbo.dev/agents/pi-agent-v1.schema.json";
|
|
72
118
|
};
|
|
73
119
|
readonly inference: {
|
|
74
120
|
readonly type: "object";
|
|
@@ -85,13 +131,20 @@ export declare const declarationSchema: {
|
|
|
85
131
|
};
|
|
86
132
|
};
|
|
87
133
|
};
|
|
88
|
-
readonly
|
|
134
|
+
readonly build: {
|
|
89
135
|
readonly type: "object";
|
|
90
136
|
readonly additionalProperties: false;
|
|
91
|
-
readonly required: readonly ["
|
|
137
|
+
readonly required: readonly ["command"];
|
|
92
138
|
readonly properties: {
|
|
93
|
-
readonly
|
|
94
|
-
readonly
|
|
139
|
+
readonly command: {
|
|
140
|
+
readonly type: "array";
|
|
141
|
+
readonly minItems: 1;
|
|
142
|
+
readonly maxItems: 64;
|
|
143
|
+
readonly items: {
|
|
144
|
+
readonly type: "string";
|
|
145
|
+
readonly minLength: 1;
|
|
146
|
+
readonly maxLength: 1024;
|
|
147
|
+
};
|
|
95
148
|
};
|
|
96
149
|
};
|
|
97
150
|
};
|
|
@@ -106,23 +159,13 @@ export declare const revisionSchema: {
|
|
|
106
159
|
readonly title: "Managed Project revision v1";
|
|
107
160
|
readonly type: "object";
|
|
108
161
|
readonly additionalProperties: false;
|
|
109
|
-
readonly required: readonly ["version", "
|
|
162
|
+
readonly required: readonly ["version", "agent", "source", "inference"];
|
|
110
163
|
readonly properties: {
|
|
111
164
|
readonly version: {
|
|
112
165
|
readonly const: 1;
|
|
113
166
|
};
|
|
114
|
-
readonly
|
|
115
|
-
readonly
|
|
116
|
-
readonly additionalProperties: false;
|
|
117
|
-
readonly required: readonly ["mode", "runtime"];
|
|
118
|
-
readonly properties: {
|
|
119
|
-
readonly mode: {
|
|
120
|
-
readonly const: "managed";
|
|
121
|
-
};
|
|
122
|
-
readonly runtime: {
|
|
123
|
-
readonly const: "pi";
|
|
124
|
-
};
|
|
125
|
-
};
|
|
167
|
+
readonly agent: {
|
|
168
|
+
readonly $ref: "https://contracts.tonbo.dev/agents/pi-agent-v1.schema.json";
|
|
126
169
|
};
|
|
127
170
|
readonly source: {
|
|
128
171
|
readonly type: "object";
|
|
@@ -155,16 +198,6 @@ export declare const revisionSchema: {
|
|
|
155
198
|
};
|
|
156
199
|
};
|
|
157
200
|
};
|
|
158
|
-
readonly session_capture: {
|
|
159
|
-
readonly type: "object";
|
|
160
|
-
readonly additionalProperties: false;
|
|
161
|
-
readonly required: readonly ["adapter"];
|
|
162
|
-
readonly properties: {
|
|
163
|
-
readonly adapter: {
|
|
164
|
-
readonly const: "pi-jsonl-v3";
|
|
165
|
-
};
|
|
166
|
-
};
|
|
167
|
-
};
|
|
168
201
|
readonly service: {
|
|
169
202
|
readonly $ref: "https://contracts.tonbo.dev/agents/project-service-v1.schema.json";
|
|
170
203
|
};
|
|
@@ -1,5 +1,73 @@
|
|
|
1
|
-
// Generated from contracts/agents/project-service-v1.schema.json and contracts/agents/kubernetes-profiles-v1.schema.json and contracts/agents/tonbo-declaration-v1.schema.json and contracts/agents/managed-revision-v1.schema.json and contracts/agents/source-bundle-v1.json and contracts/agents/pi-session-v1.json.
|
|
1
|
+
// Generated from contracts/agents/pi-agent-v1.schema.json and contracts/agents/project-service-v1.schema.json and contracts/agents/kubernetes-profiles-v1.schema.json and contracts/agents/tonbo-declaration-v1.schema.json and contracts/agents/managed-revision-v1.schema.json and contracts/agents/source-bundle-v1.json and contracts/agents/pi-session-v1.json.
|
|
2
2
|
// Run pnpm generate:contracts after changing a canonical Agent contract.
|
|
3
|
+
export const piAgentSchema = {
|
|
4
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
5
|
+
"$id": "https://contracts.tonbo.dev/agents/pi-agent-v1.schema.json",
|
|
6
|
+
"title": "PI Agent v1",
|
|
7
|
+
"type": "object",
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"required": [
|
|
10
|
+
"runtime",
|
|
11
|
+
"driver"
|
|
12
|
+
],
|
|
13
|
+
"properties": {
|
|
14
|
+
"runtime": {
|
|
15
|
+
"const": "pi"
|
|
16
|
+
},
|
|
17
|
+
"secrets": {
|
|
18
|
+
"type": "array",
|
|
19
|
+
"maxItems": 32,
|
|
20
|
+
"uniqueItems": true,
|
|
21
|
+
"items": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"pattern": "^[A-Z_][A-Z0-9_]{0,127}$"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"driver": {
|
|
27
|
+
"oneOf": [
|
|
28
|
+
{
|
|
29
|
+
"type": "object",
|
|
30
|
+
"additionalProperties": false,
|
|
31
|
+
"required": [
|
|
32
|
+
"kind"
|
|
33
|
+
],
|
|
34
|
+
"properties": {
|
|
35
|
+
"kind": {
|
|
36
|
+
"const": "native"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"type": "object",
|
|
42
|
+
"additionalProperties": false,
|
|
43
|
+
"required": [
|
|
44
|
+
"kind",
|
|
45
|
+
"protocol",
|
|
46
|
+
"command"
|
|
47
|
+
],
|
|
48
|
+
"properties": {
|
|
49
|
+
"kind": {
|
|
50
|
+
"const": "command"
|
|
51
|
+
},
|
|
52
|
+
"protocol": {
|
|
53
|
+
"const": "pi-rpc-v1"
|
|
54
|
+
},
|
|
55
|
+
"command": {
|
|
56
|
+
"type": "array",
|
|
57
|
+
"minItems": 1,
|
|
58
|
+
"maxItems": 64,
|
|
59
|
+
"items": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"minLength": 1,
|
|
62
|
+
"maxLength": 1024
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
3
71
|
export const projectServiceSchema = {
|
|
4
72
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
5
73
|
"$id": "https://contracts.tonbo.dev/agents/project-service-v1.schema.json",
|
|
@@ -60,28 +128,14 @@ export const declarationSchema = {
|
|
|
60
128
|
"additionalProperties": false,
|
|
61
129
|
"required": [
|
|
62
130
|
"version",
|
|
63
|
-
"
|
|
64
|
-
"session_capture"
|
|
131
|
+
"agent"
|
|
65
132
|
],
|
|
66
133
|
"properties": {
|
|
67
134
|
"version": {
|
|
68
135
|
"const": 1
|
|
69
136
|
},
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"additionalProperties": false,
|
|
73
|
-
"required": [
|
|
74
|
-
"mode",
|
|
75
|
-
"runtime"
|
|
76
|
-
],
|
|
77
|
-
"properties": {
|
|
78
|
-
"mode": {
|
|
79
|
-
"const": "managed"
|
|
80
|
-
},
|
|
81
|
-
"runtime": {
|
|
82
|
-
"const": "pi"
|
|
83
|
-
}
|
|
84
|
-
}
|
|
137
|
+
"agent": {
|
|
138
|
+
"$ref": "https://contracts.tonbo.dev/agents/pi-agent-v1.schema.json"
|
|
85
139
|
},
|
|
86
140
|
"inference": {
|
|
87
141
|
"type": "object",
|
|
@@ -100,15 +154,22 @@ export const declarationSchema = {
|
|
|
100
154
|
}
|
|
101
155
|
}
|
|
102
156
|
},
|
|
103
|
-
"
|
|
157
|
+
"build": {
|
|
104
158
|
"type": "object",
|
|
105
159
|
"additionalProperties": false,
|
|
106
160
|
"required": [
|
|
107
|
-
"
|
|
161
|
+
"command"
|
|
108
162
|
],
|
|
109
163
|
"properties": {
|
|
110
|
-
"
|
|
111
|
-
"
|
|
164
|
+
"command": {
|
|
165
|
+
"type": "array",
|
|
166
|
+
"minItems": 1,
|
|
167
|
+
"maxItems": 64,
|
|
168
|
+
"items": {
|
|
169
|
+
"type": "string",
|
|
170
|
+
"minLength": 1,
|
|
171
|
+
"maxLength": 1024
|
|
172
|
+
}
|
|
112
173
|
}
|
|
113
174
|
}
|
|
114
175
|
},
|
|
@@ -125,30 +186,16 @@ export const revisionSchema = {
|
|
|
125
186
|
"additionalProperties": false,
|
|
126
187
|
"required": [
|
|
127
188
|
"version",
|
|
128
|
-
"
|
|
189
|
+
"agent",
|
|
129
190
|
"source",
|
|
130
|
-
"inference"
|
|
131
|
-
"session_capture"
|
|
191
|
+
"inference"
|
|
132
192
|
],
|
|
133
193
|
"properties": {
|
|
134
194
|
"version": {
|
|
135
195
|
"const": 1
|
|
136
196
|
},
|
|
137
|
-
"
|
|
138
|
-
"
|
|
139
|
-
"additionalProperties": false,
|
|
140
|
-
"required": [
|
|
141
|
-
"mode",
|
|
142
|
-
"runtime"
|
|
143
|
-
],
|
|
144
|
-
"properties": {
|
|
145
|
-
"mode": {
|
|
146
|
-
"const": "managed"
|
|
147
|
-
},
|
|
148
|
-
"runtime": {
|
|
149
|
-
"const": "pi"
|
|
150
|
-
}
|
|
151
|
-
}
|
|
197
|
+
"agent": {
|
|
198
|
+
"$ref": "https://contracts.tonbo.dev/agents/pi-agent-v1.schema.json"
|
|
152
199
|
},
|
|
153
200
|
"source": {
|
|
154
201
|
"type": "object",
|
|
@@ -187,18 +234,6 @@ export const revisionSchema = {
|
|
|
187
234
|
}
|
|
188
235
|
}
|
|
189
236
|
},
|
|
190
|
-
"session_capture": {
|
|
191
|
-
"type": "object",
|
|
192
|
-
"additionalProperties": false,
|
|
193
|
-
"required": [
|
|
194
|
-
"adapter"
|
|
195
|
-
],
|
|
196
|
-
"properties": {
|
|
197
|
-
"adapter": {
|
|
198
|
-
"const": "pi-jsonl-v3"
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
},
|
|
202
237
|
"service": {
|
|
203
238
|
"$ref": "https://contracts.tonbo.dev/agents/project-service-v1.schema.json"
|
|
204
239
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function terminalPrompt(question: string): Promise<string>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createInterface } from "node:readline/promises";
|
|
2
|
+
export async function terminalPrompt(question) {
|
|
3
|
+
const prompt = createInterface({ input: process.stdin, output: process.stderr });
|
|
4
|
+
try {
|
|
5
|
+
return await prompt.question(`? ${question}`);
|
|
6
|
+
}
|
|
7
|
+
finally {
|
|
8
|
+
prompt.close();
|
|
9
|
+
}
|
|
10
|
+
}
|
package/dist/src/source.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SourceBundle } from "./types.js";
|
|
2
|
+
export declare function validatePiPackages(root: string): Promise<void>;
|
|
2
3
|
export declare function findDeclarationRoot(start: string): Promise<string>;
|
|
3
4
|
/**
|
|
4
5
|
* Build one deterministic local-source snapshot. Git metadata and timestamps
|
package/dist/src/source.js
CHANGED
|
@@ -15,7 +15,76 @@ const DEFAULT_IGNORES = [
|
|
|
15
15
|
"!.env.example",
|
|
16
16
|
".tonbo-cache/",
|
|
17
17
|
".tonbo-system/",
|
|
18
|
+
".pi/npm/",
|
|
19
|
+
".pi/git/",
|
|
18
20
|
];
|
|
21
|
+
const EXACT_NPM_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
22
|
+
const GIT_COMMIT = /^[0-9a-f]{40}$/i;
|
|
23
|
+
function packageSource(value, index) {
|
|
24
|
+
if (typeof value === "string")
|
|
25
|
+
return value;
|
|
26
|
+
if (value && typeof value === "object" && "source" in value && typeof value.source === "string")
|
|
27
|
+
return value.source;
|
|
28
|
+
throw new Error(`.pi/settings.json packages[${index}] must be a source string or object.`);
|
|
29
|
+
}
|
|
30
|
+
async function validatePackageSource(root, source) {
|
|
31
|
+
if (source.startsWith("npm:")) {
|
|
32
|
+
const specifier = source.slice(4);
|
|
33
|
+
const separator = specifier.lastIndexOf("@");
|
|
34
|
+
if (separator <= 0 || !EXACT_NPM_VERSION.test(specifier.slice(separator + 1))) {
|
|
35
|
+
throw new Error(`PI package ${source} must pin an exact npm version, for example npm:my-agent@1.2.3.`);
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (source.startsWith("git:")) {
|
|
40
|
+
const separator = source.lastIndexOf("@");
|
|
41
|
+
if (separator <= "git:".length || !GIT_COMMIT.test(source.slice(separator + 1))) {
|
|
42
|
+
throw new Error(`PI package ${source} must pin a full 40-character Git commit.`);
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (source.startsWith("./") || source.startsWith("../")) {
|
|
47
|
+
const settingsDirectory = path.join(root, ".pi");
|
|
48
|
+
const resolved = path.resolve(settingsDirectory, source);
|
|
49
|
+
const relative = path.relative(root, resolved);
|
|
50
|
+
if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
|
51
|
+
throw new Error(`Local PI package ${source} resolves outside the deployed project.`);
|
|
52
|
+
}
|
|
53
|
+
let metadata;
|
|
54
|
+
try {
|
|
55
|
+
metadata = await lstat(resolved);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (error.code === "ENOENT") {
|
|
59
|
+
throw new Error(`Local PI package ${source} does not exist.`);
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
64
|
+
throw new Error(`Local PI package ${source} must resolve to a real project directory.`);
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
throw new Error(`PI package ${source} must use an exact npm version, a full Git commit, or a project-local path.`);
|
|
69
|
+
}
|
|
70
|
+
export async function validatePiPackages(root) {
|
|
71
|
+
const filename = path.join(root, ".pi", "settings.json");
|
|
72
|
+
let settings;
|
|
73
|
+
try {
|
|
74
|
+
settings = JSON.parse(await readFile(filename, "utf8"));
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
if (error.code === "ENOENT")
|
|
78
|
+
return;
|
|
79
|
+
throw new Error(`Could not read ${filename} as JSON.`, { cause: error });
|
|
80
|
+
}
|
|
81
|
+
if (!settings || typeof settings !== "object" || !("packages" in settings))
|
|
82
|
+
return;
|
|
83
|
+
const packages = settings.packages;
|
|
84
|
+
if (!Array.isArray(packages))
|
|
85
|
+
throw new Error(`${filename} packages must be an array.`);
|
|
86
|
+
await Promise.all(packages.map((value, index) => validatePackageSource(root, packageSource(value, index))));
|
|
87
|
+
}
|
|
19
88
|
export async function findDeclarationRoot(start) {
|
|
20
89
|
let candidate = path.resolve(start);
|
|
21
90
|
for (;;) {
|
|
@@ -103,6 +172,7 @@ function addEntry(pack, file, contents) {
|
|
|
103
172
|
*/
|
|
104
173
|
export async function buildSourceBundle(root) {
|
|
105
174
|
const resolvedRoot = path.resolve(root);
|
|
175
|
+
await validatePiPackages(resolvedRoot);
|
|
106
176
|
const files = await collectFiles(resolvedRoot);
|
|
107
177
|
const payloadBytes = files.reduce((total, file) => total + file.size, 0);
|
|
108
178
|
if (payloadBytes > MAX_BUNDLE_BYTES) {
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
export interface TonboDeclaration {
|
|
2
2
|
version: 1;
|
|
3
|
-
|
|
4
|
-
mode: "managed";
|
|
5
|
-
runtime: "pi";
|
|
6
|
-
};
|
|
3
|
+
agent: PiAgent;
|
|
7
4
|
inference: {
|
|
8
5
|
model: string;
|
|
9
6
|
};
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
build?: {
|
|
8
|
+
command: string[];
|
|
12
9
|
};
|
|
13
10
|
service?: {
|
|
14
11
|
command: string[];
|
|
@@ -20,10 +17,7 @@ export interface TonboDeclaration {
|
|
|
20
17
|
}
|
|
21
18
|
export interface ManagedRevisionSpec {
|
|
22
19
|
version: 1;
|
|
23
|
-
|
|
24
|
-
mode: "managed";
|
|
25
|
-
runtime: "pi";
|
|
26
|
-
};
|
|
20
|
+
agent: PiAgent;
|
|
27
21
|
source: {
|
|
28
22
|
format: "tar-v1";
|
|
29
23
|
sha256: string;
|
|
@@ -32,9 +26,6 @@ export interface ManagedRevisionSpec {
|
|
|
32
26
|
inference: {
|
|
33
27
|
model: string;
|
|
34
28
|
};
|
|
35
|
-
session_capture: {
|
|
36
|
-
adapter: "pi-jsonl-v3";
|
|
37
|
-
};
|
|
38
29
|
service?: {
|
|
39
30
|
command: string[];
|
|
40
31
|
secrets?: string[];
|
|
@@ -43,6 +34,17 @@ export interface ManagedRevisionSpec {
|
|
|
43
34
|
};
|
|
44
35
|
};
|
|
45
36
|
}
|
|
37
|
+
export type PiAgent = {
|
|
38
|
+
runtime: "pi";
|
|
39
|
+
secrets?: string[];
|
|
40
|
+
driver: {
|
|
41
|
+
kind: "native";
|
|
42
|
+
} | {
|
|
43
|
+
kind: "command";
|
|
44
|
+
protocol: "pi-rpc-v1";
|
|
45
|
+
command: string[];
|
|
46
|
+
};
|
|
47
|
+
};
|
|
46
48
|
export interface SourceBundle {
|
|
47
49
|
bytes: Buffer;
|
|
48
50
|
format: "tar-v1";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tonbo/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "Deploy one persistent Agent per Project from the command line.",
|
|
5
5
|
"homepage": "https://tonbo.dev",
|
|
6
6
|
"bugs": {
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"ajv": "8.20.0",
|
|
34
34
|
"commander": "^14.0.3",
|
|
35
35
|
"ignore": "^7.0.5",
|
|
36
|
+
"smol-toml": "1.6.1",
|
|
36
37
|
"tar-stream": "^3.1.7"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|