@agentuity/cli 2.0.0-beta.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/bin/cli.ts +3 -1
  2. package/dist/cmd/build/ci.d.ts +1 -1
  3. package/dist/cmd/build/ci.d.ts.map +1 -1
  4. package/dist/cmd/build/ci.js +70 -63
  5. package/dist/cmd/build/ci.js.map +1 -1
  6. package/dist/cmd/build/index.d.ts.map +1 -1
  7. package/dist/cmd/build/index.js +0 -3
  8. package/dist/cmd/build/index.js.map +1 -1
  9. package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -1
  10. package/dist/cmd/build/vite/agent-discovery.js +26 -2
  11. package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
  12. package/dist/cmd/build/vite/route-discovery.d.ts +5 -0
  13. package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
  14. package/dist/cmd/build/vite/route-discovery.js +13 -11
  15. package/dist/cmd/build/vite/route-discovery.js.map +1 -1
  16. package/dist/cmd/build/vite/static-renderer.d.ts +3 -2
  17. package/dist/cmd/build/vite/static-renderer.d.ts.map +1 -1
  18. package/dist/cmd/build/vite/static-renderer.js +28 -58
  19. package/dist/cmd/build/vite/static-renderer.js.map +1 -1
  20. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  21. package/dist/cmd/build/vite/vite-builder.js +33 -0
  22. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  23. package/dist/cmd/cloud/deploy-fork.d.ts +10 -0
  24. package/dist/cmd/cloud/deploy-fork.d.ts.map +1 -1
  25. package/dist/cmd/cloud/deploy-fork.js +71 -32
  26. package/dist/cmd/cloud/deploy-fork.js.map +1 -1
  27. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  28. package/dist/cmd/cloud/deploy.js +53 -11
  29. package/dist/cmd/cloud/deploy.js.map +1 -1
  30. package/dist/cmd/cloud/sandbox/create.d.ts.map +1 -1
  31. package/dist/cmd/cloud/sandbox/create.js +5 -0
  32. package/dist/cmd/cloud/sandbox/create.js.map +1 -1
  33. package/dist/cmd/cloud/sandbox/exec.d.ts.map +1 -1
  34. package/dist/cmd/cloud/sandbox/exec.js +76 -66
  35. package/dist/cmd/cloud/sandbox/exec.js.map +1 -1
  36. package/dist/cmd/cloud/sandbox/job/index.d.ts.map +1 -1
  37. package/dist/cmd/cloud/sandbox/job/index.js +12 -1
  38. package/dist/cmd/cloud/sandbox/job/index.js.map +1 -1
  39. package/dist/cmd/cloud/sandbox/job/logs.d.ts +3 -0
  40. package/dist/cmd/cloud/sandbox/job/logs.d.ts.map +1 -0
  41. package/dist/cmd/cloud/sandbox/job/logs.js +124 -0
  42. package/dist/cmd/cloud/sandbox/job/logs.js.map +1 -0
  43. package/dist/cmd/cloud/sandbox/run.d.ts.map +1 -1
  44. package/dist/cmd/cloud/sandbox/run.js +14 -2
  45. package/dist/cmd/cloud/sandbox/run.js.map +1 -1
  46. package/dist/cmd/cloud/sandbox/snapshot/build.js +2 -2
  47. package/dist/cmd/cloud/sandbox/snapshot/build.js.map +1 -1
  48. package/dist/cmd/coder/hub-url.d.ts.map +1 -1
  49. package/dist/cmd/coder/hub-url.js +3 -1
  50. package/dist/cmd/coder/hub-url.js.map +1 -1
  51. package/dist/cmd/coder/start.js +6 -6
  52. package/dist/cmd/coder/start.js.map +1 -1
  53. package/dist/cmd/coder/tui-init.d.ts +2 -2
  54. package/dist/cmd/coder/tui-init.js +2 -2
  55. package/dist/cmd/coder/tui-init.js.map +1 -1
  56. package/dist/cmd/project/show.d.ts.map +1 -1
  57. package/dist/cmd/project/show.js +9 -0
  58. package/dist/cmd/project/show.js.map +1 -1
  59. package/dist/cmd/support/report.d.ts.map +1 -1
  60. package/dist/cmd/support/report.js +19 -10
  61. package/dist/cmd/support/report.js.map +1 -1
  62. package/dist/errors.d.ts +24 -10
  63. package/dist/errors.d.ts.map +1 -1
  64. package/dist/errors.js +42 -12
  65. package/dist/errors.js.map +1 -1
  66. package/dist/schema-generator.d.ts.map +1 -1
  67. package/dist/schema-generator.js +2 -12
  68. package/dist/schema-generator.js.map +1 -1
  69. package/dist/steps.d.ts.map +1 -1
  70. package/dist/steps.js +38 -0
  71. package/dist/steps.js.map +1 -1
  72. package/dist/tui.d.ts.map +1 -1
  73. package/dist/tui.js +25 -9
  74. package/dist/tui.js.map +1 -1
  75. package/dist/utils/stream-capture.d.ts +9 -0
  76. package/dist/utils/stream-capture.d.ts.map +1 -0
  77. package/dist/utils/stream-capture.js +34 -0
  78. package/dist/utils/stream-capture.js.map +1 -0
  79. package/dist/utils/stream-url.d.ts +23 -0
  80. package/dist/utils/stream-url.d.ts.map +1 -0
  81. package/dist/utils/stream-url.js +153 -0
  82. package/dist/utils/stream-url.js.map +1 -0
  83. package/dist/utils/zip.d.ts.map +1 -1
  84. package/dist/utils/zip.js +19 -10
  85. package/dist/utils/zip.js.map +1 -1
  86. package/package.json +9 -7
  87. package/src/cmd/build/ci.ts +82 -80
  88. package/src/cmd/build/index.ts +0 -4
  89. package/src/cmd/build/vite/agent-discovery.ts +30 -5
  90. package/src/cmd/build/vite/route-discovery.ts +25 -12
  91. package/src/cmd/build/vite/static-renderer.ts +33 -64
  92. package/src/cmd/build/vite/vite-builder.ts +36 -0
  93. package/src/cmd/cloud/deploy-fork.ts +90 -33
  94. package/src/cmd/cloud/deploy.ts +68 -12
  95. package/src/cmd/cloud/sandbox/create.ts +7 -0
  96. package/src/cmd/cloud/sandbox/exec.ts +102 -90
  97. package/src/cmd/cloud/sandbox/job/index.ts +12 -1
  98. package/src/cmd/cloud/sandbox/job/logs.ts +139 -0
  99. package/src/cmd/cloud/sandbox/run.ts +16 -2
  100. package/src/cmd/cloud/sandbox/snapshot/build.ts +2 -2
  101. package/src/cmd/coder/hub-url.ts +3 -1
  102. package/src/cmd/coder/start.ts +6 -6
  103. package/src/cmd/coder/tui-init.ts +4 -4
  104. package/src/cmd/project/show.ts +9 -0
  105. package/src/cmd/support/report.ts +21 -10
  106. package/src/errors.ts +44 -12
  107. package/src/schema-generator.ts +2 -12
  108. package/src/steps.ts +38 -0
  109. package/src/tui.ts +24 -9
  110. package/src/utils/stream-capture.ts +39 -0
  111. package/src/utils/stream-url.ts +226 -0
  112. package/src/utils/zip.ts +22 -10
