@aiscene/shared 8.0.2 → 8.0.4

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 (177) hide show
  1. package/dist/es/agent-tools/agent-behavior-init-args.mjs +44 -0
  2. package/dist/es/agent-tools/base-tools.mjs +163 -0
  3. package/dist/es/agent-tools/chrome-path.mjs +50 -0
  4. package/dist/es/agent-tools/cli-report-session.mjs +78 -0
  5. package/dist/es/agent-tools/error-formatter.mjs +106 -0
  6. package/dist/es/agent-tools/index.mjs +9 -0
  7. package/dist/es/agent-tools/init-arg-utils.mjs +38 -0
  8. package/dist/es/agent-tools/observation-artifact.mjs +5 -0
  9. package/dist/es/agent-tools/observation-record.mjs +248 -0
  10. package/dist/es/agent-tools/tool-defaults.mjs +54 -0
  11. package/dist/es/agent-tools/tool-generator.mjs +529 -0
  12. package/dist/es/agent-tools/types.mjs +3 -0
  13. package/dist/es/agent-tools/user-prompt.mjs +66 -0
  14. package/dist/es/cli/cli-args.mjs +95 -0
  15. package/dist/es/cli/cli-error.mjs +24 -0
  16. package/dist/es/cli/cli-runner.mjs +10 -40
  17. package/dist/es/cli/index.mjs +4 -2
  18. package/dist/es/cli/interrupt.mjs +116 -0
  19. package/dist/es/cli/record-command.mjs +130 -0
  20. package/dist/es/cli/screenshot-file.mjs +24 -0
  21. package/dist/es/cli/verbose-ai-act.mjs +230 -0
  22. package/dist/es/cli/verbose-screenshot.mjs +134 -0
  23. package/dist/es/cli/verbose.mjs +404 -0
  24. package/dist/es/constants/example-code.mjs +2 -2
  25. package/dist/es/constants/index.mjs +6 -1
  26. package/dist/es/env/model-config-manager.mjs +3 -0
  27. package/dist/es/env/parse-model-config.mjs +6 -1
  28. package/dist/es/env/types.mjs +4 -1
  29. package/dist/es/env/utils.mjs +1 -9
  30. package/dist/es/extractor/index.mjs +2 -2
  31. package/dist/es/img/index.mjs +2 -2
  32. package/dist/es/img/info.mjs +58 -17
  33. package/dist/es/img/photon-loader.mjs +4 -0
  34. package/dist/es/key-alias-utils.mjs +19 -0
  35. package/dist/es/mcp/base-server.mjs +10 -9
  36. package/dist/es/mcp/base-tools.mjs +72 -4
  37. package/dist/es/mcp/cli-report-session.mjs +78 -0
  38. package/dist/es/mcp/error-formatter.mjs +19 -0
  39. package/dist/es/mcp/index.mjs +2 -0
  40. package/dist/es/mcp/init-arg-utils.mjs +38 -0
  41. package/dist/es/mcp/inject-report-html-plugin.mjs +1 -1
  42. package/dist/es/mcp/tool-generator.mjs +65 -17
  43. package/dist/es/node/fs.mjs +1 -1
  44. package/dist/es/recorder.mjs +249 -0
  45. package/dist/lib/agent-tools/agent-behavior-init-args.js +87 -0
  46. package/dist/lib/agent-tools/base-tools.js +197 -0
  47. package/dist/lib/agent-tools/chrome-path.js +87 -0
  48. package/dist/lib/agent-tools/cli-report-session.js +121 -0
  49. package/dist/lib/agent-tools/error-formatter.js +149 -0
  50. package/dist/lib/agent-tools/index.js +114 -0
  51. package/dist/lib/agent-tools/init-arg-utils.js +78 -0
  52. package/dist/lib/agent-tools/observation-artifact.js +42 -0
  53. package/dist/lib/agent-tools/observation-record.js +297 -0
  54. package/dist/lib/agent-tools/tool-defaults.js +97 -0
  55. package/dist/lib/agent-tools/tool-generator.js +569 -0
  56. package/dist/lib/agent-tools/types.js +40 -0
  57. package/dist/lib/agent-tools/user-prompt.js +103 -0
  58. package/dist/lib/cli/cli-args.js +138 -0
  59. package/dist/lib/cli/cli-error.js +61 -0
  60. package/dist/lib/cli/cli-runner.js +19 -46
  61. package/dist/lib/cli/index.js +8 -3
  62. package/dist/lib/cli/interrupt.js +156 -0
  63. package/dist/lib/cli/record-command.js +164 -0
  64. package/dist/lib/cli/screenshot-file.js +58 -0
  65. package/dist/lib/cli/verbose-ai-act.js +273 -0
  66. package/dist/lib/cli/verbose-screenshot.js +177 -0
  67. package/dist/lib/cli/verbose.js +465 -0
  68. package/dist/lib/constants/example-code.js +2 -2
  69. package/dist/lib/constants/index.js +23 -3
  70. package/dist/lib/env/model-config-manager.js +3 -0
  71. package/dist/lib/env/parse-model-config.js +6 -1
  72. package/dist/lib/env/types.js +9 -0
  73. package/dist/lib/env/utils.js +0 -11
  74. package/dist/lib/extractor/index.js +3 -0
  75. package/dist/lib/img/index.js +3 -0
  76. package/dist/lib/img/info.js +65 -18
  77. package/dist/lib/img/photon-loader.js +38 -0
  78. package/dist/lib/key-alias-utils.js +62 -0
  79. package/dist/lib/mcp/base-server.js +11 -10
  80. package/dist/lib/mcp/base-tools.js +74 -6
  81. package/dist/lib/mcp/cli-report-session.js +121 -0
  82. package/dist/lib/mcp/error-formatter.js +53 -0
  83. package/dist/lib/mcp/index.js +24 -10
  84. package/dist/lib/mcp/init-arg-utils.js +78 -0
  85. package/dist/lib/mcp/inject-report-html-plugin.js +1 -1
  86. package/dist/lib/mcp/tool-generator.js +74 -26
  87. package/dist/lib/node/fs.js +1 -1
  88. package/dist/lib/recorder.js +307 -0
  89. package/dist/types/agent-tools/agent-behavior-init-args.d.ts +17 -0
  90. package/dist/types/agent-tools/base-tools.d.ts +158 -0
  91. package/dist/types/agent-tools/chrome-path.d.ts +2 -0
  92. package/dist/types/agent-tools/cli-report-session.d.ts +12 -0
  93. package/dist/types/agent-tools/error-formatter.d.ts +30 -0
  94. package/dist/types/agent-tools/index.d.ts +9 -0
  95. package/dist/types/agent-tools/init-arg-utils.d.ts +13 -0
  96. package/dist/types/agent-tools/observation-artifact.d.ts +10 -0
  97. package/dist/types/agent-tools/observation-record.d.ts +38 -0
  98. package/dist/types/agent-tools/tool-defaults.d.ts +63 -0
  99. package/dist/types/agent-tools/tool-generator.d.ts +13 -0
  100. package/dist/types/agent-tools/types.d.ts +213 -0
  101. package/dist/types/agent-tools/user-prompt.d.ts +13 -0
  102. package/dist/types/cli/cli-args.d.ts +8 -0
  103. package/dist/types/cli/cli-error.d.ts +5 -0
  104. package/dist/types/cli/cli-runner.d.ts +4 -7
  105. package/dist/types/cli/index.d.ts +3 -1
  106. package/dist/types/cli/interrupt.d.ts +49 -0
  107. package/dist/types/cli/record-command.d.ts +3 -0
  108. package/dist/types/cli/screenshot-file.d.ts +10 -0
  109. package/dist/types/cli/verbose-ai-act.d.ts +44 -0
  110. package/dist/types/cli/verbose-screenshot.d.ts +10 -0
  111. package/dist/types/cli/verbose.d.ts +40 -0
  112. package/dist/types/constants/example-code.d.ts +1 -1
  113. package/dist/types/constants/index.d.ts +5 -0
  114. package/dist/types/env/types.d.ts +14 -5
  115. package/dist/types/env/utils.d.ts +0 -31
  116. package/dist/types/extractor/index.d.ts +1 -1
  117. package/dist/types/img/index.d.ts +1 -1
  118. package/dist/types/img/info.d.ts +7 -0
  119. package/dist/types/img/photon-loader.d.ts +2 -0
  120. package/dist/types/key-alias-utils.d.ts +9 -0
  121. package/dist/types/mcp/base-tools.d.ts +74 -5
  122. package/dist/types/mcp/cli-report-session.d.ts +12 -0
  123. package/dist/types/mcp/error-formatter.d.ts +12 -0
  124. package/dist/types/mcp/index.d.ts +2 -0
  125. package/dist/types/mcp/init-arg-utils.d.ts +13 -0
  126. package/dist/types/mcp/inject-report-html-plugin.d.ts +2 -2
  127. package/dist/types/mcp/launcher-helper.d.ts +3 -3
  128. package/dist/types/mcp/tool-generator.d.ts +3 -3
  129. package/dist/types/mcp/types.d.ts +10 -0
  130. package/dist/types/recorder.d.ts +113 -0
  131. package/package.json +1 -1
  132. package/src/agent-tools/agent-behavior-init-args.ts +109 -0
  133. package/src/agent-tools/base-tools.ts +399 -0
  134. package/src/agent-tools/chrome-path.ts +74 -0
  135. package/src/agent-tools/cli-report-session.ts +130 -0
  136. package/src/agent-tools/error-formatter.ts +177 -0
  137. package/src/agent-tools/index.ts +9 -0
  138. package/src/agent-tools/init-arg-utils.ts +105 -0
  139. package/src/agent-tools/observation-artifact.ts +29 -0
  140. package/src/agent-tools/observation-record.ts +331 -0
  141. package/src/agent-tools/tool-defaults.ts +119 -0
  142. package/src/agent-tools/tool-generator.ts +866 -0
  143. package/src/agent-tools/types.ts +250 -0
  144. package/src/agent-tools/user-prompt.ts +102 -0
  145. package/src/cli/cli-args.ts +173 -0
  146. package/src/cli/cli-error.ts +24 -0
  147. package/src/cli/cli-runner.ts +37 -56
  148. package/src/cli/index.ts +3 -7
  149. package/src/cli/interrupt.ts +207 -0
  150. package/src/cli/record-command.ts +177 -0
  151. package/src/cli/screenshot-file.ts +61 -0
  152. package/src/cli/verbose-ai-act.ts +387 -0
  153. package/src/cli/verbose-screenshot.ts +269 -0
  154. package/src/cli/verbose.ts +753 -0
  155. package/src/constants/example-code.ts +2 -2
  156. package/src/constants/index.ts +10 -0
  157. package/src/env/model-config-manager.ts +3 -0
  158. package/src/env/parse-model-config.ts +19 -1
  159. package/src/env/types.ts +13 -2
  160. package/src/env/utils.ts +0 -50
  161. package/src/extractor/index.ts +1 -1
  162. package/src/img/index.ts +14 -0
  163. package/src/img/info.ts +97 -45
  164. package/src/img/photon-loader.ts +5 -0
  165. package/src/img/transform.ts +261 -2
  166. package/src/key-alias-utils.ts +23 -0
  167. package/src/mcp/base-server.ts +10 -12
  168. package/src/mcp/base-tools.ts +218 -11
  169. package/src/mcp/cli-report-session.ts +130 -0
  170. package/src/mcp/error-formatter.ts +52 -0
  171. package/src/mcp/index.ts +2 -0
  172. package/src/mcp/init-arg-utils.ts +105 -0
  173. package/src/mcp/inject-report-html-plugin.ts +3 -3
  174. package/src/mcp/launcher-helper.ts +3 -3
  175. package/src/mcp/tool-generator.ts +98 -20
  176. package/src/mcp/types.ts +18 -1
  177. package/src/recorder.ts +625 -0
