@agentuity/cli 0.1.33 → 0.1.35

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 (122) hide show
  1. package/bin/cli.ts +107 -6
  2. package/dist/cmd/ai/index.d.ts.map +1 -1
  3. package/dist/cmd/ai/index.js +0 -6
  4. package/dist/cmd/ai/index.js.map +1 -1
  5. package/dist/cmd/ai/opencode/uninstall.js +1 -1
  6. package/dist/cmd/ai/opencode/uninstall.js.map +1 -1
  7. package/dist/cmd/build/vite/bun-dev-server.d.ts +6 -0
  8. package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
  9. package/dist/cmd/build/vite/bun-dev-server.js +100 -33
  10. package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
  11. package/dist/cmd/cloud/apikey/create.d.ts.map +1 -1
  12. package/dist/cmd/cloud/apikey/create.js +1 -1
  13. package/dist/cmd/cloud/apikey/create.js.map +1 -1
  14. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  15. package/dist/cmd/cloud/deploy.js +4 -1
  16. package/dist/cmd/cloud/deploy.js.map +1 -1
  17. package/dist/cmd/cloud/env/delete.d.ts.map +1 -1
  18. package/dist/cmd/cloud/env/delete.js +100 -45
  19. package/dist/cmd/cloud/env/delete.js.map +1 -1
  20. package/dist/cmd/cloud/env/pull.d.ts.map +1 -1
  21. package/dist/cmd/cloud/env/pull.js +1 -1
  22. package/dist/cmd/cloud/env/pull.js.map +1 -1
  23. package/dist/cmd/cloud/machine/delete.js +1 -1
  24. package/dist/cmd/cloud/machine/delete.js.map +1 -1
  25. package/dist/cmd/cloud/sandbox/exec.d.ts.map +1 -1
  26. package/dist/cmd/cloud/sandbox/exec.js +10 -35
  27. package/dist/cmd/cloud/sandbox/exec.js.map +1 -1
  28. package/dist/cmd/dev/index.d.ts.map +1 -1
  29. package/dist/cmd/dev/index.js +58 -10
  30. package/dist/cmd/dev/index.js.map +1 -1
  31. package/dist/cmd/index.d.ts.map +1 -1
  32. package/dist/cmd/index.js +1 -0
  33. package/dist/cmd/index.js.map +1 -1
  34. package/dist/cmd/support/index.d.ts +2 -0
  35. package/dist/cmd/support/index.d.ts.map +1 -0
  36. package/dist/cmd/support/index.js +11 -0
  37. package/dist/cmd/support/index.js.map +1 -0
  38. package/dist/cmd/support/logs/index.d.ts +3 -0
  39. package/dist/cmd/support/logs/index.d.ts.map +1 -0
  40. package/dist/cmd/support/logs/index.js +9 -0
  41. package/dist/cmd/support/logs/index.js.map +1 -0
  42. package/dist/cmd/support/logs/path.d.ts +3 -0
  43. package/dist/cmd/support/logs/path.d.ts.map +1 -0
  44. package/dist/cmd/support/logs/path.js +52 -0
  45. package/dist/cmd/support/logs/path.js.map +1 -0
  46. package/dist/cmd/support/logs/show.d.ts +3 -0
  47. package/dist/cmd/support/logs/show.d.ts.map +1 -0
  48. package/dist/cmd/support/logs/show.js +121 -0
  49. package/dist/cmd/support/logs/show.js.map +1 -0
  50. package/dist/cmd/support/report.d.ts +3 -0
  51. package/dist/cmd/support/report.d.ts.map +1 -0
  52. package/dist/cmd/support/report.js +299 -0
  53. package/dist/cmd/support/report.js.map +1 -0
  54. package/dist/cmd/support/system.d.ts +3 -0
  55. package/dist/cmd/support/system.d.ts.map +1 -0
  56. package/dist/cmd/support/system.js +120 -0
  57. package/dist/cmd/support/system.js.map +1 -0
  58. package/dist/cmd/version/index.d.ts.map +1 -1
  59. package/dist/cmd/version/index.js +1 -0
  60. package/dist/cmd/version/index.js.map +1 -1
  61. package/dist/composite-logger.d.ts +35 -0
  62. package/dist/composite-logger.d.ts.map +1 -0
  63. package/dist/composite-logger.js +78 -0
  64. package/dist/composite-logger.js.map +1 -0
  65. package/dist/index.d.ts +2 -0
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +2 -0
  68. package/dist/index.js.map +1 -1
  69. package/dist/internal-logger.d.ts +77 -0
  70. package/dist/internal-logger.d.ts.map +1 -0
  71. package/dist/internal-logger.js +340 -0
  72. package/dist/internal-logger.js.map +1 -0
  73. package/dist/types.d.ts +6 -0
  74. package/dist/types.d.ts.map +1 -1
  75. package/dist/types.js.map +1 -1
  76. package/dist/utils/installation-type.d.ts.map +1 -1
  77. package/dist/utils/installation-type.js +54 -16
  78. package/dist/utils/installation-type.js.map +1 -1
  79. package/package.json +6 -6
  80. package/src/cmd/ai/index.ts +0 -6
  81. package/src/cmd/ai/opencode/uninstall.ts +1 -1
  82. package/src/cmd/build/vite/bun-dev-server.ts +113 -36
  83. package/src/cmd/cloud/apikey/create.ts +3 -1
  84. package/src/cmd/cloud/deploy.ts +4 -1
  85. package/src/cmd/cloud/env/delete.ts +100 -45
  86. package/src/cmd/cloud/env/pull.ts +1 -6
  87. package/src/cmd/cloud/machine/delete.ts +1 -1
  88. package/src/cmd/cloud/sandbox/exec.ts +10 -41
  89. package/src/cmd/dev/index.ts +59 -11
  90. package/src/cmd/index.ts +1 -0
  91. package/src/cmd/support/index.ts +11 -0
  92. package/src/cmd/support/logs/index.ts +9 -0
  93. package/src/cmd/support/logs/path.ts +56 -0
  94. package/src/cmd/support/logs/show.ts +144 -0
  95. package/src/cmd/support/report.ts +364 -0
  96. package/src/cmd/support/system.ts +130 -0
  97. package/src/cmd/version/index.ts +1 -0
  98. package/src/composite-logger.ts +86 -0
  99. package/src/index.ts +7 -0
  100. package/src/internal-logger.ts +411 -0
  101. package/src/types.ts +6 -0
  102. package/src/utils/installation-type.ts +55 -16
  103. package/dist/cmd/ai/skills/generate.d.ts +0 -3
  104. package/dist/cmd/ai/skills/generate.d.ts.map +0 -1
  105. package/dist/cmd/ai/skills/generate.js +0 -65
  106. package/dist/cmd/ai/skills/generate.js.map +0 -1
  107. package/dist/cmd/ai/skills/generator.d.ts +0 -4
  108. package/dist/cmd/ai/skills/generator.d.ts.map +0 -1
  109. package/dist/cmd/ai/skills/generator.js +0 -410
  110. package/dist/cmd/ai/skills/generator.js.map +0 -1
  111. package/dist/cmd/ai/skills/index.d.ts +0 -4
  112. package/dist/cmd/ai/skills/index.d.ts.map +0 -1
  113. package/dist/cmd/ai/skills/index.js +0 -21
  114. package/dist/cmd/ai/skills/index.js.map +0 -1
  115. package/dist/cmd/dev/skills.d.ts +0 -10
  116. package/dist/cmd/dev/skills.d.ts.map +0 -1
  117. package/dist/cmd/dev/skills.js +0 -57
  118. package/dist/cmd/dev/skills.js.map +0 -1
  119. package/src/cmd/ai/skills/generate.ts +0 -75
  120. package/src/cmd/ai/skills/generator.ts +0 -527
  121. package/src/cmd/ai/skills/index.ts +0 -23
  122. package/src/cmd/dev/skills.ts +0 -82
