@aigne/cli 1.39.1 → 1.40.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/CHANGELOG.md +7 -0
- package/dist/commands/aigne.js +2 -0
- package/dist/commands/deploy.d.ts +11 -0
- package/dist/commands/deploy.js +254 -0
- package/package.json +2 -2
- package/templates/blocklet/blocklet.md +25 -0
- package/templates/blocklet/blocklet.yml +57 -0
- package/templates/blocklet/logo.png +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.40.0](https://github.com/AIGNE-io/aigne-framework/compare/cli-v1.39.1...cli-v1.40.0) (2025-08-22)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **cli:** support aigne deploy command ([#399](https://github.com/AIGNE-io/aigne-framework/issues/399)) ([b69cba9](https://github.com/AIGNE-io/aigne-framework/commit/b69cba901d95882f847032f41d963e2fa6893ab6))
|
|
9
|
+
|
|
3
10
|
## [1.39.1](https://github.com/AIGNE-io/aigne-framework/compare/cli-v1.39.0...cli-v1.39.1) (2025-08-21)
|
|
4
11
|
|
|
5
12
|
|
package/dist/commands/aigne.js
CHANGED
|
@@ -3,6 +3,7 @@ import { AIGNE_CLI_VERSION } from "../constants.js";
|
|
|
3
3
|
import { asciiLogo } from "../utils/ascii-logo.js";
|
|
4
4
|
import { createAppCommands } from "./app.js";
|
|
5
5
|
import { createCreateCommand } from "./create.js";
|
|
6
|
+
import { createDeployCommands } from "./deploy.js";
|
|
6
7
|
import { createHubCommand } from "./hub.js";
|
|
7
8
|
import { createObservabilityCommand } from "./observe.js";
|
|
8
9
|
import { createRunCommand } from "./run.js";
|
|
@@ -21,6 +22,7 @@ export function createAIGNECommand(options) {
|
|
|
21
22
|
.command(createObservabilityCommand())
|
|
22
23
|
.command(createAppCommands())
|
|
23
24
|
.command(createHubCommand())
|
|
25
|
+
.command(createDeployCommands())
|
|
24
26
|
.demandCommand()
|
|
25
27
|
.alias("help", "h")
|
|
26
28
|
.alias("version", "v")
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CommandModule } from "yargs";
|
|
2
|
+
interface DeployOptions {
|
|
3
|
+
path?: string;
|
|
4
|
+
endpoint?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function fileExists(p: string): Promise<boolean>;
|
|
7
|
+
export declare const DEPLOYED_FILE: string;
|
|
8
|
+
export declare function run(cmd: string, args?: string[], opts?: any): Promise<string>;
|
|
9
|
+
export declare function createDeployCommands(): CommandModule<unknown, DeployOptions>;
|
|
10
|
+
export declare const deploy: (path: string, endpoint: string) => Promise<void>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { access, copyFile, mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { basename, isAbsolute, join, resolve } from "node:path";
|
|
6
|
+
import { Listr } from "@aigne/listr2";
|
|
7
|
+
import { input as inputInquirer, select as selectInquirer } from "@inquirer/prompts";
|
|
8
|
+
import { ListrInquirerPromptAdapter } from "@listr2/prompt-adapter-inquirer";
|
|
9
|
+
import { parse, stringify } from "yaml";
|
|
10
|
+
import { isTest } from "../utils/aigne-hub/constants.js";
|
|
11
|
+
export async function fileExists(p) {
|
|
12
|
+
try {
|
|
13
|
+
await access(p, constants.F_OK);
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export const DEPLOYED_FILE = isTest ? "deployed.test.yaml" : "deployed.yaml";
|
|
21
|
+
export async function run(cmd, args = [], opts = {}) {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const { catchOutput = true, ...rest } = opts;
|
|
24
|
+
const spawnOpts = { shell: true, ...rest, stdio: catchOutput ? "pipe" : "inherit" };
|
|
25
|
+
const child = spawn(cmd, args, spawnOpts);
|
|
26
|
+
if (catchOutput) {
|
|
27
|
+
let stdout = "";
|
|
28
|
+
let stderr = "";
|
|
29
|
+
child.stdout?.on("data", (data) => {
|
|
30
|
+
stdout += data.toString();
|
|
31
|
+
});
|
|
32
|
+
child.stderr?.on("data", (data) => {
|
|
33
|
+
stderr += data.toString();
|
|
34
|
+
});
|
|
35
|
+
child.on("close", (code) => {
|
|
36
|
+
if (code === 0) {
|
|
37
|
+
resolve(stdout.trim());
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
reject(new Error(stderr.trim() || `Process failed with code ${code}`));
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
child.on("close", (code) => {
|
|
46
|
+
if (code === 0) {
|
|
47
|
+
resolve("");
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
reject(new Error(`${cmd} ${args.join(" ")} failed with code ${code}`));
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export function createDeployCommands() {
|
|
57
|
+
return {
|
|
58
|
+
command: "deploy",
|
|
59
|
+
describe: "Deploy an aigne application",
|
|
60
|
+
builder: (yargs) => {
|
|
61
|
+
return yargs
|
|
62
|
+
.option("path", {
|
|
63
|
+
type: "string",
|
|
64
|
+
})
|
|
65
|
+
.option("endpoint", {
|
|
66
|
+
type: "string",
|
|
67
|
+
describe: "Deploy an aigne application to a specified endpoint.",
|
|
68
|
+
});
|
|
69
|
+
},
|
|
70
|
+
handler: async (argv) => {
|
|
71
|
+
const path = argv.path;
|
|
72
|
+
const endpoint = argv.endpoint;
|
|
73
|
+
if (!path) {
|
|
74
|
+
console.error("path is required");
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
if (!endpoint) {
|
|
78
|
+
console.error("endpoint is required");
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
const absolutePath = isAbsolute(path) ? path : resolve(process.cwd(), path);
|
|
82
|
+
await deploy(absolutePath, endpoint);
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
async function copyDir(src, dest) {
|
|
87
|
+
await mkdir(dest, { recursive: true });
|
|
88
|
+
const entries = await readdir(src, { withFileTypes: true });
|
|
89
|
+
const skip = [".deploy", "node_modules"];
|
|
90
|
+
for (const entry of entries) {
|
|
91
|
+
const srcPath = join(src, entry.name);
|
|
92
|
+
const destPath = join(dest, entry.name);
|
|
93
|
+
if (skip.includes(entry.name))
|
|
94
|
+
continue;
|
|
95
|
+
if (entry.isDirectory()) {
|
|
96
|
+
await copyDir(srcPath, destPath);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
await copyFile(srcPath, destPath);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export const deploy = async (path, endpoint) => {
|
|
104
|
+
const aigneHomeDir = join(homedir(), ".aigne");
|
|
105
|
+
if (!(await fileExists(aigneHomeDir))) {
|
|
106
|
+
await mkdir(aigneHomeDir, { recursive: true });
|
|
107
|
+
}
|
|
108
|
+
const deployRoot = join(path, ".deploy");
|
|
109
|
+
const agentDest = join(deployRoot, "agent");
|
|
110
|
+
const tasks = new Listr([
|
|
111
|
+
{
|
|
112
|
+
title: "Prepare deploy environment",
|
|
113
|
+
task: async (ctx, task) => {
|
|
114
|
+
ctx.logs = [];
|
|
115
|
+
task.output = "Preparing deploy environment...";
|
|
116
|
+
const entryFile = join(path, "aigne.yaml");
|
|
117
|
+
if (!(await fileExists(entryFile))) {
|
|
118
|
+
throw new Error(`Entry file not found: ${entryFile}`);
|
|
119
|
+
}
|
|
120
|
+
await rm(deployRoot, { recursive: true, force: true });
|
|
121
|
+
task.output = "Copying template files...";
|
|
122
|
+
const templatePath = join(import.meta.dirname, "../../templates/blocklet");
|
|
123
|
+
await copyDir(templatePath, deployRoot);
|
|
124
|
+
await copyDir(path, agentDest);
|
|
125
|
+
const specialFiles = [
|
|
126
|
+
"package.json",
|
|
127
|
+
"package-lock.json",
|
|
128
|
+
"README.md",
|
|
129
|
+
"CHANGELOG.md",
|
|
130
|
+
"biome.json",
|
|
131
|
+
];
|
|
132
|
+
for (const file of specialFiles) {
|
|
133
|
+
const srcFile = join(path, file);
|
|
134
|
+
if (await fileExists(srcFile)) {
|
|
135
|
+
await copyFile(srcFile, join(deployRoot, file));
|
|
136
|
+
await rm(join(deployRoot, file), { force: true });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (await fileExists(join(deployRoot, "package.json"))) {
|
|
140
|
+
task.output = "Running npm install...";
|
|
141
|
+
await run("npm", ["install"], { cwd: deployRoot });
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
title: "Check Blocklet CLI",
|
|
147
|
+
task: async (_, task) => {
|
|
148
|
+
try {
|
|
149
|
+
task.output = "Checking Blocklet CLI Version...";
|
|
150
|
+
await run("blocklet", ["--version"], { cwd: deployRoot });
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
task.output = "Blocklet CLI not installed, asking to install...";
|
|
154
|
+
const { install } = await task
|
|
155
|
+
.prompt(ListrInquirerPromptAdapter)
|
|
156
|
+
.run(selectInquirer, {
|
|
157
|
+
type: "list",
|
|
158
|
+
name: "install",
|
|
159
|
+
message: "Install Blocklet CLI?",
|
|
160
|
+
choices: ["yes", "no"],
|
|
161
|
+
default: "yes",
|
|
162
|
+
});
|
|
163
|
+
if (install === "yes") {
|
|
164
|
+
task.output = "Installing Blocklet CLI...";
|
|
165
|
+
await run("npm", ["install", "-g", "@blocklet/cli"], { cwd: deployRoot });
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
throw new Error("Blocklet CLI not found, please install manually: npm install -g @blocklet/cli");
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
title: "Configure Blocklet",
|
|
175
|
+
task: async (ctx, task) => {
|
|
176
|
+
task.output = "Configuring Blocklet...";
|
|
177
|
+
if (await fileExists(join(deployRoot, "blocklet.yml"))) {
|
|
178
|
+
const entryFile = join(path, "aigne.yaml");
|
|
179
|
+
const aigneFile = await readFile(entryFile, "utf-8");
|
|
180
|
+
const aigneData = parse(aigneFile);
|
|
181
|
+
const agentName = aigneData.name;
|
|
182
|
+
const deployed = parse(await readFile(join(aigneHomeDir, DEPLOYED_FILE), "utf-8").catch(() => stringify({})));
|
|
183
|
+
let blockletName = deployed[path]?.name;
|
|
184
|
+
if (!blockletName) {
|
|
185
|
+
task.output = "Asking for Blocklet name...";
|
|
186
|
+
blockletName = await task
|
|
187
|
+
.prompt(ListrInquirerPromptAdapter)
|
|
188
|
+
.run(inputInquirer, {
|
|
189
|
+
type: "input",
|
|
190
|
+
name: "blockletName",
|
|
191
|
+
message: "Please input agent blocklet name:",
|
|
192
|
+
default: deployed[path]?.name || agentName || basename(path),
|
|
193
|
+
validate: (input) => {
|
|
194
|
+
if (input.trim() === "")
|
|
195
|
+
return "Blocklet name cannot be empty.";
|
|
196
|
+
return true;
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
let did = deployed[path]?.did;
|
|
201
|
+
if (!did) {
|
|
202
|
+
task.output = "Creating DID...";
|
|
203
|
+
const info = await run("blocklet", ["create", "--did-only"], {
|
|
204
|
+
cwd: deployRoot,
|
|
205
|
+
catchOutput: true,
|
|
206
|
+
});
|
|
207
|
+
const match = info.match(/Created Blocklet DID:\s+(\S+)/);
|
|
208
|
+
if (match?.[1]) {
|
|
209
|
+
did = match[1];
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
throw new Error(`DID not found. Output content: ${info}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
task.output = `Blocklet name: ${blockletName}, DID: ${did}`;
|
|
216
|
+
ctx.logs.push(task.output);
|
|
217
|
+
const yml = await readFile(join(deployRoot, "blocklet.yml"), "utf-8");
|
|
218
|
+
const data = parse(yml);
|
|
219
|
+
data.name = blockletName;
|
|
220
|
+
data.title = blockletName;
|
|
221
|
+
data.did = did;
|
|
222
|
+
await writeFile(join(deployRoot, "blocklet.yml"), stringify(data));
|
|
223
|
+
await writeFile(join(aigneHomeDir, DEPLOYED_FILE), stringify({ ...deployed, [path]: { name: blockletName, did } }));
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
title: "Bundle Blocklet",
|
|
229
|
+
task: async (_, task) => {
|
|
230
|
+
task.output = "Running blocklet bundle...";
|
|
231
|
+
await run("blocklet", ["bundle", "--create-release"], { cwd: deployRoot });
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
], {
|
|
235
|
+
concurrent: false,
|
|
236
|
+
exitOnError: true,
|
|
237
|
+
rendererOptions: {
|
|
238
|
+
collapseSubtasks: false,
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
try {
|
|
242
|
+
await tasks.run();
|
|
243
|
+
await run("blocklet", ["deploy", "--endpoint", endpoint, ".blocklet/bundle"], {
|
|
244
|
+
cwd: deployRoot,
|
|
245
|
+
catchOutput: false,
|
|
246
|
+
});
|
|
247
|
+
await rm(deployRoot, { recursive: true, force: true });
|
|
248
|
+
console.log(`✅ Deploy completed: ${path} -> ${endpoint}`);
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
console.error(`❌ Deploy failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aigne/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.40.0",
|
|
4
4
|
"description": "Your command center for agent development",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -76,10 +76,10 @@
|
|
|
76
76
|
"yargs": "^18.0.0",
|
|
77
77
|
"zod": "^3.25.67",
|
|
78
78
|
"@aigne/agent-library": "^1.21.26",
|
|
79
|
+
"@aigne/aigne-hub": "^0.6.8",
|
|
79
80
|
"@aigne/agentic-memory": "^1.0.26",
|
|
80
81
|
"@aigne/core": "^1.55.0",
|
|
81
82
|
"@aigne/default-memory": "^1.1.8",
|
|
82
|
-
"@aigne/aigne-hub": "^0.6.8",
|
|
83
83
|
"@aigne/observability-api": "^0.10.0",
|
|
84
84
|
"@aigne/openai": "^0.12.2"
|
|
85
85
|
},
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# AIGNE Runtime Blocklet
|
|
2
|
+
|
|
3
|
+
This project is packaged as a Blocklet agent application.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
### Install AIGNE CLI
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g aigne
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Start the Project
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
aigne run agent
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Testing
|
|
20
|
+
|
|
21
|
+
Run the following command to execute test cases:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
aigne test
|
|
25
|
+
```
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: agent-blocklet
|
|
2
|
+
title: Agent Blocklet
|
|
3
|
+
version: 0.0.1
|
|
4
|
+
description: AIGNE Runtime is a specialized runtime engine designed for executing AI agents, serving as the core runtime environment for the Blocklet system
|
|
5
|
+
files:
|
|
6
|
+
- agent/
|
|
7
|
+
- logo.png
|
|
8
|
+
- blocklet.yml
|
|
9
|
+
- blocklet.md
|
|
10
|
+
keywords:
|
|
11
|
+
- aigne
|
|
12
|
+
- agent
|
|
13
|
+
- blocklet
|
|
14
|
+
author:
|
|
15
|
+
name: wangshijun
|
|
16
|
+
email: wangshijun2010@gmail.com
|
|
17
|
+
url: http://github.com/wangshijun
|
|
18
|
+
license: MIT
|
|
19
|
+
main: agent
|
|
20
|
+
logo: logo.png
|
|
21
|
+
community: ''
|
|
22
|
+
documentation: ''
|
|
23
|
+
homepage: ''
|
|
24
|
+
environments: []
|
|
25
|
+
specVersion: 1.2.8
|
|
26
|
+
interfaces:
|
|
27
|
+
- type: web
|
|
28
|
+
name: publicUrl
|
|
29
|
+
path: /
|
|
30
|
+
prefix: '*'
|
|
31
|
+
port: BLOCKLET_PORT
|
|
32
|
+
protocol: tcp
|
|
33
|
+
did: ""
|
|
34
|
+
engine:
|
|
35
|
+
interpreter: blocklet
|
|
36
|
+
source:
|
|
37
|
+
store: https://store.blocklet.dev
|
|
38
|
+
name: z2qa6yt75HHQL3cS4ao7j2aqVodExoBAN7xeS
|
|
39
|
+
version: latest
|
|
40
|
+
args: []
|
|
41
|
+
timeout:
|
|
42
|
+
start: 10
|
|
43
|
+
payment:
|
|
44
|
+
price: []
|
|
45
|
+
share: []
|
|
46
|
+
requirements:
|
|
47
|
+
abtnode: '>=1.1.0'
|
|
48
|
+
os: '*'
|
|
49
|
+
cpu: '*'
|
|
50
|
+
scripts:
|
|
51
|
+
dev: blocklet dev
|
|
52
|
+
capabilities:
|
|
53
|
+
clusterMode: false
|
|
54
|
+
component: true
|
|
55
|
+
navigation: true
|
|
56
|
+
components: []
|
|
57
|
+
|
|
Binary file
|