@aigne/cli 1.36.4 → 1.37.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 +25 -0
- package/dist/commands/app.js +6 -91
- package/dist/tracer/terminal.d.ts +2 -0
- package/dist/tracer/terminal.js +58 -1
- package/dist/utils/listr.js +5 -3
- package/dist/utils/run-with-aigne.d.ts +0 -1
- package/dist/utils/run-with-aigne.js +11 -44
- package/dist/utils/yargs.d.ts +20 -0
- package/dist/utils/yargs.js +107 -0
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.37.0](https://github.com/AIGNE-io/aigne-framework/compare/cli-v1.36.4...cli-v1.37.0) (2025-08-18)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **cli:** add support for array inputs in CLI arguments ([#378](https://github.com/AIGNE-io/aigne-framework/issues/378)) ([827ae11](https://github.com/AIGNE-io/aigne-framework/commit/827ae112de8d1a2e997b272b759090b6e5b8d395))
|
|
9
|
+
* **cli:** support hide or collapse task for agents in CLI ([#381](https://github.com/AIGNE-io/aigne-framework/issues/381)) ([05b372d](https://github.com/AIGNE-io/aigne-framework/commit/05b372d431a862f7cdfa2a90bb4b7b2379bf97ab))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* **cli:** only log API requests at info level and above ([#376](https://github.com/AIGNE-io/aigne-framework/issues/376)) ([03fc4d9](https://github.com/AIGNE-io/aigne-framework/commit/03fc4d9aad6e81aeae3b2eb02a62f7acade3bd77))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Dependencies
|
|
18
|
+
|
|
19
|
+
* The following workspace dependencies were updated
|
|
20
|
+
* dependencies
|
|
21
|
+
* @aigne/agent-library bumped to 1.21.22
|
|
22
|
+
* @aigne/agentic-memory bumped to 1.0.22
|
|
23
|
+
* @aigne/aigne-hub bumped to 0.6.4
|
|
24
|
+
* @aigne/core bumped to 1.51.0
|
|
25
|
+
* @aigne/default-memory bumped to 1.1.4
|
|
26
|
+
* @aigne/openai bumped to 0.11.4
|
|
27
|
+
|
|
3
28
|
## [1.36.4](https://github.com/AIGNE-io/aigne-framework/compare/cli-v1.36.3...cli-v1.36.4) (2025-08-16)
|
|
4
29
|
|
|
5
30
|
|
package/dist/commands/app.js
CHANGED
|
@@ -2,18 +2,15 @@ import assert from "node:assert";
|
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
|
-
import {
|
|
6
|
-
import { isatty } from "node:tty";
|
|
5
|
+
import { join } from "node:path";
|
|
7
6
|
import { loadModel } from "@aigne/aigne-hub";
|
|
8
|
-
import {
|
|
9
|
-
import { pick } from "@aigne/core/utils/type-utils.js";
|
|
7
|
+
import { AIGNE } from "@aigne/core";
|
|
10
8
|
import { Listr, PRESET_TIMER } from "@aigne/listr2";
|
|
11
9
|
import { joinURL } from "ufo";
|
|
12
|
-
import { parse } from "yaml";
|
|
13
|
-
import { ZodBoolean, ZodNumber, ZodObject, ZodString, ZodType } from "zod";
|
|
14
10
|
import { downloadAndExtract } from "../utils/download.js";
|
|
15
11
|
import { loadAIGNE } from "../utils/load-aigne.js";
|
|
16
|
-
import { runAgentWithAIGNE
|
|
12
|
+
import { runAgentWithAIGNE } from "../utils/run-with-aigne.js";
|
|
13
|
+
import { parseAgentInput, withAgentInputSchema } from "../utils/yargs.js";
|
|
17
14
|
import { serveMCPServerFromDir } from "./serve-mcp.js";
|
|
18
15
|
const NPM_PACKAGE_CACHE_TIME_MS = 1000 * 60 * 60 * 24; // 1 day
|
|
19
16
|
const builtinApps = [
|
|
@@ -85,50 +82,16 @@ const upgradeCommandModule = ({ name, dir, isLatest, version, }) => ({
|
|
|
85
82
|
},
|
|
86
83
|
});
|
|
87
84
|
const agentCommandModule = ({ dir, agent, }) => {
|
|
88
|
-
const inputSchema = agent.inputSchema instanceof ZodObject ? agent.inputSchema.shape : {};
|
|
89
85
|
return {
|
|
90
86
|
command: agent.name,
|
|
91
87
|
aliases: agent.alias || [],
|
|
92
88
|
describe: agent.description || "",
|
|
93
|
-
builder: (yargs) =>
|
|
94
|
-
for (const [option, config] of Object.entries(inputSchema)) {
|
|
95
|
-
const innerType = innerZodType(config);
|
|
96
|
-
yargs.option(option, {
|
|
97
|
-
// TODO: support more types
|
|
98
|
-
type: innerType instanceof ZodBoolean
|
|
99
|
-
? "boolean"
|
|
100
|
-
: innerType instanceof ZodNumber
|
|
101
|
-
? "number"
|
|
102
|
-
: "string",
|
|
103
|
-
description: config.description,
|
|
104
|
-
});
|
|
105
|
-
if (!(config.isNullable() || config.isOptional())) {
|
|
106
|
-
yargs.demandOption(option);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return yargs
|
|
110
|
-
.option("input", {
|
|
111
|
-
type: "array",
|
|
112
|
-
description: "Input to the agent, use @<file> to read from a file",
|
|
113
|
-
alias: ["i"],
|
|
114
|
-
})
|
|
115
|
-
.option("format", {
|
|
116
|
-
type: "string",
|
|
117
|
-
description: 'Input format, can be "json" or "yaml"',
|
|
118
|
-
choices: ["json", "yaml"],
|
|
119
|
-
});
|
|
120
|
-
},
|
|
89
|
+
builder: async (yargs) => withAgentInputSchema(yargs, agent),
|
|
121
90
|
handler: async (input) => {
|
|
122
91
|
await invokeCLIAgentFromDir({ dir, agent: agent.name, input });
|
|
123
92
|
},
|
|
124
93
|
};
|
|
125
94
|
};
|
|
126
|
-
function innerZodType(type) {
|
|
127
|
-
if ("innerType" in type._def && type._def.innerType instanceof ZodType) {
|
|
128
|
-
return innerZodType(type._def.innerType);
|
|
129
|
-
}
|
|
130
|
-
return type;
|
|
131
|
-
}
|
|
132
95
|
export async function invokeCLIAgentFromDir(options) {
|
|
133
96
|
const aigne = await loadAIGNE({
|
|
134
97
|
path: options.dir,
|
|
@@ -137,61 +100,13 @@ export async function invokeCLIAgentFromDir(options) {
|
|
|
137
100
|
try {
|
|
138
101
|
const agent = aigne.cli.agents[options.agent];
|
|
139
102
|
assert(agent, `Agent ${options.agent} not found in ${options.dir}`);
|
|
140
|
-
const
|
|
141
|
-
const input = Object.fromEntries(await Promise.all(Object.entries(pick(options.input, Object.keys(inputSchema))).map(async ([key, val]) => {
|
|
142
|
-
if (typeof val === "string" && val.startsWith("@")) {
|
|
143
|
-
const schema = inputSchema[key];
|
|
144
|
-
val = await readFileAsInput(val, {
|
|
145
|
-
format: schema instanceof ZodString ? "raw" : undefined,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
return [key, val];
|
|
149
|
-
})));
|
|
150
|
-
const rawInput = options.input.input ||
|
|
151
|
-
(isatty(process.stdin.fd) || !(await stdinHasData())
|
|
152
|
-
? null
|
|
153
|
-
: [await readAllString(process.stdin)].filter(Boolean));
|
|
154
|
-
if (rawInput) {
|
|
155
|
-
for (const raw of rawInput) {
|
|
156
|
-
const parsed = raw.startsWith("@")
|
|
157
|
-
? await readFileAsInput(raw, { format: options.input.format })
|
|
158
|
-
: raw;
|
|
159
|
-
if (typeof parsed !== "string") {
|
|
160
|
-
Object.assign(input, parsed);
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
const inputKey = agent instanceof AIAgent ? agent.inputKey : undefined;
|
|
164
|
-
if (inputKey) {
|
|
165
|
-
Object.assign(input, { [inputKey]: parsed });
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
103
|
+
const input = await parseAgentInput(options.input, agent);
|
|
170
104
|
await runAgentWithAIGNE(aigne, agent, { input });
|
|
171
105
|
}
|
|
172
106
|
finally {
|
|
173
107
|
await aigne.shutdown();
|
|
174
108
|
}
|
|
175
109
|
}
|
|
176
|
-
async function readFileAsInput(value, { format } = {}) {
|
|
177
|
-
if (value.startsWith("@")) {
|
|
178
|
-
const ext = extname(value);
|
|
179
|
-
value = await readFile(value.slice(1), "utf8");
|
|
180
|
-
if (!format) {
|
|
181
|
-
if (ext === ".json")
|
|
182
|
-
format = "json";
|
|
183
|
-
else if (ext === ".yaml" || ext === ".yml")
|
|
184
|
-
format = "yaml";
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
if (format === "json") {
|
|
188
|
-
return JSON.parse(value);
|
|
189
|
-
}
|
|
190
|
-
else if (format === "yaml") {
|
|
191
|
-
return parse(value);
|
|
192
|
-
}
|
|
193
|
-
return value;
|
|
194
|
-
}
|
|
195
110
|
export async function loadApplication({ name, dir, forceUpgrade = false, }) {
|
|
196
111
|
name = `@aigne/${name}`;
|
|
197
112
|
dir ??= join(homedir(), ".aigne", "registry.npmjs.org", name);
|
package/dist/tracer/terminal.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EOL } from "node:os";
|
|
2
2
|
import { inspect } from "node:util";
|
|
3
|
-
import { AIAgent, ChatModel, DEFAULT_OUTPUT_KEY, UserAgent, } from "@aigne/core";
|
|
3
|
+
import { AIAgent, ChatModel, DEFAULT_OUTPUT_KEY, mergeContextUsage, newEmptyContextUsage, UserAgent, } from "@aigne/core";
|
|
4
4
|
import { promiseWithResolvers } from "@aigne/core/utils/promise.js";
|
|
5
5
|
import { flat, omit } from "@aigne/core/utils/type-utils.js";
|
|
6
6
|
import { figures } from "@aigne/listr2";
|
|
@@ -28,13 +28,39 @@ export class TerminalTracer {
|
|
|
28
28
|
formatResult: (result, options) => [this.formatResult(agent, context, result, options)].filter(Boolean),
|
|
29
29
|
}, [], { concurrent: true });
|
|
30
30
|
this.listr = listr;
|
|
31
|
+
const collapsedMap = new Map();
|
|
32
|
+
const hideContextIds = new Set();
|
|
31
33
|
const onStart = async ({ context, agent, ...event }) => {
|
|
32
34
|
if (agent instanceof UserAgent)
|
|
33
35
|
return;
|
|
36
|
+
if (agent.taskRenderMode === "hide") {
|
|
37
|
+
hideContextIds.add(context.id);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
else if (agent.taskRenderMode === "collapse") {
|
|
41
|
+
collapsedMap.set(context.id, {
|
|
42
|
+
ancestor: { contextId: context.id },
|
|
43
|
+
usage: newEmptyContextUsage(),
|
|
44
|
+
models: new Set(),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (context.parentId) {
|
|
48
|
+
if (hideContextIds.has(context.parentId)) {
|
|
49
|
+
hideContextIds.add(context.id);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const collapsed = collapsedMap.get(context.parentId);
|
|
53
|
+
if (collapsed) {
|
|
54
|
+
collapsedMap.set(context.id, collapsed);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
34
58
|
const contextId = context.id;
|
|
35
59
|
const parentContextId = context.parentId;
|
|
36
60
|
const task = {
|
|
37
61
|
...promiseWithResolvers(),
|
|
62
|
+
agent,
|
|
63
|
+
input: event.input,
|
|
38
64
|
listr: promiseWithResolvers(),
|
|
39
65
|
startTime: Date.now(),
|
|
40
66
|
};
|
|
@@ -66,6 +92,34 @@ export class TerminalTracer {
|
|
|
66
92
|
const onSuccess = async ({ context, agent, output, ...event }) => {
|
|
67
93
|
const contextId = context.id;
|
|
68
94
|
const parentContextId = context.parentId;
|
|
95
|
+
const collapsed = collapsedMap.get(contextId);
|
|
96
|
+
if (collapsed) {
|
|
97
|
+
if (agent instanceof ChatModel) {
|
|
98
|
+
const { usage, model } = output;
|
|
99
|
+
if (usage)
|
|
100
|
+
mergeContextUsage(collapsed.usage, usage);
|
|
101
|
+
if (model)
|
|
102
|
+
collapsed.models.add(model);
|
|
103
|
+
}
|
|
104
|
+
const task = this.tasks[collapsed.ancestor.contextId];
|
|
105
|
+
if (task) {
|
|
106
|
+
task.usage = collapsed.usage;
|
|
107
|
+
task.extraTitleMetadata ??= {};
|
|
108
|
+
if (collapsed.models.size)
|
|
109
|
+
task.extraTitleMetadata.model = [...collapsed.models].join(",");
|
|
110
|
+
const { taskWrapper } = await task.listr.promise;
|
|
111
|
+
taskWrapper.title = await this.formatTaskTitle(task.agent, {
|
|
112
|
+
input: task.input,
|
|
113
|
+
task,
|
|
114
|
+
usage: Boolean(task.usage.inputTokens || task.usage.outputTokens || task.usage.aigneHubCredits),
|
|
115
|
+
time: context.id === collapsed.ancestor.contextId,
|
|
116
|
+
});
|
|
117
|
+
if (context.id === collapsed.ancestor.contextId) {
|
|
118
|
+
task?.resolve();
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
69
123
|
const task = this.tasks[contextId];
|
|
70
124
|
if (!task)
|
|
71
125
|
return;
|
|
@@ -173,6 +227,9 @@ export class TerminalTracer {
|
|
|
173
227
|
const items = [
|
|
174
228
|
[chalk.yellow(usage.inputTokens), chalk.grey("input tokens")],
|
|
175
229
|
[chalk.cyan(usage.outputTokens), chalk.grey("output tokens")],
|
|
230
|
+
usage.aigneHubCredits
|
|
231
|
+
? [chalk.blue(usage.aigneHubCredits.toFixed()), chalk.grey("AIGNE Hub credits")]
|
|
232
|
+
: undefined,
|
|
176
233
|
usage.agentCalls ? [chalk.magenta(usage.agentCalls), chalk.grey("agent calls")] : undefined,
|
|
177
234
|
];
|
|
178
235
|
const content = items.filter((i) => !!i).map((i) => i.join(" "));
|
package/dist/utils/listr.js
CHANGED
|
@@ -51,9 +51,11 @@ export class AIGNEListr extends Listr {
|
|
|
51
51
|
try {
|
|
52
52
|
this.ctx = {};
|
|
53
53
|
this.spinner.start();
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
if (logger.enabled(LogLevel.INFO)) {
|
|
55
|
+
const request = this.myOptions.formatRequest();
|
|
56
|
+
if (request)
|
|
57
|
+
console.log(request);
|
|
58
|
+
}
|
|
57
59
|
logger.logMessage = (...args) => this.logs.push(format(...args));
|
|
58
60
|
for (const method of ["debug", "log", "info", "warn", "error"]) {
|
|
59
61
|
console[method] = (...args) => {
|
|
@@ -1,21 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
1
|
+
import { mkdir, stat, writeFile } from "node:fs/promises";
|
|
3
2
|
import { dirname, isAbsolute, join } from "node:path";
|
|
4
3
|
import { isatty } from "node:tty";
|
|
5
|
-
import { promisify } from "node:util";
|
|
6
4
|
import { exists } from "@aigne/agent-library/utils/fs.js";
|
|
7
5
|
import { availableModels, parseModelOption } from "@aigne/aigne-hub";
|
|
8
|
-
import {
|
|
6
|
+
import { DEFAULT_OUTPUT_KEY, UserAgent, } from "@aigne/core";
|
|
9
7
|
import { getLevelFromEnv, LogLevel, logger } from "@aigne/core/utils/logger.js";
|
|
10
|
-
import {
|
|
8
|
+
import { isEmpty, tryOrThrow } from "@aigne/core/utils/type-utils.js";
|
|
11
9
|
import chalk from "chalk";
|
|
12
|
-
import { parse } from "yaml";
|
|
13
10
|
import yargs from "yargs";
|
|
14
11
|
import { hideBin } from "yargs/helpers";
|
|
15
|
-
import { ZodError,
|
|
12
|
+
import { ZodError, z } from "zod";
|
|
16
13
|
import { TerminalTracer } from "../tracer/terminal.js";
|
|
17
14
|
import { loadAIGNE } from "./load-aigne.js";
|
|
18
15
|
import { DEFAULT_CHAT_INPUT_KEY, runChatLoopInTerminal, } from "./run-chat-loop.js";
|
|
16
|
+
import { parseAgentInput, withAgentInputSchema } from "./yargs.js";
|
|
19
17
|
export const createRunAIGNECommand = (yargs) => yargs
|
|
20
18
|
.option("chat", {
|
|
21
19
|
describe: "Run chat loop in terminal",
|
|
@@ -80,7 +78,7 @@ export const createRunAIGNECommand = (yargs) => yargs
|
|
|
80
78
|
.option("log-level", {
|
|
81
79
|
describe: `Log level for detailed debugging information. Values: ${Object.values(LogLevel).join(", ")}`,
|
|
82
80
|
type: "string",
|
|
83
|
-
default: getLevelFromEnv(logger.options.ns) || LogLevel.
|
|
81
|
+
default: getLevelFromEnv(logger.options.ns) || LogLevel.SILENT,
|
|
84
82
|
coerce: customZodError("--log-level", (s) => z.nativeEnum(LogLevel).parse(s)),
|
|
85
83
|
})
|
|
86
84
|
.option("aigne-hub-url", {
|
|
@@ -88,38 +86,11 @@ export const createRunAIGNECommand = (yargs) => yargs
|
|
|
88
86
|
type: "string",
|
|
89
87
|
});
|
|
90
88
|
export async function parseAgentInputByCommander(agent, options = {}) {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (typeof value === "string" && value.startsWith("@")) {
|
|
97
|
-
value = await readFile(value.slice(1), "utf8");
|
|
98
|
-
}
|
|
99
|
-
return [key, value];
|
|
100
|
-
})));
|
|
101
|
-
const rawInput = options.input ||
|
|
102
|
-
(isatty(process.stdin.fd) || !(await stdinHasData())
|
|
103
|
-
? null
|
|
104
|
-
: [await readAllString(process.stdin)].filter(Boolean));
|
|
105
|
-
if (rawInput?.length) {
|
|
106
|
-
for (let raw of rawInput) {
|
|
107
|
-
if (raw.startsWith("@")) {
|
|
108
|
-
raw = await readFile(raw.slice(1), "utf8");
|
|
109
|
-
}
|
|
110
|
-
if (options.format === "json") {
|
|
111
|
-
Object.assign(input, JSON.parse(raw));
|
|
112
|
-
}
|
|
113
|
-
else if (options.format === "yaml") {
|
|
114
|
-
Object.assign(input, parse(raw));
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
Object.assign(input, typeof options.inputKey === "string"
|
|
118
|
-
? { [options.inputKey]: raw }
|
|
119
|
-
: { [DEFAULT_CHAT_INPUT_KEY]: raw });
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
89
|
+
const args = await withAgentInputSchema(yargs(), agent)
|
|
90
|
+
.showHelpOnFail(false)
|
|
91
|
+
.fail(() => { })
|
|
92
|
+
.parseAsync(options.argv ?? process.argv);
|
|
93
|
+
const input = await parseAgentInput({ ...args, input: options.input || args.input }, agent);
|
|
123
94
|
if (isEmpty(input)) {
|
|
124
95
|
const defaultInput = options.defaultInput || process.env.INITIAL_CALL;
|
|
125
96
|
Object.assign(input, typeof defaultInput === "string"
|
|
@@ -214,7 +185,3 @@ export async function runAgentWithAIGNE(aigne, agent, { outputKey, chatLoopOptio
|
|
|
214
185
|
}
|
|
215
186
|
return { result };
|
|
216
187
|
}
|
|
217
|
-
export async function stdinHasData() {
|
|
218
|
-
const stats = await promisify(fstat)(0);
|
|
219
|
-
return stats.isFIFO() || stats.isFile();
|
|
220
|
-
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type Agent, type Message } from "@aigne/core";
|
|
2
|
+
import type { Argv } from "yargs";
|
|
3
|
+
import { ZodType } from "zod";
|
|
4
|
+
export declare function inferZodType(type: ZodType, opts?: {
|
|
5
|
+
array?: boolean;
|
|
6
|
+
optional?: boolean;
|
|
7
|
+
}): {
|
|
8
|
+
type: "string" | "number" | "boolean";
|
|
9
|
+
array?: boolean;
|
|
10
|
+
optional?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare function withAgentInputSchema(yargs: Argv, agent: Agent): Argv<{
|
|
13
|
+
input?: string[];
|
|
14
|
+
format?: "json" | "yaml";
|
|
15
|
+
}>;
|
|
16
|
+
export declare function parseAgentInput(i: Message & {
|
|
17
|
+
input?: string[];
|
|
18
|
+
format?: "json" | "yaml";
|
|
19
|
+
}, agent: Agent): Promise<any>;
|
|
20
|
+
export declare function stdinHasData(): Promise<boolean>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { fstat } from "node:fs";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { extname } from "node:path";
|
|
4
|
+
import { isatty } from "node:tty";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { AIAgent, readAllString } from "@aigne/core";
|
|
7
|
+
import { pick } from "@aigne/core/utils/type-utils.js";
|
|
8
|
+
import { parse } from "yaml";
|
|
9
|
+
import { ZodAny, ZodArray, ZodBoolean, ZodNumber, ZodObject, ZodString, ZodType, ZodUnknown, } from "zod";
|
|
10
|
+
export function inferZodType(type, opts = {}) {
|
|
11
|
+
if (type instanceof ZodUnknown || type instanceof ZodAny) {
|
|
12
|
+
return { type: "string", optional: true };
|
|
13
|
+
}
|
|
14
|
+
opts.optional ??= type.isNullable() || type.isOptional();
|
|
15
|
+
if ("innerType" in type._def && type._def.innerType instanceof ZodType) {
|
|
16
|
+
return inferZodType(type._def.innerType, opts);
|
|
17
|
+
}
|
|
18
|
+
if (type instanceof ZodArray) {
|
|
19
|
+
return inferZodType(type.element, { ...opts, array: true });
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
...opts,
|
|
23
|
+
array: opts.array || undefined,
|
|
24
|
+
optional: opts.optional || undefined,
|
|
25
|
+
type: type instanceof ZodBoolean ? "boolean" : type instanceof ZodNumber ? "number" : "string",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function withAgentInputSchema(yargs, agent) {
|
|
29
|
+
const inputSchema = agent.inputSchema instanceof ZodObject ? agent.inputSchema.shape : {};
|
|
30
|
+
for (const [option, config] of Object.entries(inputSchema)) {
|
|
31
|
+
const type = inferZodType(config);
|
|
32
|
+
yargs.option(option, {
|
|
33
|
+
type: type.type,
|
|
34
|
+
description: config.description,
|
|
35
|
+
array: type.array,
|
|
36
|
+
});
|
|
37
|
+
if (!type.optional) {
|
|
38
|
+
yargs.demandOption(option);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return yargs
|
|
42
|
+
.option("input", {
|
|
43
|
+
type: "string",
|
|
44
|
+
array: true,
|
|
45
|
+
description: "Input to the agent, use @<file> to read from a file",
|
|
46
|
+
alias: ["i"],
|
|
47
|
+
})
|
|
48
|
+
.option("format", {
|
|
49
|
+
type: "string",
|
|
50
|
+
description: 'Input format, can be "json" or "yaml"',
|
|
51
|
+
choices: ["json", "yaml"],
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
export async function parseAgentInput(i, agent) {
|
|
55
|
+
const inputSchema = agent.inputSchema instanceof ZodObject ? agent.inputSchema.shape : {};
|
|
56
|
+
const input = Object.fromEntries(await Promise.all(Object.entries(pick(i, Object.keys(inputSchema))).map(async ([key, val]) => {
|
|
57
|
+
if (typeof val === "string" && val.startsWith("@")) {
|
|
58
|
+
const schema = inputSchema[key];
|
|
59
|
+
val = await readFileAsInput(val, {
|
|
60
|
+
format: schema instanceof ZodString ? "raw" : undefined,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return [key, val];
|
|
64
|
+
})));
|
|
65
|
+
const rawInput = i.input ||
|
|
66
|
+
(isatty(process.stdin.fd) || !(await stdinHasData())
|
|
67
|
+
? null
|
|
68
|
+
: [await readAllString(process.stdin)].filter(Boolean));
|
|
69
|
+
if (rawInput) {
|
|
70
|
+
for (const raw of rawInput) {
|
|
71
|
+
const parsed = raw.startsWith("@") ? await readFileAsInput(raw, { format: i.format }) : raw;
|
|
72
|
+
if (typeof parsed !== "string") {
|
|
73
|
+
Object.assign(input, parsed);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const inputKey = agent instanceof AIAgent ? agent.inputKey : undefined;
|
|
77
|
+
if (inputKey) {
|
|
78
|
+
Object.assign(input, { [inputKey]: parsed });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return input;
|
|
84
|
+
}
|
|
85
|
+
async function readFileAsInput(value, { format } = {}) {
|
|
86
|
+
if (value.startsWith("@")) {
|
|
87
|
+
const ext = extname(value);
|
|
88
|
+
value = await readFile(value.slice(1), "utf8");
|
|
89
|
+
if (!format) {
|
|
90
|
+
if (ext === ".json")
|
|
91
|
+
format = "json";
|
|
92
|
+
else if (ext === ".yaml" || ext === ".yml")
|
|
93
|
+
format = "yaml";
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (format === "json") {
|
|
97
|
+
return JSON.parse(value);
|
|
98
|
+
}
|
|
99
|
+
else if (format === "yaml") {
|
|
100
|
+
return parse(value);
|
|
101
|
+
}
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
export async function stdinHasData() {
|
|
105
|
+
const stats = await promisify(fstat)(0);
|
|
106
|
+
return stats.isFIFO() || stats.isFile();
|
|
107
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aigne/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.37.0",
|
|
4
4
|
"description": "Your command center for agent development",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -72,13 +72,13 @@
|
|
|
72
72
|
"yaml": "^2.8.0",
|
|
73
73
|
"yargs": "^18.0.0",
|
|
74
74
|
"zod": "^3.25.67",
|
|
75
|
-
"@aigne/agent-library": "^1.21.
|
|
76
|
-
"@aigne/aigne-hub": "^0.6.
|
|
77
|
-
"@aigne/agentic-memory": "^1.0.
|
|
78
|
-
"@aigne/core": "^1.
|
|
79
|
-
"@aigne/default-memory": "^1.1.
|
|
80
|
-
"@aigne/
|
|
81
|
-
"@aigne/
|
|
75
|
+
"@aigne/agent-library": "^1.21.22",
|
|
76
|
+
"@aigne/aigne-hub": "^0.6.4",
|
|
77
|
+
"@aigne/agentic-memory": "^1.0.22",
|
|
78
|
+
"@aigne/core": "^1.51.0",
|
|
79
|
+
"@aigne/default-memory": "^1.1.4",
|
|
80
|
+
"@aigne/observability-api": "^0.9.0",
|
|
81
|
+
"@aigne/openai": "^0.11.4"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
84
|
"@types/archiver": "^6.0.3",
|