@@ -0,0 +1,153 @@
1
+ import { writeAndDrain } from '@agentuity/server';
2
+ import * as tui from '../tui';
3
+ export class StreamFetchError extends Error {
4
+ status;
5
+ statusText;
6
+ constructor(status, statusText, message) {
7
+ super(message);
8
+ this.status = status;
9
+ this.statusText = statusText;
10
+ this.name = 'StreamFetchError';
11
+ }
12
+ }
13
+ function escapeRegExp(str) {
14
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
15
+ }
16
+ export async function streamUrlToWritable(url, writable, logger, options = {}) {
17
+ const { signal, follow, timestamps, grep, tail, json, label = 'stream', raw = false, v2 = false, } = options;
18
+ const streamStart = Date.now();
19
+ let bytesRead = 0;
20
+ let chunks = 0;
21
+ try {
22
+ const fetchUrl = new URL(url);
23
+ if (follow || v2) {
24
+ fetchUrl.searchParams.set('v', '2');
25
+ }
26
+ if (follow) {
27
+ fetchUrl.searchParams.set('follow', 'true');
28
+ }
29
+ const redactedUrl = fetchUrl.origin + fetchUrl.pathname + (fetchUrl.search ? '?REDACTED' : '');
30
+ logger.debug('[%s] fetching: %s', label, redactedUrl);
31
+ const response = await fetch(fetchUrl.href, { signal });
32
+ logger.debug('[%s] response status=%d in %dms', label, response.status, Date.now() - streamStart);
33
+ if (!response.ok || !response.body) {
34
+ logger.debug('[%s] not ok or no body', label);
35
+ if (!json) {
36
+ tui.error(`Failed to fetch stream: ${response.status} ${response.statusText}`);
37
+ }
38
+ throw new StreamFetchError(response.status, response.statusText, `Failed to fetch stream: ${response.status} ${response.statusText}`);
39
+ }
40
+ const reader = response.body.getReader();
41
+ if (raw) {
42
+ while (true) {
43
+ const { done, value } = await reader.read();
44
+ if (done) {
45
+ logger.debug('[%s] EOF after %dms (%d chunks, %d bytes)', label, Date.now() - streamStart, chunks, bytesRead);
46
+ break;
47
+ }
48
+ if (value) {
49
+ chunks++;
50
+ bytesRead += value.length;
51
+ if (chunks <= 3 || chunks % 100 === 0) {
52
+ logger.debug('[%s] chunk #%d: %d bytes (total: %d bytes, +%dms)', label, chunks, value.length, bytesRead, Date.now() - streamStart);
53
+ }
54
+ await writeAndDrain(writable, value);
55
+ }
56
+ }
57
+ }
58
+ else {
59
+ const decoder = new TextDecoder();
60
+ let leftover = '';
61
+ const grepPattern = grep ? new RegExp(escapeRegExp(grep), 'i') : null;
62
+ const needsFiltering = tail !== undefined || grepPattern !== null;
63
+ const tailBuffer = [];
64
+ const maxTail = tail ?? Infinity;
65
+ const liveOutput = follow && needsFiltering;
66
+ const outputLine = async (line) => {
67
+ if (json) {
68
+ const obj = {
69
+ timestamp: new Date().toISOString(),
70
+ stream: label,
71
+ message: line,
72
+ };
73
+ await writeAndDrain(writable, Buffer.from(JSON.stringify(obj) + '\n'));
74
+ }
75
+ else {
76
+ const formatted = timestamps ? formatLineWithTimestamp(line) : line;
77
+ await writeAndDrain(writable, Buffer.from(formatted + '\n'));
78
+ }
79
+ };
80
+ const processFilteredLine = async (line) => {
81
+ if (grepPattern && !grepPattern.test(line)) {
82
+ return;
83
+ }
84
+ if (tail !== undefined) {
85
+ tailBuffer.push(line);
86
+ if (tailBuffer.length > maxTail) {
87
+ tailBuffer.shift();
88
+ }
89
+ if (liveOutput) {
90
+ await outputLine(line);
91
+ }
92
+ }
93
+ else {
94
+ await outputLine(line);
95
+ }
96
+ };
97
+ while (true) {
98
+ const { done, value } = await reader.read();
99
+ if (done) {
100
+ if (leftover) {
101
+ if (needsFiltering) {
102
+ await processFilteredLine(leftover);
103
+ }
104
+ else {
105
+ await outputLine(leftover);
106
+ }
107
+ }
108
+ logger.debug('[%s] EOF after %dms (%d chunks, %d bytes)', label, Date.now() - streamStart, chunks, bytesRead);
109
+ break;
110
+ }
111
+ if (value) {
112
+ chunks++;
113
+ bytesRead += value.length;
114
+ const text = leftover + decoder.decode(value, { stream: true });
115
+ const lines = text.split('\n');
116
+ leftover = lines.pop() ?? '';
117
+ for (const line of lines) {
118
+ if (needsFiltering) {
119
+ await processFilteredLine(line);
120
+ }
121
+ else {
122
+ await outputLine(line);
123
+ }
124
+ }
125
+ }
126
+ }
127
+ if (!liveOutput && needsFiltering && tailBuffer.length > 0) {
128
+ for (const line of tailBuffer) {
129
+ await outputLine(line);
130
+ }
131
+ }
132
+ }
133
+ return { bytesRead, chunks };
134
+ }
135
+ catch (err) {
136
+ if (err instanceof Error && err.name === 'AbortError') {
137
+ logger.debug('[%s] aborted after %dms', label, Date.now() - streamStart);
138
+ return { bytesRead, chunks };
139
+ }
140
+ logger.debug('[%s] error after %dms: %s', label, Date.now() - streamStart, err);
141
+ throw err;
142
+ }
143
+ }
144
+ function formatLineWithTimestamp(line) {
145
+ const timestamp = new Date().toLocaleTimeString('en-US', {
146
+ hour12: false,
147
+ hour: '2-digit',
148
+ minute: '2-digit',
149
+ second: '2-digit',
150
+ });
151
+ return `${tui.muted(timestamp)} ${line}`;
152
+ }
153
+ //# sourceMappingURL=stream-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-url.js","sourceRoot":"","sources":["../../src/utils/stream-url.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"zip.d.ts","sourceRoot":"","sources":["../../src/utils/zip.ts"],"names":[],"mappings":"AAMA,UAAU,OAAO;IAChB,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;CACzD;AAED,wBAAsB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,iBA4C1E"}
1
+ {"version":3,"file":"zip.d.ts","sourceRoot":"","sources":["../../src/utils/zip.ts"],"names":[],"mappings":"AAOA,UAAU,OAAO;IAChB,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;CACzD;AAED,wBAAsB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,iBAuD1E"}
package/dist/utils/zip.js CHANGED
@@ -1,10 +1,21 @@
1
- import { readFileSync, lstatSync } from 'node:fs';
2
- import { relative } from 'node:path';
1
+ import { createWriteStream, lstatSync } from 'node:fs';
2
+ import { mkdir } from 'node:fs/promises';
3
+ import { dirname, relative } from 'node:path';
3
4
  import { Glob } from 'bun';
