@aigne/cli 1.74.0-beta → 1.74.0-beta.2

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.
Files changed (128) hide show
  1. package/README.md +153 -14
  2. package/dist/commands/aigne.cjs +3 -4
  3. package/dist/commands/aigne.mjs +3 -4
  4. package/dist/commands/aigne.mjs.map +1 -1
  5. package/dist/commands/app/agent.cjs +2 -27
  6. package/dist/commands/app/agent.mjs +2 -26
  7. package/dist/commands/app/agent.mjs.map +1 -1
  8. package/dist/commands/create.cjs +1 -1
  9. package/dist/commands/create.mjs +1 -1
  10. package/dist/commands/eval.cjs +42 -3
  11. package/dist/commands/eval.mjs +43 -4
  12. package/dist/commands/eval.mjs.map +1 -1
  13. package/dist/commands/explain.cjs +340 -0
  14. package/dist/commands/explain.mjs +340 -0
  15. package/dist/commands/explain.mjs.map +1 -0
  16. package/dist/commands/hub.cjs +111 -14
  17. package/dist/commands/hub.mjs +111 -14
  18. package/dist/commands/hub.mjs.map +1 -1
  19. package/dist/commands/observe.cjs +44 -1
  20. package/dist/commands/observe.mjs +44 -1
  21. package/dist/commands/observe.mjs.map +1 -1
  22. package/dist/commands/run-skill.cjs +29 -13
  23. package/dist/commands/run-skill.mjs +29 -13
  24. package/dist/commands/run-skill.mjs.map +1 -1
  25. package/dist/commands/run.cjs +5 -4
  26. package/dist/commands/run.mjs +5 -4
  27. package/dist/commands/run.mjs.map +1 -1
  28. package/dist/commands/serve-mcp.cjs +49 -4
  29. package/dist/commands/serve-mcp.mjs +49 -3
  30. package/dist/commands/serve-mcp.mjs.map +1 -1
  31. package/dist/commands/shell.cjs +106 -0
  32. package/dist/commands/shell.mjs +105 -0
  33. package/dist/commands/shell.mjs.map +1 -0
  34. package/dist/shell/repl.cjs +544 -0
  35. package/dist/shell/repl.mjs +543 -0
  36. package/dist/shell/repl.mjs.map +1 -0
  37. package/dist/shell/tools/ask-user.cjs +191 -0
  38. package/dist/shell/tools/ask-user.mjs +187 -0
  39. package/dist/shell/tools/ask-user.mjs.map +1 -0
  40. package/dist/shell/tools/index.cjs +2 -0
  41. package/dist/shell/tools/index.mjs +4 -0
  42. package/dist/shell/tools/render.cjs +189 -0
  43. package/dist/shell/tools/render.mjs +186 -0
  44. package/dist/shell/tools/render.mjs.map +1 -0
  45. package/dist/tracer/terminal.cjs +120 -133
  46. package/dist/tracer/terminal.mjs +121 -134
  47. package/dist/tracer/terminal.mjs.map +1 -1
  48. package/dist/ui/utils/terminal-select.cjs +73 -0
  49. package/dist/ui/utils/terminal-select.mjs +72 -0
  50. package/dist/ui/utils/terminal-select.mjs.map +1 -0
  51. package/dist/utils/agent-v1.cjs +2 -2
  52. package/dist/utils/agent-v1.mjs +2 -2
  53. package/dist/utils/aigne-hub/credential.cjs +3 -3
  54. package/dist/utils/aigne-hub/credential.mjs +3 -3
  55. package/dist/utils/aigne-hub/model.cjs +2 -2
  56. package/dist/utils/aigne-hub/model.mjs +1 -1
  57. package/dist/utils/ascii-logo.cjs +12 -13
  58. package/dist/utils/ascii-logo.d.cts.map +1 -1
  59. package/dist/utils/ascii-logo.d.mts.map +1 -1
  60. package/dist/utils/ascii-logo.mjs +12 -13
  61. package/dist/utils/ascii-logo.mjs.map +1 -1
  62. package/dist/utils/evaluation/evaluator.cjs +1 -1
  63. package/dist/utils/evaluation/evaluator.mjs +1 -1
  64. package/dist/utils/evaluation/reporter.cjs +78 -1
  65. package/dist/utils/evaluation/reporter.mjs +76 -1
  66. package/dist/utils/evaluation/reporter.mjs.map +1 -1
  67. package/dist/utils/exit-codes.cjs +73 -0
  68. package/dist/utils/exit-codes.d.cts +52 -0
  69. package/dist/utils/exit-codes.d.cts.map +1 -0
  70. package/dist/utils/exit-codes.d.mts +52 -0
  71. package/dist/utils/exit-codes.d.mts.map +1 -0
  72. package/dist/utils/exit-codes.mjs +71 -0
  73. package/dist/utils/exit-codes.mjs.map +1 -0
  74. package/dist/utils/output.cjs +61 -0
  75. package/dist/utils/output.d.cts +43 -0
  76. package/dist/utils/output.d.cts.map +1 -0
  77. package/dist/utils/output.d.mts +43 -0
  78. package/dist/utils/output.d.mts.map +1 -0
  79. package/dist/utils/output.mjs +56 -0
  80. package/dist/utils/output.mjs.map +1 -0
  81. package/dist/utils/run-chat-loop.cjs +1 -1
  82. package/dist/utils/run-chat-loop.mjs +1 -1
  83. package/dist/utils/run-with-aigne.cjs +10 -3
  84. package/dist/utils/run-with-aigne.d.cts.map +1 -1
  85. package/dist/utils/run-with-aigne.d.mts.map +1 -1
  86. package/dist/utils/run-with-aigne.mjs +10 -3
  87. package/dist/utils/run-with-aigne.mjs.map +1 -1
  88. package/dist/utils/serve-mcp.cjs +1 -1
  89. package/dist/utils/serve-mcp.mjs +1 -1
  90. package/dist/utils/view.cjs +34 -0
  91. package/dist/utils/view.d.cts +47 -0
  92. package/dist/utils/view.d.cts.map +1 -0
  93. package/dist/utils/view.d.mts +47 -0
  94. package/dist/utils/view.d.mts.map +1 -0
  95. package/dist/utils/view.mjs +33 -0
  96. package/dist/utils/view.mjs.map +1 -0
  97. package/dist/utils/yargs.cjs +27 -5
  98. package/dist/utils/yargs.d.cts +13 -0
  99. package/dist/utils/yargs.d.cts.map +1 -1
  100. package/dist/utils/yargs.d.mts +14 -1
  101. package/dist/utils/yargs.d.mts.map +1 -1
  102. package/dist/utils/yargs.mjs +32 -10
  103. package/dist/utils/yargs.mjs.map +1 -1
  104. package/package.json +20 -16
  105. package/dist/commands/app/app.cjs +0 -92
  106. package/dist/commands/app/app.mjs +0 -90
  107. package/dist/commands/app/app.mjs.map +0 -1
  108. package/dist/commands/app/cli.cjs +0 -6
  109. package/dist/commands/app/cli.d.cts +0 -1
  110. package/dist/commands/app/cli.d.mts +0 -1
  111. package/dist/commands/app/cli.mjs +0 -8
  112. package/dist/commands/app/cli.mjs.map +0 -1
  113. package/dist/commands/app/upgrade.cjs +0 -243
  114. package/dist/commands/app/upgrade.mjs +0 -240
  115. package/dist/commands/app/upgrade.mjs.map +0 -1
  116. package/dist/commands/app.cjs +0 -53
  117. package/dist/commands/app.mjs +0 -53
  118. package/dist/commands/app.mjs.map +0 -1
  119. package/dist/commands/deploy.cjs +0 -237
  120. package/dist/commands/deploy.mjs +0 -237
  121. package/dist/commands/deploy.mjs.map +0 -1
  122. package/dist/utils/listr.cjs +0 -226
  123. package/dist/utils/listr.d.cts +0 -71
  124. package/dist/utils/listr.d.cts.map +0 -1
  125. package/dist/utils/listr.d.mts +0 -71
  126. package/dist/utils/listr.d.mts.map +0 -1
  127. package/dist/utils/listr.mjs +0 -222
  128. package/dist/utils/listr.mjs.map +0 -1
@@ -1,19 +1,19 @@
1
1
  import { AIGNE_HUB_CREDITS_NOT_ENOUGH_ERROR_TYPE } from "../constants.mjs";
2
+ import { formatJSON, formatLLMDSL } from "../utils/output.mjs";
2
3
  import { terminalInput } from "../ui/utils/terminal-input.mjs";
4
+ import { stopThinkingIndicator } from "../shell/tools/ask-user.mjs";
3
5
  import checkbox_default from "../utils/inquirer/checkbox.mjs";
4
- import { AIGNEListr, AIGNEListrRenderer } from "../utils/listr.mjs";
5
6
  import { highlightUrl } from "../utils/string-utils.mjs";
6
7
  import { parseDuration } from "../utils/time.mjs";
7
8
  import chalk from "chalk";
8
- import { figures } from "@aigne/listr2";
9
- import { withProtocol } from "ufo";
10
- import { AIAgent, ChatModel, DEFAULT_OUTPUT_FILE_KEY, DEFAULT_OUTPUT_KEY, UserAgent, mergeContextUsage, newEmptyContextUsage } from "@aigne/core";
9
+ import { AIAgent, ChatModel, DEFAULT_OUTPUT_FILE_KEY, DEFAULT_OUTPUT_KEY, UserAgent, isAgentResponseProgress, mergeContextUsage, newEmptyContextUsage } from "@aigne/core";
11
10
  import { flat, omit } from "@aigne/core/utils/type-utils";
12
11
  import { EOL } from "node:os";
13
- import * as prompts from "@inquirer/prompts";
12
+ import { withProtocol } from "ufo";
14
13
  import { inspect } from "node:util";
15
- import { promiseWithResolvers } from "@aigne/core/utils/promise";
14
+ import { mergeAgentResponseChunk } from "@aigne/core/utils/stream-utils";
16
15
  import { markedTerminal } from "@aigne/marked-terminal";
16
+ import * as prompts from "@inquirer/prompts";
17
17
  import { Marked } from "marked";
18
18
  import terminalImage from "terminal-image";
19
19
  import terminalLink from "terminal-link";
@@ -25,22 +25,20 @@ var TerminalTracer = class {
25
25
  this.context = context;
26
26
  this.options = options;
27
27
  }