@@ -0,0 +1,24 @@
1
+ function _define_property(obj, key, value) {
2
+ if (key in obj) Object.defineProperty(obj, key, {
3
+ value: value,
4
+ enumerable: true,
5
+ configurable: true,
6
+ writable: true
7
+ });
8
+ else obj[key] = value;
9
+ return obj;
10
+ }
11
+ class CLIError extends Error {
12
+ constructor(message, exitCode = 1){
13
+ super(message), _define_property(this, "exitCode", void 0), this.exitCode = exitCode;
14
+ }
15
+ }
16
+ function reportCLIError(error, log = console.error) {
17
+ if (error instanceof CLIError) {
18
+ log(error.message);
19
+ return error.exitCode;
20
+ }
21
+ log(error);
22
+ return 1;
23
+ }
24
+ export { CLIError, reportCLIError };
@@ -3,44 +3,9 @@ import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import dotenv from "dotenv";
5
5
  import { getDebug } from "../logger.mjs";
6
- function _define_property(obj, key, value) {
7
- if (key in obj) Object.defineProperty(obj, key, {
8
- value: value,
9
- enumerable: true,
10
- configurable: true,
11
- writable: true
12
- });
13
- else obj[key] = value;
14
- return obj;
15
- }
6
+ import { formatCliValidationError, getCliOptionDisplay, parseCliArgs, parseValue } from "./cli-args.mjs";
7
+ import { CLIError, reportCLIError } from "./cli-error.mjs";
16
8
  const debug = getDebug('cli-runner');