4
- import AdmZip from 'adm-zip';
5
+ import archiver from 'archiver';
5
6
  import { toForwardSlash } from './normalize-path';
6
7
  export async function zipDir(dir, outdir, options) {
7
- const zip = new AdmZip();
8
+ await mkdir(dirname(outdir), { recursive: true });
9
+ const output = createWriteStream(outdir);
10
+ const zip = archiver('zip', {
11
+ zlib: { level: 9 },
12
+ });
13
+ const writeDone = new Promise((resolve, reject) => {
14
+ output.on('close', resolve);
15
+ output.on('error', reject);
16
+ zip.on('error', reject);
17
+ });
18
+ zip.pipe(output);
8
19
  const files = await Array.fromAsync(new Glob('**/*').scan({ cwd: dir, absolute: true, dot: true, followSymlinks: false }));
9
20
  const total = files.length;
10
21
  let count = 0;
@@ -23,11 +34,8 @@ export async function zipDir(dir, outdir, options) {
23
34
  // across machines and would cause EISDIR errors on extraction.
24
35
  const stat = lstatSync(file);
25
36
  if (!stat.isSymbolicLink() && !stat.isDirectory()) {
26
- // Use addFile with explicit Unix permissions (0o644) instead of addLocalFile.
27
- // On Windows, addLocalFile relies on OS file stats which may produce zip entries
28
- // with incorrect Unix permission bits, causing EACCES errors when extracted on Linux.
29
- const data = readFileSync(file);
30
- zip.addFile(rel, data, '', 0o644);
37
+ // Set explicit Unix permissions (0o644) for portability across OSes.
38
+ zip.file(file, { name: rel, mode: 0o644 });
31
39
  }
32
40
  }
33
41
  catch (err) {
@@ -41,7 +49,8 @@ export async function zipDir(dir, outdir, options) {
41
49
  await Bun.sleep(10); // give some time for the progress bar to render
42
50
  }
43
51
  }
44
- await zip.writeZip(outdir);
52
+ await zip.finalize();
53
+ await writeDone;
45
54
  if (options?.progress) {
46
55
  options.progress(100);
47
56
  await Bun.sleep(100); // give some time for the progress bar to render
@@ -1 +1 @@
1
- {"version":3,"file":"zip.js","sourceRoot":"","sources":["../../src/utils/zip.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAO,MAAM,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAOlD,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,GAAW,EAAE,MAAc,EAAE,OAAiB;IAC1E,MAAM,GAAG,GAAG,IAAI,MAAM,EAAE,CAAC;IACzB,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,SAAS,CAClC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CACrF,CAAC;IACF,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IAC3B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;QAChD,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;YACrB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;gBAChC,IAAI,GAAG,IAAI,CAAC;YACb,CAAC;QACF,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,CAAC;YACX,IAAI,CAAC;gBACJ,mEAAmE;gBACnE,mEAAmE;gBACnE,+DAA+D;gBAC/D,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;gBAC7B,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;oBACnD,8EAA8E;oBAC9E,iFAAiF;oBACjF,sFAAsF;oBACtF,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;oBAChC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;gBACnC,CAAC;YACF,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,MAAM,IAAI,KAAK,CAAC,8BAA8B,GAAG,KAAK,IAAI,GAAG,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YAChF,CAAC;QACF,CAAC;QACD,KAAK,EAAE,CAAC;QACR,IAAI,OAAO,EAAE,QAAQ,EAAE,CAAC;YACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;YACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC3B,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,gDAAgD;QACtE,CAAC;IACF,CAAC;IACD,MAAM,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC3B,IAAI,OAAO,EAAE,QAAQ,EAAE,CAAC;QACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACtB,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,gDAAgD;IACvE,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"zip.js","sourceRoot":"","sources":["../../src/utils/zip.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentuity/cli",
3
- "version": "2.0.0-beta.1",
3
+ "version": "2.0.0",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Agentuity employees and contributors",
6
6
  "type": "module",
@@ -41,13 +41,15 @@
41
41
  "prepublishOnly": "bun run clean && bun run build"
42
42
  },
43
43
  "dependencies": {
44
- "@agentuity/auth": "2.0.0-beta.1",
45
- "@agentuity/core": "2.0.0-beta.1",
46
- "@agentuity/server": "2.0.0-beta.1",
44
+ "@agentuity/auth": "2.0.0",
45
+ "@agentuity/core": "2.0.0",
46
+ "@agentuity/frontend": "2.0.0",
47
+ "@agentuity/server": "2.0.0",
47
48
  "@datasert/cronjs-parser": "^1.4.0",
48
49
  "@vitejs/plugin-react": "^5.1.2",
49
50
  "acorn-loose": "^8.5.2",
50
51
  "adm-zip": "^0.5.16",
52
+ "archiver": "^7.0.1",
51
53
  "astring": "^1.9.0",
52
54
  "cli-table3": "^0.6.5",
53
55
  "commander": "^14.0.2",
@@ -58,12 +60,12 @@
58
60
  "tar-fs": "^3.1.1",
59
61
  "typescript": "^5.9.0",
60
62
  "vite": "^7.2.7",
61
- "zod": "^4.3.5",
62
- "@agentuity/frontend": "2.0.0-beta.1"
63
+ "zod": "^4.3.5"
63
64
  },
64
65
  "devDependencies": {
65
- "@agentuity/test-utils": "2.0.0-beta.1",
66
+ "@agentuity/test-utils": "2.0.0",
66
67
  "@types/adm-zip": "^0.5.7",
68
+ "@types/archiver": "^7.0.0",
67
69
  "@types/bun": "latest",
68
70
  "@types/tar-fs": "^2.0.4",
69
71
  "bun-plugin-tailwind": "^0.1.2",
@@ -7,7 +7,7 @@ import { ErrorCode } from '../../errors';
7
7
  import * as tui from '../../tui';
8
8
 
9
9
  export interface CIBuildOptions {
10
- url: string;
10
+ url?: string;
11
11
  directory?: string;
12
12
  trigger?: string;
13
13
  event?: string;
@@ -22,44 +22,18 @@ export interface CIBuildOptions {
22
22
  logsUrl?: string;
23
23
  }
24
24
 
25
- async function streamProcessOutput(proc: ReturnType<typeof spawn>): Promise<void> {
26
- const forwardStream = async (
27
- stream: ReadableStream<Uint8Array> | number | undefined,
28
- isStderr: boolean
29
- ) => {
30
- if (typeof stream === 'number') return;
31
- if (!stream) return;
32
- const reader = stream.getReader();
33
- const target = isStderr ? process.stderr : process.stdout;
34
-
35
- while (true) {
36
- const { done, value } = await reader.read();
37
- if (done) break;
38
- target.write(value);
39
- }
40
- };
41
-
42
- await Promise.all([
43
- forwardStream(proc.stdout, false),
44
- forwardStream(proc.stderr, true),
45
- proc.exited,
46
- ]);
47
- }
48
-
49
25
  async function runCommand(cmd: string[], cwd: string): Promise<number> {
50
26
  const proc = spawn({
51
27
  cmd,
52
28
  cwd,
53
29
  env: { ...process.env, CI: 'true' },
54
30
  stdin: 'ignore',
55
- stdout: 'pipe',
56
- stderr: 'pipe',
31
+ stdout: 'inherit',
32
+ stderr: 'inherit',
57
33
  });
58
-
59
- await streamProcessOutput(proc);
34
+ await proc.exited;
60
35
  return proc.exitCode ?? 1;
61
36
  }
62
-
63
37
  async function downloadSource(url: string, targetPath: string): Promise<void> {
64
38
  let lastError: unknown;
65
39
 
@@ -105,60 +79,77 @@ function buildDeployArgs(opts: CIBuildOptions): string[] {
105
79
 
106
80
  export async function runCIBuild(opts: CIBuildOptions, _logger: Logger): Promise<void> {
107
81
  let tempDir = '';
82
+ // Track subprocess exit code so we can clean up in finally before calling process.exit().
83
+ // Bun does not reliably honor process.exitCode, so an explicit process.exit() is required.
84
+ let pendingExitCode: number | undefined;
108
85
 
109
86
  try {
110
- tempDir = await mkdtemp(join(tmpdir(), 'agentuity-ci-build-'));
111
- const sourceZipPath = join(tempDir, 'source.zip');
112
- const extractPath = join(tempDir, 'build');
113
-
114
- tui.info('1️⃣ Downloading source code from GitHub...');
115
- await downloadSource(opts.url, sourceZipPath);
116
-
117
- tui.info('2️⃣ Unzipping source code from GitHub...');
118
- await mkdir(extractPath, { recursive: true });
119
- const unzipExit = await runCommand(
120
- ['unzip', '-q', sourceZipPath, '-d', extractPath],
121
- tempDir
122
- );
123
- if (unzipExit !== 0 && unzipExit !== 1) {
124
- tui.fatal(`Failed to unzip source archive (exit ${unzipExit})`, ErrorCode.BUILD_FAILED);
125
- }
126
-
127
- const extractedEntries = await readdir(extractPath, { withFileTypes: true });
128
- const extractedDirs = extractedEntries.filter((entry) => entry.isDirectory());
129
- if (extractedDirs.length !== 1) {
130
- tui.fatal(
131
- `Expected one root directory after unzip, found ${extractedDirs.length}`,
132
- ErrorCode.BUILD_FAILED
87
+ let projectDir: string;
88
+
89
+ if (opts.url) {
90
+ // Download and extract source from URL
91
+ tempDir = await mkdtemp(join(tmpdir(), 'agentuity-ci-build-'));
92
+ const sourceZipPath = join(tempDir, 'source.zip');
93
+ const extractPath = join(tempDir, 'build');
94
+
95
+ tui.info('1️⃣ Downloading source code from GitHub...');
96
+ await downloadSource(opts.url, sourceZipPath);
97
+
98
+ tui.info('2️⃣ Unzipping source code from GitHub...');
99
+ await mkdir(extractPath, { recursive: true });
100
+ const unzipExit = await runCommand(
101
+ ['unzip', '-q', sourceZipPath, '-d', extractPath],
102
+ tempDir
133
103
  );
134
- }
104
+ if (unzipExit !== 0 && unzipExit !== 1) {
105
+ tui.error(`Failed to unzip source archive (exit ${unzipExit})`);
106
+ pendingExitCode = unzipExit;
107
+ return;
108
+ }
135
109
 
136
- const sourceRoot = extractedDirs.at(0);
137
- if (!sourceRoot) {
138
- tui.fatal('Could not determine extracted source directory', ErrorCode.BUILD_FAILED);
139
- }
110
+ const extractedEntries = await readdir(extractPath, { withFileTypes: true });
111
+ const extractedDirs = extractedEntries.filter((entry) => entry.isDirectory());
112
+ if (extractedDirs.length !== 1) {
113
+ tui.fatal(
114
+ `Expected one root directory after unzip, found ${extractedDirs.length}`,
115
+ ErrorCode.BUILD_FAILED
116
+ );
117
+ }
140
118
 
141
- const sourceRootDir = join(extractPath, sourceRoot.name);
142
- let projectDir = sourceRootDir;
143
- if (opts.directory) {
144
- projectDir = join(sourceRootDir, opts.directory);
145
- }
119
+ const sourceRoot = extractedDirs.at(0);
120
+ if (!sourceRoot) {
121
+ tui.fatal('Could not determine extracted source directory', ErrorCode.BUILD_FAILED);
122
+ }
146
123
 
147
- const projectStats = await stat(projectDir).catch(() => null);
148
- if (!projectStats?.isDirectory()) {
149
- tui.fatal(`Build directory not found: ${projectDir}`, ErrorCode.CONFIG_INVALID);
150
- }
124
+ const sourceRootDir = join(extractPath, sourceRoot.name);
125
+ projectDir = sourceRootDir;
126
+ if (opts.directory) {
127
+ projectDir = join(sourceRootDir, opts.directory);
128
+ }
151
129
 
152
- // Resolve symlinks and verify the project dir is within the source root
153
- const realProjectDir = await realpath(projectDir).catch(() => null);
154
- const realSourceRoot = await realpath(sourceRootDir).catch(() => null);
155
- if (!realProjectDir || !realSourceRoot || !realProjectDir.startsWith(realSourceRoot)) {
156
- tui.fatal(
157
- 'Directory path escapes the source root (path traversal denied)',
158
- ErrorCode.CONFIG_INVALID
159
- );
130
+ const projectStats = await stat(projectDir).catch(() => null);
131
+ if (!projectStats?.isDirectory()) {
132
+ tui.fatal(`Build directory not found: ${projectDir}`, ErrorCode.CONFIG_INVALID);
133
+ }
134
+
135
+ // Resolve symlinks and verify the project dir is within the source root
136
+ const realProjectDir = await realpath(projectDir).catch(() => null);
137
+ const realSourceRoot = await realpath(sourceRootDir).catch(() => null);
138
+ if (!realProjectDir || !realSourceRoot || !realProjectDir.startsWith(realSourceRoot)) {
139
+ tui.fatal(
140
+ 'Directory path escapes the source root (path traversal denied)',
141
+ ErrorCode.CONFIG_INVALID
142
+ );
143
+ }
144
+ projectDir = realProjectDir;
145
+ } else {
146
+ // No URL — use current working directory (source already present, e.g. snapshot-based deploy)
147
+ tui.info('1️⃣ Using local source (no download URL provided)...');
148
+ projectDir = process.cwd();
149
+ if (opts.directory) {
150
+ projectDir = join(projectDir, opts.directory);
151
+ }
160
152
  }
161
- projectDir = realProjectDir;
162
153
 
163
154
  const sdkKey = process.env.AGENTUITY_SDK_KEY;
164
155
  if (sdkKey) {
@@ -168,7 +159,9 @@ export async function runCIBuild(opts: CIBuildOptions, _logger: Logger): Promise
168
159
  tui.info('3️⃣ Installing your project dependencies...');
169
160
  const installExit = await runCommand(['bun', 'install'], projectDir);
170
161
  if (installExit !== 0) {
171
- tui.fatal(`Dependency installation failed (exit ${installExit})`, ErrorCode.BUILD_FAILED);
162
+ tui.error(`Dependency installation failed (exit ${installExit})`);
163
+ pendingExitCode = installExit;
164
+ return;
172
165
  }
173
166
 
174
167
  const packageJsonPath = join(projectDir, 'package.json');
@@ -185,7 +178,9 @@ export async function runCIBuild(opts: CIBuildOptions, _logger: Logger): Promise
185
178
  tui.info('🔧 Running predeploy script...');
186
179
  const predeployExit = await runCommand(['bun', 'run', '--bun', 'predeploy'], projectDir);
187
180
  if (predeployExit !== 0) {
188
- tui.fatal(`Predeploy failed (exit ${predeployExit})`, ErrorCode.BUILD_FAILED);
181
+ tui.error(`Predeploy failed (exit ${predeployExit})`);
182
+ pendingExitCode = predeployExit;
183
+ return;
189
184
  }
190
185
  }
191
186
 
@@ -200,14 +195,18 @@ export async function runCIBuild(opts: CIBuildOptions, _logger: Logger): Promise
200
195
  tui.info(`Using CLI: ${cliExists ? localCliBin : 'bunx --bun agentuity'}`);
201
196
  const deployExit = await runCommand(deployCmd, projectDir);
202
197
  if (deployExit !== 0) {
203
- tui.fatal(`Deploy failed (exit ${deployExit})`, ErrorCode.BUILD_FAILED);
198
+ tui.error(`Deploy failed (exit ${deployExit})`);
199
+ pendingExitCode = deployExit;
200
+ return;
204
201
  }
205
202
 
206
203
  if (scripts?.postdeploy) {
207
204
  tui.info('🔧 Running postdeploy script...');
208
205
  const postdeployExit = await runCommand(['bun', 'run', '--bun', 'postdeploy'], projectDir);
209
206
  if (postdeployExit !== 0) {
210
- tui.fatal(`Postdeploy failed (exit ${postdeployExit})`, ErrorCode.BUILD_FAILED);
207
+ tui.error(`Postdeploy failed (exit ${postdeployExit})`);
208
+ pendingExitCode = postdeployExit;
209
+ return;
211
210
  }
212
211
  }
213
212
 
@@ -219,5 +218,8 @@ export async function runCIBuild(opts: CIBuildOptions, _logger: Logger): Promise
219
218
  if (tempDir) {
220
219
  await rm(tempDir, { recursive: true, force: true });
221
220
  }
221
+ if (pendingExitCode !== undefined) {
222
+ process.exit(pendingExitCode);
223
+ }
222
224
  }
223
225
  }
@@ -58,10 +58,6 @@ export const command = createCommand({
58
58
  const { opts, projectDir, project } = ctx;
59
59
 
60
60
  if (opts.ci) {
61
- if (!opts.url) {
62
- tui.fatal('--url is required when using --ci mode', ErrorCode.CONFIG_INVALID);
63
- }
64
-
65
61
  const { runCIBuild } = await import('./ci');
66
62
  await runCIBuild(
67
63
  {
@@ -279,12 +279,25 @@ async function importAgentMetadata(
279
279
  evals: evals.length > 0 ? evals : undefined,
280
280
  };
281
281
  } catch (error) {
282
- logger.warn(
283
- '[agent-discovery] Failed to import agent %s: %s',
284
- filePath,
285
- error instanceof Error ? error.message : String(error)
282
+ const errorMsg = error instanceof Error ? error.message : String(error);
283
+ throw new Error(
284
+ `Failed to import agent at ${relativeFilename}:\n` +
285
+ ` ${errorMsg}\n\n` +
286
+ `Agent files are imported at build time to extract metadata. Code that ` +
287
+ `runs at module scope (outside of setup() or handler()) will execute ` +
288
+ `during the build — including SDK client constructors that require API ` +
289
+ `keys or environment variables.\n\n` +
290
+ `To fix this, move initialization code into setup() or handler():\n\n` +
291
+ ` // ❌ Don't do this — runs at import time:\n` +
292
+ ` const client = new OpenAI();\n` +
293
+ ` export default createAgent('my-agent', { handler: async (ctx) => { ... } });\n\n` +
294
+ ` // ✅ Do this instead — runs at request time:\n` +
295
+ ` export default createAgent('my-agent', {\n` +
296
+ ` setup: (ctx) => ({ client: new OpenAI() }),\n` +
297
+ ` handler: async (ctx) => { const { client } = ctx.setup; ... },\n` +
298
+ ` });\n\n` +
299
+ `See https://agentuity.dev/docs/agents#setup for more information.`
286
300
  );
287
- return null;
288
301
  }
289
302
  }
290
303
 
@@ -325,6 +338,18 @@ export async function discoverAgents(
325
338
  continue;
326
339
  }
327
340
 
341
+ // Skip test files (*.test.ts, *.spec.ts) and test directories
342
+ if (
343
+ file.startsWith('test/') ||
344
+ file.includes('/test/') ||
345
+ file.startsWith('__tests__/') ||
346
+ file.includes('/__tests__/') ||
347
+ file.endsWith('.test.ts') ||
348
+ file.endsWith('.spec.ts')
349
+ ) {
350
+ continue;
351
+ }
352
+
328
353
  const relativeFilename = toForwardSlash(relative(rootDir, filePath));
329
354
  const agentMetadata = await importAgentMetadata(
330
355
  filePath,
@@ -46,19 +46,23 @@ export function extractPathParams(path: string): string[] {
46
46
  }
47
47
 
48
48
  /**
49
- * Generate a deterministic route ID from project/deployment/path/method.
49
+ * Generate a deterministic route ID from project/deployment/type/method/filename/path/version.
50
+ * Must match the format used by the platform (prefix: route_, SHA1 hash of all components).
50
51
  */
51
- function generateRouteId(
52
+ export function generateRouteId(
52
53
  projectId: string,
53
54
  deploymentId: string,
55
+ type: string,
56
+ method: string,
57
+ filename: string,
54
58
  path: string,
55
- method: string
59
+ version: string
56
60
  ): string {
57
- const hash = createHash('sha256')
58
- .update(`${projectId}:${deploymentId}:${path}:${method}`)
59
- .digest('hex')
60
- .substring(0, 16);
61
- return `routeid_${hash}`;
61
+ const hasher = new Bun.CryptoHasher('sha1');
62
+ for (const val of [projectId, deploymentId, type, method, filename, path, version]) {
63
+ hasher.update(val);
64
+ }
65
+ return `route_${hasher.digest().toHex()}`;
62
66
  }
63
67
 
64
68
  /**
@@ -152,7 +156,7 @@ export async function discoverRoutes(
152
156
  );
153
157
 
154
158
  for (const route of routeEntries) {
155
- const method = String(route.method).toUpperCase();
159
+ const method = String(route.method).toLowerCase();
156
160
  // Combine mount path with route path
157
161
  let fullPath = route.path;
158
162
  if (mount.path !== '/' && !fullPath.startsWith(mount.path)) {
@@ -160,18 +164,27 @@ export async function discoverRoutes(
160
164
  }
161
165
 
162
166
  // Deduplicate (Hono may register same route multiple times for middleware)
163
- const routeKey = `${method} ${fullPath}`;
167
+ const routeKey = `${method.toUpperCase()} ${fullPath}`;
164
168
  if (seenRoutes.has(routeKey)) continue;
165
169
  seenRoutes.add(routeKey);
166
170
 
167
171
  const routeType = detectRouteType(route.handler);
168
- const id = generateRouteId(projectId, deploymentId, fullPath, method);
172
+ const rel = toForwardSlash(relative(rootDir, mount.routerFile));
173
+ const id = generateRouteId(
174
+ projectId,
175
+ deploymentId,
176
+ routeType,
177
+ method,
178
+ rel,
179
+ fullPath,
180
+ version
181
+ );
169
182
 
170
183
  routes.push({
171
184
  id,
172
185
  filename: toForwardSlash(relative(rootDir, mount.routerFile)),
173
186
  path: fullPath,
174
- method: method.toLowerCase(),
187
+ method,
175
188
  version,
176
189
  type: routeType,
177
190
  });