28
- tasks = {};
29
- async run(agent, input$1, options) {
28
+ result = {};
29
+ usage = newEmptyContextUsage();
30
+ /** Get TTY status, using options.isTTY if provided, otherwise process.stdout.isTTY */
31
+ get isTTY() {
32
+ return this.options.isTTY ?? process.stdout.isTTY ?? false;
33
+ }
34
+ async run(agent, input, options) {
30
35
  await this.context.observer?.serve();
31
36
  const context = this.context.newContext({ reset: true });
32
- const listr = new AIGNEListr({
33
- formatRequest: (options$1) => this.formatRequest(agent, context, input$1, options$1),
34
- formatResult: (result, options$1) => this.formatResult(agent, context, result, options$1)
35
- }, [], {
36
- concurrent: true,
37
- exitOnError: false,
38
- registerSignalListeners: false
39
- });
40
- this.listr = listr;
37
+ const request = this.formatRequest(agent, context, input);
38
+ if (request) console.log(request);
41
39
  const collapsedMap = /* @__PURE__ */ new Map();
42
40
  const hideContextIds = /* @__PURE__ */ new Set();
43
- const onStart = async ({ context: context$1, agent: agent$1, ...event }) => {
41
+ const onStart = async ({ context: context$1, agent: agent$1 }) => {
44
42
  const result = { options: { prompts: this.proxiedPrompts } };
45
43
  if (agent$1 instanceof UserAgent) return result;
46
44
  if (agent$1.taskRenderMode === "hide") {
@@ -63,87 +61,25 @@ var TerminalTracer = class {
63
61
  return result;
64
62
  }
65
63
  }
66
- const contextId = context$1.id;
67
- const parentContextId = context$1.parentId;
68
- const task = {
69
- ...promiseWithResolvers(),
70
- agent: agent$1,
71
- input: event.input,
72
- listr: promiseWithResolvers(),
73
- startTime: Date.now()
74
- };
75
- this.tasks[contextId] = task;
76
- const listrTask = {
77
- title: await this.formatTaskTitle(agent$1, { ...event }),
78
- task: (ctx, taskWrapper) => {
79
- const subtask = taskWrapper.newListr([{ task: () => task.promise }]);
80
- task.listr.resolve({
81
- subtask,
82
- taskWrapper,
83
- ctx
84
- });
85
- return subtask;
86
- },
87
- rendererOptions: {
88
- persistentOutput: true,
89
- outputBar: Number.POSITIVE_INFINITY,
90
- bottomBar: Number.POSITIVE_INFINITY
91
- }
92
- };
93
- const parentTask = parentContextId ? this.tasks[parentContextId] : void 0;
94
- if (parentTask) parentTask.listr.promise.then(({ subtask }) => {
95
- subtask.add(listrTask);
96
- });
97
- else listr.add(listrTask);
98
64
  return result;
99
65
  };
100
- const onSuccess = async ({ context: context$1, agent: agent$1, output, ...event }) => {
101
- const contextId = context$1.id;
102
- const parentContextId = context$1.parentId;
103
- const collapsed = collapsedMap.get(contextId);
66
+ const onSuccess = async ({ context: context$1, agent: agent$1, output }) => {
67
+ const collapsed = collapsedMap.get(context$1.id);
104
68
  if (collapsed) {
105
69
  if (agent$1 instanceof ChatModel) {
106
70
  const { usage, model } = output;
107
71
  if (usage) mergeContextUsage(collapsed.usage, usage);
108
72
  if (model) collapsed.models.add(model);
109
73
  }
110
- const task$1 = this.tasks[collapsed.ancestor.contextId];
111
- if (task$1) {
112
- task$1.usage = collapsed.usage;
113
- task$1.extraTitleMetadata ??= {};
114
- if (collapsed.models.size) task$1.extraTitleMetadata.model = [...collapsed.models].join(",");
115
- const { taskWrapper: taskWrapper$1 } = await task$1.listr.promise;
116
- taskWrapper$1.title = await this.formatTaskTitle(task$1.agent, {
117
- input: task$1.input,
118
- task: task$1,
119
- usage: Boolean(task$1.usage.inputTokens || task$1.usage.outputTokens || task$1.usage.aigneHubCredits || task$1.usage.cacheCreationInputTokens || task$1.usage.cacheReadInputTokens),
120
- time: context$1.id === collapsed.ancestor.contextId
121
- });
122
- if (context$1.id === collapsed.ancestor.contextId) task$1?.resolve();
123
- return;
124
- }
74
+ return;
125
75
  }
126
- const task = this.tasks[contextId];
127
- if (!task) return;
128
- task.endTime = Date.now();
129
- const { taskWrapper, ctx } = await task.listr.promise;
130
76
  if (agent$1 instanceof ChatModel) {
131
- const { usage, model } = output;
132
- task.usage = usage;
133
- task.extraTitleMetadata ??= {};
134
- if (model) task.extraTitleMetadata.model = model;
77
+ const { usage } = output;
78
+ if (usage) mergeContextUsage(this.usage, usage);
135
79
  }
136
- taskWrapper.title = await this.formatTaskTitle(agent$1, {
137
- ...event,
138
- task,
139
- usage: true,
140
- time: true
141
- });
142
- if (!parentContextId || !this.tasks[parentContextId]) Object.assign(ctx, output);
143
- task.resolve();
144
80
  };
145
81
  let retryPromptPromise;
146
- const onError = async ({ context: context$1, agent: agent$1, error, ...event }) => {
82
+ const onError = async ({ agent: agent$1, error }) => {
147
83
  if ("type" in error && error.type === AIGNE_HUB_CREDITS_NOT_ENOUGH_ERROR_TYPE) {
148
84
  if (!Object.hasOwn(error, CREDITS_ERROR_PROCESSED_FLAG)) {
149
85
  Object.defineProperty(error, CREDITS_ERROR_PROCESSED_FLAG, {
@@ -171,46 +107,34 @@ var TerminalTracer = class {
171
107
  const { retry } = await retryPromptPromise;
172
108
  if (retry) return { retry: true };
173
109
  }
174
- const contextId = context$1.id;
175
- const task = this.tasks[contextId];
176
- if (!task) return;
177
- task.endTime = Date.now();
178
- const { taskWrapper } = await task.listr.promise;
179
- taskWrapper.title = await this.formatTaskTitle(agent$1, {
180
- ...event,
181
- task,
182
- usage: true,
183
- time: true
184
- });
185
- task.reject(error);
186
110
  };
111
+ const stream = await context.invoke(agent, input, {
112
+ ...options,
113
+ hooks: flat({
114
+ priority: "high",
115
+ onStart,
116
+ onSuccess,
117
+ onError
118
+ }, options?.hooks),
119
+ streaming: true,
120
+ newContext: false
121
+ });
122
+ this.result = await this.processStream(stream);
123
+ console.log(await this.formatResult(agent, context, this.result, {
124
+ running: false,
125
+ renderImage: true,
126
+ streamed: true
127
+ }));
187
128
  return {
188
- result: await listr.run(() => context.invoke(agent, input$1, {
189
- ...options,
190
- hooks: flat({
191
- priority: "high",
192
- onStart,
193
- onSuccess,
194
- onError
195
- }, options?.hooks),
196
- streaming: true,
197
- newContext: false
198
- })),
129
+ result: this.result,
199
130
  context
200
131
  };
201
132
  }
202
- listr;
203
133
  proxiedPrompts = new Proxy({}, { get: (_target, prop) => {
204
134
  const method = prop === "checkbox" ? checkbox_default : prop === "input" ? terminalInput : prompts[prop];
205
135
  if (typeof method !== "function") throw new Error(`Unsupported prompt method ${String(prop)}`);
206
136
  return async (config) => {
207
- const renderer = this.listr?.["renderer"] instanceof AIGNEListrRenderer ? this.listr["renderer"] : void 0;
208
- await renderer?.pause();
209
- try {
210
- return await method({ ...config });
211
- } finally {
212
- await renderer?.resume();
213
- }
137
+ return await method({ ...config });
214
138
  };
215
139
  } });
216
140
  buyCreditsPromptPromise;
@@ -231,6 +155,66 @@ var TerminalTracer = class {
231
155
  this.buyCreditsPromptPromise = void 0;
232
156
  });
233
157
  }
158
+ marked = new Marked().use({ walkTokens: (token) => {
159
+ if (token.type === "code") {
160
+ if (typeof token.lang === "string") token.lang = token.lang.trim().split(/\s+/)[0];
161
+ }
162
+ } }, markedTerminal({ forceHyperLink: false }, { theme: { string: chalk.green } }));
163
+ async processStream(stream) {
164
+ const result = {};
165
+ const spinnerFrames = [
166
+ "⠋",
167
+ "⠙",
168
+ "⠹",
169
+ "⠸",
170
+ "⠼",
171
+ "⠴",
172
+ "⠦",
173
+ "⠧",
174
+ "⠇",
175
+ "⠏"
176
+ ];
177
+ let spinnerIndex = 0;
178
+ let spinnerInterval;
179
+ if (this.isTTY) {
180
+ process.stdout.write(`${spinnerFrames[0]} ${chalk.gray("Thinking...")}`);
181
+ spinnerInterval = setInterval(() => {
182
+ spinnerIndex = (spinnerIndex + 1) % spinnerFrames.length;
183
+ process.stdout.write(`\r${spinnerFrames[spinnerIndex]} ${chalk.gray("Thinking...")}`);
184
+ }, 80);
185
+ }
186
+ const stopThinking = () => {
187
+ if (spinnerInterval) {
188
+ clearInterval(spinnerInterval);
189
+ spinnerInterval = void 0;
190
+ console.log();
191
+ }
192
+ };
193
+ for await (const value of stream) {
194
+ mergeAgentResponseChunk(result, value);
195
+ if (isAgentResponseProgress(value) && value.progress.event === "message") {
196
+ const { message } = value.progress;
197
+ if (message.role === "user") continue;
198
+ if (message.role === "agent") {
199
+ stopThinking();
200
+ stopThinkingIndicator();
201
+ }
202
+ const rendered = [];
203
+ if (message.role === "agent") {
204
+ if (message.toolCalls) {
205
+ if (!this.options.suppressToolCallLogs) for (const call of message.toolCalls) rendered.push(`${chalk.bold.gray(`[${call.function.name}]`)} ${chalk.gray(`${JSON.stringify(call.function.arguments).slice(0, 200)}...`)}`);
206
+ } else if (typeof message.content === "string") rendered.push(this.marked.parse(message.content, { async: false }).trim());
207
+ else if (Array.isArray(message.content)) {
208
+ for (const msg of message.content) if (msg.type === "text") if (msg.isThinking) rendered.push(chalk.dim(chalk.grey(chalk.italic(`[Thinking] ${msg.text}`))));
209
+ else rendered.push(this.marked.parse(msg.text, { async: false }).trim());
210
+ }
211
+ }
212
+ if (rendered.length) console.log(`${chalk.green.bold("•")} ${rendered.join("\n")}\n`);
213
+ }
214
+ }
215
+ stopThinking();
216
+ return result;
217
+ }
234
218
  formatAigneHubCredits(credits, creditPrefix) {
235
219
  const formattedCredits = credits % 1 !== 0 ? parseFloat(credits.toFixed(6)).toString() : credits.toFixed();
236
220
  if (creditPrefix) return [chalk.grey("cost:"), chalk.blue(`${creditPrefix}${formattedCredits}`)];
@@ -252,18 +236,6 @@ var TerminalTracer = class {
252
236
  const duration = endTime - startTime;
253
237
  return chalk.grey(`[${parseDuration(duration)}]`);
254
238
  }
255
- async formatTaskTitle(agent, { task, usage, time, input: input$1 }) {
256
- let title = agent.name;
257
- if (agent.taskTitle) title += ` ${chalk.cyan(await agent.renderTaskTitle(input$1))}`;
258
- if (usage && task?.usage) title += ` ${this.formatTokenUsage(task.usage, task.extraTitleMetadata)}`;
259
- if (time && task?.startTime && task.endTime) title += ` ${this.formatTimeUsage(task.startTime, task.endTime)}`;
260
- return title;
261
- }
262
- marked = new Marked().use({ walkTokens: (token) => {
263
- if (token.type === "code") {
264
- if (typeof token.lang === "string") token.lang = token.lang.trim().split(/\s+/)[0];
265
- }
266
- } }, markedTerminal({ forceHyperLink: false }, { theme: { string: chalk.green } }));
267
239
  get outputKey() {
268
240
  return this.options.outputKey || DEFAULT_OUTPUT_KEY;
269
241
  }
@@ -271,8 +243,8 @@ var TerminalTracer = class {
271
243
  return this.options.outputFileKey || DEFAULT_OUTPUT_FILE_KEY;
272
244
  }
273
245
  formatRequest(agent, _context, m = {}, { running = false } = {}) {
274
- const prefix = `${chalk.grey(figures.pointer)} 💬 `;
275
- const inputKey = agent instanceof AIAgent ? agent.inputKey : void 0;
246
+ const prefix = `${chalk.grey("→")} 💬 `;
247
+ const inputKey = agent instanceof AIAgent ? agent.inputKey : agent instanceof UserAgent ? "message" : void 0;
276
248
  const msg = inputKey ? m[inputKey] : void 0;
277
249
  const message = inputKey ? omit(m, inputKey) : m;
278
250
  const r = [msg && typeof msg === "string" ? this.marked.parse(msg, { async: false }).trim() : void 0, Object.keys(message).length > 0 ? inspect(message, {
@@ -282,10 +254,25 @@ var TerminalTracer = class {
282
254
  if (!r) return void 0;
283
255
  return `${prefix}${r}`;
284
256
  }
285
- formatResult(agent, context, m = {}, { running = false, renderImage = false } = {}) {
286
- const { isTTY } = process.stdout;
257
+ formatResult(agent, context, m = {}, { running = false, renderImage = false, streamed = false } = {}) {
258
+ const view = this.options.view;
259
+ if (view === "json") return formatJSON(m);
260
+ if (view === "llm") return formatLLMDSL(m, Object.keys(m));
261
+ if (view === "default") {
262
+ const outputKey$1 = this.outputKey || (agent instanceof AIAgent ? agent.outputKey : void 0);
263
+ const msg$1 = outputKey$1 ? m[outputKey$1] : void 0;
264
+ if (msg$1 && typeof msg$1 === "string") return msg$1;
265
+ return JSON.stringify(m);
266
+ }
267
+ const isTTY = this.isTTY;
287
268
  const outputKey = this.outputKey || (agent instanceof AIAgent ? agent.outputKey : void 0);
288
- const prefix = `${chalk.grey(figures.tick)} 🤖 ${this.formatTokenUsage(context.usage)}`;
269
+ const prefix = `${chalk.grey("✓")} 🤖 ${this.formatTokenUsage(context.usage)}`;
270
+ if (streamed) {
271
+ if (renderImage) return this.formatResultData(m).then((images) => {
272
+ return [prefix, images].filter(Boolean).join(EOL.repeat(2));
273
+ });
274
+ return prefix;
275
+ }
289
276
  const msg = outputKey ? m[outputKey] : void 0;
290
277
  const message = outputKey ? omit(m, outputKey, this.outputFileKey) : m;
291
278
  const text = msg && typeof msg === "string" ? isTTY ? this.marked.parse(msg, { async: false }).trim() : msg : void 0;
@@ -1 +1 @@
1
- {"version":3,"file":"terminal.mjs","names":["input","options","agent","context","task","checkbox"],"sources":["../../src/tracer/terminal.ts"],"sourcesContent":["import { EOL } from \"node:os\";\nimport { type InspectOptions, inspect } from \"node:util\";\nimport {\n type Agent,\n type AgentHooks,\n AIAgent,\n ChatModel,\n type ChatModelOutput,\n type Context,\n type ContextUsage,\n DEFAULT_OUTPUT_FILE_KEY,\n DEFAULT_OUTPUT_KEY,\n type FileUnionContent,\n type InvokeOptions,\n type Message,\n mergeContextUsage,\n newEmptyContextUsage,\n UserAgent,\n} from \"@aigne/core\";\nimport { promiseWithResolvers } from \"@aigne/core/utils/promise\";\nimport { flat, omit } from \"@aigne/core/utils/type-utils\";\nimport { figures, type Listr } from \"@aigne/listr2\";\nimport { markedTerminal } from \"@aigne/marked-terminal\";\nimport * as prompts from \"@inquirer/prompts\";\nimport chalk from \"chalk\";\nimport { Marked } from \"marked\";\nimport terminalImage from \"terminal-image\";\nimport terminalLink from \"terminal-link\";\nimport { withProtocol } from \"ufo\";\nimport { AIGNE_HUB_CREDITS_NOT_ENOUGH_ERROR_TYPE } from \"../constants.js\";\nimport { terminalInput } from \"../ui/utils/terminal-input.js\";\nimport checkbox from \"../utils/inquirer/checkbox.js\";\nimport { AIGNEListr, AIGNEListrRenderer, type AIGNEListrTaskWrapper } from \"../utils/listr.js\";\nimport { highlightUrl } from \"../utils/string-utils.js\";\nimport { parseDuration } from \"../utils/time.js\";\n\nconst CREDITS_ERROR_PROCESSED_FLAG = \"$credits_error_processed\";\n\nexport interface TerminalTracerOptions {\n outputKey?: string;\n outputFileKey?: string;\n}\n\nexport class TerminalTracer {\n constructor(\n public readonly context: Context,\n public readonly options: TerminalTracerOptions = {},\n ) {}\n\n private tasks: { [callId: string]: Task } = {};\n\n async run(agent: Agent, input: Message, options?: InvokeOptions) {\n await this.context.observer?.serve();\n\n const context = this.context.newContext({ reset: true });\n\n const listr = new AIGNEListr(\n {\n formatRequest: (options?: { running?: boolean }) =>\n this.formatRequest(agent, context, input, options),\n formatResult: (result, options?: { running?: boolean; renderImage?: boolean }) =>\n this.formatResult(agent, context, result, options),\n },\n [],\n { concurrent: true, exitOnError: false, registerSignalListeners: false },\n );\n this.listr = listr;\n\n const collapsedMap = new Map<\n string,\n { ancestor: { contextId: string }; usage: ContextUsage; models: Set<string> }\n >();\n const hideContextIds = new Set<string>();\n\n const onStart: AgentHooks[\"onStart\"] = async ({ context, agent, ...event }) => {\n const result = { options: { prompts: this.proxiedPrompts } };\n\n if (agent instanceof UserAgent) return result;\n\n if (agent.taskRenderMode === \"hide\") {\n hideContextIds.add(context.id);\n return result;\n }\n\n if (agent.taskRenderMode === \"collapse\") {\n collapsedMap.set(context.id, {\n ancestor: { contextId: context.id },\n usage: newEmptyContextUsage(),\n models: new Set(),\n });\n }\n\n if (context.parentId) {\n if (hideContextIds.has(context.parentId)) {\n hideContextIds.add(context.id);\n return result;\n }\n\n const collapsed = collapsedMap.get(context.parentId);\n if (collapsed) {\n collapsedMap.set(context.id, collapsed);\n return result;\n }\n }\n\n const contextId = context.id;\n const parentContextId = context.parentId;\n\n const task: Task = {\n ...promiseWithResolvers(),\n agent,\n input: event.input,\n listr: promiseWithResolvers(),\n startTime: Date.now(),\n };\n this.tasks[contextId] = task;\n\n const listrTask: Parameters<typeof listr.add>[0] = {\n title: await this.formatTaskTitle(agent, { ...event }),\n task: (ctx, taskWrapper) => {\n const subtask = taskWrapper.newListr([{ task: () => task.promise }]);\n task.listr.resolve({ subtask, taskWrapper, ctx });\n return subtask;\n },\n rendererOptions: {\n persistentOutput: true,\n outputBar: Number.POSITIVE_INFINITY,\n bottomBar: Number.POSITIVE_INFINITY,\n },\n };\n\n const parentTask = parentContextId ? this.tasks[parentContextId] : undefined;\n if (parentTask) {\n parentTask.listr.promise.then(({ subtask }) => {\n subtask.add(listrTask);\n });\n } else {\n listr.add(listrTask);\n }\n\n return result;\n };\n\n const onSuccess: AgentHooks[\"onSuccess\"] = async ({ context, agent, output, ...event }) => {\n const contextId = context.id;\n const parentContextId = context.parentId;\n\n const collapsed = collapsedMap.get(contextId);\n if (collapsed) {\n if (agent instanceof ChatModel) {\n const { usage, model } = output as ChatModelOutput;\n if (usage) mergeContextUsage(collapsed.usage, usage);\n if (model) collapsed.models.add(model);\n }\n\n const task = this.tasks[collapsed.ancestor.contextId];\n if (task) {\n task.usage = collapsed.usage;\n task.extraTitleMetadata ??= {};\n if (collapsed.models.size)\n task.extraTitleMetadata.model = [...collapsed.models].join(\",\");\n\n const { taskWrapper } = await task.listr.promise;\n\n taskWrapper.title = await this.formatTaskTitle(task.agent, {\n input: task.input,\n task,\n usage: Boolean(\n task.usage.inputTokens ||\n task.usage.outputTokens ||\n task.usage.aigneHubCredits ||\n task.usage.cacheCreationInputTokens ||\n task.usage.cacheReadInputTokens,\n ),\n time: context.id === collapsed.ancestor.contextId,\n });\n\n if (context.id === collapsed.ancestor.contextId) {\n task?.resolve();\n }\n return;\n }\n }\n\n const task = this.tasks[contextId];\n if (!task) return;\n\n task.endTime = Date.now();\n\n const { taskWrapper, ctx } = await task.listr.promise;\n\n if (agent instanceof ChatModel) {\n const { usage, model } = output as ChatModelOutput;\n task.usage = usage;\n task.extraTitleMetadata ??= {};\n if (model) task.extraTitleMetadata.model = model;\n }\n\n taskWrapper.title = await this.formatTaskTitle(agent, {\n ...event,\n task,\n usage: true,\n time: true,\n });\n\n if (!parentContextId || !this.tasks[parentContextId]) {\n Object.assign(ctx, output);\n }\n\n task.resolve();\n };\n\n let retryPromptPromise: Promise<{ retry?: boolean }> | undefined;\n\n const onError: AgentHooks[\"onError\"] = async ({ context, agent, error, ...event }) => {\n if (\"type\" in error && error.type === AIGNE_HUB_CREDITS_NOT_ENOUGH_ERROR_TYPE) {\n if (!Object.hasOwn(error, CREDITS_ERROR_PROCESSED_FLAG)) {\n Object.defineProperty(error, CREDITS_ERROR_PROCESSED_FLAG, {\n value: true,\n enumerable: false,\n });\n\n const retry = await this.promptBuyCredits(error);\n\n console.log(\"\");\n\n if (retry === \"retry\") {\n return { retry: true };\n }\n }\n }\n\n if (agent instanceof ChatModel) {\n retryPromptPromise ??= this.proxiedPrompts\n .select({\n message: chalk.red(`Error: ${error.message}`),\n choices: [\n { value: \"retry\", name: \"Retry\" },\n { value: \"exit\", name: \"Exit\" },\n ],\n })\n .then((result) => ({ retry: result === \"retry\" }))\n .finally(() => {\n retryPromptPromise = undefined;\n });\n\n const { retry } = await retryPromptPromise;\n if (retry) return { retry: true };\n }\n\n const contextId = context.id;\n\n const task = this.tasks[contextId];\n if (!task) return;\n\n task.endTime = Date.now();\n\n const { taskWrapper } = await task.listr.promise;\n taskWrapper.title = await this.formatTaskTitle(agent, {\n ...event,\n task,\n usage: true,\n time: true,\n });\n\n task.reject(error);\n };\n\n const result = await listr.run(() =>\n context.invoke(agent, input, {\n ...options,\n hooks: flat(\n {\n priority: \"high\",\n onStart,\n onSuccess,\n onError,\n },\n options?.hooks,\n ),\n streaming: true,\n newContext: false,\n }),\n );\n\n return { result, context };\n }\n\n private listr?: AIGNEListr;\n\n private proxiedPrompts = new Proxy(\n {},\n {\n get: (_target, prop) => {\n const method =\n prop === \"checkbox\"\n ? checkbox\n : prop === \"input\"\n ? terminalInput\n : // biome-ignore lint/performance/noDynamicNamespaceImportAccess: we need to access prompts dynamically\n (prompts[prop as keyof typeof prompts] as (...args: any[]) => any);\n if (typeof method !== \"function\")\n throw new Error(`Unsupported prompt method ${String(prop)}`);\n\n return async (config: any) => {\n const renderer =\n this.listr?.[\"renderer\"] instanceof AIGNEListrRenderer\n ? this.listr[\"renderer\"]\n : undefined;\n await renderer?.pause();\n\n try {\n return await method({ ...config });\n } finally {\n await renderer?.resume();\n }\n };\n },\n },\n ) as typeof prompts;\n\n private buyCreditsPromptPromise: Promise<\"retry\" | \"exit\"> | undefined;\n\n private async promptBuyCredits(error: Error) {\n // Avoid multiple agents asking for credits, we will only show the prompt once\n this.buyCreditsPromptPromise ??= (async () => {\n const retry = await this.proxiedPrompts.select({\n message: highlightUrl(error.message),\n choices: [\n {\n name: \"I have bought some credits, try again\",\n value: \"retry\" as const,\n },\n {\n name: \"Exit\",\n value: \"exit\" as const,\n },\n ],\n });\n\n return retry;\n })();\n\n return this.buyCreditsPromptPromise\n .catch(() => \"exit\")\n .finally(() => {\n // Clear the promise so that we can show the prompt again if needed\n this.buyCreditsPromptPromise = undefined;\n });\n }\n\n formatAigneHubCredits(credits: number, creditPrefix?: string): [string, string] {\n const hasDecimal = credits % 1 !== 0;\n const formattedCredits = hasDecimal\n ? parseFloat(credits.toFixed(6)).toString()\n : credits.toFixed();\n\n if (creditPrefix) {\n return [chalk.grey(\"cost:\"), chalk.blue(`${creditPrefix}${formattedCredits}`)];\n }\n\n return [chalk.blue(formattedCredits), chalk.grey(\"AIGNE Hub credits\")];\n }\n\n formatTokenUsage(usage: Partial<ContextUsage>, extra?: { [key: string]: string }) {\n const items = [\n [chalk.yellow(usage.inputTokens), chalk.grey(\"input tokens\")],\n usage.cacheReadInputTokens\n ? [chalk.green(usage.cacheReadInputTokens), chalk.grey(\"cached\")]\n : undefined,\n usage.cacheCreationInputTokens\n ? [chalk.yellow(usage.cacheCreationInputTokens), chalk.grey(\"cache write\")]\n : undefined,\n [chalk.cyan(usage.outputTokens), chalk.grey(\"output tokens\")],\n usage.aigneHubCredits\n ? this.formatAigneHubCredits(usage.aigneHubCredits, usage.creditPrefix)\n : undefined,\n usage.agentCalls ? [chalk.magenta(usage.agentCalls), chalk.grey(\"agent calls\")] : undefined,\n ];\n\n const content = items.filter((i) => !!i).map((i) => i.join(\" \"));\n\n if (extra) {\n content.unshift(\n ...Object.entries(extra)\n .filter(([k, v]) => k && v)\n .map(([k, v]) => `${chalk.grey(k)}: ${v}`),\n );\n }\n\n return `${chalk.grey(\"(\")}${content.join(chalk.green(\", \"))}${chalk.grey(\")\")}`;\n }\n\n formatTimeUsage(startTime: number, endTime: number) {\n const duration = endTime - startTime;\n return chalk.grey(`[${parseDuration(duration)}]`);\n }\n\n async formatTaskTitle(\n agent: Agent,\n { task, usage, time, input }: { task?: Task; usage?: boolean; time?: boolean; input: Message },\n ) {\n let title = agent.name;\n\n if (agent.taskTitle) {\n title += ` ${chalk.cyan(await agent.renderTaskTitle(input))}`;\n }\n\n if (usage && task?.usage)\n title += ` ${this.formatTokenUsage(task.usage, task.extraTitleMetadata)}`;\n if (time && task?.startTime && task.endTime)\n title += ` ${this.formatTimeUsage(task.startTime, task.endTime)}`;\n\n return title;\n }\n\n private marked = new Marked().use(\n {\n // marked-terminal does not support code block meta, so we need to strip it\n walkTokens: (token) => {\n if (token.type === \"code\") {\n if (typeof token.lang === \"string\") {\n token.lang = token.lang.trim().split(/\\s+/)[0];\n }\n }\n },\n },\n markedTerminal(\n { forceHyperLink: false },\n {\n theme: {\n string: chalk.green,\n },\n },\n ),\n );\n\n get outputKey() {\n return this.options.outputKey || DEFAULT_OUTPUT_KEY;\n }\n\n get outputFileKey() {\n return this.options.outputFileKey || DEFAULT_OUTPUT_FILE_KEY;\n }\n\n formatRequest(agent: Agent, _context: Context, m: Message = {}, { running = false } = {}) {\n const prefix = `${chalk.grey(figures.pointer)} 💬 `;\n\n const inputKey = agent instanceof AIAgent ? agent.inputKey : undefined;\n\n const msg = inputKey ? m[inputKey] : undefined;\n const message = inputKey ? omit(m, inputKey) : m;\n\n const text =\n msg && typeof msg === \"string\" ? this.marked.parse(msg, { async: false }).trim() : undefined;\n\n const json =\n Object.keys(message).length > 0\n ? inspect(message, { colors: true, ...(running ? this.runningInspectOptions : undefined) })\n : undefined;\n\n const r = [text, json].filter(Boolean).join(EOL).trim();\n if (!r) return undefined;\n\n return `${prefix}${r}`;\n }\n\n formatResult(\n agent: Agent,\n context: Context,\n m: Message = {},\n { running = false, renderImage = false } = {},\n ): string | Promise<string> {\n const { isTTY } = process.stdout;\n const outputKey = this.outputKey || (agent instanceof AIAgent ? agent.outputKey : undefined);\n\n const prefix = `${chalk.grey(figures.tick)} 🤖 ${this.formatTokenUsage(context.usage)}`;\n\n const msg = outputKey ? m[outputKey] : undefined;\n const message = outputKey ? omit(m, outputKey, this.outputFileKey) : m;\n\n const text =\n msg && typeof msg === \"string\"\n ? isTTY\n ? this.marked.parse(msg, { async: false }).trim()\n : msg\n : undefined;\n\n const json =\n Object.keys(message).length > 0\n ? inspect(message, { colors: isTTY, ...(running ? this.runningInspectOptions : undefined) })\n : undefined;\n\n if (renderImage) {\n return this.formatResultData(m).then((images) => {\n return [prefix, text, images, json].filter(Boolean).join(EOL.repeat(2));\n });\n }\n\n return [prefix, text, json].filter(Boolean).join(EOL.repeat(2));\n }\n\n async formatResultData(output: Message): Promise<string | undefined> {\n const files = output[this.outputFileKey] as FileUnionContent[];\n if (!Array.isArray(files)) return;\n\n const options: Parameters<typeof terminalImage.file>[1] = {\n height: 30,\n };\n\n return (\n await Promise.all(\n files.map(async (item) => {\n const image =\n item.type === \"local\"\n ? await terminalImage.file(item.path, options)\n : item.type === \"file\"\n ? await terminalImage.buffer(Buffer.from(item.data, \"base64\"), options)\n : undefined;\n\n const link =\n item.type === \"local\"\n ? withProtocol(item.path, \"file://\")\n : item.type === \"url\"\n ? item.url\n : undefined;\n const text = [\n link ? chalk.cyan(terminalLink(link, link)) : undefined,\n item.filename,\n item.mimeType ? chalk.gray(item.mimeType) : undefined,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return [image, text].filter(Boolean).join(EOL);\n }),\n )\n )\n .filter(Boolean)\n .join(EOL);\n }\n\n protected runningInspectOptions: InspectOptions = {\n maxArrayLength: 3,\n maxStringLength: 200,\n };\n}\n\ntype Task = ReturnType<typeof promiseWithResolvers<void>> & {\n listr: ReturnType<\n typeof promiseWithResolvers<{\n ctx: object;\n subtask: Listr;\n taskWrapper: AIGNEListrTaskWrapper;\n }>\n >;\n agent: Agent;\n input: Message;\n startTime?: number;\n endTime?: number;\n usage?: Partial<ContextUsage>;\n extraTitleMetadata?: { [key: string]: string };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAoCA,MAAM,+BAA+B;AAOrC,IAAa,iBAAb,MAA4B;CAC1B,YACE,AAAgB,SAChB,AAAgB,UAAiC,EAAE,EACnD;EAFgB;EACA;;CAGlB,AAAQ,QAAoC,EAAE;CAE9C,MAAM,IAAI,OAAc,SAAgB,SAAyB;AAC/D,QAAM,KAAK,QAAQ,UAAU,OAAO;EAEpC,MAAM,UAAU,KAAK,QAAQ,WAAW,EAAE,OAAO,MAAM,CAAC;EAExD,MAAM,QAAQ,IAAI,WAChB;GACE,gBAAgB,cACd,KAAK,cAAc,OAAO,SAASA,SAAOC,UAAQ;GACpD,eAAe,QAAQ,cACrB,KAAK,aAAa,OAAO,SAAS,QAAQA,UAAQ;GACrD,EACD,EAAE,EACF;GAAE,YAAY;GAAM,aAAa;GAAO,yBAAyB;GAAO,CACzE;AACD,OAAK,QAAQ;EAEb,MAAM,+BAAe,IAAI,KAGtB;EACH,MAAM,iCAAiB,IAAI,KAAa;EAExC,MAAM,UAAiC,OAAO,EAAE,oBAAS,gBAAO,GAAG,YAAY;GAC7E,MAAM,SAAS,EAAE,SAAS,EAAE,SAAS,KAAK,gBAAgB,EAAE;AAE5D,OAAIC,mBAAiB,UAAW,QAAO;AAEvC,OAAIA,QAAM,mBAAmB,QAAQ;AACnC,mBAAe,IAAIC,UAAQ,GAAG;AAC9B,WAAO;;AAGT,OAAID,QAAM,mBAAmB,WAC3B,cAAa,IAAIC,UAAQ,IAAI;IAC3B,UAAU,EAAE,WAAWA,UAAQ,IAAI;IACnC,OAAO,sBAAsB;IAC7B,wBAAQ,IAAI,KAAK;IAClB,CAAC;AAGJ,OAAIA,UAAQ,UAAU;AACpB,QAAI,eAAe,IAAIA,UAAQ,SAAS,EAAE;AACxC,oBAAe,IAAIA,UAAQ,GAAG;AAC9B,YAAO;;IAGT,MAAM,YAAY,aAAa,IAAIA,UAAQ,SAAS;AACpD,QAAI,WAAW;AACb,kBAAa,IAAIA,UAAQ,IAAI,UAAU;AACvC,YAAO;;;GAIX,MAAM,YAAYA,UAAQ;GAC1B,MAAM,kBAAkBA,UAAQ;GAEhC,MAAM,OAAa;IACjB,GAAG,sBAAsB;IACzB;IACA,OAAO,MAAM;IACb,OAAO,sBAAsB;IAC7B,WAAW,KAAK,KAAK;IACtB;AACD,QAAK,MAAM,aAAa;GAExB,MAAM,YAA6C;IACjD,OAAO,MAAM,KAAK,gBAAgBD,SAAO,EAAE,GAAG,OAAO,CAAC;IACtD,OAAO,KAAK,gBAAgB;KAC1B,MAAM,UAAU,YAAY,SAAS,CAAC,EAAE,YAAY,KAAK,SAAS,CAAC,CAAC;AACpE,UAAK,MAAM,QAAQ;MAAE;MAAS;MAAa;MAAK,CAAC;AACjD,YAAO;;IAET,iBAAiB;KACf,kBAAkB;KAClB,WAAW,OAAO;KAClB,WAAW,OAAO;KACnB;IACF;GAED,MAAM,aAAa,kBAAkB,KAAK,MAAM,mBAAmB;AACnE,OAAI,WACF,YAAW,MAAM,QAAQ,MAAM,EAAE,cAAc;AAC7C,YAAQ,IAAI,UAAU;KACtB;OAEF,OAAM,IAAI,UAAU;AAGtB,UAAO;;EAGT,MAAM,YAAqC,OAAO,EAAE,oBAAS,gBAAO,QAAQ,GAAG,YAAY;GACzF,MAAM,YAAYC,UAAQ;GAC1B,MAAM,kBAAkBA,UAAQ;GAEhC,MAAM,YAAY,aAAa,IAAI,UAAU;AAC7C,OAAI,WAAW;AACb,QAAID,mBAAiB,WAAW;KAC9B,MAAM,EAAE,OAAO,UAAU;AACzB,SAAI,MAAO,mBAAkB,UAAU,OAAO,MAAM;AACpD,SAAI,MAAO,WAAU,OAAO,IAAI,MAAM;;IAGxC,MAAME,SAAO,KAAK,MAAM,UAAU,SAAS;AAC3C,QAAIA,QAAM;AACR,YAAK,QAAQ,UAAU;AACvB,YAAK,uBAAuB,EAAE;AAC9B,SAAI,UAAU,OAAO,KACnB,QAAK,mBAAmB,QAAQ,CAAC,GAAG,UAAU,OAAO,CAAC,KAAK,IAAI;KAEjE,MAAM,EAAE,+BAAgB,MAAMA,OAAK,MAAM;AAEzC,mBAAY,QAAQ,MAAM,KAAK,gBAAgBA,OAAK,OAAO;MACzD,OAAOA,OAAK;MACZ;MACA,OAAO,QACLA,OAAK,MAAM,eACTA,OAAK,MAAM,gBACXA,OAAK,MAAM,mBACXA,OAAK,MAAM,4BACXA,OAAK,MAAM,qBACd;MACD,MAAMD,UAAQ,OAAO,UAAU,SAAS;MACzC,CAAC;AAEF,SAAIA,UAAQ,OAAO,UAAU,SAAS,UACpC,SAAM,SAAS;AAEjB;;;GAIJ,MAAM,OAAO,KAAK,MAAM;AACxB,OAAI,CAAC,KAAM;AAEX,QAAK,UAAU,KAAK,KAAK;GAEzB,MAAM,EAAE,aAAa,QAAQ,MAAM,KAAK,MAAM;AAE9C,OAAID,mBAAiB,WAAW;IAC9B,MAAM,EAAE,OAAO,UAAU;AACzB,SAAK,QAAQ;AACb,SAAK,uBAAuB,EAAE;AAC9B,QAAI,MAAO,MAAK,mBAAmB,QAAQ;;AAG7C,eAAY,QAAQ,MAAM,KAAK,gBAAgBA,SAAO;IACpD,GAAG;IACH;IACA,OAAO;IACP,MAAM;IACP,CAAC;AAEF,OAAI,CAAC,mBAAmB,CAAC,KAAK,MAAM,iBAClC,QAAO,OAAO,KAAK,OAAO;AAG5B,QAAK,SAAS;;EAGhB,IAAI;EAEJ,MAAM,UAAiC,OAAO,EAAE,oBAAS,gBAAO,OAAO,GAAG,YAAY;AACpF,OAAI,UAAU,SAAS,MAAM,SAAS,yCACpC;QAAI,CAAC,OAAO,OAAO,OAAO,6BAA6B,EAAE;AACvD,YAAO,eAAe,OAAO,8BAA8B;MACzD,OAAO;MACP,YAAY;MACb,CAAC;KAEF,MAAM,QAAQ,MAAM,KAAK,iBAAiB,MAAM;AAEhD,aAAQ,IAAI,GAAG;AAEf,SAAI,UAAU,QACZ,QAAO,EAAE,OAAO,MAAM;;;AAK5B,OAAIA,mBAAiB,WAAW;AAC9B,2BAAuB,KAAK,eACzB,OAAO;KACN,SAAS,MAAM,IAAI,UAAU,MAAM,UAAU;KAC7C,SAAS,CACP;MAAE,OAAO;MAAS,MAAM;MAAS,EACjC;MAAE,OAAO;MAAQ,MAAM;MAAQ,CAChC;KACF,CAAC,CACD,MAAM,YAAY,EAAE,OAAO,WAAW,SAAS,EAAE,CACjD,cAAc;AACb,0BAAqB;MACrB;IAEJ,MAAM,EAAE,UAAU,MAAM;AACxB,QAAI,MAAO,QAAO,EAAE,OAAO,MAAM;;GAGnC,MAAM,YAAYC,UAAQ;GAE1B,MAAM,OAAO,KAAK,MAAM;AACxB,OAAI,CAAC,KAAM;AAEX,QAAK,UAAU,KAAK,KAAK;GAEzB,MAAM,EAAE,gBAAgB,MAAM,KAAK,MAAM;AACzC,eAAY,QAAQ,MAAM,KAAK,gBAAgBD,SAAO;IACpD,GAAG;IACH;IACA,OAAO;IACP,MAAM;IACP,CAAC;AAEF,QAAK,OAAO,MAAM;;AAoBpB,SAAO;GAAE,QAjBM,MAAM,MAAM,UACzB,QAAQ,OAAO,OAAOF,SAAO;IAC3B,GAAG;IACH,OAAO,KACL;KACE,UAAU;KACV;KACA;KACA;KACD,EACD,SAAS,MACV;IACD,WAAW;IACX,YAAY;IACb,CAAC,CACH;GAEgB;GAAS;;CAG5B,AAAQ;CAER,AAAQ,iBAAiB,IAAI,MAC3B,EAAE,EACF,EACE,MAAM,SAAS,SAAS;EACtB,MAAM,SACJ,SAAS,aACLK,mBACA,SAAS,UACP,gBAEC,QAAQ;AACjB,MAAI,OAAO,WAAW,WACpB,OAAM,IAAI,MAAM,6BAA6B,OAAO,KAAK,GAAG;AAE9D,SAAO,OAAO,WAAgB;GAC5B,MAAM,WACJ,KAAK,QAAQ,uBAAuB,qBAChC,KAAK,MAAM,cACX;AACN,SAAM,UAAU,OAAO;AAEvB,OAAI;AACF,WAAO,MAAM,OAAO,EAAE,GAAG,QAAQ,CAAC;aAC1B;AACR,UAAM,UAAU,QAAQ;;;IAI/B,CACF;CAED,AAAQ;CAER,MAAc,iBAAiB,OAAc;AAE3C,OAAK,6BAA6B,YAAY;AAe5C,UAdc,MAAM,KAAK,eAAe,OAAO;IAC7C,SAAS,aAAa,MAAM,QAAQ;IACpC,SAAS,CACP;KACE,MAAM;KACN,OAAO;KACR,EACD;KACE,MAAM;KACN,OAAO;KACR,CACF;IACF,CAAC;MAGA;AAEJ,SAAO,KAAK,wBACT,YAAY,OAAO,CACnB,cAAc;AAEb,QAAK,0BAA0B;IAC/B;;CAGN,sBAAsB,SAAiB,cAAyC;EAE9E,MAAM,mBADa,UAAU,MAAM,IAE/B,WAAW,QAAQ,QAAQ,EAAE,CAAC,CAAC,UAAU,GACzC,QAAQ,SAAS;AAErB,MAAI,aACF,QAAO,CAAC,MAAM,KAAK,QAAQ,EAAE,MAAM,KAAK,GAAG,eAAe,mBAAmB,CAAC;AAGhF,SAAO,CAAC,MAAM,KAAK,iBAAiB,EAAE,MAAM,KAAK,oBAAoB,CAAC;;CAGxE,iBAAiB,OAA8B,OAAmC;EAgBhF,MAAM,UAfQ;GACZ,CAAC,MAAM,OAAO,MAAM,YAAY,EAAE,MAAM,KAAK,eAAe,CAAC;GAC7D,MAAM,uBACF,CAAC,MAAM,MAAM,MAAM,qBAAqB,EAAE,MAAM,KAAK,SAAS,CAAC,GAC/D;GACJ,MAAM,2BACF,CAAC,MAAM,OAAO,MAAM,yBAAyB,EAAE,MAAM,KAAK,cAAc,CAAC,GACzE;GACJ,CAAC,MAAM,KAAK,MAAM,aAAa,EAAE,MAAM,KAAK,gBAAgB,CAAC;GAC7D,MAAM,kBACF,KAAK,sBAAsB,MAAM,iBAAiB,MAAM,aAAa,GACrE;GACJ,MAAM,aAAa,CAAC,MAAM,QAAQ,MAAM,WAAW,EAAE,MAAM,KAAK,cAAc,CAAC,GAAG;GACnF,CAEqB,QAAQ,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,EAAE,KAAK,IAAI,CAAC;AAEhE,MAAI,MACF,SAAQ,QACN,GAAG,OAAO,QAAQ,MAAM,CACrB,QAAQ,CAAC,GAAG,OAAO,KAAK,EAAE,CAC1B,KAAK,CAAC,GAAG,OAAO,GAAG,MAAM,KAAK,EAAE,CAAC,IAAI,IAAI,CAC7C;AAGH,SAAO,GAAG,MAAM,KAAK,IAAI,GAAG,QAAQ,KAAK,MAAM,MAAM,KAAK,CAAC,GAAG,MAAM,KAAK,IAAI;;CAG/E,gBAAgB,WAAmB,SAAiB;EAClD,MAAM,WAAW,UAAU;AAC3B,SAAO,MAAM,KAAK,IAAI,cAAc,SAAS,CAAC,GAAG;;CAGnD,MAAM,gBACJ,OACA,EAAE,MAAM,OAAO,MAAM,kBACrB;EACA,IAAI,QAAQ,MAAM;AAElB,MAAI,MAAM,UACR,UAAS,IAAI,MAAM,KAAK,MAAM,MAAM,gBAAgBL,QAAM,CAAC;AAG7D,MAAI,SAAS,MAAM,MACjB,UAAS,IAAI,KAAK,iBAAiB,KAAK,OAAO,KAAK,mBAAmB;AACzE,MAAI,QAAQ,MAAM,aAAa,KAAK,QAClC,UAAS,IAAI,KAAK,gBAAgB,KAAK,WAAW,KAAK,QAAQ;AAEjE,SAAO;;CAGT,AAAQ,SAAS,IAAI,QAAQ,CAAC,IAC5B,EAEE,aAAa,UAAU;AACrB,MAAI,MAAM,SAAS,QACjB;OAAI,OAAO,MAAM,SAAS,SACxB,OAAM,OAAO,MAAM,KAAK,MAAM,CAAC,MAAM,MAAM,CAAC;;IAInD,EACD,eACE,EAAE,gBAAgB,OAAO,EACzB,EACE,OAAO,EACL,QAAQ,MAAM,OACf,EACF,CACF,CACF;CAED,IAAI,YAAY;AACd,SAAO,KAAK,QAAQ,aAAa;;CAGnC,IAAI,gBAAgB;AAClB,SAAO,KAAK,QAAQ,iBAAiB;;CAGvC,cAAc,OAAc,UAAmB,IAAa,EAAE,EAAE,EAAE,UAAU,UAAU,EAAE,EAAE;EACxF,MAAM,SAAS,GAAG,MAAM,KAAK,QAAQ,QAAQ,CAAC;EAE9C,MAAM,WAAW,iBAAiB,UAAU,MAAM,WAAW;EAE7D,MAAM,MAAM,WAAW,EAAE,YAAY;EACrC,MAAM,UAAU,WAAW,KAAK,GAAG,SAAS,GAAG;EAU/C,MAAM,IAAI,CAPR,OAAO,OAAO,QAAQ,WAAW,KAAK,OAAO,MAAM,KAAK,EAAE,OAAO,OAAO,CAAC,CAAC,MAAM,GAAG,QAGnF,OAAO,KAAK,QAAQ,CAAC,SAAS,IAC1B,QAAQ,SAAS;GAAE,QAAQ;GAAM,GAAI,UAAU,KAAK,wBAAwB;GAAY,CAAC,GACzF,OAEgB,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI,CAAC,MAAM;AACvD,MAAI,CAAC,EAAG,QAAO;AAEf,SAAO,GAAG,SAAS;;CAGrB,aACE,OACA,SACA,IAAa,EAAE,EACf,EAAE,UAAU,OAAO,cAAc,UAAU,EAAE,EACnB;EAC1B,MAAM,EAAE,UAAU,QAAQ;EAC1B,MAAM,YAAY,KAAK,cAAc,iBAAiB,UAAU,MAAM,YAAY;EAElF,MAAM,SAAS,GAAG,MAAM,KAAK,QAAQ,KAAK,CAAC,MAAM,KAAK,iBAAiB,QAAQ,MAAM;EAErF,MAAM,MAAM,YAAY,EAAE,aAAa;EACvC,MAAM,UAAU,YAAY,KAAK,GAAG,WAAW,KAAK,cAAc,GAAG;EAErE,MAAM,OACJ,OAAO,OAAO,QAAQ,WAClB,QACE,KAAK,OAAO,MAAM,KAAK,EAAE,OAAO,OAAO,CAAC,CAAC,MAAM,GAC/C,MACF;EAEN,MAAM,OACJ,OAAO,KAAK,QAAQ,CAAC,SAAS,IAC1B,QAAQ,SAAS;GAAE,QAAQ;GAAO,GAAI,UAAU,KAAK,wBAAwB;GAAY,CAAC,GAC1F;AAEN,MAAI,YACF,QAAO,KAAK,iBAAiB,EAAE,CAAC,MAAM,WAAW;AAC/C,UAAO;IAAC;IAAQ;IAAM;IAAQ;IAAK,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI,OAAO,EAAE,CAAC;IACvE;AAGJ,SAAO;GAAC;GAAQ;GAAM;GAAK,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI,OAAO,EAAE,CAAC;;CAGjE,MAAM,iBAAiB,QAA8C;EACnE,MAAM,QAAQ,OAAO,KAAK;AAC1B,MAAI,CAAC,MAAM,QAAQ,MAAM,CAAE;EAE3B,MAAM,UAAoD,EACxD,QAAQ,IACT;AAED,UACE,MAAM,QAAQ,IACZ,MAAM,IAAI,OAAO,SAAS;GACxB,MAAM,QACJ,KAAK,SAAS,UACV,MAAM,cAAc,KAAK,KAAK,MAAM,QAAQ,GAC5C,KAAK,SAAS,SACZ,MAAM,cAAc,OAAO,OAAO,KAAK,KAAK,MAAM,SAAS,EAAE,QAAQ,GACrE;GAER,MAAM,OACJ,KAAK,SAAS,UACV,aAAa,KAAK,MAAM,UAAU,GAClC,KAAK,SAAS,QACZ,KAAK,MACL;AASR,UAAO,CAAC,OARK;IACX,OAAO,MAAM,KAAK,aAAa,MAAM,KAAK,CAAC,GAAG;IAC9C,KAAK;IACL,KAAK,WAAW,MAAM,KAAK,KAAK,SAAS,GAAG;IAC7C,CACE,OAAO,QAAQ,CACf,KAAK,IAAI,CAEQ,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;IAC9C,CACH,EAEA,OAAO,QAAQ,CACf,KAAK,IAAI;;CAGd,AAAU,wBAAwC;EAChD,gBAAgB;EAChB,iBAAiB;EAClB"}
1
+ {"version":3,"file":"terminal.mjs","names":["agent","context","checkbox","outputKey","msg"],"sources":["../../src/tracer/terminal.ts"],"sourcesContent":["import { EOL } from \"node:os\";\nimport { type InspectOptions, inspect } from \"node:util\";\nimport {\n type Agent,\n type AgentHooks,\n type AgentResponseStream,\n AIAgent,\n ChatModel,\n type ChatModelOutput,\n type Context,\n type ContextUsage,\n DEFAULT_OUTPUT_FILE_KEY,\n DEFAULT_OUTPUT_KEY,\n type FileUnionContent,\n type InvokeOptions,\n isAgentResponseProgress,\n type Message,\n mergeContextUsage,\n newEmptyContextUsage,\n UserAgent,\n} from \"@aigne/core\";\nimport { mergeAgentResponseChunk } from \"@aigne/core/utils/stream-utils\";\nimport { flat, omit } from \"@aigne/core/utils/type-utils\";\nimport { markedTerminal } from \"@aigne/marked-terminal\";\nimport * as prompts from \"@inquirer/prompts\";\nimport chalk from \"chalk\";\nimport { Marked } from \"marked\";\nimport terminalImage from \"terminal-image\";\nimport terminalLink from \"terminal-link\";\nimport { withProtocol } from \"ufo\";\nimport { AIGNE_HUB_CREDITS_NOT_ENOUGH_ERROR_TYPE } from \"../constants.js\";\nimport { stopThinkingIndicator } from \"../shell/tools/ask-user.js\";\nimport { terminalInput } from \"../ui/utils/terminal-input.js\";\nimport checkbox from \"../utils/inquirer/checkbox.js\";\nimport { formatJSON, formatLLMDSL } from \"../utils/output.js\";\nimport { highlightUrl } from \"../utils/string-utils.js\";\nimport { parseDuration } from \"../utils/time.js\";\nimport type { ViewType } from \"../utils/view.js\";\n\nconst CREDITS_ERROR_PROCESSED_FLAG = \"$credits_error_processed\";\n\nexport interface TerminalTracerOptions {\n outputKey?: string;\n outputFileKey?: string;\n /** Output view type for formatting results */\n view?: ViewType;\n /** Suppress tool call logging (useful for interactive shells) */\n suppressToolCallLogs?: boolean;\n /** Override TTY detection (useful for testing) */\n isTTY?: boolean;\n}\n\nexport class TerminalTracer {\n constructor(\n public readonly context: Context,\n public readonly options: TerminalTracerOptions = {},\n ) {}\n\n private result: Message = {};\n private usage: ContextUsage = newEmptyContextUsage();\n\n /** Get TTY status, using options.isTTY if provided, otherwise process.stdout.isTTY */\n private get isTTY(): boolean {\n return this.options.isTTY ?? process.stdout.isTTY ?? false;\n }\n\n async run(agent: Agent, input: Message, options?: InvokeOptions) {\n await this.context.observer?.serve();\n\n const context = this.context.newContext({ reset: true });\n\n // Print request\n const request = this.formatRequest(agent, context, input);\n if (request) console.log(request);\n\n const collapsedMap = new Map<\n string,\n { ancestor: { contextId: string }; usage: ContextUsage; models: Set<string> }\n >();\n const hideContextIds = new Set<string>();\n\n const onStart: AgentHooks[\"onStart\"] = async ({ context, agent }) => {\n const result = { options: { prompts: this.proxiedPrompts } };\n\n if (agent instanceof UserAgent) return result;\n\n if (agent.taskRenderMode === \"hide\") {\n hideContextIds.add(context.id);\n return result;\n }\n\n if (agent.taskRenderMode === \"collapse\") {\n collapsedMap.set(context.id, {\n ancestor: { contextId: context.id },\n usage: newEmptyContextUsage(),\n models: new Set(),\n });\n }\n\n if (context.parentId) {\n if (hideContextIds.has(context.parentId)) {\n hideContextIds.add(context.id);\n return result;\n }\n\n const collapsed = collapsedMap.get(context.parentId);\n if (collapsed) {\n collapsedMap.set(context.id, collapsed);\n return result;\n }\n }\n\n return result;\n };\n\n const onSuccess: AgentHooks[\"onSuccess\"] = async ({ context, agent, output }) => {\n const collapsed = collapsedMap.get(context.id);\n if (collapsed) {\n if (agent instanceof ChatModel) {\n const { usage, model } = output as ChatModelOutput;\n if (usage) mergeContextUsage(collapsed.usage, usage);\n if (model) collapsed.models.add(model);\n }\n return;\n }\n\n if (agent instanceof ChatModel) {\n const { usage } = output as ChatModelOutput;\n if (usage) mergeContextUsage(this.usage, usage);\n }\n };\n\n let retryPromptPromise: Promise<{ retry?: boolean }> | undefined;\n\n const onError: AgentHooks[\"onError\"] = async ({ agent, error }) => {\n if (\"type\" in error && error.type === AIGNE_HUB_CREDITS_NOT_ENOUGH_ERROR_TYPE) {\n if (!Object.hasOwn(error, CREDITS_ERROR_PROCESSED_FLAG)) {\n Object.defineProperty(error, CREDITS_ERROR_PROCESSED_FLAG, {\n value: true,\n enumerable: false,\n });\n\n const retry = await this.promptBuyCredits(error);\n\n console.log(\"\");\n\n if (retry === \"retry\") {\n return { retry: true };\n }\n }\n }\n\n if (agent instanceof ChatModel) {\n retryPromptPromise ??= this.proxiedPrompts\n .select({\n message: chalk.red(`Error: ${error.message}`),\n choices: [\n { value: \"retry\", name: \"Retry\" },\n { value: \"exit\", name: \"Exit\" },\n ],\n })\n .then((result) => ({ retry: result === \"retry\" }))\n .finally(() => {\n retryPromptPromise = undefined;\n });\n\n const { retry } = await retryPromptPromise;\n if (retry) return { retry: true };\n }\n };\n\n const stream = await context.invoke(agent, input, {\n ...options,\n hooks: flat(\n {\n priority: \"high\",\n onStart,\n onSuccess,\n onError,\n },\n options?.hooks,\n ),\n streaming: true,\n newContext: false,\n });\n\n this.result = await this.processStream(stream);\n\n // Print result (streamed: true since content was already printed via streaming)\n console.log(\n await this.formatResult(agent, context, this.result, {\n running: false,\n renderImage: true,\n streamed: true,\n }),\n );\n\n return { result: this.result, context };\n }\n\n private proxiedPrompts = new Proxy(\n {},\n {\n get: (_target, prop) => {\n const method =\n prop === \"checkbox\"\n ? checkbox\n : prop === \"input\"\n ? terminalInput\n : // biome-ignore lint/performance/noDynamicNamespaceImportAccess: we need to access prompts dynamically\n (prompts[prop as keyof typeof prompts] as (...args: any[]) => any);\n if (typeof method !== \"function\")\n throw new Error(`Unsupported prompt method ${String(prop)}`);\n\n return async (config: any) => {\n return await method({ ...config });\n };\n },\n },\n ) as typeof prompts;\n\n private buyCreditsPromptPromise: Promise<\"retry\" | \"exit\"> | undefined;\n\n private async promptBuyCredits(error: Error) {\n // Avoid multiple agents asking for credits, we will only show the prompt once\n this.buyCreditsPromptPromise ??= (async () => {\n const retry = await this.proxiedPrompts.select({\n message: highlightUrl(error.message),\n choices: [\n {\n name: \"I have bought some credits, try again\",\n value: \"retry\" as const,\n },\n {\n name: \"Exit\",\n value: \"exit\" as const,\n },\n ],\n });\n\n return retry;\n })();\n\n return this.buyCreditsPromptPromise\n .catch(() => \"exit\")\n .finally(() => {\n // Clear the promise so that we can show the prompt again if needed\n this.buyCreditsPromptPromise = undefined;\n });\n }\n\n private marked = new Marked().use(\n {\n // marked-terminal does not support code block meta, so we need to strip it\n walkTokens: (token) => {\n if (token.type === \"code\") {\n if (typeof token.lang === \"string\") {\n token.lang = token.lang.trim().split(/\\s+/)[0];\n }\n }\n },\n },\n markedTerminal(\n { forceHyperLink: false },\n {\n theme: {\n string: chalk.green,\n },\n },\n ),\n );\n\n private async processStream(stream: AgentResponseStream<Message>): Promise<Message> {\n const result: Message = {};\n\n // Show thinking indicator while waiting for response\n const spinnerFrames = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\n let spinnerIndex = 0;\n let spinnerInterval: ReturnType<typeof setInterval> | undefined;\n\n if (this.isTTY) {\n // Write first frame immediately\n process.stdout.write(`${spinnerFrames[0]} ${chalk.gray(\"Thinking...\")}`);\n spinnerInterval = setInterval(() => {\n spinnerIndex = (spinnerIndex + 1) % spinnerFrames.length;\n process.stdout.write(`\\r${spinnerFrames[spinnerIndex]} ${chalk.gray(\"Thinking...\")}`);\n }, 80);\n }\n\n const stopThinking = () => {\n if (spinnerInterval) {\n clearInterval(spinnerInterval);\n spinnerInterval = undefined;\n // Just move to a new line - leave \"Thinking...\" visible as a log\n console.log();\n }\n };\n\n for await (const value of stream) {\n mergeAgentResponseChunk(result, value);\n\n if (isAgentResponseProgress(value) && value.progress.event === \"message\") {\n const { message } = value.progress;\n\n // Skip user messages - they're already printed by formatRequest\n if (message.role === \"user\") continue;\n\n // Stop thinking indicator when any agent message arrives (including tool calls)\n if (message.role === \"agent\") {\n stopThinking();\n stopThinkingIndicator(); // Also stop ask_user's thinking indicator\n }\n\n const rendered: string[] = [];\n\n if (message.role === \"agent\") {\n if (message.toolCalls) {\n // Skip tool call logging if suppressToolCallLogs is enabled\n if (!this.options.suppressToolCallLogs) {\n for (const call of message.toolCalls) {\n rendered.push(\n `${chalk.bold.gray(`[${call.function.name}]`)} ${chalk.gray(`${JSON.stringify(call.function.arguments).slice(0, 200)}...`)}`,\n );\n }\n }\n } else if (typeof message.content === \"string\") {\n rendered.push(this.marked.parse(message.content, { async: false }).trim());\n } else if (Array.isArray(message.content)) {\n for (const msg of message.content) {\n if (msg.type === \"text\") {\n if (msg.isThinking) {\n rendered.push(chalk.dim(chalk.grey(chalk.italic(`[Thinking] ${msg.text}`))));\n } else {\n rendered.push(this.marked.parse(msg.text, { async: false }).trim());\n }\n }\n }\n }\n }\n\n if (rendered.length) {\n console.log(`${chalk.green.bold(\"•\")} ${rendered.join(\"\\n\")}\\n`);\n }\n }\n }\n\n // Ensure spinner is stopped even if no messages came through\n stopThinking();\n\n return result;\n }\n\n formatAigneHubCredits(credits: number, creditPrefix?: string): [string, string] {\n const hasDecimal = credits % 1 !== 0;\n const formattedCredits = hasDecimal\n ? parseFloat(credits.toFixed(6)).toString()\n : credits.toFixed();\n\n if (creditPrefix) {\n return [chalk.grey(\"cost:\"), chalk.blue(`${creditPrefix}${formattedCredits}`)];\n }\n\n return [chalk.blue(formattedCredits), chalk.grey(\"AIGNE Hub credits\")];\n }\n\n formatTokenUsage(usage: Partial<ContextUsage>, extra?: { [key: string]: string }) {\n const items = [\n [chalk.yellow(usage.inputTokens), chalk.grey(\"input tokens\")],\n usage.cacheReadInputTokens\n ? [chalk.green(usage.cacheReadInputTokens), chalk.grey(\"cached\")]\n : undefined,\n usage.cacheCreationInputTokens\n ? [chalk.yellow(usage.cacheCreationInputTokens), chalk.grey(\"cache write\")]\n : undefined,\n [chalk.cyan(usage.outputTokens), chalk.grey(\"output tokens\")],\n usage.aigneHubCredits\n ? this.formatAigneHubCredits(usage.aigneHubCredits, usage.creditPrefix)\n : undefined,\n usage.agentCalls ? [chalk.magenta(usage.agentCalls), chalk.grey(\"agent calls\")] : undefined,\n ];\n\n const content = items.filter((i) => !!i).map((i) => i.join(\" \"));\n\n if (extra) {\n content.unshift(\n ...Object.entries(extra)\n .filter(([k, v]) => k && v)\n .map(([k, v]) => `${chalk.grey(k)}: ${v}`),\n );\n }\n\n return `${chalk.grey(\"(\")}${content.join(chalk.green(\", \"))}${chalk.grey(\")\")}`;\n }\n\n formatTimeUsage(startTime: number, endTime: number) {\n const duration = endTime - startTime;\n return chalk.grey(`[${parseDuration(duration)}]`);\n }\n\n get outputKey() {\n return this.options.outputKey || DEFAULT_OUTPUT_KEY;\n }\n\n get outputFileKey() {\n return this.options.outputFileKey || DEFAULT_OUTPUT_FILE_KEY;\n }\n\n formatRequest(agent: Agent, _context: Context, m: Message = {}, { running = false } = {}) {\n const prefix = `${chalk.grey(\"→\")} 💬 `;\n\n // For AIAgent, use its inputKey; for UserAgent, default to \"message\"\n const inputKey =\n agent instanceof AIAgent\n ? agent.inputKey\n : agent instanceof UserAgent\n ? \"message\"\n : undefined;\n\n const msg = inputKey ? m[inputKey] : undefined;\n const message = inputKey ? omit(m, inputKey) : m;\n\n const text =\n msg && typeof msg === \"string\" ? this.marked.parse(msg, { async: false }).trim() : undefined;\n\n const json =\n Object.keys(message).length > 0\n ? inspect(message, { colors: true, ...(running ? this.runningInspectOptions : undefined) })\n : undefined;\n\n const r = [text, json].filter(Boolean).join(EOL).trim();\n if (!r) return undefined;\n\n return `${prefix}${r}`;\n }\n\n formatResult(\n agent: Agent,\n context: Context,\n m: Message = {},\n { running = false, renderImage = false, streamed = false } = {},\n ): string | Promise<string> {\n const view = this.options.view;\n\n // Handle structured output modes (json, llm, default/machine-truth)\n if (view === \"json\") {\n return formatJSON(m);\n }\n\n if (view === \"llm\") {\n const fields = Object.keys(m);\n return formatLLMDSL(m, fields);\n }\n\n if (view === \"default\") {\n // Machine truth: just the main output value\n const outputKey = this.outputKey || (agent instanceof AIAgent ? agent.outputKey : undefined);\n const msg = outputKey ? m[outputKey] : undefined;\n if (msg && typeof msg === \"string\") {\n return msg;\n }\n // Fallback to JSON if no string output\n return JSON.stringify(m);\n }\n\n // Human view (default for TTY)\n const isTTY = this.isTTY;\n const outputKey = this.outputKey || (agent instanceof AIAgent ? agent.outputKey : undefined);\n\n const prefix = `${chalk.grey(\"✓\")} 🤖 ${this.formatTokenUsage(context.usage)}`;\n\n // When streaming, content was already printed via processStream\n // Only show the stats prefix line, no need to duplicate text or show JSON\n if (streamed) {\n if (renderImage) {\n return this.formatResultData(m).then((images) => {\n return [prefix, images].filter(Boolean).join(EOL.repeat(2));\n });\n }\n return prefix;\n }\n\n const msg = outputKey ? m[outputKey] : undefined;\n const message = outputKey ? omit(m, outputKey, this.outputFileKey) : m;\n\n const text =\n msg && typeof msg === \"string\"\n ? isTTY\n ? this.marked.parse(msg, { async: false }).trim()\n : msg\n : undefined;\n\n const json =\n Object.keys(message).length > 0\n ? inspect(message, { colors: isTTY, ...(running ? this.runningInspectOptions : undefined) })\n : undefined;\n\n if (renderImage) {\n return this.formatResultData(m).then((images) => {\n return [prefix, text, images, json].filter(Boolean).join(EOL.repeat(2));\n });\n }\n\n return [prefix, text, json].filter(Boolean).join(EOL.repeat(2));\n }\n\n async formatResultData(output: Message): Promise<string | undefined> {\n const files = output[this.outputFileKey] as FileUnionContent[];\n if (!Array.isArray(files)) return;\n\n const options: Parameters<typeof terminalImage.file>[1] = {\n height: 30,\n };\n\n return (\n await Promise.all(\n files.map(async (item) => {\n const image =\n item.type === \"local\"\n ? await terminalImage.file(item.path, options)\n : item.type === \"file\"\n ? await terminalImage.buffer(Buffer.from(item.data, \"base64\"), options)\n : undefined;\n\n const link =\n item.type === \"local\"\n ? withProtocol(item.path, \"file://\")\n : item.type === \"url\"\n ? item.url\n : undefined;\n const text = [\n link ? chalk.cyan(terminalLink(link, link)) : undefined,\n item.filename,\n item.mimeType ? chalk.gray(item.mimeType) : undefined,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return [image, text].filter(Boolean).join(EOL);\n }),\n )\n )\n .filter(Boolean)\n .join(EOL);\n }\n\n protected runningInspectOptions: InspectOptions = {\n maxArrayLength: 3,\n maxStringLength: 200,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs');
2
+ let chalk = require("chalk");
3
+ chalk = require_rolldown_runtime.__toESM(chalk);
4
+ let ink = require("ink");
5
+ let react = require("react");
6
+ let react_jsx_runtime = require("react/jsx-runtime");
7
+
8
+ //#region src/ui/utils/terminal-select.tsx
9
+ async function terminalSelect({ render: r = ink.render, message, choices, clear = true }) {
10
+ if (process.stdin.isTTY) {
11
+ process.stdin.resume();
12
+ if (process.stdin.setRawMode) process.stdin.setRawMode(false);
13
+ }
14
+ return new Promise((resolve, reject) => {
15
+ const handleSigInt = () => {
16
+ reject(/* @__PURE__ */ new Error("Selection aborted"));
17
+ };
18
+ process.addListener("SIGINT", handleSigInt);
19
+ const clean = () => process.removeListener("SIGINT", handleSigInt);
20
+ const app = r(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TerminalSelect, {
21
+ message,
22
+ choices,
23
+ onSubmit: (value) => {
24
+ if (clear) app.clear();
25
+ app.unmount();
26
+ resolve(value);
27
+ clean();
28
+ },
29
+ onCancel: () => {
30
+ if (clear) app.clear();
31
+ app.unmount();
32
+ reject(/* @__PURE__ */ new Error("Selection cancelled"));
33
+ clean();
34
+ }
35
+ }), { exitOnCtrlC: false });
36
+ });
37
+ }
38
+ function TerminalSelect(props) {
39
+ const [selectedIndex, setSelectedIndex] = (0, react.useState)(0);
40
+ (0, ink.useInput)((input, key) => {
41
+ if (input === "c" && key.ctrl) {
42
+ props.onCancel();
43
+ return;
44
+ }
45
+ if (key.upArrow) setSelectedIndex((prev) => prev > 0 ? prev - 1 : props.choices.length - 1);
46
+ else if (key.downArrow) setSelectedIndex((prev) => prev < props.choices.length - 1 ? prev + 1 : 0);
47
+ else if (key.return) {
48
+ const choice = props.choices[selectedIndex];
49
+ if (choice) props.onSubmit(choice.value);
50
+ }
51
+ });
52
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Box, {
53
+ flexDirection: "column",
54
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Text, { children: [
55
+ chalk.default.cyan("?"),
56
+ " ",
57
+ chalk.default.bold(props.message),
58
+ " ",
59
+ chalk.default.gray("(Use arrow keys)")
60
+ ] }), props.choices.map((choice, index) => {
61
+ const isSelected = index === selectedIndex;
62
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Text, { children: [
63
+ isSelected ? chalk.default.cyan("❯") : " ",
64
+ " ",
65
+ isSelected ? chalk.default.cyan(choice.name) : choice.name,
66
+ choice.description ? chalk.default.gray(` - ${choice.description}`) : ""
67
+ ] }, choice.value);
68
+ })]
69
+ });
70
+ }
71
+
72
+ //#endregion
73
+ exports.terminalSelect = terminalSelect;
@@ -0,0 +1,72 @@
1
+ import chalk from "chalk";
2
+ import { Box, Text, render, useInput } from "ink";
3
+ import { useState } from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+
6
+ //#region src/ui/utils/terminal-select.tsx
7
+ async function terminalSelect({ render: r = render, message, choices, clear = true }) {
8
+ if (process.stdin.isTTY) {
9
+ process.stdin.resume();
10
+ if (process.stdin.setRawMode) process.stdin.setRawMode(false);
11
+ }
12
+ return new Promise((resolve, reject) => {
13
+ const handleSigInt = () => {
14
+ reject(/* @__PURE__ */ new Error("Selection aborted"));
15
+ };
16
+ process.addListener("SIGINT", handleSigInt);
17
+ const clean = () => process.removeListener("SIGINT", handleSigInt);
18
+ const app = r(/* @__PURE__ */ jsx(TerminalSelect, {
19
+ message,
20
+ choices,
21
+ onSubmit: (value) => {
22
+ if (clear) app.clear();
23
+ app.unmount();
24
+ resolve(value);
25
+ clean();
26
+ },
27
+ onCancel: () => {
28
+ if (clear) app.clear();
29
+ app.unmount();
30
+ reject(/* @__PURE__ */ new Error("Selection cancelled"));
31
+ clean();
32
+ }
33
+ }), { exitOnCtrlC: false });
34
+ });
35
+ }
36
+ function TerminalSelect(props) {
37
+ const [selectedIndex, setSelectedIndex] = useState(0);
38
+ useInput((input, key) => {
39
+ if (input === "c" && key.ctrl) {
40
+ props.onCancel();
41
+ return;
42
+ }
43
+ if (key.upArrow) setSelectedIndex((prev) => prev > 0 ? prev - 1 : props.choices.length - 1);
44
+ else if (key.downArrow) setSelectedIndex((prev) => prev < props.choices.length - 1 ? prev + 1 : 0);
45
+ else if (key.return) {
46
+ const choice = props.choices[selectedIndex];
47
+ if (choice) props.onSubmit(choice.value);
48
+ }
49
+ });
50
+ return /* @__PURE__ */ jsxs(Box, {
51
+ flexDirection: "column",
52
+ children: [/* @__PURE__ */ jsxs(Text, { children: [
53
+ chalk.cyan("?"),
54
+ " ",
55
+ chalk.bold(props.message),
56
+ " ",
57
+ chalk.gray("(Use arrow keys)")
58
+ ] }), props.choices.map((choice, index) => {
59
+ const isSelected = index === selectedIndex;
60
+ return /* @__PURE__ */ jsxs(Text, { children: [
61
+ isSelected ? chalk.cyan("❯") : " ",
62
+ " ",
63
+ isSelected ? chalk.cyan(choice.name) : choice.name,
64
+ choice.description ? chalk.gray(` - ${choice.description}`) : ""
65
+ ] }, choice.value);
66
+ })]
67
+ });
68
+ }
69
+
70
+ //#endregion
71
+ export { terminalSelect };
72
+ //# sourceMappingURL=terminal-select.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-select.mjs","names":[],"sources":["../../../src/ui/utils/terminal-select.tsx"],"sourcesContent":["import chalk from \"chalk\";\nimport { Box, render, Text, useInput } from \"ink\";\nimport { useState } from \"react\";\n\nexport interface SelectChoice {\n name: string;\n value: string;\n description?: string;\n}\n\nexport async function terminalSelect({\n render: r = render,\n message,\n choices,\n clear = true,\n}: {\n message: string;\n choices: SelectChoice[];\n render?: typeof render;\n clear?: boolean;\n}): Promise<string> {\n // Ensure stdin is ready for Ink (previous Ink app may have left it in a bad state)\n if (process.stdin.isTTY) {\n process.stdin.resume();\n // Ensure raw mode is off so Ink can set it up fresh\n if (process.stdin.setRawMode) {\n process.stdin.setRawMode(false);\n }\n }\n\n return new Promise<string>((resolve, reject) => {\n const handleSigInt = () => {\n reject(new Error(\"Selection aborted\"));\n };\n process.addListener(\"SIGINT\", handleSigInt);\n const clean = () => process.removeListener(\"SIGINT\", handleSigInt);\n\n const app = r(\n <TerminalSelect\n message={message}\n choices={choices}\n onSubmit={(value) => {\n if (clear) app.clear();\n app.unmount();\n resolve(value);\n clean();\n }}\n onCancel={() => {\n if (clear) app.clear();\n app.unmount();\n reject(new Error(\"Selection cancelled\"));\n clean();\n }}\n />,\n { exitOnCtrlC: false },\n );\n });\n}\n\nfunction TerminalSelect(props: {\n message: string;\n choices: SelectChoice[];\n onSubmit: (value: string) => void;\n onCancel: () => void;\n}) {\n const [selectedIndex, setSelectedIndex] = useState(0);\n\n useInput((input, key) => {\n if (input === \"c\" && key.ctrl) {\n props.onCancel();\n return;\n }\n\n if (key.upArrow) {\n setSelectedIndex((prev) => (prev > 0 ? prev - 1 : props.choices.length - 1));\n } else if (key.downArrow) {\n setSelectedIndex((prev) => (prev < props.choices.length - 1 ? prev + 1 : 0));\n } else if (key.return) {\n const choice = props.choices[selectedIndex];\n if (choice) {\n props.onSubmit(choice.value);\n }\n }\n });\n\n return (\n <Box flexDirection=\"column\">\n <Text>\n {chalk.cyan(\"?\")} {chalk.bold(props.message)} {chalk.gray(\"(Use arrow keys)\")}\n </Text>\n {props.choices.map((choice, index) => {\n const isSelected = index === selectedIndex;\n const prefix = isSelected ? chalk.cyan(\"❯\") : \" \";\n const text = isSelected ? chalk.cyan(choice.name) : choice.name;\n const desc = choice.description ? chalk.gray(` - ${choice.description}`) : \"\";\n return (\n <Text key={choice.value}>\n {prefix} {text}\n {desc}\n </Text>\n );\n })}\n </Box>\n );\n}\n"],"mappings":";;;;;;AAUA,eAAsB,eAAe,EACnC,QAAQ,IAAI,QACZ,SACA,SACA,QAAQ,QAMU;AAElB,KAAI,QAAQ,MAAM,OAAO;AACvB,UAAQ,MAAM,QAAQ;AAEtB,MAAI,QAAQ,MAAM,WAChB,SAAQ,MAAM,WAAW,MAAM;;AAInC,QAAO,IAAI,SAAiB,SAAS,WAAW;EAC9C,MAAM,qBAAqB;AACzB,0BAAO,IAAI,MAAM,oBAAoB,CAAC;;AAExC,UAAQ,YAAY,UAAU,aAAa;EAC3C,MAAM,cAAc,QAAQ,eAAe,UAAU,aAAa;EAElE,MAAM,MAAM,EACV,oBAAC;GACU;GACA;GACT,WAAW,UAAU;AACnB,QAAI,MAAO,KAAI,OAAO;AACtB,QAAI,SAAS;AACb,YAAQ,MAAM;AACd,WAAO;;GAET,gBAAgB;AACd,QAAI,MAAO,KAAI,OAAO;AACtB,QAAI,SAAS;AACb,2BAAO,IAAI,MAAM,sBAAsB,CAAC;AACxC,WAAO;;IAET,EACF,EAAE,aAAa,OAAO,CACvB;GACD;;AAGJ,SAAS,eAAe,OAKrB;CACD,MAAM,CAAC,eAAe,oBAAoB,SAAS,EAAE;AAErD,WAAU,OAAO,QAAQ;AACvB,MAAI,UAAU,OAAO,IAAI,MAAM;AAC7B,SAAM,UAAU;AAChB;;AAGF,MAAI,IAAI,QACN,mBAAkB,SAAU,OAAO,IAAI,OAAO,IAAI,MAAM,QAAQ,SAAS,EAAG;WACnE,IAAI,UACb,mBAAkB,SAAU,OAAO,MAAM,QAAQ,SAAS,IAAI,OAAO,IAAI,EAAG;WACnE,IAAI,QAAQ;GACrB,MAAM,SAAS,MAAM,QAAQ;AAC7B,OAAI,OACF,OAAM,SAAS,OAAO,MAAM;;GAGhC;AAEF,QACE,qBAAC;EAAI,eAAc;aACjB,qBAAC;GACE,MAAM,KAAK,IAAI;GAAC;GAAE,MAAM,KAAK,MAAM,QAAQ;GAAC;GAAE,MAAM,KAAK,mBAAmB;MACxE,EACN,MAAM,QAAQ,KAAK,QAAQ,UAAU;GACpC,MAAM,aAAa,UAAU;AAI7B,UACE,qBAAC;IAJY,aAAa,MAAM,KAAK,IAAI,GAAG;IAKlC;IAJC,aAAa,MAAM,KAAK,OAAO,KAAK,GAAG,OAAO;IAC9C,OAAO,cAAc,MAAM,KAAK,MAAM,OAAO,cAAc,GAAG;QAE9D,OAAO,MAGX;IAET;GACE"}
@@ -1,8 +1,8 @@
1
1
  const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
- let node_path = require("node:path");
3
2
  let node_fs_promises = require("node:fs/promises");
4
- let yaml = require("yaml");
3
+ let node_path = require("node:path");
5
4
  let glob = require("glob");
5
+ let yaml = require("yaml");
6
6
 
7
7
  //#region src/utils/agent-v1.ts
8
8
  async function isV1Package(src) {
@@ -1,7 +1,7 @@
1
- import { join } from "node:path";
2
1
  import { readFile, stat, writeFile } from "node:fs/promises";
3
- import { parse, stringify } from "yaml";
2
+ import { join } from "node:path";
4
3
  import { glob } from "glob";
4
+ import { parse, stringify } from "yaml";
5
5
 
6
6
  //#region src/utils/agent-v1.ts
7
7
  async function isV1Package(src) {