17
- class CLIError extends Error {
18
- constructor(message, exitCode = 1){
19
- super(message), _define_property(this, "exitCode", void 0), this.exitCode = exitCode;
20
- }
21
- }
22
- function parseValue(raw) {
23
- if (raw.startsWith('{') || raw.startsWith('[')) try {
24
- return JSON.parse(raw);
25
- } catch {}
26
- if (/^-?\d+(\.\d+)?$/.test(raw)) return Number(raw);
27
- return raw;
28
- }
29
- function parseCliArgs(args) {
30
- const result = {};
31
- for(let i = 0; i < args.length; i++){
32
- const arg = args[i];
33
- if (!arg.startsWith('--')) continue;
34
- const body = arg.slice(2);
35
- const eqIdx = body.indexOf('=');
36
- if (eqIdx >= 0) result[body.slice(0, eqIdx)] = parseValue(body.slice(eqIdx + 1));
37
- else if (args[i + 1] && !args[i + 1].startsWith('--')) {
38
- i++;
39
- result[body] = parseValue(args[i]);
40
- } else result[body] = true;
41
- }
42
- return result;
43
- }
44
9
  function outputContentItem(item, isError) {
45
10
  switch(item.type){
46
11
  case 'text':
@@ -72,10 +37,13 @@ function printCommandHelp(scriptName, cmd) {
72
37
  console.log(def.description);
73
38
  const schemaEntries = Object.entries(def.schema);
74
39
  if (schemaEntries.length > 0) {
40
+ const optionWidth = Math.max(22, ...schemaEntries.map(([key])=>getCliOptionDisplay(key, def.cli?.options?.[key]).label.length));
75
41
  console.log('\nOptions:');
76
42
  for (const [key, zodType] of schemaEntries){
43
+ const { label, aliases } = getCliOptionDisplay(key, def.cli?.options?.[key]);
77
44
  const desc = zodType.description ?? '';
78
- console.log(` --${key.padEnd(20)} ${desc}`);
45
+ const aliasText = aliases.length > 0 ? ` (aliases: ${aliases.join(', ')})` : '';
46
+ console.log(` ${label.padEnd(optionWidth)} ${desc}${aliasText}`);
79
47
  }
80
48
  }
81
49
  }
@@ -137,16 +105,18 @@ async function runToolsCLI(tools, scriptName, options) {
137
105
  throw new CLIError(`Unknown command: ${commandName}`);
138
106
  }
139
107
  const parsedArgs = parseCliArgs(restArgs);
140
- debug('command: %s, args: %s', match.name, JSON.stringify(parsedArgs));
141
108
  if (true === parsedArgs.help) {
142
109
  debug('showing command help for: %s', match.name);
143
110
  printCommandHelp(scriptName, match);
144
111
  return;
145
112
  }
113
+ const cliValidationError = formatCliValidationError(scriptName, match.name, match.def, parsedArgs);
114
+ if (cliValidationError) throw new CLIError(cliValidationError);
115
+ debug('command: %s, args: %s', match.name, JSON.stringify(parsedArgs));
146
116
  const result = await match.def.handler(parsedArgs);
147
117
  debug('command %s completed, isError: %s', match.name, result.isError ?? false);
148
118
  outputResult(result);
149
119
  await tools.destroy();
150
120
  if (result.isError) throw new CLIError('Command failed', 1);
151
121
  }
152
- export { CLIError, parseCliArgs, parseValue, removePrefix, runToolsCLI };
122
+ export { CLIError, parseCliArgs, parseValue, removePrefix, reportCLIError, runToolsCLI };
@@ -1,2 +1,4 @@
1
- import { CLIError, parseCliArgs, parseValue, removePrefix, runToolsCLI } from "./cli-runner.mjs";
2
- export { CLIError, parseCliArgs, parseValue, removePrefix, runToolsCLI };
1
+ import { CLIError, reportCLIError } from "./cli-error.mjs";
2
+ import { parseCliArgs, parseValue } from "./cli-args.mjs";
3
+ import { removePrefix, runToolsCLI } from "./cli-runner.mjs";
4
+ export { CLIError, parseCliArgs, parseValue, removePrefix, reportCLIError, runToolsCLI };
@@ -0,0 +1,116 @@
1
+ const activeInterruptWaiters = new WeakMap();
2
+ const noop = ()=>{};
3
+ const sigintExitCode = 130;
4
+ function guardTerminalCtrlC(input, onInterrupt) {
5
+ if (!input?.isTTY || !input.setRawMode) return noop;
6
+ const wasRaw = true === input.isRaw;
7
+ const wasFlowing = true === input.readableFlowing;
8
+ const onData = (chunk)=>{
9
+ const includesCtrlC = 'string' == typeof chunk && chunk.includes('\u0003') || chunk instanceof Uint8Array && chunk.includes(3);
10
+ if (includesCtrlC) onInterrupt();
11
+ };
12
+ const dispose = ()=>{
13
+ input.removeListener('data', onData);
14
+ try {
15
+ if (!wasFlowing) input.pause?.();
16
+ } finally{
17
+ if (!wasRaw) input.setRawMode?.(false);
18
+ }
19
+ };
20
+ input.setRawMode(true);
21
+ try {
22
+ input.on('data', onData);
23
+ } catch (error) {
24
+ dispose();
25
+ throw error;
26
+ }
27
+ return dispose;
28
+ }
29
+ function registerCliInterruptWaiter(source) {
30
+ const key = source;
31
+ activeInterruptWaiters.set(key, (activeInterruptWaiters.get(key) ?? 0) + 1);
32
+ let active = true;
33
+ return ()=>{
34
+ if (!active) return;
35
+ active = false;
36
+ const remaining = (activeInterruptWaiters.get(key) ?? 1) - 1;
37
+ if (remaining > 0) activeInterruptWaiters.set(key, remaining);
38
+ else activeInterruptWaiters.delete(key);
39
+ };
40
+ }
41
+ function hasActiveCliInterruptWaiter(source = process) {
42
+ return (activeInterruptWaiters.get(source) ?? 0) > 0;
43
+ }
44
+ function createCliInterruptWaiter(watchdogMs, options = {}) {
45
+ const source = options.source ?? process;
46
+ const input = options.input ?? (source === process ? process.stdin : void 0);
47
+ const forceExit = options.forceExit ?? (source === process ? (exitCode)=>{
48
+ process.exit(exitCode);
49
+ } : void 0);
50
+ const unregisterWaiter = registerCliInterruptWaiter(source);
51
+ let timer;
52
+ let finished = false;
53
+ let disposed = false;
54
+ let disposeInput = noop;
55
+ let resolveResult;
56
+ let rejectResult;
57
+ const result = new Promise((resolve, reject)=>{
58
+ resolveResult = resolve;
59
+ rejectResult = reject;
60
+ });
61
+ const finish = (reason)=>{
62
+ if (finished) return;
63
+ finished = true;
64
+ if (timer) {
65
+ clearTimeout(timer);
66
+ timer = void 0;
67
+ }
68
+ resolveResult(reason);
69
+ };
70
+ const onSigint = ()=>finish('sigint');
71
+ const onSigterm = ()=>finish('sigterm');
72
+ const onSighup = ()=>finish('sighup');
73
+ function dispose() {
74
+ if (disposed) return;
75
+ disposed = true;
76
+ source.removeListener('SIGINT', onSigint);
77
+ source.removeListener('SIGTERM', onSigterm);
78
+ source.removeListener('SIGHUP', onSighup);
79
+ try {
80
+ disposeInput();
81
+ } finally{
82
+ if (timer) {
83
+ clearTimeout(timer);
84
+ timer = void 0;
85
+ }
86
+ unregisterWaiter();
87
+ }
88
+ }
89
+ const onTerminalCtrlC = ()=>{
90
+ if (!finished) return void finish('sigint');
91
+ dispose();
92
+ forceExit?.(sigintExitCode);
93
+ };
94
+ try {
95
+ source.on('SIGINT', onSigint);
96
+ source.on('SIGTERM', onSigterm);
97
+ source.on('SIGHUP', onSighup);
98
+ disposeInput = guardTerminalCtrlC(input, onTerminalCtrlC);
99
+ if (watchdogMs > 0) timer = setTimeout(()=>finish('watchdog'), watchdogMs);
100
+ } catch (error) {
101
+ dispose();
102
+ rejectResult(error);
103
+ }
104
+ return {
105
+ result,
106
+ dispose
107
+ };
108
+ }
109
+ function waitForCliInterrupt(watchdogMs, source = process, input = source === process ? process.stdin : void 0) {
110
+ const waiter = createCliInterruptWaiter(watchdogMs, {
111
+ source,
112
+ input
113
+ });
114
+ return waiter.result.finally(waiter.dispose);
115
+ }
116
+ export { createCliInterruptWaiter, hasActiveCliInterruptWaiter, waitForCliInterrupt };
@@ -0,0 +1,130 @@
1
+ import { z } from "zod";
2
+ import { getErrorMessage } from "../agent-tools/error-formatter.mjs";
3
+ import { resolveObservationArtifactAdapter } from "../agent-tools/observation-artifact.mjs";
4
+ import { writeUIObservationRecord } from "../agent-tools/observation-record.mjs";
5
+ import { createCliInterruptWaiter } from "./interrupt.mjs";
6
+ import { attachCliVerboseDumpListener, emitCliVerboseEvent } from "./verbose.mjs";
7
+ const recordCliMetadata = {
8
+ positionals: [
9
+ 'action'
10
+ ],
11
+ options: {
12
+ intervalMs: {
13
+ preferredName: 'interval-ms',
14
+ aliases: [
15
+ 'intervalMs'
16
+ ]
17
+ },
18
+ maxFrames: {
19
+ preferredName: 'max-frames',
20
+ aliases: [
21
+ 'maxFrames'
22
+ ]
23
+ },
24
+ watchdogMs: {
25
+ preferredName: 'watchdog-ms',
26
+ aliases: [
27
+ 'watchdogMs'
28
+ ]
29
+ },
30
+ action: {
31
+ hidden: true
32
+ }
33
+ }
34
+ };
35
+ function mergeCliMetadata(base, extra) {
36
+ return {
37
+ positionals: base.positionals,
38
+ options: {
39
+ ...base.options ?? {},
40
+ ...extra?.options ?? {}
41
+ }
42
+ };
43
+ }
44
+ function createErrorResult(message) {
45
+ return {
46
+ content: [
47
+ {
48
+ type: 'text',
49
+ text: message
50
+ }
51
+ ],
52
+ isError: true
53
+ };
54
+ }
55
+ function createRecordCliCommand(getAgent, initArgSchema = {}, initArgCliMetadata) {
56
+ return {
57
+ name: 'record',
58
+ description: 'Record the page/screen in the foreground until Ctrl+C or a termination signal, then save the ordered frame window for a later assert command.',
59
+ schema: {
60
+ action: z.literal('start').describe('Start a foreground recording. Press Ctrl+C to finish.'),
61
+ output: z.string().optional().describe('Path for the JSON observation manifest. Frames are stored in an adjacent <name>.frames directory. Defaults to a generated path under midscene_run/output.'),
62
+ intervalMs: z.number().min(200).optional().describe('Sampling interval in milliseconds. Defaults to 1000; minimum 200 (5fps).'),
63
+ maxFrames: z.number().int().min(2).optional().describe('Maximum buffered frames. Defaults to 30; minimum 2.'),
64
+ watchdogMs: z.number().min(0).optional().describe('Safety timeout in milliseconds. Defaults to 300000 (5 minutes); 0 disables it.'),
65
+ ...initArgSchema
66
+ },
67
+ cli: mergeCliMetadata(recordCliMetadata, initArgCliMetadata),
68
+ handler: async (args = {})=>{
69
+ if ('start' !== args.action) return createErrorResult('record requires the start operation (for example: record start --output ./observation.json)');
70
+ try {
71
+ const agent = await getAgent(args);
72
+ emitCliVerboseEvent({
73
+ event: 'agent_ready',
74
+ tool: 'record'
75
+ });
76
+ if (!agent.startObserving) throw new Error('record is not supported because this agent does not provide startObserving');
77
+ const observationArtifacts = resolveObservationArtifactAdapter(agent);
78
+ if (!observationArtifacts) throw new Error('record is not supported because this agent does not provide observation artifact persistence');
79
+ const unsubscribeVerbose = attachCliVerboseDumpListener(agent, {
80
+ toolName: 'record'
81
+ });
82
+ let observer;
83
+ let interruptWaiter;
84
+ try {
85
+ const watchdogMs = args.watchdogMs ?? 300000;
86
+ observer = await agent.startObserving({
87
+ intervalMs: args.intervalMs,
88
+ maxFrames: args.maxFrames,
89
+ watchdogMs
90
+ });
91
+ interruptWaiter = createCliInterruptWaiter(watchdogMs);
92
+ emitCliVerboseEvent({
93
+ event: 'recording_ready',
94
+ tool: 'record',
95
+ watchdogMs
96
+ });
97
+ const stopReason = await interruptWaiter.result;
98
+ emitCliVerboseEvent({
99
+ event: 'recording_stopping',
100
+ tool: 'record',
101
+ reason: stopReason
102
+ });
103
+ const observation = await observer.stop();
104
+ const record = await observationArtifacts.exportRecord(observation);
105
+ const outputPath = writeUIObservationRecord(record, args.output);
106
+ return {
107
+ content: [
108
+ {
109
+ type: 'text',
110
+ text: `Observation record saved: ${outputPath}`
111
+ }
112
+ ]
113
+ };
114
+ } finally{
115
+ try {
116
+ await observer?.dispose?.();
117
+ } finally{
118
+ interruptWaiter?.dispose();
119
+ unsubscribeVerbose();
120
+ }
121
+ }
122
+ } catch (error) {
123
+ const errorMessage = getErrorMessage(error);
124
+ console.error('Error executing record:', errorMessage);
125
+ return createErrorResult(`Failed to execute record: ${errorMessage}`);
126
+ }
127
+ }
128
+ };
129
+ }
130
+ export { createRecordCliCommand };
@@ -0,0 +1,24 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ function safeScreenshotFilenamePart(value) {
5
+ const text = 'string' == typeof value && value.length > 0 ? value : 'shot';
6
+ return text.replace(/[^a-zA-Z0-9._-]/g, '_') || 'shot';
7
+ }
8
+ function extensionFromImageMetadata(mimeType, extension) {
9
+ if ('jpeg' === extension || 'jpg' === extension) return 'jpeg';
10
+ if ('png' === extension) return 'png';
11
+ return 'image/jpeg' === mimeType ? 'jpeg' : 'png';
12
+ }
13
+ function writeCliScreenshotFile(rawBase64, options = {}) {
14
+ const extension = extensionFromImageMetadata(options.mimeType, options.extension);
15
+ const directory = options.directoryPath ? options.directoryPath : options.directoryName ? join(tmpdir(), options.directoryName) : tmpdir();
16
+ if (!existsSync(directory)) mkdirSync(directory, {
17
+ recursive: true
18
+ });
19
+ const filename = void 0 !== options.id ? `${safeScreenshotFilenamePart(options.id)}.${extension}` : `${options.filenamePrefix ?? 'screenshot'}-${Date.now()}.${extension}`;
20
+ const filePath = join(directory, filename);
21
+ if (false !== options.overwrite || !existsSync(filePath)) writeFileSync(filePath, Buffer.from(rawBase64, 'base64'));
22
+ return filePath;
23
+ }
24
+ export { writeCliScreenshotFile };
@@ -0,0 +1,230 @@
1
+ import { collectScreenshotRefs, pathForReportScreenshot } from "./verbose-screenshot.mjs";
2
+ const cliAiActProgressScope = 'aiAct';
3
+ function isRecord(value) {
4
+ return 'object' == typeof value && null !== value && !Array.isArray(value);
5
+ }
6
+ function compactText(value) {
7
+ if (null == value || '' === value) return '';
8
+ if ('string' == typeof value) return value.length > 180 ? `${value.slice(0, 177)}...` : value;
9
+ if ('number' == typeof value || 'boolean' == typeof value) return String(value);
10
+ try {
11
+ const json = JSON.stringify(value);
12
+ return json.length > 180 ? `${json.slice(0, 177)}...` : json;
13
+ } catch {
14
+ return String(value);
15
+ }
16
+ }
17
+ function numericValue(value) {
18
+ return 'number' == typeof value && Number.isFinite(value) ? value : void 0;
19
+ }
20
+ function firstText(values) {
21
+ for (const value of values){
22
+ const text = compactText(value);
23
+ if (text) return text;
24
+ }
25
+ return '';
26
+ }
27
+ function planPrefix(event) {
28
+ const planIndex = numericValue(event.planIndex);
29
+ if (!planIndex) return '[Midscene][aiAct]';
30
+ const planLimit = numericValue(event.planLimit);
31
+ return `[Midscene][aiAct][Plan ${Math.round(planIndex)}${planLimit ? `/${Math.round(planLimit)}` : ''}]`;
32
+ }
33
+ function eventKey(event, suffix) {
34
+ const sequence = numericValue(event.sequence);
35
+ if (sequence) return `aiAct:${sequence}:${suffix}`;
36
+ return [
37
+ 'aiAct',
38
+ compactText(event.phase),
39
+ compactText(event.planIndex),
40
+ suffix
41
+ ].filter(Boolean).join(':');
42
+ }
43
+ function integerText(value) {
44
+ return String(Math.round(value));
45
+ }
46
+ function numberTuple2(value) {
47
+ if (Array.isArray(value) && value.length >= 2 && 'number' == typeof value[0] && 'number' == typeof value[1]) return [
48
+ value[0],
49
+ value[1]
50
+ ];
51
+ }
52
+ function numberTuple4(value) {
53
+ if (Array.isArray(value) && value.length >= 4 && value.slice(0, 4).every((item)=>'number' == typeof item)) return [
54
+ value[0],
55
+ value[1],
56
+ value[2],
57
+ value[3]
58
+ ];
59
+ }
60
+ function parseAction(value) {
61
+ if (!isRecord(value) || 'string' != typeof value.name) return;
62
+ return {
63
+ name: value.name,
64
+ target: 'string' == typeof value.target ? value.target : void 0,
65
+ point: numberTuple2(value.point),
66
+ bbox: numberTuple4(value.bbox),
67
+ param: value.param
68
+ };
69
+ }
70
+ function formatPoint(point) {
71
+ return `(${integerText(point[0])}, ${integerText(point[1])})`;
72
+ }
73
+ function formatBbox(bbox) {
74
+ return `(${bbox.map(integerText).join(',')})`;
75
+ }
76
+ function sleepActionText(action) {
77
+ if ('Sleep' !== action.name || !isRecord(action.param)) return;
78
+ const timeMs = numericValue(action.param.timeMs) ?? numericValue(action.param.duration) ?? numericValue(action.param.timeoutMs);
79
+ return void 0 !== timeMs ? `Sleep ${integerText(timeMs)}ms` : void 0;
80
+ }
81
+ function plannedActionText(action) {
82
+ if (action.point) {
83
+ const targetSegment = action.target ? ` "${action.target}"` : '';
84
+ const bboxSegment = action.bbox ? `, bbox=${formatBbox(action.bbox)}` : '';
85
+ return `${action.name}${targetSegment} at ${formatPoint(action.point)}${bboxSegment}`;
86
+ }
87
+ const sleep = sleepActionText(action);
88
+ if (sleep) return sleep;
89
+ const paramText = compactText(action.param);
90
+ return paramText ? `${action.name}: ${paramText}` : action.name;
91
+ }
92
+ function runningActionText(action) {
93
+ if (action.point) return `${action.name} at ${formatPoint(action.point)}`;
94
+ return plannedActionText(action);
95
+ }
96
+ function normalizeAiActProgressEventForCli(event, screenshotOptions = {}) {
97
+ if (!isRecord(event) || event.scope !== cliAiActProgressScope) return;
98
+ const data = isRecord(event.data) ? event.data : {};
99
+ const screenshots = collectScreenshotRefs(data.screenshot, screenshotOptions);
100
+ const latestPath = screenshots.slice().reverse().find((item)=>'string' == typeof item.path && item.path.length > 0)?.path;
101
+ const screenshotPath = 'string' == typeof latestPath ? pathForReportScreenshot(latestPath, screenshotOptions.reportFile) : void 0;
102
+ const { prompt, planIndex, planLimit, action, thought, log, output, durationMs, error } = data;
103
+ return {
104
+ phase: event.phase,
105
+ sequence: event.sequence,
106
+ prompt,
107
+ planIndex,
108
+ planLimit,
109
+ action,
110
+ thought,
111
+ log,
112
+ output,
113
+ durationMs,
114
+ error,
115
+ ...screenshots.length > 0 ? {
116
+ screenshots
117
+ } : {},
118
+ ...screenshotPath ? {
119
+ screenshotPath
120
+ } : {}
121
+ };
122
+ }
123
+ function buildAiActProgressEventLines(event) {
124
+ const phase = 'string' == typeof event.phase ? event.phase : '';
125
+ const prefix = planPrefix(event);
126
+ const error = compactText(event.error);
127
+ const duration = 'number' == typeof event.durationMs ? ` cost=${Math.round(event.durationMs)}ms` : '';
128
+ const action = parseAction(event.action);
129
+ switch(phase){
130
+ case 'start':
131
+ {
132
+ const prompt = compactText(event.prompt);
133
+ return [
134
+ {
135
+ key: `aiAct:start:${prompt}`,
136
+ text: prompt ? `[Midscene][aiAct] Start: ${prompt}` : '[Midscene][aiAct] Start'
137
+ }
138
+ ];
139
+ }
140
+ case 'plan_thinking':
141
+ return event.screenshotPath ? [
142
+ {
143
+ key: eventKey(event, 'thinking'),
144
+ text: `${prefix} Thinking with the latest screenshot: ${event.screenshotPath}`
145
+ }
146
+ ] : [
147
+ {
148
+ key: eventKey(event, 'thinking'),
149
+ text: `${prefix} Thinking with the latest screenshot`
150
+ }
151
+ ];
152
+ case 'plan_planned':
153
+ {
154
+ const text = firstText([
155
+ event.log,
156
+ event.thought,
157
+ event.output
158
+ ]);
159
+ return text ? [
160
+ {
161
+ key: eventKey(event, 'planned'),
162
+ text: `${prefix} Planned: ${text}`
163
+ }
164
+ ] : [];
165
+ }
166
+ case 'plan_action':
167
+ return action ? [
168
+ {
169
+ key: eventKey(event, 'action'),
170
+ text: `${prefix} Action: ${plannedActionText(action)}`
171
+ }
172
+ ] : [];
173
+ case 'plan_failed':
174
+ return [
175
+ {
176
+ key: eventKey(event, 'plan_failed'),
177
+ text: `${prefix} Failed${error ? `: ${error}` : ''}`
178
+ }
179
+ ];
180
+ case 'action_running':
181
+ return action ? [
182
+ {
183
+ key: eventKey(event, 'action_running'),
184
+ text: `[Midscene][aiAct][Action] Running: ${runningActionText(action)}`
185
+ }
186
+ ] : [];
187
+ case 'action_done':
188
+ return action ? [
189
+ {
190
+ key: eventKey(event, 'action_done'),
191
+ text: `[Midscene][aiAct][Action] Done: ${action.name}${duration}`
192
+ }
193
+ ] : [];
194
+ case 'action_failed':
195
+ return [
196
+ {
197
+ key: eventKey(event, 'action_failed'),
198
+ text: `[Midscene][aiAct][Action] Failed${action ? `: ${action.name}` : ''}${duration}${error ? ` error=${error}` : ''}`
199
+ }
200
+ ];
201
+ case 'complete':
202
+ {
203
+ const text = firstText([
204
+ event.output,
205
+ event.log,
206
+ event.thought
207
+ ]);
208
+ return [
209
+ {
210
+ key: eventKey(event, 'complete'),
211
+ text: `[Midscene][aiAct] Complete${text ? `: ${text}` : ''}`
212
+ }
213
+ ];
214
+ }
215
+ case 'failed':
216
+ return [
217
+ {
218
+ key: eventKey(event, 'failed'),
219
+ text: `[Midscene][aiAct] Failed${error ? `: ${error}` : ''}`
220
+ }
221
+ ];
222
+ default:
223
+ return [];
224
+ }
225
+ }
226
+ const aiActCliProgressRenderer = {
227
+ normalize: normalizeAiActProgressEventForCli,
228
+ buildLines: buildAiActProgressEventLines
229
+ };
230
+ export { aiActCliProgressRenderer, buildAiActProgressEventLines, cliAiActProgressScope, normalizeAiActProgressEventForCli };