@ao-ai/cli 0.0.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/.env.local +3 -0
- package/ao-ai-cli-0.0.0.tgz +0 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +187 -0
- package/dist/index.js.map +1 -0
- package/dist/utils.d.ts +10 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +73 -0
- package/dist/utils.js.map +1 -0
- package/package.json +31 -0
- package/src/index.ts +255 -0
- package/src/utils.ts +94 -0
- package/tsconfig.json +42 -0
package/.env.local
ADDED
|
Binary file
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import open from "open";
|
|
6
|
+
import toml from "toml";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import http from "http";
|
|
9
|
+
import { API_BASE_URL, authSuccessMessage, getAPIKey, readConfig, requireAuth, WEB_BASE_URL, writeConfig, zipProject, } from "./utils.js";
|
|
10
|
+
import { Readable } from "stream";
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import keytar from "keytar";
|
|
13
|
+
const program = new Command();
|
|
14
|
+
program.name("ao").description("AO CLI").version("0.0.1");
|
|
15
|
+
program
|
|
16
|
+
.command("init")
|
|
17
|
+
.action(() => {
|
|
18
|
+
const currentDir = process.cwd();
|
|
19
|
+
const filePath = path.join(currentDir, "ao.toml");
|
|
20
|
+
if (fs.existsSync(filePath)) {
|
|
21
|
+
console.log("AO configuration file already exists.");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const initialContent = `
|
|
25
|
+
name = "my-project"
|
|
26
|
+
|
|
27
|
+
[runtime]
|
|
28
|
+
max_retries = 3
|
|
29
|
+
timeout = 300 # 5 minutes
|
|
30
|
+
`;
|
|
31
|
+
fs.writeFileSync(filePath, initialContent.trim());
|
|
32
|
+
console.log("Initialized AO configuration file at:", filePath);
|
|
33
|
+
})
|
|
34
|
+
.description("");
|
|
35
|
+
program.command("login").action(async () => {
|
|
36
|
+
const apiKey = await getAPIKey();
|
|
37
|
+
if (apiKey) {
|
|
38
|
+
console.log(chalk.green("You are already logged in."));
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
const server = http.createServer(async (req, res) => {
|
|
42
|
+
const address = server.address();
|
|
43
|
+
const port = typeof address === "object" && address ? address.port : 0;
|
|
44
|
+
const url = new URL(req.url, `http://localhost:${port}`);
|
|
45
|
+
const apiKey = url.searchParams.get("apiKey");
|
|
46
|
+
if (apiKey) {
|
|
47
|
+
await keytar.setPassword("ao-cli", "api-key", apiKey);
|
|
48
|
+
res.writeHead(302, {
|
|
49
|
+
Location: `${WEB_BASE_URL}/auth/cli/success`,
|
|
50
|
+
});
|
|
51
|
+
res.end();
|
|
52
|
+
authSuccessMessage();
|
|
53
|
+
server.close();
|
|
54
|
+
process.exit(0);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
await new Promise((resolve) => {
|
|
58
|
+
server.listen(0, resolve);
|
|
59
|
+
});
|
|
60
|
+
const { port } = server.address();
|
|
61
|
+
const authUrl = `${WEB_BASE_URL}/auth/cli/start?redirect=http://localhost:${port}`;
|
|
62
|
+
await open(authUrl);
|
|
63
|
+
});
|
|
64
|
+
program.command("logout").action(async () => {
|
|
65
|
+
await keytar.deletePassword("ao-cli", "api-key");
|
|
66
|
+
console.log(chalk.green("✓ Logged out successfully"));
|
|
67
|
+
});
|
|
68
|
+
program.command("deploy").action(async () => {
|
|
69
|
+
const apiKey = await requireAuth();
|
|
70
|
+
const currentDir = process.cwd();
|
|
71
|
+
const filePath = path.join(currentDir, "ao.toml");
|
|
72
|
+
const pyProjectFilePath = path.join(currentDir, "pyproject.toml");
|
|
73
|
+
const requirementsFilePath = path.join(currentDir, "requirements.txt");
|
|
74
|
+
if (!fs.existsSync(filePath)) {
|
|
75
|
+
console.error("AO configuration file not found. Please run 'ao init' first.");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (!fs.existsSync(requirementsFilePath)) {
|
|
79
|
+
console.error("requirements.txt is required");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (!fs.existsSync(pyProjectFilePath)) {
|
|
83
|
+
console.error("pyproject.toml file not found. Please make sure you have a pyproject.toml file in the current directory.");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
console.log("Deploying agent...");
|
|
87
|
+
// Get the configuration from ao.toml
|
|
88
|
+
const configContent = fs.readFileSync(filePath, "utf-8");
|
|
89
|
+
// Parse the TOML content and extract necessary information for deployment
|
|
90
|
+
let config;
|
|
91
|
+
try {
|
|
92
|
+
config = toml.parse(configContent);
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
console.error("Invalid TOML syntax in ao.toml:", error);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
const configFileSchema = z.object({
|
|
99
|
+
name: z
|
|
100
|
+
.string()
|
|
101
|
+
.lowercase()
|
|
102
|
+
.min(3, "The 'name' field must be at least 3 characters long."),
|
|
103
|
+
deploy: z.object({
|
|
104
|
+
entrypoint: z.string(),
|
|
105
|
+
}),
|
|
106
|
+
runtime: z.object({
|
|
107
|
+
max_retries: z.number().int().nonnegative().optional().default(3),
|
|
108
|
+
timeout: z.number().int().positive().optional().default(300),
|
|
109
|
+
}),
|
|
110
|
+
schedule: z
|
|
111
|
+
.object({
|
|
112
|
+
cron: z.string(),
|
|
113
|
+
})
|
|
114
|
+
.optional(),
|
|
115
|
+
});
|
|
116
|
+
// Check if the AO config file structure is valid
|
|
117
|
+
const validation = configFileSchema.safeParse(config);
|
|
118
|
+
if (!validation.success) {
|
|
119
|
+
console.error("Invalid ao.toml structure:");
|
|
120
|
+
console.error(validation.error.message);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
// Zip the agent code and dependencies
|
|
124
|
+
// 1. Node stream → Web stream
|
|
125
|
+
console.log("Packing project...");
|
|
126
|
+
const zipNodeStream = await zipProject(); // Readable (Node)
|
|
127
|
+
const zipWebStream = Readable.toWeb(zipNodeStream);
|
|
128
|
+
// 2. Web stream → Blob (bien tipado)
|
|
129
|
+
const zipBlob = await new Response(zipWebStream).blob();
|
|
130
|
+
const form = new FormData();
|
|
131
|
+
form.append("file", zipBlob, "agent.zip");
|
|
132
|
+
const response = await fetch(`${API_BASE_URL}/deploy`, {
|
|
133
|
+
method: "POST",
|
|
134
|
+
headers: {
|
|
135
|
+
"X-API-Key": `Bearer ${apiKey}`,
|
|
136
|
+
},
|
|
137
|
+
body: form,
|
|
138
|
+
});
|
|
139
|
+
if (!response.ok) {
|
|
140
|
+
console.error("Deployment failed:", response.statusText);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
console.log("Deployment successful!");
|
|
144
|
+
console.log("You can view your new deployment at:", `https://aodeploy.com/dashboard/deployments`);
|
|
145
|
+
});
|
|
146
|
+
program
|
|
147
|
+
.command("run")
|
|
148
|
+
.requiredOption("--input <string>", "Input for the agent")
|
|
149
|
+
.requiredOption("-d, --deployment <string>", "Specific deployment to run")
|
|
150
|
+
.action(async (options) => {
|
|
151
|
+
const apiKey = await requireAuth();
|
|
152
|
+
console.log("Running agent...");
|
|
153
|
+
const response = await fetch(`${API_BASE_URL}/run`, {
|
|
154
|
+
method: "POST",
|
|
155
|
+
headers: {
|
|
156
|
+
"Content-Type": "application/json",
|
|
157
|
+
"X-API-Key": `Bearer ${apiKey}`,
|
|
158
|
+
},
|
|
159
|
+
body: JSON.stringify({
|
|
160
|
+
input: options.input,
|
|
161
|
+
deploymentId: options.deployment,
|
|
162
|
+
}),
|
|
163
|
+
});
|
|
164
|
+
if (!response.ok) {
|
|
165
|
+
console.error("Running agent failed:", response.statusText);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
const env = program.command("env").description("Manage environment variables");
|
|
170
|
+
env.command("set <key> <value>").action((key, value) => {
|
|
171
|
+
const config = readConfig();
|
|
172
|
+
config.envs = { ...config.envs, [key]: value };
|
|
173
|
+
writeConfig(config);
|
|
174
|
+
console.log(`✓ ${key} set`);
|
|
175
|
+
});
|
|
176
|
+
env.command("list").action(() => {
|
|
177
|
+
const config = readConfig();
|
|
178
|
+
console.log(config.envs ?? "No env keys configured");
|
|
179
|
+
});
|
|
180
|
+
env.command("delete <key>").action((key) => {
|
|
181
|
+
const config = readConfig();
|
|
182
|
+
delete config.envs?.[key];
|
|
183
|
+
writeConfig(config);
|
|
184
|
+
console.log(`✓ ${key} deleted`);
|
|
185
|
+
});
|
|
186
|
+
program.parse();
|
|
187
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { PassThrough } from "stream";
|
|
2
|
+
export declare function zipProject(): Promise<PassThrough>;
|
|
3
|
+
export declare function getAPIKey(): Promise<string | null>;
|
|
4
|
+
export declare function requireAuth(): Promise<string>;
|
|
5
|
+
export declare function authSuccessMessage(): Promise<void>;
|
|
6
|
+
export declare const API_BASE_URL: string;
|
|
7
|
+
export declare const WEB_BASE_URL: string;
|
|
8
|
+
export declare function readConfig(): any;
|
|
9
|
+
export declare function writeConfig(data: object): void;
|
|
10
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAMrC,wBAAsB,UAAU,yBAuB/B;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIxD;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAWnD;AAED,wBAAsB,kBAAkB,kBAwBvC;AAED,eAAO,MAAM,YAAY,QAC6B,CAAC;AAEvD,eAAO,MAAM,YAAY,QAAmD,CAAC;AAI7E,wBAAgB,UAAU,QAGzB;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,QAGvC"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import archiver from "archiver";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { PassThrough } from "stream";
|
|
4
|
+
import keytar from "keytar";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import os from "os";
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
export async function zipProject() {
|
|
9
|
+
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
10
|
+
const stream = new PassThrough();
|
|
11
|
+
archive.pipe(stream);
|
|
12
|
+
archive.glob("**/*", {
|
|
13
|
+
cwd: process.cwd(),
|
|
14
|
+
ignore: [
|
|
15
|
+
"**/node_modules/**",
|
|
16
|
+
"**/.git/**",
|
|
17
|
+
"**/.gitignore",
|
|
18
|
+
"**/.venv/**",
|
|
19
|
+
"**/__pycache__/**",
|
|
20
|
+
"**/.env",
|
|
21
|
+
"**/.env.**",
|
|
22
|
+
"**/dist/**",
|
|
23
|
+
],
|
|
24
|
+
});
|
|
25
|
+
archive.finalize();
|
|
26
|
+
return stream;
|
|
27
|
+
}
|
|
28
|
+
export async function getAPIKey() {
|
|
29
|
+
const apiKey = await keytar.getPassword("ao-cli", "api-key");
|
|
30
|
+
return apiKey || null;
|
|
31
|
+
}
|
|
32
|
+
export async function requireAuth() {
|
|
33
|
+
const apiKey = await getAPIKey();
|
|
34
|
+
if (!apiKey) {
|
|
35
|
+
console.error(chalk.red("✗ You are not logged in. Please run 'ao login' first."));
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
return apiKey;
|
|
39
|
+
}
|
|
40
|
+
export async function authSuccessMessage() {
|
|
41
|
+
console.log("");
|
|
42
|
+
console.log(chalk.white(`
|
|
43
|
+
$@@@@$$ $@$@@@@@@@@@@@@$$$$
|
|
44
|
+
$@@@@@@@@$ $@@@@@@@@@@@@@@@@@@@@@$$
|
|
45
|
+
$@@@@@@@@@@@ $@@@@@@@$$$$$$$$$$@@$$ $$
|
|
46
|
+
$@@@@@$ @@@@@@$ $@@@@@@$ @@@@@@$
|
|
47
|
+
$@@@@@$ $@@@@@$ @@@@@$ $@@@@$
|
|
48
|
+
$@@@@@@$ $@@@@@$ @@@@@$ $@@@@@$
|
|
49
|
+
$@@@@@$ $@@@@@$ $@$$$ @@@@@$
|
|
50
|
+
$@@@@@@ $@@@@$ $@$ $@@@@@$
|
|
51
|
+
@@@@@@$ $@@@@$ $@@@@@
|
|
52
|
+
$@@@@@@$ $$@@@@@@@$@@@@@$$ $@@@@@$
|
|
53
|
+
$@@@@@$ $$$$$ $$@@@@@$@@@@@@@$@$$@@$$@@$$$@@@@@@@$
|
|
54
|
+
$@@@@@$ $$$$ $@@@@@$$@@@@@@@@@@@@@@@@@@@@@@@$
|
|
55
|
+
@@@@@@$ $$@@@@$ $$$@@@@@@@@@@@@@@@@$$
|
|
56
|
+
`));
|
|
57
|
+
console.log("");
|
|
58
|
+
console.log(chalk.green.bold("✔ Logged in successfully!"));
|
|
59
|
+
console.log("");
|
|
60
|
+
}
|
|
61
|
+
export const API_BASE_URL = process.env.AO_API_URL ?? "https://api.aodeploy.com";
|
|
62
|
+
export const WEB_BASE_URL = process.env.AO_WEB_URL ?? "https://aodeploy.com";
|
|
63
|
+
const CONFIG_PATH = path.join(os.homedir(), ".ao", "config.json");
|
|
64
|
+
export function readConfig() {
|
|
65
|
+
if (!fs.existsSync(CONFIG_PATH))
|
|
66
|
+
return {};
|
|
67
|
+
return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
|
|
68
|
+
}
|
|
69
|
+
export function writeConfig(data) {
|
|
70
|
+
fs.mkdirSync(path.dirname(CONFIG_PATH), { recursive: true });
|
|
71
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(data, null, 2));
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ao-ai/cli",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "AO CLI",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"author": "Martin",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"bin": {
|
|
11
|
+
"ao": "dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"archiver": "^4.0.2",
|
|
19
|
+
"chalk": "^5.6.2",
|
|
20
|
+
"commander": "^14.0.3",
|
|
21
|
+
"keytar": "^7.9.0",
|
|
22
|
+
"stream": "^0.0.3",
|
|
23
|
+
"toml": "^3.0.0",
|
|
24
|
+
"zod": "^4.3.6"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/archiver": "^7.0.0",
|
|
28
|
+
"@types/node": "^25.3.0",
|
|
29
|
+
"typescript": "^5.9.3"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import open from "open";
|
|
7
|
+
import toml from "toml";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import http from "http";
|
|
10
|
+
import {
|
|
11
|
+
API_BASE_URL,
|
|
12
|
+
authSuccessMessage,
|
|
13
|
+
getAPIKey,
|
|
14
|
+
readConfig,
|
|
15
|
+
requireAuth,
|
|
16
|
+
WEB_BASE_URL,
|
|
17
|
+
writeConfig,
|
|
18
|
+
zipProject,
|
|
19
|
+
} from "./utils.js";
|
|
20
|
+
import { Readable } from "stream";
|
|
21
|
+
import chalk from "chalk";
|
|
22
|
+
import keytar from "keytar";
|
|
23
|
+
|
|
24
|
+
const program = new Command();
|
|
25
|
+
|
|
26
|
+
program.name("ao").description("AO CLI").version("0.0.1");
|
|
27
|
+
|
|
28
|
+
program
|
|
29
|
+
.command("init")
|
|
30
|
+
.action(() => {
|
|
31
|
+
const currentDir = process.cwd();
|
|
32
|
+
|
|
33
|
+
const filePath = path.join(currentDir, "ao.toml");
|
|
34
|
+
|
|
35
|
+
if (fs.existsSync(filePath)) {
|
|
36
|
+
console.log("AO configuration file already exists.");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const initialContent = `
|
|
41
|
+
name = "my-project"
|
|
42
|
+
|
|
43
|
+
[runtime]
|
|
44
|
+
max_retries = 3
|
|
45
|
+
timeout = 300 # 5 minutes
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
fs.writeFileSync(filePath, initialContent.trim());
|
|
49
|
+
|
|
50
|
+
console.log("Initialized AO configuration file at:", filePath);
|
|
51
|
+
})
|
|
52
|
+
.description("");
|
|
53
|
+
|
|
54
|
+
program.command("login").action(async () => {
|
|
55
|
+
const apiKey = await getAPIKey();
|
|
56
|
+
|
|
57
|
+
if (apiKey) {
|
|
58
|
+
console.log(chalk.green("You are already logged in."));
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const server = http.createServer(async (req, res) => {
|
|
63
|
+
const address = server.address();
|
|
64
|
+
const port = typeof address === "object" && address ? address.port : 0;
|
|
65
|
+
|
|
66
|
+
const url = new URL(req.url!, `http://localhost:${port}`);
|
|
67
|
+
const apiKey = url.searchParams.get("apiKey");
|
|
68
|
+
|
|
69
|
+
if (apiKey) {
|
|
70
|
+
await keytar.setPassword("ao-cli", "api-key", apiKey);
|
|
71
|
+
res.writeHead(302, {
|
|
72
|
+
Location: `${WEB_BASE_URL}/auth/cli/success`,
|
|
73
|
+
});
|
|
74
|
+
res.end();
|
|
75
|
+
|
|
76
|
+
authSuccessMessage();
|
|
77
|
+
server.close();
|
|
78
|
+
process.exit(0);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await new Promise<void>((resolve) => {
|
|
83
|
+
server.listen(0, resolve);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const { port } = server.address() as any;
|
|
87
|
+
|
|
88
|
+
const authUrl = `${WEB_BASE_URL}/auth/cli/start?redirect=http://localhost:${port}`;
|
|
89
|
+
await open(authUrl);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
program.command("logout").action(async () => {
|
|
93
|
+
await keytar.deletePassword("ao-cli", "api-key");
|
|
94
|
+
|
|
95
|
+
console.log(chalk.green("✓ Logged out successfully"));
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
program.command("deploy").action(async () => {
|
|
99
|
+
const apiKey = await requireAuth();
|
|
100
|
+
|
|
101
|
+
const currentDir = process.cwd();
|
|
102
|
+
|
|
103
|
+
const filePath = path.join(currentDir, "ao.toml");
|
|
104
|
+
|
|
105
|
+
const pyProjectFilePath = path.join(currentDir, "pyproject.toml");
|
|
106
|
+
|
|
107
|
+
const requirementsFilePath = path.join(currentDir, "requirements.txt");
|
|
108
|
+
|
|
109
|
+
if (!fs.existsSync(filePath)) {
|
|
110
|
+
console.error(
|
|
111
|
+
"AO configuration file not found. Please run 'ao init' first.",
|
|
112
|
+
);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!fs.existsSync(requirementsFilePath)) {
|
|
117
|
+
console.error("requirements.txt is required");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!fs.existsSync(pyProjectFilePath)) {
|
|
122
|
+
console.error(
|
|
123
|
+
"pyproject.toml file not found. Please make sure you have a pyproject.toml file in the current directory.",
|
|
124
|
+
);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
console.log("Deploying agent...");
|
|
129
|
+
|
|
130
|
+
// Get the configuration from ao.toml
|
|
131
|
+
const configContent = fs.readFileSync(filePath, "utf-8");
|
|
132
|
+
|
|
133
|
+
// Parse the TOML content and extract necessary information for deployment
|
|
134
|
+
let config;
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
config = toml.parse(configContent);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error("Invalid TOML syntax in ao.toml:", error);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const configFileSchema = z.object({
|
|
144
|
+
name: z
|
|
145
|
+
.string()
|
|
146
|
+
.lowercase()
|
|
147
|
+
.min(3, "The 'name' field must be at least 3 characters long."),
|
|
148
|
+
deploy: z.object({
|
|
149
|
+
entrypoint: z.string(),
|
|
150
|
+
}),
|
|
151
|
+
runtime: z.object({
|
|
152
|
+
max_retries: z.number().int().nonnegative().optional().default(3),
|
|
153
|
+
timeout: z.number().int().positive().optional().default(300),
|
|
154
|
+
}),
|
|
155
|
+
schedule: z
|
|
156
|
+
.object({
|
|
157
|
+
cron: z.string(),
|
|
158
|
+
})
|
|
159
|
+
.optional(),
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Check if the AO config file structure is valid
|
|
163
|
+
const validation = configFileSchema.safeParse(config);
|
|
164
|
+
|
|
165
|
+
if (!validation.success) {
|
|
166
|
+
console.error("Invalid ao.toml structure:");
|
|
167
|
+
console.error(validation.error.message);
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Zip the agent code and dependencies
|
|
172
|
+
// 1. Node stream -> Web stream
|
|
173
|
+
console.log("Packing project...");
|
|
174
|
+
|
|
175
|
+
const zipNodeStream = await zipProject(); // Readable (Node)
|
|
176
|
+
const zipWebStream = Readable.toWeb(zipNodeStream);
|
|
177
|
+
|
|
178
|
+
// 2. Web stream -> Blob
|
|
179
|
+
const zipBlob = await new Response(zipWebStream).blob();
|
|
180
|
+
|
|
181
|
+
// Read env variables
|
|
182
|
+
const env = readConfig();
|
|
183
|
+
|
|
184
|
+
const form = new FormData();
|
|
185
|
+
form.append("file", zipBlob, "agent.zip");
|
|
186
|
+
form.append("envs", JSON.stringify(env.envs ?? {}));
|
|
187
|
+
|
|
188
|
+
const response = await fetch(`${API_BASE_URL}/deploy`, {
|
|
189
|
+
method: "POST",
|
|
190
|
+
headers: {
|
|
191
|
+
"X-API-Key": `Bearer ${apiKey}`,
|
|
192
|
+
},
|
|
193
|
+
body: form,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
if (!response.ok) {
|
|
197
|
+
console.error("Deployment failed:", response.statusText);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
console.log("Deployment successful!");
|
|
202
|
+
console.log(
|
|
203
|
+
"You can view your new deployment at:",
|
|
204
|
+
`https://aodeploy.com/dashboard/deployments`,
|
|
205
|
+
);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
program
|
|
209
|
+
.command("run")
|
|
210
|
+
.requiredOption("--input <string>", "Input for the agent")
|
|
211
|
+
.requiredOption("-d, --deployment <string>", "Specific deployment to run")
|
|
212
|
+
.action(async (options) => {
|
|
213
|
+
const apiKey = await requireAuth();
|
|
214
|
+
|
|
215
|
+
console.log("Running agent...");
|
|
216
|
+
const response = await fetch(`${API_BASE_URL}/run`, {
|
|
217
|
+
method: "POST",
|
|
218
|
+
headers: {
|
|
219
|
+
"Content-Type": "application/json",
|
|
220
|
+
"X-API-Key": `Bearer ${apiKey}`,
|
|
221
|
+
},
|
|
222
|
+
body: JSON.stringify({
|
|
223
|
+
input: options.input,
|
|
224
|
+
deploymentId: options.deployment,
|
|
225
|
+
}),
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
if (!response.ok) {
|
|
229
|
+
console.error("Running agent failed:", response.statusText);
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const env = program.command("env").description("Manage environment variables");
|
|
235
|
+
|
|
236
|
+
env.command("set <key> <value>").action((key, value) => {
|
|
237
|
+
const config = readConfig();
|
|
238
|
+
config.envs = { ...config.envs, [key]: value };
|
|
239
|
+
writeConfig(config);
|
|
240
|
+
console.log(`✓ ${key} set`);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
env.command("list").action(() => {
|
|
244
|
+
const config = readConfig();
|
|
245
|
+
console.log(config.envs ?? "No env keys configured");
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
env.command("delete <key>").action((key) => {
|
|
249
|
+
const config = readConfig();
|
|
250
|
+
delete config.envs?.[key];
|
|
251
|
+
writeConfig(config);
|
|
252
|
+
console.log(`✓ ${key} deleted`);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
program.parse();
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import archiver from "archiver";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { PassThrough } from "stream";
|
|
4
|
+
import keytar from "keytar";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import os from "os";
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
|
|
9
|
+
export async function zipProject() {
|
|
10
|
+
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
11
|
+
const stream = new PassThrough();
|
|
12
|
+
|
|
13
|
+
archive.pipe(stream);
|
|
14
|
+
|
|
15
|
+
archive.glob("**/*", {
|
|
16
|
+
cwd: process.cwd(),
|
|
17
|
+
ignore: [
|
|
18
|
+
"**/node_modules/**",
|
|
19
|
+
"**/.git/**",
|
|
20
|
+
"**/.gitignore",
|
|
21
|
+
"**/.venv/**",
|
|
22
|
+
"**/__pycache__/**",
|
|
23
|
+
"**/.env",
|
|
24
|
+
"**/.env.**",
|
|
25
|
+
"**/dist/**",
|
|
26
|
+
],
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
archive.finalize();
|
|
30
|
+
|
|
31
|
+
return stream;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function getAPIKey(): Promise<string | null> {
|
|
35
|
+
const apiKey = await keytar.getPassword("ao-cli", "api-key");
|
|
36
|
+
|
|
37
|
+
return apiKey || null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function requireAuth(): Promise<string> {
|
|
41
|
+
const apiKey = await getAPIKey();
|
|
42
|
+
|
|
43
|
+
if (!apiKey) {
|
|
44
|
+
console.error(
|
|
45
|
+
chalk.red("✗ You are not logged in. Please run 'ao login' first."),
|
|
46
|
+
);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return apiKey;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function authSuccessMessage() {
|
|
54
|
+
console.log("");
|
|
55
|
+
|
|
56
|
+
console.log(
|
|
57
|
+
chalk.white(`
|
|
58
|
+
$@@@@$$ $@$@@@@@@@@@@@@$$$$
|
|
59
|
+
$@@@@@@@@$ $@@@@@@@@@@@@@@@@@@@@@$$
|
|
60
|
+
$@@@@@@@@@@@ $@@@@@@@$$$$$$$$$$@@$$ $$
|
|
61
|
+
$@@@@@$ @@@@@@$ $@@@@@@$ @@@@@@$
|
|
62
|
+
$@@@@@$ $@@@@@$ @@@@@$ $@@@@$
|
|
63
|
+
$@@@@@@$ $@@@@@$ @@@@@$ $@@@@@$
|
|
64
|
+
$@@@@@$ $@@@@@$ $@$$$ @@@@@$
|
|
65
|
+
$@@@@@@ $@@@@$ $@$ $@@@@@$
|
|
66
|
+
@@@@@@$ $@@@@$ $@@@@@
|
|
67
|
+
$@@@@@@$ $$@@@@@@@$@@@@@$$ $@@@@@$
|
|
68
|
+
$@@@@@$ $$$$$ $$@@@@@$@@@@@@@$@$$@@$$@@$$$@@@@@@@$
|
|
69
|
+
$@@@@@$ $$$$ $@@@@@$$@@@@@@@@@@@@@@@@@@@@@@@$
|
|
70
|
+
@@@@@@$ $$@@@@$ $$$@@@@@@@@@@@@@@@@$$
|
|
71
|
+
`),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
console.log("");
|
|
75
|
+
console.log(chalk.green.bold("✔ Logged in successfully!"));
|
|
76
|
+
console.log("");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const API_BASE_URL =
|
|
80
|
+
process.env.AO_API_URL ?? "https://api.aodeploy.com";
|
|
81
|
+
|
|
82
|
+
export const WEB_BASE_URL = process.env.AO_WEB_URL ?? "https://aodeploy.com";
|
|
83
|
+
|
|
84
|
+
const CONFIG_PATH = path.join(os.homedir(), ".ao", "config.json");
|
|
85
|
+
|
|
86
|
+
export function readConfig() {
|
|
87
|
+
if (!fs.existsSync(CONFIG_PATH)) return {};
|
|
88
|
+
return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function writeConfig(data: object) {
|
|
92
|
+
fs.mkdirSync(path.dirname(CONFIG_PATH), { recursive: true });
|
|
93
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(data, null, 2));
|
|
94
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Visit https://aka.ms/tsconfig to read more about this file
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
// File Layout
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
|
|
8
|
+
// Environment Settings
|
|
9
|
+
// See also https://aka.ms/tsconfig/module
|
|
10
|
+
"module": "nodenext",
|
|
11
|
+
"target": "esnext",
|
|
12
|
+
// For nodejs:
|
|
13
|
+
"lib": ["esnext"],
|
|
14
|
+
"types": ["node"],
|
|
15
|
+
|
|
16
|
+
// Other Outputs
|
|
17
|
+
"sourceMap": true,
|
|
18
|
+
"declaration": true,
|
|
19
|
+
"declarationMap": true,
|
|
20
|
+
|
|
21
|
+
// Stricter Typechecking Options
|
|
22
|
+
"noUncheckedIndexedAccess": true,
|
|
23
|
+
"exactOptionalPropertyTypes": true,
|
|
24
|
+
|
|
25
|
+
// Style Options
|
|
26
|
+
// "noImplicitReturns": true,
|
|
27
|
+
// "noImplicitOverride": true,
|
|
28
|
+
// "noUnusedLocals": true,
|
|
29
|
+
// "noUnusedParameters": true,
|
|
30
|
+
// "noFallthroughCasesInSwitch": true,
|
|
31
|
+
// "noPropertyAccessFromIndexSignature": true,
|
|
32
|
+
|
|
33
|
+
// Recommended Options
|
|
34
|
+
"strict": true,
|
|
35
|
+
"jsx": "react-jsx",
|
|
36
|
+
"verbatimModuleSyntax": true,
|
|
37
|
+
"isolatedModules": true,
|
|
38
|
+
"noUncheckedSideEffectImports": true,
|
|
39
|
+
"moduleDetection": "force",
|
|
40
|
+
"skipLibCheck": true,
|
|
41
|
+
}
|
|
42
|
+
}
|