@@ -1,7 +1,24 @@
1
1
  /**
2
2
  * Detects how the CLI was installed and is being run
3
3
  */
4
+ import fs from 'node:fs';
4
5
  import os from 'node:os';
6
+ /**
7
+ * Resolve a path to its real path (following symlinks) and normalize to POSIX separators.
8
+ * Returns the original path if resolution fails.
9
+ */
10
+ function resolveRealPath(path) {
11
+ if (!path)
12
+ return '';
13
+ try {
14
+ // fs.realpathSync resolves symlinks (e.g., /tmp -> /private/tmp on macOS)
15
+ return fs.realpathSync(path).replace(/\\/g, '/');
16
+ }
17
+ catch {
18
+ // If the path doesn't exist or can't be resolved, return normalized original
19
+ return path.replace(/\\/g, '/');
20
+ }
21
+ }
5
22
  /**
6
23
  * Determines the installation type based on how the CLI is being executed
7
24
  *
@@ -10,29 +27,50 @@ import os from 'node:os';
10
27
  * @returns 'source' - Running from source code (development)
11
28
  */
12
29
  export function getInstallationType() {
13
- // Normalize paths to POSIX separators for cross-platform compatibility
30
+ // Bun.main already returns the resolved real path, just normalize separators
14
31
  const mainPath = Bun.main.replace(/\\/g, '/');
15
- // Bun.argv[1] contains the original invocation path (before symlink resolution)
16
- const invokedPath = (Bun.argv[1] ?? '').replace(/\\/g, '/');
17
- // Get bun's global bin directory from BUN_INSTALL or default to ~/.bun/bin
18
- // Use os.homedir() as primary fallback to avoid "undefined/.bun" paths
19
- const home = os.homedir() ?? process.env.HOME ?? process.env.USERPROFILE ?? '';
20
- const bunInstall = (process.env.BUN_INSTALL ?? (home ? `${home}/.bun` : '')).replace(/\\/g, '/');
21
- const globalBinDir = bunInstall ? `${bunInstall}/bin/` : '';
22
- // Global install: invoked from bun's global bin directory (e.g., ~/.bun/bin/agentuity)
23
- // This handles symlinks created by `bun add -g @agentuity/cli`
24
- if (globalBinDir && invokedPath.startsWith(globalBinDir)) {
25
- return 'global';
32
+ // Get home directory reliably and resolve symlinks
33
+ // On macOS, os.homedir() returns /Users/xxx which is already real
34
+ const home = resolveRealPath(os.homedir() ?? process.env.HOME ?? process.env.USERPROFILE ?? '');
35
+ // Get bun install directory from BUN_INSTALL or default to ~/.bun
36
+ // Resolve symlinks to handle cases like BUN_INSTALL=/tmp/... on macOS where /tmp -> /private/tmp
37
+ const bunInstallRaw = process.env.BUN_INSTALL ?? (home ? `${home}/.bun` : '');
38
+ const bunInstall = resolveRealPath(bunInstallRaw);
39
+ // GLOBAL DETECTION: Check if running from bun's global install location
40
+ // When installed via `bun add -g`, the CLI lives at ~/.bun/node_modules/@agentuity/cli/
41
+ // or ~/.bun/install/global/node_modules/@agentuity/cli/
42
+ if (bunInstall) {
43
+ // Check for ~/.bun/node_modules/@agentuity/cli/ (common bun global layout)
44
+ if (mainPath.startsWith(`${bunInstall}/node_modules/@agentuity/cli/`)) {
45
+ return 'global';
46
+ }
47
+ // Check for ~/.bun/install/global/node_modules/@agentuity/cli/ (alternative layout)
48
+ if (mainPath.startsWith(`${bunInstall}/install/global/`)) {
49
+ return 'global';
50
+ }
51
+ }
52
+ // GLOBAL DETECTION: Check for legacy ~/.agentuity/ installation
53
+ // The install.sh script may install to ~/.agentuity/node_modules/@agentuity/cli/
54
+ // or create a shim at ~/.agentuity/bin/agentuity
55
+ if (home) {
56
+ const agentuityDir = resolveRealPath(`${home}/.agentuity`);
57
+ if (mainPath.startsWith(`${agentuityDir}/`)) {
58
+ return 'global';
59
+ }
26
60
  }
27
- // Also check the resolved path for explicit global install locations
28
- if (mainPath.includes('/.bun/install/global/')) {
61
+ // GLOBAL DETECTION: Fallback check for any path containing /.bun/ before node_modules
62
+ // This catches edge cases where BUN_INSTALL might not match the actual path
63
+ if (mainPath.includes('/.bun/') && mainPath.includes('/node_modules/@agentuity/cli/')) {
29
64
  return 'global';
30
65
  }
31
- // Local project install: ./node_modules/@agentuity/cli/...
66
+ // LOCAL DETECTION: Running from a project's node_modules
67
+ // This is when someone runs `bunx agentuity` or has it as a project dependency
68
+ // At this point, we've ruled out global installs, so any node_modules path is local
32
69
  if (mainPath.includes('/node_modules/@agentuity/cli/')) {
33
70
  return 'local';
34
71
  }
35
- // Source/development: packages/cli/bin/cli.ts or similar
72
+ // SOURCE DETECTION: Running from source code (development)
73
+ // This is when running directly from the monorepo: packages/cli/bin/cli.ts
36
74
  return 'source';
37
75
  }
38
76
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"installation-type.js","sourceRoot":"","sources":["../../src/utils/installation-type.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AAIzB;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB;IAClC,uEAAuE;IACvE,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC9C,gFAAgF;IAChF,MAAM,WAAW,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAE5D,2EAA2E;IAC3E,uEAAuE;IACvE,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;IAC/E,MAAM,UAAU,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACjG,MAAM,YAAY,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAE5D,uFAAuF;IACvF,+DAA+D;IAC/D,IAAI,YAAY,IAAI,WAAW,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC1D,OAAO,QAAQ,CAAC;IACjB,CAAC;IAED,qEAAqE;IACrE,IAAI,QAAQ,CAAC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,CAAC;QAChD,OAAO,QAAQ,CAAC;IACjB,CAAC;IAED,2DAA2D;IAC3D,IAAI,QAAQ,CAAC,QAAQ,CAAC,+BAA+B,CAAC,EAAE,CAAC;QACxD,OAAO,OAAO,CAAC;IAChB,CAAC;IAED,yDAAyD;IACzD,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC9B,OAAO,mBAAmB,EAAE,KAAK,QAAQ,CAAC;AAC3C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc;IAC7B,OAAO,mBAAmB,EAAE,KAAK,OAAO,CAAC;AAC1C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC9B,OAAO,mBAAmB,EAAE,KAAK,QAAQ,CAAC;AAC3C,CAAC"}
1
+ {"version":3,"file":"installation-type.js","sourceRoot":"","sources":["../../src/utils/installation-type.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AAIzB;;;GAGG;AACH,SAAS,eAAe,CAAC,IAAY;IACpC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,IAAI,CAAC;QACJ,0EAA0E;QAC1E,OAAO,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACR,6EAA6E;QAC7E,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACjC,CAAC;AACF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB;IAClC,6EAA6E;IAC7E,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAE9C,mDAAmD;IACnD,kEAAkE;IAClE,MAAM,IAAI,GAAG,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IAEhG,kEAAkE;IAClE,iGAAiG;IACjG,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC9E,MAAM,UAAU,GAAG,eAAe,CAAC,aAAa,CAAC,CAAC;IAElD,wEAAwE;IACxE,wFAAwF;IACxF,wDAAwD;IACxD,IAAI,UAAU,EAAE,CAAC;QAChB,2EAA2E;QAC3E,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,UAAU,+BAA+B,CAAC,EAAE,CAAC;YACvE,OAAO,QAAQ,CAAC;QACjB,CAAC;QACD,oFAAoF;QACpF,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,UAAU,kBAAkB,CAAC,EAAE,CAAC;YAC1D,OAAO,QAAQ,CAAC;QACjB,CAAC;IACF,CAAC;IAED,gEAAgE;IAChE,iFAAiF;IACjF,iDAAiD;IACjD,IAAI,IAAI,EAAE,CAAC;QACV,MAAM,YAAY,GAAG,eAAe,CAAC,GAAG,IAAI,aAAa,CAAC,CAAC;QAC3D,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,YAAY,GAAG,CAAC,EAAE,CAAC;YAC7C,OAAO,QAAQ,CAAC;QACjB,CAAC;IACF,CAAC;IAED,sFAAsF;IACtF,4EAA4E;IAC5E,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,+BAA+B,CAAC,EAAE,CAAC;QACvF,OAAO,QAAQ,CAAC;IACjB,CAAC;IAED,yDAAyD;IACzD,+EAA+E;IAC/E,oFAAoF;IACpF,IAAI,QAAQ,CAAC,QAAQ,CAAC,+BAA+B,CAAC,EAAE,CAAC;QACxD,OAAO,OAAO,CAAC;IAChB,CAAC;IAED,2DAA2D;IAC3D,2EAA2E;IAC3E,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC9B,OAAO,mBAAmB,EAAE,KAAK,QAAQ,CAAC;AAC3C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc;IAC7B,OAAO,mBAAmB,EAAE,KAAK,OAAO,CAAC;AAC1C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC9B,OAAO,mBAAmB,EAAE,KAAK,QAAQ,CAAC;AAC3C,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentuity/cli",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Agentuity employees and contributors",
6
6
  "type": "module",
@@ -40,9 +40,9 @@
40
40
  "prepublishOnly": "bun run clean && bun run build"
41
41
  },
42
42
  "dependencies": {
43
- "@agentuity/auth": "0.1.33",
44
- "@agentuity/core": "0.1.33",
45
- "@agentuity/server": "0.1.33",
43
+ "@agentuity/auth": "0.1.35",
44
+ "@agentuity/core": "0.1.35",
45
+ "@agentuity/server": "0.1.35",
46
46
  "@datasert/cronjs-parser": "^1.4.0",
47
47
  "@terascope/fetch-github-release": "^2.2.1",
48
48
  "@vitejs/plugin-react": "^5.1.2",
@@ -60,10 +60,10 @@
60
60
  "typescript": "^5.9.0",
61
61
  "vite": "^7.2.7",
62
62
  "zod": "^4.3.5",
63
- "@agentuity/frontend": "0.1.33"
63
+ "@agentuity/frontend": "0.1.35"
64
64
  },
65
65
  "devDependencies": {
66
- "@agentuity/test-utils": "0.1.33",
66
+ "@agentuity/test-utils": "0.1.35",
67
67
  "@types/adm-zip": "^0.5.7",
68
68
  "@types/bun": "latest",
69
69
  "@types/tar-fs": "^2.0.4",
@@ -3,7 +3,6 @@ import cadenceCommand from './cadence';
3
3
  import capabilitiesCommand from './capabilities';
4
4
  import promptCommand from './prompt';
5
5
  import schemaCommand from './schema';
6
- import skillsCommand from './skills';
7
6
  import opencodeCommand from './opencode';
8
7
  import { getCommand } from '../../command-prefix';
9
8
 
@@ -25,10 +24,6 @@ export const command = createCommand({
25
24
  command: getCommand('ai schema show'),
26
25
  description: 'Output CLI schema for AI consumption',
27
26
  },
28
- {
29
- command: getCommand('ai skills generate --output ./skills'),
30
- description: 'Generate Agent Skills from CLI schema',
31
- },
32
27
  ],
33
28
  subcommands: [
34
29
  opencodeCommand,
@@ -36,6 +31,5 @@ export const command = createCommand({
36
31
  capabilitiesCommand,
37
32
  promptCommand,
38
33
  schemaCommand,
39
- skillsCommand,
40
34
  ],
41
35
  });
@@ -64,7 +64,7 @@ export const uninstallSubcommand = createSubcommand({
64
64
  }
65
65
  } catch (error) {
66
66
  if (!jsonMode) {
67
- tui.warn(`Failed to parse Open Code config: ${error}`);
67
+ tui.warning(`Failed to parse Open Code config: ${error}`);
68
68
  }
69
69
  }
70
70
  }
@@ -15,6 +15,9 @@ export interface BunDevServerOptions {
15
15
  deploymentId?: string;
16
16
  logger: Logger;
17
17
  vitePort: number; // Port of already-running Vite asset server
18
+ inspect?: boolean; // Enable bun debugger
19
+ inspectWait?: boolean; // Enable bun debugger and wait for connection
20
+ inspectBrk?: boolean; // Enable bun debugger with breakpoint at first line
18
21
  }
19
22
 
20
23
  export interface BunDevServerResult {
@@ -30,17 +33,15 @@ export interface BunDevServerResult {
30
33
  *
31
34
  * The bundle is loaded here to ensure AI Gateway routing patches are active.
32
35
  * Vite port is read from process.env.VITE_PORT at runtime.
36
+ *
37
+ * When debugger flags (inspect, inspectWait, inspectBrk) are passed, bun is spawned
38
+ * as a subprocess to enable passing the debugger CLI flags.
33
39
  */
34
40
  export async function startBunDevServer(options: BunDevServerOptions): Promise<BunDevServerResult> {
35
- const { rootDir, port = 3500, logger, vitePort } = options;
41
+ const { rootDir, port = 3500, logger, vitePort, inspect, inspectWait, inspectBrk } = options;
36
42
 
37
43
  logger.debug('Starting Bun dev server (Vite already running on port %d)...', vitePort);
38
44
 
39
- // Load the bundled app - this will start Bun.serve() internally
40
- // IMPORTANT: We must import the bundled .agentuity/app.js (NOT src/generated/app.ts)
41
- // because the bundled version has LLM provider patches applied that enable AI Gateway routing.
42
- // Importing the source file directly would bypass these patches.
43
- logger.debug('📦 Loading bundled app (Bun server will start)...');
44
45
  const appPath = `${rootDir}/.agentuity/app.js`;
45
46
 
46
47
  // Verify bundle exists before attempting to load
@@ -54,29 +55,45 @@ export async function startBunDevServer(options: BunDevServerOptions): Promise<B
54
55
  // Set PORT env var so the generated app uses the correct port
55
56
  process.env.PORT = String(port);
56
57
 
57
- // Import the generated app with cache-busting query parameter.
58
- // Bun's module cache is keyed by the full specifier including query string,
59
- // so adding a unique timestamp forces a fresh import on each reload.
60
- const cacheBuster = `?t=${Date.now()}`;
61
- try {
62
- await import(appPath + cacheBuster);
63
- } catch (err) {
64
- const errorMessage = err instanceof Error ? err.message : String(err);
65
- logger.error('Failed to import generated app from %s: %s', appPath, errorMessage);
66
- throw new Error(`Failed to load generated app: ${errorMessage}`);
67
- }
58
+ // Check if any debugger flag is enabled
59
+ const useDebugger = inspect || inspectWait || inspectBrk;
68
60
 
69
- // Wait for server to actually start listening
70
- // The generated app sets (globalThis as any).__AGENTUITY_SERVER__ when server starts
71
- const maxRetries = 50; // Increased retries for slower systems
72
- const retryDelay = 100; // ms
73
- let serverReady = false;
61
+ if (useDebugger) {
62
+ // Spawn bun as subprocess with debugger flag
63
+ logger.debug('📦 Spawning bun with debugger enabled...');
64
+
65
+ // Determine which debugger flag to use (priority: inspectBrk > inspectWait > inspect)
66
+ let debugFlag: string;
67
+ if (inspectBrk) {
68
+ debugFlag = '--inspect-brk';
69
+ } else if (inspectWait) {
70
+ debugFlag = '--inspect-wait';
71
+ } else {
72
+ debugFlag = '--inspect';
73
+ }
74
74
 
75
- for (let i = 0; i < maxRetries; i++) {
76
- // Check if global server object exists
75
+ logger.debug('Using debugger flag: %s', debugFlag);
76
+
77
+ const bunProcess = Bun.spawn(['bun', debugFlag, 'run', appPath], {
78
+ cwd: rootDir,
79
+ stdout: 'inherit',
80
+ stderr: 'inherit',
81
+ env: {
82
+ ...process.env,
83
+ PORT: String(port),
84
+ },
85
+ });
86
+
87
+ // Store the process globally so it can be killed on shutdown
77
88
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
- if ((globalThis as any).__AGENTUITY_SERVER__) {
79
- // Server object exists, now verify it's actually listening by making a request
89
+ (globalThis as any).__AGENTUITY_BUN_SUBPROCESS__ = bunProcess;
90
+
91
+ // Wait for server to actually start listening
92
+ const maxRetries = 50;
93
+ const retryDelay = 100;
94
+ let serverReady = false;
95
+
96
+ for (let i = 0; i < maxRetries; i++) {
80
97
  try {
81
98
  await fetch(`http://127.0.0.1:${port}/`, {
82
99
  method: 'HEAD',
@@ -88,19 +105,79 @@ export async function startBunDevServer(options: BunDevServerOptions): Promise<B
88
105
  } catch {
89
106
  // Connection refused or timeout - server not ready yet
90
107
  }
108
+ // Wait before next check
109
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
91
110
  }
92
- // Wait before next check
93
- await new Promise((resolve) => setTimeout(resolve, retryDelay));
94
- }
95
111
 
96
- if (!serverReady) {
97
- throw new Error(
98
- `Bun server failed to start on port ${port} after ${maxRetries * retryDelay}ms`
99
- );
100
- }
112
+ if (!serverReady) {
113
+ // Kill the subprocess if server didn't start
114
+ try {
115
+ bunProcess.kill();
116
+ } catch (err) {
117
+ logger.debug('Error killing subprocess during startup failure: %s', err);
118
+ }
119
+ throw new Error(
120
+ `Bun server failed to start on port ${port} after ${maxRetries * retryDelay}ms`
121
+ );
122
+ }
123
+
124
+ logger.debug(`Bun dev server started on http://127.0.0.1:${port} with debugger enabled`);
125
+ logger.debug(`Asset requests (/@vite/*, /src/web/*, etc.) proxied to Vite:${vitePort}`);
126
+ } else {
127
+ // Load the bundled app - this will start Bun.serve() internally
128
+ // IMPORTANT: We must import the bundled .agentuity/app.js (NOT src/generated/app.ts)
129
+ // because the bundled version has LLM provider patches applied that enable AI Gateway routing.
130
+ // Importing the source file directly would bypass these patches.
131
+ logger.debug('📦 Loading bundled app (Bun server will start)...');
132
+
133
+ // Import the generated app with cache-busting query parameter.
134
+ // Bun's module cache is keyed by the full specifier including query string,
135
+ // so adding a unique timestamp forces a fresh import on each reload.
136
+ const cacheBuster = `?t=${Date.now()}`;
137
+ try {
138
+ await import(appPath + cacheBuster);
139
+ } catch (err) {
140
+ const errorMessage = err instanceof Error ? err.message : String(err);
141
+ logger.error('Failed to import generated app from %s: %s', appPath, errorMessage);
142
+ throw new Error(`Failed to load generated app: ${errorMessage}`);
143
+ }
144
+
145
+ // Wait for server to actually start listening
146
+ // The generated app sets (globalThis as any).__AGENTUITY_SERVER__ when server starts
147
+ const maxRetries = 50; // Increased retries for slower systems
148
+ const retryDelay = 100; // ms
149
+ let serverReady = false;
101
150
 
102
- logger.debug(`Bun dev server started on http://127.0.0.1:${port}`);
103
- logger.debug(`Asset requests (/@vite/*, /src/web/*, etc.) proxied to Vite:${vitePort}`);
151
+ for (let i = 0; i < maxRetries; i++) {
152
+ // Check if global server object exists
153
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
154
+ if ((globalThis as any).__AGENTUITY_SERVER__) {
155
+ // Server object exists, now verify it's actually listening by making a request
156
+ try {
157
+ await fetch(`http://127.0.0.1:${port}/`, {
158
+ method: 'HEAD',
159
+ signal: AbortSignal.timeout(1000),
160
+ });
161
+ // Any response (even 404) means server is listening
162
+ serverReady = true;
163
+ break;
164
+ } catch {
165
+ // Connection refused or timeout - server not ready yet
166
+ }
167
+ }
168
+ // Wait before next check
169
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
170
+ }
171
+
172
+ if (!serverReady) {
173
+ throw new Error(
174
+ `Bun server failed to start on port ${port} after ${maxRetries * retryDelay}ms`
175
+ );
176
+ }
177
+
178
+ logger.debug(`Bun dev server started on http://127.0.0.1:${port}`);
179
+ logger.debug(`Asset requests (/@vite/*, /src/web/*, etc.) proxied to Vite:${vitePort}`);
180
+ }
104
181
 
105
182
  return {
106
183
  bunServerPort: port,
@@ -95,7 +95,9 @@ export const createSubcommand = createSubcommandHelper({
95
95
  tui.newline();
96
96
  tui.success('API key created successfully!');
97
97
  tui.newline();
98
- tui.warn('Make sure to copy the API key value now. You will not be able to see it again.');
98
+ tui.warning(
99
+ 'Make sure to copy the API key value now. You will not be able to see it again.'
100
+ );
99
101
  tui.newline();
100
102
 
101
103
  const rows = [
@@ -168,7 +168,10 @@ export const deploySubcommand = createSubcommand({
168
168
  const hasTTY = process.stdin.isTTY && process.stdout.isTTY;
169
169
  if (project.region) {
170
170
  try {
171
- const serverProject = await projectGet(apiClient, { id: project.projectId, keys: false });
171
+ const serverProject = await projectGet(apiClient, {
172
+ id: project.projectId,
173
+ keys: false,
174
+ });
172
175
  const serverRegion = serverProject.cloudRegion;
173
176
 
174
177
  if (serverRegion && serverRegion !== project.region) {
@@ -14,24 +14,30 @@ import { resolveOrgId, isOrgScope } from './org-util';
14
14
 
15
15
  const EnvDeleteResponseSchema = z.object({
16
16
  success: z.boolean().describe('Whether the operation succeeded'),
17
- key: z.string().describe('Variable key that was deleted'),
17
+ keys: z.array(z.string()).describe('Variable keys that were deleted'),
18
18
  path: z
19
19
  .string()
20
20
  .optional()
21
- .describe('Local file path where variable was removed (project scope only)'),
22
- secret: z.boolean().describe('Whether a secret was deleted'),
23
- scope: z.enum(['project', 'org']).describe('The scope from which the variable was deleted'),
21
+ .describe('Local file path where variables were removed (project scope only)'),
22
+ secrets: z.array(z.string()).describe('Keys that were secrets'),
23
+ env: z.array(z.string()).describe('Keys that were environment variables'),
24
+ scope: z.enum(['project', 'org']).describe('The scope from which the variables were deleted'),
25
+ notFound: z.array(z.string()).optional().describe('Keys that were not found'),
24
26
  });
25
27
 
26
28
  export const deleteSubcommand = createSubcommand({
27
29
  name: 'delete',
28
30
  aliases: ['del', 'remove', 'rm'],
29
- description: 'Delete an environment variable or secret',
31
+ description: 'Delete one or more environment variables or secrets',
30
32
  tags: ['destructive', 'deletes-resource', 'slow', 'requires-auth'],
31
33
  idempotent: true,
32
34
  examples: [
33
- { command: getCommand('env delete OLD_FEATURE_FLAG'), description: 'Delete variable' },
35
+ { command: getCommand('env delete OLD_FEATURE_FLAG'), description: 'Delete a variable' },
34
36
  { command: getCommand('env rm API_KEY'), description: 'Delete a secret' },
37
+ {
38
+ command: getCommand('env rm KEY1 KEY2 KEY3'),
39
+ description: 'Delete multiple variables at once',
40
+ },
35
41
  {
36
42
  command: getCommand('env rm OPENAI_API_KEY --org'),
37
43
  description: 'Delete org-level secret',
@@ -41,7 +47,7 @@ export const deleteSubcommand = createSubcommand({
41
47
  optional: { project: true },
42
48
  schema: {
43
49
  args: z.object({
44
- key: z.string().describe('the variable or secret key to delete'),
50
+ key: z.array(z.string()).describe('the variable or secret key(s) to delete'),
45
51
  }),
46
52
  options: z.object({
47
53
  org: z
@@ -57,6 +63,7 @@ export const deleteSubcommand = createSubcommand({
57
63
  async handler(ctx) {
58
64
  const { args, project, projectDir, apiClient, config, opts } = ctx;
59
65
  const useOrgScope = isOrgScope(opts?.org);
66
+ const keys = args.key;
60
67
 
61
68
  // Require project context if not using org scope
62
69
  if (!useOrgScope && !project) {
@@ -65,10 +72,11 @@ export const deleteSubcommand = createSubcommand({
65
72
  );
66
73
  }
67
74
 
68
- // Validate key doesn't start with reserved AGENTUITY_ prefix (except AGENTUITY_PUBLIC_)
69
- if (isReservedAgentuityKey(args.key)) {
75
+ // Validate no keys start with reserved AGENTUITY_ prefix (except AGENTUITY_PUBLIC_)
76
+ const reservedKeys = keys.filter(isReservedAgentuityKey);
77
+ if (reservedKeys.length > 0) {
70
78
  tui.fatal(
71
- 'Cannot delete AGENTUITY_ prefixed variables. These are reserved for system use.'
79
+ `Cannot delete AGENTUITY_ prefixed variables: ${reservedKeys.join(', ')}. These are reserved for system use.`
72
80
  );
73
81
  }
74
82
 
@@ -76,85 +84,132 @@ export const deleteSubcommand = createSubcommand({
76
84
  // Organization scope
77
85
  const orgId = await resolveOrgId(apiClient, config, opts!.org!);
78
86
 
79
- // First, determine if this key exists in env or secrets
80
- const orgData = await tui.spinner('Checking organization variable', () => {
87
+ // First, determine which keys exist in env or secrets
88
+ const orgData = await tui.spinner('Checking organization variables', () => {
81
89
  return orgEnvGet(apiClient, { id: orgId, mask: true });
82
90
  });
83
91
 
84
- const isSecret = orgData.secrets?.[args.key] !== undefined;
85
- const isEnv = orgData.env?.[args.key] !== undefined;
92
+ const secretKeys: string[] = [];
93
+ const envKeys: string[] = [];
94
+ const notFoundKeys: string[] = [];
95
+
96
+ for (const key of keys) {
97
+ if (orgData.secrets?.[key] !== undefined) {
98
+ secretKeys.push(key);
99
+ } else if (orgData.env?.[key] !== undefined) {
100
+ envKeys.push(key);
101
+ } else {
102
+ notFoundKeys.push(key);
103
+ }
104
+ }
86
105
 
87
- if (!isSecret && !isEnv) {
106
+ // If all keys are not found, fail
107
+ if (secretKeys.length === 0 && envKeys.length === 0) {
88
108
  tui.fatal(
89
- `Variable '${args.key}' not found in organization`,
109
+ `No variables found in organization: ${keys.join(', ')}`,
90
110
  ErrorCode.RESOURCE_NOT_FOUND
91
111
  );
92
112
  }
93
113
 
94
- // Delete from cloud
95
- const label = isSecret ? 'secret' : 'environment variable';
96
- await tui.spinner(`Deleting organization ${label} from cloud`, () => {
114
+ // Delete from cloud (batch operation)
115
+ const totalToDelete = secretKeys.length + envKeys.length;
116
+ const label = totalToDelete === 1 ? 'variable' : 'variables';
117
+ await tui.spinner(`Deleting ${totalToDelete} organization ${label} from cloud`, () => {
97
118
  return orgEnvDelete(apiClient, {
98
119
  id: orgId,
99
- ...(isSecret ? { secrets: [args.key] } : { env: [args.key] }),
120
+ ...(secretKeys.length > 0 ? { secrets: secretKeys } : {}),
121
+ ...(envKeys.length > 0 ? { env: envKeys } : {}),
100
122
  });
101
123
  });
102
124
 
125
+ const deletedKeys = [...secretKeys, ...envKeys];
126
+ if (notFoundKeys.length > 0) {
127
+ tui.warning(`Variables not found (skipped): ${notFoundKeys.join(', ')}`);
128
+ }
103
129
  tui.success(
104
- `Organization ${isSecret ? 'secret' : 'environment variable'} '${args.key}' deleted successfully`
130
+ `Deleted ${deletedKeys.length} organization variable(s): ${deletedKeys.join(', ')}`
105
131
  );
106
132
 
107
133
  return {
108
134
  success: true,
109
- key: args.key,
110
- secret: isSecret,
135
+ keys: deletedKeys,
136
+ secrets: secretKeys,
137
+ env: envKeys,
111
138
  scope: 'org' as const,
139
+ ...(notFoundKeys.length > 0 ? { notFound: notFoundKeys } : {}),
112
140
  };
113
141
  } else {
114
- // Project scope (existing behavior)
115
- const projectData = await tui.spinner('Checking variable', () => {
142
+ // Project scope
143
+ const projectData = await tui.spinner('Checking variables', () => {
116
144
  return projectGet(apiClient, { id: project!.projectId, mask: true });
117
145
  });
118
146
 
119
- const isSecret = projectData.secrets?.[args.key] !== undefined;
120
- const isEnv = projectData.env?.[args.key] !== undefined;
147
+ const secretKeys: string[] = [];
148
+ const envKeys: string[] = [];
149
+ const notFoundKeys: string[] = [];
150
+
151
+ for (const key of keys) {
152
+ if (projectData.secrets?.[key] !== undefined) {
153
+ secretKeys.push(key);
154
+ } else if (projectData.env?.[key] !== undefined) {
155
+ envKeys.push(key);
156
+ } else {
157
+ notFoundKeys.push(key);
158
+ }
159
+ }
121
160
 
122
- if (!isSecret && !isEnv) {
123
- tui.fatal(`Variable '${args.key}' not found`, ErrorCode.RESOURCE_NOT_FOUND);
161
+ // If all keys are not found, fail
162
+ if (secretKeys.length === 0 && envKeys.length === 0) {
163
+ tui.fatal(`No variables found: ${keys.join(', ')}`, ErrorCode.RESOURCE_NOT_FOUND);
124
164
  }
125
165
 
126
- // Delete from cloud using the correct field
127
- const label = isSecret ? 'secret' : 'environment variable';
128
- await tui.spinner(`Deleting ${label} from cloud`, () => {
166
+ // Delete from cloud (batch operation)
167
+ const totalToDelete = secretKeys.length + envKeys.length;
168
+ const label = totalToDelete === 1 ? 'variable' : 'variables';
169
+ await tui.spinner(`Deleting ${totalToDelete} ${label} from cloud`, () => {
129
170
  return projectEnvDelete(apiClient, {
130
171
  id: project!.projectId,
131
- ...(isSecret ? { secrets: [args.key] } : { env: [args.key] }),
172
+ ...(secretKeys.length > 0 ? { secrets: secretKeys } : {}),
173
+ ...(envKeys.length > 0 ? { env: envKeys } : {}),
132
174
  });
133
175
  });
134
176
 
135
- // Update local .env file only if we have a project directory
136
- // (not when using --project-id without being in a project folder)
177
+ // Update local .env file only if we have a project directory and an existing .env file
137
178
  let envFilePath: string | undefined;
138
179
  if (projectDir) {
139
180
  envFilePath = await findExistingEnvFile(projectDir);
140
- const currentEnv = await readEnvFile(envFilePath);
141
- delete currentEnv[args.key];
181
+ if (envFilePath) {
182
+ const currentEnv = await readEnvFile(envFilePath);
183
+ const originalKeyCount = Object.keys(currentEnv).length;
184
+ for (const key of [...secretKeys, ...envKeys]) {
185
+ delete currentEnv[key];
186
+ }
187
+ // Only write if we actually removed keys (avoid creating empty file)
188
+ const keysRemoved = originalKeyCount > Object.keys(currentEnv).length;
189
+ if (keysRemoved) {
190
+ await writeEnvFile(envFilePath, currentEnv, { preserveExisting: false });
191
+ }
192
+ }
193
+ }
142
194
 
143
- // Write the updated env, preserveExisting: false since we already have the full state
144
- await writeEnvFile(envFilePath, currentEnv, { preserveExisting: false });
195
+ const deletedKeys = [...secretKeys, ...envKeys];
196
+ if (notFoundKeys.length > 0) {
197
+ tui.warning(`Variables not found (skipped): ${notFoundKeys.join(', ')}`);
145
198
  }
146
199
 
147
- const successMsg = envFilePath
148
- ? `${isSecret ? 'Secret' : 'Environment variable'} '${args.key}' deleted successfully (cloud + ${envFilePath})`
149
- : `${isSecret ? 'Secret' : 'Environment variable'} '${args.key}' deleted successfully (cloud only)`;
150
- tui.success(successMsg);
200
+ const locationMsg = envFilePath ? ` (cloud + ${envFilePath})` : ' (cloud only)';
201
+ tui.success(
202
+ `Deleted ${deletedKeys.length} variable(s): ${deletedKeys.join(', ')}${locationMsg}`
203
+ );
151
204
 
152
205
  return {
153
206
  success: true,
154
- key: args.key,
207
+ keys: deletedKeys,
155
208
  path: envFilePath,
156
- secret: isSecret,
209
+ secrets: secretKeys,
210
+ env: envKeys,
157
211
  scope: 'project' as const,
212
+ ...(notFoundKeys.length > 0 ? { notFound: notFoundKeys } : {}),
158
213
  };
159
214
  }
160
215
  },
@@ -2,12 +2,7 @@ import { z } from 'zod';
2
2
  import { createSubcommand } from '../../../types';
3
3
  import * as tui from '../../../tui';
4
4
  import { projectGet, orgEnvGet } from '@agentuity/server';
5
- import {
6
- findExistingEnvFile,
7
- readEnvFile,
8
- writeEnvFile,
9
- mergeEnvVars,
10
- } from '../../../env-util';
5
+ import { findExistingEnvFile, readEnvFile, writeEnvFile, mergeEnvVars } from '../../../env-util';
11
6
  import { getCommand } from '../../../command-prefix';
12
7
  import { resolveOrgId, isOrgScope } from './org-util';
13
8
 
@@ -49,7 +49,7 @@ export const deleteSubcommand = createSubcommand({
49
49
  if (!opts.confirm) {
50
50
  if (process.stdin.isTTY) {
51
51
  tui.newline();
52
- tui.warn(`You are about to delete machine ${tui.bold(machine.id)}`);
52
+ tui.warning(`You are about to delete machine ${tui.bold(machine.id)}`);
53
53
  if (machine.orgName) {
54
54
  tui.info(`Organization: ${machine.orgName}`);
55
55
  }