Repository navigation
fix(create-agentuity): match dist-tag logic with publish script #1288
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,19 +6,33 @@ const require = createRequire(import.meta.url); | |||||||||||||||||||||||||
| const pkg = require('./package.json'); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Determine the dist-tag based on create-agentuity version | ||||||||||||||||||||||||||
| // Prerelease versions (e.g., 2.0.0-beta.1) should use @next | ||||||||||||||||||||||||||
| // Stable versions should use @latest | ||||||||||||||||||||||||||
| function getDistTag(version) { | ||||||||||||||||||||||||||
| // Check for prerelease identifiers: alpha, beta, rc, canary, next, etc. | ||||||||||||||||||||||||||
| // Must match the logic in scripts/publish.ts: | ||||||||||||||||||||||||||
| // - Beta versions (-beta.) → use @beta | ||||||||||||||||||||||||||
| // - Other prereleases (-alpha., -rc., etc.) → use @next | ||||||||||||||||||||||||||
| // - Stable versions → use @latest | ||||||||||||||||||||||||||
| export function getDistTag(version) { | ||||||||||||||||||||||||||
| // Check for beta prerelease first | ||||||||||||||||||||||||||
| if (/-beta\./.test(version)) { | ||||||||||||||||||||||||||
| return 'beta'; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| // Check for other prerelease identifiers: alpha, rc, canary, next, etc. | ||||||||||||||||||||||||||
| if (/-([a-zA-Z]+)/.test(version)) { | ||||||||||||||||||||||||||
| return 'next'; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| return 'latest'; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const distTag = getDistTag(pkg.version); | ||||||||||||||||||||||||||
| const args = process.argv.slice(2); | ||||||||||||||||||||||||||
| const result = spawnSync('bunx', [`@agentuity/cli@${distTag}`, 'create', ...args], { | ||||||||||||||||||||||||||
| stdio: 'inherit', | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
| process.exit(result.status || 0); | ||||||||||||||||||||||||||
| // Only run when executed directly, not when imported for testing | ||||||||||||||||||||||||||
| const isMain = | ||||||||||||||||||||||||||
| typeof Bun !== 'undefined' | ||||||||||||||||||||||||||
| ? Bun.main === new URL(import.meta.url).pathname | ||||||||||||||||||||||||||
| : process.argv[1] === new URL(import.meta.url).pathname; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (isMain) { | ||||||||||||||||||||||||||
| const distTag = getDistTag(pkg.version); | ||||||||||||||||||||||||||
| const args = process.argv.slice(2); | ||||||||||||||||||||||||||
| const result = spawnSync('bunx', [`@agentuity/cli@${distTag}`, 'create', ...args], { | ||||||||||||||||||||||||||
| stdio: 'inherit', | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
| process.exit(result.status || 0); | ||||||||||||||||||||||||||
|
Comment on lines
+34
to
+37
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: result.status and result.error in Node.js child_process.spawnSync: - result.status: Set to the exit code of the child process when it exits normally (integer, typically 0 for success). Set to null if the process was terminated by a signal (result.signal is set) or if spawning failed (result.error is set). - result.error: Set to an Error instance only when the spawning itself fails (e.g., command not found - ENOENT, permission denied - EACCES, too many processes - EMFILE/ENFILE). In this case, result.status is null, and stdout/stderr/output are null. If the child spawns successfully but exits with non-zero code or signal, result.error is undefined. Exit code handling recommendations: - Always check if (result.error) { /* handle spawn failure, e.g. log result.error.code/path/syscall / } - Then if (result.status !== 0 || result.signal) { / handle child failure */ }. Non-zero status indicates child error; signal (non-null) indicates termination by signal (e.g. SIGTERM, SIGKILL). For signals, consider using util.convertProcessSignalToExitCode(signal) to map to an equivalent exit code. - Example from docs (adapted for spawnSync): try { const result = spawnSync('ls'); if (result.error) { console.error(Spawn failed: ${result.error.code}); // e.g. 'ENOENT' } else if (result.status !== 0) { console.error(Child exited with code ${result.status}); console.error(stdout: ${result.stdout}); console.error(stderr: ${result.stderr}); } else if (result.signal) { console.error(Killed by signal ${result.signal}); } else { // success } } catch (err) { // Only for timeout or other sync errors } Note: spawnSync does not throw on non-zero exit (unlike execSync); always inspect the returned object. Use { stdio: 'pipe' } or specific config to capture stdout/stderr. Citations:
🏁 Script executed: cat -n apps/create-agentuity/bin.js | sed -n '30,40p'Repository: agentuity/sdk Length of output: 361 🏁 Script executed: rg "spawnSync|execSync" apps/create-agentuity/bin.jsRepository: agentuity/sdk Length of output: 190 Check for spawn failures before exiting. Line 37 uses 🔧 Proposed fix const result = spawnSync('bunx', [`@agentuity/cli@${distTag}`, 'create', ...args], {
stdio: 'inherit',
});
- process.exit(result.status || 0);
+ if (result.error) {
+ console.error(result.error.message);
+ process.exit(1);
+ }
+ process.exit(result.status ?? 1);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| #!/usr/bin/env bun test | ||
| import { describe, test, expect } from 'bun:test'; | ||
| import { getDistTag } from './bin.js'; | ||
|
|
||
| describe('getDistTag', () => { | ||
| describe('beta versions', () => { | ||
| test('2.0.0-beta.0 should return beta', () => { | ||
| expect(getDistTag('2.0.0-beta.0')).toBe('beta'); | ||
| }); | ||
| test('2.0.0-beta.1 should return beta', () => { | ||
| expect(getDistTag('2.0.0-beta.1')).toBe('beta'); | ||
| }); | ||
| test('1.0.0-beta.10 should return beta', () => { | ||
| expect(getDistTag('1.0.0-beta.10')).toBe('beta'); | ||
| }); | ||
| }); | ||
|
|
||
| describe('other prerelease versions', () => { | ||
| test('2.0.0-alpha.0 should return next', () => { | ||
| expect(getDistTag('2.0.0-alpha.0')).toBe('next'); | ||
| }); | ||
| test('2.0.0-rc.1 should return next', () => { | ||
| expect(getDistTag('2.0.0-rc.1')).toBe('next'); | ||
| }); | ||
| test('1.0.0-canary.3 should return next', () => { | ||
| expect(getDistTag('1.0.0-canary.3')).toBe('next'); | ||
| }); | ||
| test('2.0.0-next.5 should return next', () => { | ||
| expect(getDistTag('2.0.0-next.5')).toBe('next'); | ||
| }); | ||
| }); | ||
|
|
||
| describe('stable versions', () => { | ||
| test('1.0.0 should return latest', () => { | ||
| expect(getDistTag('1.0.0')).toBe('latest'); | ||
| }); | ||
| test('1.0.62 should return latest', () => { | ||
| expect(getDistTag('1.0.62')).toBe('latest'); | ||
| }); | ||
| test('2.0.0 should return latest', () => { | ||
| expect(getDistTag('2.0.0')).toBe('latest'); | ||
| }); | ||
| test('10.20.30 should return latest', () => { | ||
| expect(getDistTag('10.20.30')).toBe('latest'); | ||
| }); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
In Bun and Node.js ESM, what is the recommended cross-platform way to detect "executed as main module" from import.meta.url, and is URL.pathname safe for Windows path comparisons?💡 Result:
In Bun and recent Node.js (v22.18+ LTS and v24.2+ current as of 2026), the recommended cross-platform way to detect if the module is executed as the main module is to use import.meta.main, a boolean that is true if the current module is the entry point of the process. This property is available in Bun docs and Node.js ESM documentation. It eliminates the need for manual URL comparisons. For older Node.js versions lacking import.meta.main, the cross-platform fallback is: js import { pathToFileURL } from 'node:url'; import { fileURLToPath } from 'node:url'; const mainPath = process.argv[1] ? fileURLToPath(pathToFileURL(process.argv[1]).href) : null; const currentPath = fileURLToPath(import.meta.url); const isMain = mainPath && mainPath === currentPath; However, with import.meta.main standardized across Bun and modern Node.js, it is the preferred method. URL.pathname is NOT safe for Windows path comparisons. On Windows, new URL(import.meta.url).pathname starts with / (e.g., /C:/path/file.js), using forward slashes, which does not match process.argv[1] (e.g., C:\path\file.js with backslashes). Direct string or pathname comparisons fail cross-platform and on Windows. Always convert via fileURLToPath(import.meta.url) and pathToFileURL(process.argv[1]) for safe handling of percent-encoding, slashes, and drive letters.
Citations:
🏁 Script executed:
cat -n apps/create-agentuity/bin.js | head -35Repository: agentuity/sdk
Length of output: 1439
🏁 Script executed:
Repository: agentuity/sdk
Length of output: 39
🏁 Script executed:
Repository: agentuity/sdk
Length of output: 93
🏁 Script executed:
Repository: agentuity/sdk
Length of output: 39
🏁 Script executed:
Repository: agentuity/sdk
Length of output: 599
🏁 Script executed:
Repository: agentuity/sdk
Length of output: 39
🏁 Script executed:
Repository: agentuity/sdk
Length of output: 78
Use
import.meta.mainfor cross-platformisMaindetection, orfileURLToPath()as a fallback.Lines 28–29 compare filesystem paths against
URL.pathname, which is unsafe on Windows and will cause CLI execution to fail. On Windows,new URL(import.meta.url).pathnameproduces/C:/path/file.js(with leading/and forward slashes) whileprocess.argv[1]containsC:\path\file.js(backslashes), so the comparison always fails.For modern Node.js (v22.18+ LTS, v24.2+) and Bun, use
import.meta.main(a boolean flag that is true when the module is executed as the entry point). For older Node.js versions, usefileURLToPath(import.meta.url)to safely convert the URL to a filesystem path for comparison.🔧 Proposed fix (modern)
import { spawnSync } from 'node:child_process'; import { createRequire } from 'node:module'; @@ -const isMain = - typeof Bun !== 'undefined' - ? Bun.main === new URL(import.meta.url).pathname - : process.argv[1] === new URL(import.meta.url).pathname; +const isMain = import.meta.main;🤖 Prompt for AI Agents