diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index d4d059f6a..53c1a94ff 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -6,14 +6,14 @@ }, "metadata": { "description": "Agentuity plugins for Claude Code", - "version": "3.1.16" + "version": "3.1.17" }, "plugins": [ { "name": "agentuity", "source": "./packages/claude-code", "description": "Deploy websites, apps, and AI agents to Agentuity — with managed databases, storage, queues, and more", - "version": "3.1.16", + "version": "3.1.17", "author": { "name": "Agentuity" }, diff --git a/package.json b/package.json index d469b493c..6be276f69 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sdk-mono", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "packageManager": "bun@1.3.11", "private": true, diff --git a/packages/adapter/package.json b/packages/adapter/package.json index ed6fec3b4..f631e8aa5 100644 --- a/packages/adapter/package.json +++ b/packages/adapter/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/adapter", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "description": "HTTP client adapter for Agentuity service clients", diff --git a/packages/aigateway/package.json b/packages/aigateway/package.json index 928001265..553b34613 100644 --- a/packages/aigateway/package.json +++ b/packages/aigateway/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/aigateway", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 4e69a0a4f..d3e41879b 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/analytics", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "description": "Browser analytics for Agentuity applications", diff --git a/packages/api/package.json b/packages/api/package.json index 554107b50..5d1ecc52d 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/api", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "description": "Platform HTTP client for Agentuity control-plane APIs", diff --git a/packages/claude-code/.claude-plugin/plugin.json b/packages/claude-code/.claude-plugin/plugin.json index 705962449..c0b875659 100644 --- a/packages/claude-code/.claude-plugin/plugin.json +++ b/packages/claude-code/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "agentuity", - "version": "3.1.16", + "version": "3.1.17", "description": "Deploy websites, apps, and AI agents to Agentuity — with managed databases, storage, queues, and more", "author": { "name": "Agentuity", diff --git a/packages/claude-code/package.json b/packages/claude-code/package.json index 5f16c9c47..02065e8a3 100644 --- a/packages/claude-code/package.json +++ b/packages/claude-code/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/claude-code", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity", "description": "Claude Code plugin for deploying websites, apps, and AI agents to Agentuity — with managed databases, storage, sandboxes, queues, cron, email, and more", diff --git a/packages/cli/package.json b/packages/cli/package.json index 3a96750ab..81733573e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/cli", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/cli/src/cmd/build/adapters/nextjs.ts b/packages/cli/src/cmd/build/adapters/nextjs.ts index c4f602641..20a22035d 100644 --- a/packages/cli/src/cmd/build/adapters/nextjs.ts +++ b/packages/cli/src/cmd/build/adapters/nextjs.ts @@ -238,10 +238,15 @@ export const nextjsAdapter: BuildAdapter = { } // Copy `public/` next to server.js for the same reason. + // Next assetPrefix only rewrites /_next/* — public/ files + // (e.g. /next.svg) stay origin-relative. Packaging owns CDN + // rewrite + launch.static.include when publicStaticDir is set. + let packagedPublicDir: string | undefined; if (existsSync(publicPath)) { const publicDst = join(serverDir, 'public'); mkdirSync(publicDst, { recursive: true }); cpSync(publicPath, publicDst, { recursive: true }); + packagedPublicDir = publicDst; } logs.push( @@ -257,6 +262,7 @@ export const nextjsAdapter: BuildAdapter = { workingDirectory, staticDir: packagedStaticDir, staticAssetPublicPath: framework.staticAssetPublicPath, + publicStaticDir: packagedPublicDir, port: framework.port ?? 3000, duration: Date.now() - started, logs, @@ -267,6 +273,8 @@ export const nextjsAdapter: BuildAdapter = { // and the package manifests Hadron needs to install production // dependencies before launch. This path is brittle (`next start` // needs the full Next.js install) so we warn the user. + // Public CDN rewrite / launch.static.include require standalone + // packaging (publicStaticDir is only set on that path). logger.debug('No standalone output found — copying full .next directory'); const nextDst = join(outputDir, '.next'); cpSync(join(projectDir, '.next'), nextDst, { recursive: true }); diff --git a/packages/cli/src/cmd/build/adapters/types.ts b/packages/cli/src/cmd/build/adapters/types.ts index a474a37cb..2f9738a89 100644 --- a/packages/cli/src/cmd/build/adapters/types.ts +++ b/packages/cli/src/cmd/build/adapters/types.ts @@ -42,6 +42,13 @@ export interface BuildResult { /** Public URL path prefix for files inside staticDir */ staticAssetPublicPath?: string; + /** + * Absolute path to packaged root-public assets (e.g. Next `public/`). + * Used when {@link staticAssetPublicPath} is a non-empty built prefix + * (`_next/static`) so CDN upload + HTML rewrite can also ship `/logo.svg`. + */ + publicStaticDir?: string; + /** Port the app listens on */ port?: number; diff --git a/packages/cli/src/cmd/build/package/index.ts b/packages/cli/src/cmd/build/package/index.ts index 3783da9f4..06b8c4b45 100644 --- a/packages/cli/src/cmd/build/package/index.ts +++ b/packages/cli/src/cmd/build/package/index.ts @@ -2,22 +2,31 @@ * Build output packaging. * * After a framework adapter builds the project, the packager: - * 1. Generates launch metadata (how to start the app) - * 2. Writes launch.json + * 1. Rewrites root-public asset URLs onto the CDN base when applicable + * 2. Generates launch metadata (how to start the app) + * 3. Writes launch.json * * The output is a self-contained directory ready for deployment * as a buildpack image layer or traditional zip upload. + * + * Public CDN policy lives here (not in framework adapters): adapters only + * stage files and set `BuildResult.publicStaticDir`; packaging owns + * rewrite + `launch.static.include`. */ import type { DetectedFramework } from '../detect/types.ts'; import type { MonorepoContext } from '../detect/monorepo.ts'; import type { BuildResult } from '../adapters/types.ts'; +import { resolveAgentuityCdnBase } from '../adapters/cdn-origin.ts'; import { generateLaunchMetadata, + isSplitCdnLayout, readUserLaunchOverride, + resolveProcessRoot, writeLaunchMetadata, type LaunchMetadata, } from './launch.ts'; +import { rewritePublicAssetUrlsInTree } from './public-cdn.ts'; export interface PackageResult { /** Absolute path to the packaged output */ @@ -31,6 +40,9 @@ export interface PackageResult { /** Path to static assets (if any) */ staticDir?: string; + + /** Human-readable packaging log lines (e.g. public URL rewrites) */ + logs: string[]; } export interface PackageBuildOutputOptions { @@ -38,6 +50,32 @@ export interface PackageBuildOutputOptions { cdnBaseUrl?: string; } +/** + * When CDN base is known and packaging staged a split-layout public root, + * rewrite root-absolute `/file` refs under the process tree to the CDN base. + */ +function rewriteStagedPublicAssets( + buildResult: BuildResult, + monorepo: MonorepoContext | undefined, + cdnBase: string +): string[] { + const logs: string[] = []; + if (!buildResult.publicStaticDir) return logs; + + const publicPath = buildResult.staticAssetPublicPath ?? ''; + if (!isSplitCdnLayout(publicPath)) return logs; + + const treeRoot = resolveProcessRoot(buildResult, monorepo); + const rw = rewritePublicAssetUrlsInTree(treeRoot, buildResult.publicStaticDir, cdnBase); + if (rw.publicFileCount > 0) { + logs.push( + `✓ CDN: rewrote public/ refs in ${rw.filesChanged}/${rw.filesScanned} files ` + + `(${rw.publicFileCount} public asset(s) → ${cdnBase})` + ); + } + return logs; +} + /** * Package a build result into a deployment-ready directory. * @@ -54,6 +92,14 @@ export async function packageBuildOutput( options?: PackageBuildOutputOptions ): Promise { const override = projectDir ? await readUserLaunchOverride(projectDir) : null; + const logs: string[] = []; + + // Same CDN chain as launch.static.baseUrl / adapters. Rewrite before + // writing launch.json so staged HTML/JS already point at the CDN. + const cdnBase = resolveAgentuityCdnBase({ cdnBaseUrl: options?.cdnBaseUrl }); + if (cdnBase) { + logs.push(...rewriteStagedPublicAssets(buildResult, monorepo, cdnBase)); + } // Generate launch metadata (with optional user override applied). // In monorepo mode, every process inherits the subpackage as its @@ -74,8 +120,16 @@ export async function packageBuildOutput( launch, hasStaticAssets: !!buildResult.staticDir || !!launch.static, staticDir: buildResult.staticDir, + logs, }; } -// Re-export -export type { LaunchMetadata, LaunchStaticAssets, ProcessDefinition } from './launch.ts'; +// Re-export public launch contract types (not rewrite helpers — those stay +// package-internal / testable via their module path). +export type { + LaunchMetadata, + LaunchStaticAssets, + LaunchStaticRoot, + ProcessDefinition, +} from './launch.ts'; +export { joinCdnAssetUrl } from './launch.ts'; diff --git a/packages/cli/src/cmd/build/package/launch.ts b/packages/cli/src/cmd/build/package/launch.ts index bfe349417..2785c2f38 100644 --- a/packages/cli/src/cmd/build/package/launch.ts +++ b/packages/cli/src/cmd/build/package/launch.ts @@ -55,6 +55,13 @@ const UserLaunchProcessSchema = z }) .passthrough(); +const UserLaunchStaticRootSchema = z + .object({ + directory: z.string(), + publicPath: z.string(), + }) + .passthrough(); + const UserLaunchStaticSchema = z .object({ directory: z.string(), @@ -63,6 +70,10 @@ const UserLaunchStaticSchema = z .string() .nullish() .transform((v) => v ?? undefined), + include: z + .array(UserLaunchStaticRootSchema) + .nullish() + .transform((v) => v ?? undefined), }) .passthrough(); @@ -195,33 +206,70 @@ export interface ProcessDefinition { } /** - * Static/CDN asset locations recorded in launch.json. - * - * Consumers (CDN upload, pilot) compose public URLs as: - * `{baseUrl}{publicPath}/{pathWithinDirectory}` + * One CDN upload root. * - * `baseUrl` is optional: when set (from `--cdn-base-url` or the platform - * default), frameworks that bake asset URLs at build time should have - * used the same prefix. When omitted, the platform may still upload - * files under `publicPath` using its own CDN root. + * Compose the public object URL with {@link joinCdnAssetUrl}: + * - non-empty publicPath → `{baseUrl}{publicPath}/{pathWithinDirectory}` + * - empty publicPath → `{baseUrl}{pathWithinDirectory}` (CDN base root) */ -export interface LaunchStaticAssets { +export interface LaunchStaticRoot { /** - * Directory of static assets relative to the process working - * directory (or deploy root when no workingDirectory is set). - * Posix separators. Example: `.next/static`, `dist`, `.output/public`. + * Directory relative to the process working directory (or deploy root). + * Posix separators. Example: `.next/static`, `public`, `dist`. */ directory: string; /** * URL path prefix for files inside `directory` (no leading slash). - * Framework-defined; e.g. `_next/static` for Next.js, `''` for Vite SPA. + * e.g. `_next/static` for Next built assets, `''` for Next `public/`. */ publicPath: string; +} + +/** + * Static/CDN asset locations recorded in launch.json. + * + * The primary `directory`/`publicPath` is the framework build tree. + * Optional `include` lists extra roots (Next `public/`) that must also + * be uploaded when `baseUrl` is set — `assetPrefix` alone does not cover them. + * + * `baseUrl` is optional: when set (from `--cdn-base-url` or the platform + * default), frameworks that bake asset URLs at build time should have + * used the same prefix. When omitted, the platform may still upload + * files under `publicPath` using its own CDN root. + * + * `include` is only emitted when `baseUrl` is known and packaging staged a + * split-layout public root (`publicStaticDir` + non-empty primary publicPath). + */ +export interface LaunchStaticAssets extends LaunchStaticRoot { /** * Absolute CDN base URL with trailing slash when known at package time. * Example: `https://cdn.agentuity.com/org_123/assets/`. */ baseUrl?: string; + /** + * Extra CDN roots (e.g. Next `public/` with publicPath `""`). + * Deploy clients should upload each root the same way as the primary. + * Present only when {@link baseUrl} is set. + */ + include?: LaunchStaticRoot[]; +} + +/** + * Join a CDN object URL without double-slashes when `publicPath` is empty. + * + * @param baseUrl - absolute CDN base (trailing slash optional) + * @param publicPath - URL path prefix, no leading slash (`''` → base root) + * @param pathWithinDirectory - path relative to that root + */ +export function joinCdnAssetUrl( + baseUrl: string, + publicPath: string, + pathWithinDirectory: string +): string { + const base = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`; + const prefix = publicPath.replace(/^\/+|\/+$/g, ''); + const rel = pathWithinDirectory.replace(/^\/+/, ''); + return prefix ? `${base}${prefix}/${rel}` : `${base}${rel}`; } /** @@ -267,6 +315,47 @@ function toPosixPath(p: string): string { return p.split('\\').join('/'); } +/** + * Absolute process cwd used for launch-relative paths: workingDirectory + * when set, else monorepo subpath, else the deploy/output root. + */ +export function resolveProcessRoot( + buildResult: Pick, + monorepo?: MonorepoContext +): string { + const workRel = buildResult.workingDirectory ?? monorepo?.subpath; + return workRel ? join(buildResult.outputDir, workRel) : buildResult.outputDir; +} + +/** + * Relativize an absolute staged path under `processRoot` (posix). + * Returns `undefined` if the path escapes the root (and optional + * output-root fallback when `allowOutputFallback` is set). + */ +export function relativeToProcessRoot( + absPath: string, + processRoot: string, + options?: { allowOutputFallback?: boolean; outputDir?: string } +): string | undefined { + let rel = toPosixPath(relative(processRoot, absPath)); + if (!rel || rel === '') return '.'; + if (!rel.startsWith('..')) return rel; + if (options?.allowOutputFallback && options.outputDir) { + // Fallback: static staged outside subpath but still under output. + rel = toPosixPath(relative(options.outputDir, absPath)); + if (!rel.startsWith('..')) return rel || '.'; + } + return undefined; +} + +/** + * True when the build uses a split CDN layout: built assets under a + * non-empty publicPath (e.g. `_next/static`) plus a separate public root. + */ +export function isSplitCdnLayout(publicPath: string): boolean { + return publicPath !== ''; +} + /** * Resolve the static asset block for launch.json from the staged build * result (preferred) or framework detection fallback. @@ -286,20 +375,11 @@ export function resolveLaunchStatic( // (adapter packaging layout wins; monorepo.subpath is the fallback // for adapters that do not set workingDirectory); otherwise to the // deploy/output root. - const workRel = buildResult.workingDirectory ?? monorepo?.subpath; - const processRoot = workRel ? join(buildResult.outputDir, workRel) : buildResult.outputDir; - let rel = toPosixPath(relative(processRoot, buildResult.staticDir)); - if (!rel || rel === '') { - directory = '.'; - } else if (!rel.startsWith('..')) { - directory = rel; - } else { - // Fallback: relative to output root (e.g. static staged outside subpath). - rel = toPosixPath(relative(buildResult.outputDir, buildResult.staticDir)); - if (!rel.startsWith('..')) { - directory = rel || '.'; - } - } + const processRoot = resolveProcessRoot(buildResult, monorepo); + directory = relativeToProcessRoot(buildResult.staticDir, processRoot, { + allowOutputFallback: true, + outputDir: buildResult.outputDir, + }); } else if (framework.staticDir) { // Build did not resolve an absolute staged path, but detection knew // where assets should live relative to the project/working dir. @@ -312,10 +392,24 @@ export function resolveLaunchStatic( // then AGENTUITY_CDN_BASE_URL / AGENTUITY_CDN_ORIGIN / deployment id. const baseUrl = resolveAgentuityCdnBase({ cdnBaseUrl }); + // Extra roots (Next public/): only when CDN base is known, packaging + // staged publicStaticDir, and primary publicPath is a non-empty build + // prefix (split CDN layout). Without baseUrl, deployers use the primary + // root only — include would be meaningless without a CDN target. + const include: LaunchStaticRoot[] = []; + if (baseUrl && buildResult.publicStaticDir && isSplitCdnLayout(publicPath)) { + const processRoot = resolveProcessRoot(buildResult, monorepo); + const pubRel = relativeToProcessRoot(buildResult.publicStaticDir, processRoot); + if (pubRel && pubRel !== directory) { + include.push({ directory: pubRel, publicPath: '' }); + } + } + return { directory, publicPath, ...(baseUrl ? { baseUrl } : {}), + ...(include.length > 0 ? { include } : {}), }; } diff --git a/packages/cli/src/cmd/build/package/public-cdn.ts b/packages/cli/src/cmd/build/package/public-cdn.ts new file mode 100644 index 000000000..a0a1c5dde --- /dev/null +++ b/packages/cli/src/cmd/build/package/public-cdn.ts @@ -0,0 +1,169 @@ +/** + * CDN packaging helpers for framework "public/" (or root-static) files. + * + * Next.js `assetPrefix` only rewrites `/_next/*`. Files under `public/` + * (e.g. `/next.svg`) stay origin-relative unless we: + * 1. List them for CDN upload (`launch.static.include`) + * 2. Rewrite root-absolute references to `{cdnBase}{relpath}` + * + * Same pattern applies to any framework where staticAssetPublicPath is a + * non-empty prefix of the built tree and loose public files live beside it. + * + * Call sites: packaging (`packageBuildOutput`) owns rewrite + include emission; + * adapters only stage `publicStaticDir` on {@link BuildResult}. + */ + +import { existsSync, readdirSync, readFileSync, writeFileSync, type Dirent } from 'node:fs'; +import { join, relative } from 'node:path'; +import { toPosixPath } from '../deploy-ignore.ts'; + +const REWRITE_EXTENSIONS = new Set([ + '.html', + '.htm', + '.js', + '.mjs', + '.cjs', + '.json', + '.css', + '.rsc', + '.map', + '.txt', +]); + +const SKIP_DIR_NAMES = new Set(['node_modules', '.git']); + +/** + * Escape a path for use inside a RegExp. + */ +function escapeRegExp(s: string): string { + return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +/** + * Walk `root` depth-first, invoking `onFile` for each regular file. + * Skips `node_modules` / `.git` and optional absolute dirs in `skipDirs`. + */ +function walkFiles( + root: string, + onFile: (fullPath: string, entryName: string) => void, + skipDirs?: ReadonlySet +): void { + if (!existsSync(root)) return; + const stack: string[] = [root]; + while (stack.length > 0) { + const dir = stack.pop()!; + let entries: Dirent[]; + try { + entries = readdirSync(dir, { withFileTypes: true }) as Dirent[]; + } catch { + continue; + } + for (const entry of entries) { + if (SKIP_DIR_NAMES.has(entry.name)) continue; + const full = join(dir, entry.name); + if (entry.isDirectory()) { + if (skipDirs?.has(full)) continue; + stack.push(full); + continue; + } + if (!entry.isFile()) continue; + onFile(full, entry.name); + } + } +} + +/** + * List files under `publicDir` as posix paths relative to that root + * (e.g. `next.svg`, `icons/logo.png`). Empty if dir missing. + */ +export function listPublicRelativeFiles(publicDir: string): string[] { + if (!existsSync(publicDir)) return []; + const out: string[] = []; + walkFiles(publicDir, (full) => { + const rel = toPosixPath(relative(publicDir, full)); + if (!rel || rel.startsWith('..')) return; + out.push(rel); + }); + return out.sort(); +} + +/** + * Rewrite root-absolute references to known public files so they load from CDN. + * + * Matches `/file.svg`, `"/file.svg"`, `'/file.svg'`, `` `/file.svg` ``, and + * `url(/file.svg)` style, only for exact relative paths listed in `publicFiles`. + * + * @param content - file text + * @param publicFiles - posix relpaths from public root (no leading slash) + * @param cdnBase - absolute CDN base **with** trailing slash + */ +export function rewritePublicAssetUrlsInText( + content: string, + publicFiles: readonly string[], + cdnBase: string +): string { + const base = cdnBase.endsWith('/') ? cdnBase : `${cdnBase}/`; + if (!publicFiles.length) return content; + + // Longest paths first so `icons/a.svg` wins over a hypothetical shorter + // collision when used as a suffix (we match full path after /). + const sorted = [...publicFiles].sort((a, b) => b.length - a.length); + const alt = sorted.map(escapeRegExp).join('|'); + // Require a boundary before the leading slash so we don't match + // `https://cdn…/genesis/next.svg` again or `/_next/static/…`. + // Preceding char: start, quote, =, (, whitespace, or >. + const re = new RegExp(`(^|["'\`=(\\s>])\\/(${alt})(?=["'\`\\s),>\\?#]|$)`, 'g'); + return content.replace(re, `$1${base}$2`); +} + +export interface RewritePublicAssetsResult { + filesScanned: number; + filesChanged: number; + publicFileCount: number; +} + +/** + * Walk `treeRoot` (typically the process working directory), rewrite text + * files that reference `/publicRel` paths to `{cdnBase}{publicRel}`. + * Skips the public directory itself (no HTML to rewrite there). + */ +export function rewritePublicAssetUrlsInTree( + treeRoot: string, + publicDir: string, + cdnBase: string +): RewritePublicAssetsResult { + const publicFiles = listPublicRelativeFiles(publicDir); + if (!publicFiles.length || !existsSync(treeRoot)) { + return { filesScanned: 0, filesChanged: 0, publicFileCount: publicFiles.length }; + } + + let filesScanned = 0; + let filesChanged = 0; + const skipDirs = new Set([publicDir]); + + walkFiles( + treeRoot, + (full, entryName) => { + const lower = entryName.toLowerCase(); + const dot = lower.lastIndexOf('.'); + const ext = dot >= 0 ? lower.slice(dot) : ''; + if (!REWRITE_EXTENSIONS.has(ext)) return; + + filesScanned++; + let text: string; + try { + text = readFileSync(full, 'utf-8'); + } catch { + return; + } + const next = rewritePublicAssetUrlsInText(text, publicFiles, cdnBase); + if (next !== text) { + writeFileSync(full, next, 'utf-8'); + filesChanged++; + } + }, + skipDirs + ); + + return { filesScanned, filesChanged, publicFileCount: publicFiles.length }; +} diff --git a/packages/cli/src/cmd/build/run.ts b/packages/cli/src/cmd/build/run.ts index 5fc430071..469195632 100644 --- a/packages/cli/src/cmd/build/run.ts +++ b/packages/cli/src/cmd/build/run.ts @@ -235,6 +235,7 @@ export async function runBuildPipeline(input: BuildPipelineInput): Promise { }); }); + test('includes public/ root when publicStaticDir set with CDN base (Next split CDN)', () => { + const framework: DetectedFramework = { + name: 'nextjs', + runtime: 'node', + packageManager: 'npm', + buildCommand: 'next build', + buildOutput: '.next', + startCommand: 'node server.js', + staticDir: '.next/static', + staticAssetPublicPath: '_next/static', + confidence: 'high', + }; + + const buildResult: BuildResult = { + outputDir: '/tmp/output', + startCommand: 'node server.js', + workingDirectory: 'test-nextjs', + staticDir: '/tmp/output/test-nextjs/.next/static', + staticAssetPublicPath: '_next/static', + publicStaticDir: '/tmp/output/test-nextjs/public', + duration: 1000, + logs: [], + }; + + const metadata = generateLaunchMetadata( + framework, + buildResult, + null, + undefined, + 'https://cdn.agentcompany.com/genesis/' + ); + expect(metadata.static).toEqual({ + directory: '.next/static', + publicPath: '_next/static', + baseUrl: 'https://cdn.agentcompany.com/genesis/', + include: [{ directory: 'public', publicPath: '' }], + }); + }); + + test('omits include when publicStaticDir set but no CDN baseUrl', () => { + const framework: DetectedFramework = { + name: 'nextjs', + runtime: 'node', + packageManager: 'npm', + buildCommand: 'next build', + buildOutput: '.next', + startCommand: 'node server.js', + staticDir: '.next/static', + staticAssetPublicPath: '_next/static', + confidence: 'high', + }; + + const buildResult: BuildResult = { + outputDir: '/tmp/output', + startCommand: 'node server.js', + workingDirectory: 'test-nextjs', + staticDir: '/tmp/output/test-nextjs/.next/static', + staticAssetPublicPath: '_next/static', + publicStaticDir: '/tmp/output/test-nextjs/public', + duration: 1000, + logs: [], + }; + + const metadata = generateLaunchMetadata(framework, buildResult); + expect(metadata.static).toEqual({ + directory: '.next/static', + publicPath: '_next/static', + }); + expect(metadata.static?.include).toBeUndefined(); + }); + + test('omits include when publicPath is empty (Vite-style single root)', () => { + const framework: DetectedFramework = { + name: 'vite', + runtime: 'node', + packageManager: 'npm', + buildCommand: 'vite build', + buildOutput: 'dist', + startCommand: 'node server.js', + staticDir: 'dist', + staticAssetPublicPath: '', + confidence: 'high', + }; + + const buildResult: BuildResult = { + outputDir: '/tmp/output', + startCommand: 'node server.js', + staticDir: '/tmp/output/dist', + staticAssetPublicPath: '', + // Even if someone set this, empty publicPath is not split layout + publicStaticDir: '/tmp/output/public', + duration: 1000, + logs: [], + }; + + const metadata = generateLaunchMetadata( + framework, + buildResult, + null, + undefined, + 'https://cdn.agentcompany.com/genesis/' + ); + expect(metadata.static).toEqual({ + directory: 'dist', + publicPath: '', + baseUrl: 'https://cdn.agentcompany.com/genesis/', + }); + expect(metadata.static?.include).toBeUndefined(); + }); + + test('omits include when publicStaticDir is outside process root', () => { + const framework: DetectedFramework = { + name: 'nextjs', + runtime: 'node', + packageManager: 'npm', + buildCommand: 'next build', + buildOutput: '.next', + startCommand: 'node server.js', + staticDir: '.next/static', + staticAssetPublicPath: '_next/static', + confidence: 'high', + }; + + const buildResult: BuildResult = { + outputDir: '/tmp/output', + startCommand: 'node server.js', + workingDirectory: 'app', + staticDir: '/tmp/output/app/.next/static', + staticAssetPublicPath: '_next/static', + publicStaticDir: '/tmp/elsewhere/public', + duration: 1000, + logs: [], + }; + + const metadata = generateLaunchMetadata( + framework, + buildResult, + null, + undefined, + 'https://cdn.agentcompany.com/genesis/' + ); + expect(metadata.static?.include).toBeUndefined(); + expect(metadata.static?.baseUrl).toBe('https://cdn.agentcompany.com/genesis/'); + }); + test('omits static when framework has no static dir', () => { const framework: DetectedFramework = { name: 'nestjs', @@ -511,9 +657,126 @@ describe('Launch Metadata', () => { }); }); + // ── joinCdnAssetUrl ── + + describe('joinCdnAssetUrl', () => { + test('joins non-empty publicPath without double slashes', () => { + expect( + joinCdnAssetUrl('https://cdn.agentcompany.com/genesis/', '_next/static', 'chunks/a.js') + ).toBe('https://cdn.agentcompany.com/genesis/_next/static/chunks/a.js'); + }); + + test('empty publicPath places file at CDN base root', () => { + expect(joinCdnAssetUrl('https://cdn.agentcompany.com/genesis/', '', 'next.svg')).toBe( + 'https://cdn.agentcompany.com/genesis/next.svg' + ); + // base without trailing slash still normalizes + expect(joinCdnAssetUrl('https://cdn.example.com/x', '', 'icons/a.png')).toBe( + 'https://cdn.example.com/x/icons/a.png' + ); + }); + }); + // ── packageBuildOutput ── describe('packageBuildOutput', () => { + test('rewrites public asset URLs and emits include when CDN base set', async () => { + const staticDir = join(testDir, '.next', 'static'); + const publicDir = join(testDir, 'public'); + const serverApp = join(testDir, '.next', 'server', 'app'); + mkdirSync(staticDir, { recursive: true }); + mkdirSync(publicDir, { recursive: true }); + mkdirSync(serverApp, { recursive: true }); + writeFileSync(join(publicDir, 'next.svg'), ''); + writeFileSync( + join(serverApp, 'page.html'), + `logo` + ); + + const framework: DetectedFramework = { + name: 'nextjs', + runtime: 'node', + packageManager: 'npm', + buildCommand: 'next build', + buildOutput: '.next', + startCommand: 'node server.js', + staticDir: '.next/static', + staticAssetPublicPath: '_next/static', + confidence: 'high', + }; + + const buildResult: BuildResult = { + outputDir: testDir, + startCommand: 'node server.js', + staticDir, + staticAssetPublicPath: '_next/static', + publicStaticDir: publicDir, + duration: 1000, + logs: [], + }; + + const cdn = 'https://cdn.agentcompany.com/genesis/'; + const result = await packageBuildOutput( + framework, + buildResult, + testDir, + undefined, + undefined, + { cdnBaseUrl: cdn } + ); + + expect(result.launch.static).toEqual({ + directory: '.next/static', + publicPath: '_next/static', + baseUrl: cdn, + include: [{ directory: 'public', publicPath: '' }], + }); + expect(result.logs.some((l) => l.includes('rewrote public/'))).toBe(true); + + const html = readFileSync(join(serverApp, 'page.html'), 'utf-8'); + expect(html).toContain(`${cdn}next.svg`); + // Root-absolute _next path is not a listed public file — left alone + expect(html).toContain('src="/_next/static/x.js"'); + }); + + test('does not rewrite when CDN base is unset', async () => { + const publicDir = join(testDir, 'public'); + const serverApp = join(testDir, '.next', 'server'); + mkdirSync(publicDir, { recursive: true }); + mkdirSync(serverApp, { recursive: true }); + writeFileSync(join(publicDir, 'next.svg'), ''); + const htmlPath = join(serverApp, 'page.html'); + const original = ``; + writeFileSync(htmlPath, original); + + const framework: DetectedFramework = { + name: 'nextjs', + runtime: 'node', + packageManager: 'npm', + buildCommand: 'next build', + buildOutput: '.next', + startCommand: 'node server.js', + staticAssetPublicPath: '_next/static', + confidence: 'high', + }; + + const buildResult: BuildResult = { + outputDir: testDir, + startCommand: 'node server.js', + staticDir: join(testDir, '.next', 'static'), + staticAssetPublicPath: '_next/static', + publicStaticDir: publicDir, + duration: 1000, + logs: [], + }; + mkdirSync(buildResult.staticDir!, { recursive: true }); + + const result = await packageBuildOutput(framework, buildResult, testDir); + expect(result.launch.static?.include).toBeUndefined(); + expect(result.logs).toEqual([]); + expect(readFileSync(htmlPath, 'utf-8')).toBe(original); + }); + test('returns hasStaticAssets when staticDir exists', async () => { const staticDir = join(testDir, 'static'); mkdirSync(staticDir, { recursive: true }); diff --git a/packages/cli/test/cmd/build/package/public-cdn.test.ts b/packages/cli/test/cmd/build/package/public-cdn.test.ts new file mode 100644 index 000000000..2de8ef8ab --- /dev/null +++ b/packages/cli/test/cmd/build/package/public-cdn.test.ts @@ -0,0 +1,154 @@ +import { afterEach, describe, expect, test } from 'bun:test'; +import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; +import { + listPublicRelativeFiles, + rewritePublicAssetUrlsInText, + rewritePublicAssetUrlsInTree, +} from '../../../../src/cmd/build/package/public-cdn.ts'; + +function makeDir(): string { + const dir = join(tmpdir(), `public-cdn-${Date.now()}-${Math.random().toString(36).slice(2)}`); + mkdirSync(dir, { recursive: true }); + return dir; +} + +describe('listPublicRelativeFiles', () => { + const dirs: string[] = []; + afterEach(() => { + for (const d of dirs) rmSync(d, { recursive: true, force: true }); + dirs.length = 0; + }); + + test('lists nested public files as posix paths', () => { + const dir = makeDir(); + dirs.push(dir); + writeFileSync(join(dir, 'next.svg'), ''); + mkdirSync(join(dir, 'icons'), { recursive: true }); + writeFileSync(join(dir, 'icons', 'logo.png'), 'png'); + expect(listPublicRelativeFiles(dir)).toEqual(['icons/logo.png', 'next.svg']); + }); + + test('empty when missing', () => { + expect(listPublicRelativeFiles(join(tmpdir(), 'no-such-public-dir-xyz'))).toEqual([]); + }); + + test('skips node_modules under public', () => { + const dir = makeDir(); + dirs.push(dir); + writeFileSync(join(dir, 'ok.svg'), 'x'); + mkdirSync(join(dir, 'node_modules', 'pkg'), { recursive: true }); + writeFileSync(join(dir, 'node_modules', 'pkg', 'x.js'), 'x'); + expect(listPublicRelativeFiles(dir)).toEqual(['ok.svg']); + }); +}); + +describe('rewritePublicAssetUrlsInText', () => { + const files = ['next.svg', 'vercel.svg', 'icons/a.png']; + const base = 'https://cdn.agentcompany.com/genesis/'; + + test('rewrites quoted src and href', () => { + const in_ = `x`; + const out = rewritePublicAssetUrlsInText(in_, files, base); + expect(out).toContain('src="https://cdn.agentcompany.com/genesis/next.svg"'); + expect(out).toContain("href='https://cdn.agentcompany.com/genesis/vercel.svg'"); + }); + + test('does not rewrite _next static paths', () => { + const in_ = `src="https://cdn.agentcompany.com/genesis/_next/static/chunks/a.js"`; + expect(rewritePublicAssetUrlsInText(in_, files, base)).toBe(in_); + }); + + test('does not double-rewrite already absolute CDN public urls', () => { + const in_ = `src="https://cdn.agentcompany.com/genesis/next.svg"`; + // Leading char before /next.svg is 's' of genesis — our pattern requires boundary + // before /. Absolute CDN URLs should not match as root-absolute /next.svg. + expect(rewritePublicAssetUrlsInText(in_, files, base)).toBe(in_); + }); + + test('rewrites nested public path', () => { + const in_ = `url(/icons/a.png)`; + const out = rewritePublicAssetUrlsInText(in_, files, base); + expect(out).toBe('url(https://cdn.agentcompany.com/genesis/icons/a.png)'); + }); + + test('rewrites srcset and query/hash boundaries', () => { + const in_ = `srcset="/next.svg 1x, /vercel.svg 2x" href="/next.svg?v=1#top"`; + const out = rewritePublicAssetUrlsInText(in_, files, base); + expect(out).toContain(`${base}next.svg 1x`); + expect(out).toContain(`${base}vercel.svg 2x`); + expect(out).toContain(`${base}next.svg?v=1#top`); + }); + + test('longest path wins for nested names', () => { + const nested = ['a.png', 'icons/a.png']; + const in_ = `url(/icons/a.png) url(/a.png)`; + const out = rewritePublicAssetUrlsInText(in_, nested, base); + expect(out).toBe(`url(${base}icons/a.png) url(${base}a.png)`); + }); + + test('normalizes base without trailing slash', () => { + const out = rewritePublicAssetUrlsInText( + `src="/next.svg"`, + files, + 'https://cdn.agentcompany.com/genesis' + ); + expect(out).toBe(`src="https://cdn.agentcompany.com/genesis/next.svg"`); + }); + + test('no-op when public file list is empty', () => { + const in_ = `src="/next.svg"`; + expect(rewritePublicAssetUrlsInText(in_, [], base)).toBe(in_); + }); +}); + +describe('rewritePublicAssetUrlsInTree', () => { + const dirs: string[] = []; + afterEach(() => { + for (const d of dirs) rmSync(d, { recursive: true, force: true }); + dirs.length = 0; + }); + + test('rewrites HTML under server tree', () => { + const root = makeDir(); + dirs.push(root); + const pub = join(root, 'public'); + const server = join(root, '.next', 'server', 'app'); + mkdirSync(pub, { recursive: true }); + mkdirSync(server, { recursive: true }); + writeFileSync(join(pub, 'next.svg'), ''); + writeFileSync( + join(server, 'index.html'), + `logo` + ); + const r = rewritePublicAssetUrlsInTree(root, pub, 'https://cdn.agentcompany.com/genesis/'); + expect(r.publicFileCount).toBe(1); + expect(r.filesChanged).toBe(1); + const html = readFileSync(join(server, 'index.html'), 'utf-8'); + expect(html).toContain('https://cdn.agentcompany.com/genesis/next.svg'); + expect(html).toContain('https://cdn.x/genesis/_next/static/x.js'); + }); + + test('skips public dir itself and node_modules', () => { + const root = makeDir(); + dirs.push(root); + const pub = join(root, 'public'); + const nm = join(root, 'node_modules', 'pkg'); + mkdirSync(pub, { recursive: true }); + mkdirSync(nm, { recursive: true }); + writeFileSync(join(pub, 'next.svg'), ''); + // If we rewrote under public/, this would change — ensure we skip it + writeFileSync(join(pub, 'readme.txt'), 'see /next.svg'); + writeFileSync(join(nm, 'x.js'), `import "/next.svg"`); + writeFileSync(join(root, 'app.js'), `const u = "/next.svg"`); + + const r = rewritePublicAssetUrlsInTree(root, pub, 'https://cdn.example.com/'); + expect(r.filesChanged).toBe(1); + expect(readFileSync(join(pub, 'readme.txt'), 'utf-8')).toBe('see /next.svg'); + expect(readFileSync(join(nm, 'x.js'), 'utf-8')).toBe(`import "/next.svg"`); + expect(readFileSync(join(root, 'app.js'), 'utf-8')).toBe( + `const u = "https://cdn.example.com/next.svg"` + ); + }); +}); diff --git a/packages/cli/test/cmd/build/static-assets.test.ts b/packages/cli/test/cmd/build/static-assets.test.ts index 3540e276c..654f16d70 100644 --- a/packages/cli/test/cmd/build/static-assets.test.ts +++ b/packages/cli/test/cmd/build/static-assets.test.ts @@ -403,6 +403,73 @@ describe('Static Asset CDN Upload', () => { expect(script?.sourcePath).toBe('.next/static/chunks/app-abcdef12.js'); }, 30_000); + test('split-layout publicStaticDir enumerates at CDN base root', async () => { + // Hand-built BuildResult (no full Next build): primary `_next/static` + // plus staged `public/` as publicStaticDir. + const staticDir = join(outputDir, '.next', 'static', 'chunks'); + const publicDir = join(outputDir, 'public'); + mkdirSync(staticDir, { recursive: true }); + mkdirSync(publicDir, { recursive: true }); + writeFileSync(join(staticDir, 'app.js'), 'app()'); + writeFileSync(join(publicDir, 'next.svg'), ''); + mkdirSync(join(publicDir, 'icons'), { recursive: true }); + writeFileSync(join(publicDir, 'icons', 'logo.png'), 'png'); + + const framework = { + name: 'nextjs', + runtime: 'node' as const, + packageManager: 'npm' as const, + buildCommand: 'next build', + buildOutput: '.next', + startCommand: 'node server.js', + staticDir: '.next/static', + staticAssetPublicPath: '_next/static', + confidence: 'high' as const, + }; + + const buildResult = { + outputDir, + startCommand: 'node server.js', + staticDir: join(outputDir, '.next', 'static'), + staticAssetPublicPath: '_next/static', + publicStaticDir: publicDir, + duration: 1, + logs: [] as string[], + }; + + const packageResult = await packageBuildOutput( + framework, + buildResult, + outputDir, + undefined, + undefined, + { + cdnBaseUrl: 'https://cdn.agentcompany.com/genesis/', + } + ); + expect(packageResult.launch.static?.include).toEqual([ + { directory: 'public', publicPath: '' }, + ]); + + const metadata = await generateDeployMetadata({ + buildResult, + packageResult, + projectDir: testDir, + projectId: 'test-project', + orgId: 'test-org', + region: 'us-east-1', + deploymentId: 'test-deployment', + logger, + }); + + const filenames = metadata.assets.map((a) => a.filename); + expect(filenames).toContain('_next/static/chunks/app.js'); + expect(filenames).toContain('next.svg'); + expect(filenames).toContain('icons/logo.png'); + const logo = metadata.assets.find((a) => a.filename === 'icons/logo.png'); + expect(logo?.sourcePath).toBe('public/icons/logo.png'); + }); + // ── No staticDir → no CDN assets ── test('generic project without staticDir produces no CDN assets', async () => { diff --git a/packages/client/package.json b/packages/client/package.json index 24f0b625a..52832f7a1 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/client", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "description": "Shared client wiring for Agentuity service packages", diff --git a/packages/coder-tui/package.json b/packages/coder-tui/package.json index 0c9a20ae0..03249d243 100644 --- a/packages/coder-tui/package.json +++ b/packages/coder-tui/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/coder-tui", - "version": "3.1.16", + "version": "3.1.17", "description": "Agentuity Coder Hub extension for Pi coding agent", "license": "Apache-2.0", "author": "Agentuity employees and contributors", diff --git a/packages/coder/package.json b/packages/coder/package.json index 2c0d8edb0..98b7ae8c1 100644 --- a/packages/coder/package.json +++ b/packages/coder/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/coder", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/config/package.json b/packages/config/package.json index 2090c3172..860432e2e 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/config", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "description": "Region and service URL resolution for Agentuity clients", diff --git a/packages/core/package.json b/packages/core/package.json index f8ef7e565..dbf48b271 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/core", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/create-agentuity/package.json b/packages/create-agentuity/package.json index 8c78a5b82..7fe57fcb9 100644 --- a/packages/create-agentuity/package.json +++ b/packages/create-agentuity/package.json @@ -1,6 +1,6 @@ { "name": "create-agentuity", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "description": "Create a new Agentuity project", diff --git a/packages/db/package.json b/packages/db/package.json index c71a663ad..f29ca5a5b 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/db", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/drizzle/package.json b/packages/drizzle/package.json index 85a34cf2b..9168c3674 100644 --- a/packages/drizzle/package.json +++ b/packages/drizzle/package.json @@ -1,7 +1,7 @@ { "name": "@agentuity/drizzle", "deprecated": "Use drizzle-orm with pg directly against your DATABASE_URL.", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/email/package.json b/packages/email/package.json index afa1dbe8b..7ee3e891a 100644 --- a/packages/email/package.json +++ b/packages/email/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/email", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/genesis/package.json b/packages/genesis/package.json index a41163e16..97488eb69 100644 --- a/packages/genesis/package.json +++ b/packages/genesis/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/genesis", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "description": "Genesis SSO upstream identity verification for managed app middleware", diff --git a/packages/hono/package.json b/packages/hono/package.json index aedb05388..1354b35d3 100644 --- a/packages/hono/package.json +++ b/packages/hono/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/hono", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/keyvalue/package.json b/packages/keyvalue/package.json index 69f085e9e..547706427 100644 --- a/packages/keyvalue/package.json +++ b/packages/keyvalue/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/keyvalue", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/migrate/package.json b/packages/migrate/package.json index 623807355..173d4a238 100644 --- a/packages/migrate/package.json +++ b/packages/migrate/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/migrate", - "version": "3.1.16", + "version": "3.1.17", "description": "Migration tool for Agentuity SDK (v1→v2 and v2→v3)", "license": "Apache-2.0", "author": "Agentuity employees and contributors", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index cc6687937..358070e17 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/opencode", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "description": "Agentuity Open Code plugin with specialized AI coding agents", diff --git a/packages/pi/package.json b/packages/pi/package.json index 2c665ce3d..620ece6ee 100644 --- a/packages/pi/package.json +++ b/packages/pi/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/pi", - "version": "3.1.16", + "version": "3.1.17", "description": "Agentuity AI Gateway provider plugin for Pi coding agent", "license": "Apache-2.0", "author": "Agentuity employees and contributors", diff --git a/packages/postgres/package.json b/packages/postgres/package.json index 3babcac57..90d5f01d3 100644 --- a/packages/postgres/package.json +++ b/packages/postgres/package.json @@ -1,7 +1,7 @@ { "name": "@agentuity/postgres", "deprecated": "Use pg or drizzle-orm/node-postgres directly against your DATABASE_URL.", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/queue/package.json b/packages/queue/package.json index fbf66e727..18b2841de 100644 --- a/packages/queue/package.json +++ b/packages/queue/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/queue", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/runtime/package.json b/packages/runtime/package.json index c91668fd7..ae2ab486f 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/runtime", - "version": "3.1.16", + "version": "3.1.17", "deprecated": "Use a dedicated framework (Hono, Next.js, etc.) with @agentuity/hono middleware instead. See https://agentuity.dev/docs/migration", "license": "Apache-2.0", "author": "Agentuity employees and contributors", diff --git a/packages/sandbox/package.json b/packages/sandbox/package.json index 7bf176306..edb767734 100644 --- a/packages/sandbox/package.json +++ b/packages/sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/sandbox", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/schedule/package.json b/packages/schedule/package.json index 85cbbc170..995b15b41 100644 --- a/packages/schedule/package.json +++ b/packages/schedule/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/schedule", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/schema/package.json b/packages/schema/package.json index e32418089..5698f5bfd 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/schema", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/server/package.json b/packages/server/package.json index 5b94d5232..5e1397000 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/server", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/skills/package.json b/packages/skills/package.json index 6c084c4a8..3aeb87e83 100644 --- a/packages/skills/package.json +++ b/packages/skills/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/skills", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity", "description": "Agent Skills for AI coding agents working with the Agentuity SDK", diff --git a/packages/storage/package.json b/packages/storage/package.json index daaefe486..0b0693966 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/storage", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "description": "Dual-runtime S3 client for Agentuity storage buckets. Bun backend uses Bun.S3Client; Node backend uses @aws-sdk/client-s3.", diff --git a/packages/stream/package.json b/packages/stream/package.json index 3b3797c71..08903236f 100644 --- a/packages/stream/package.json +++ b/packages/stream/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/stream", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/task/package.json b/packages/task/package.json index 73d2b3235..9e25ef841 100644 --- a/packages/task/package.json +++ b/packages/task/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/task", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/telemetry/package.json b/packages/telemetry/package.json index 77c75f29b..0e759a8d5 100644 --- a/packages/telemetry/package.json +++ b/packages/telemetry/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/telemetry", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 4bac410f3..0f8068883 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/test-utils", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "private": true, "type": "module", diff --git a/packages/vector/package.json b/packages/vector/package.json index 8749b7323..05fe562e5 100644 --- a/packages/vector/package.json +++ b/packages/vector/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/vector", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module", diff --git a/packages/vite/package.json b/packages/vite/package.json index ff932a75d..25c221959 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/vite", - "version": "3.1.16", + "version": "3.1.17", "description": "Vite plugin for the Agentuity dev public-URL tunnel", "license": "Apache-2.0", "author": "Agentuity employees and contributors", diff --git a/packages/vscode/package.json b/packages/vscode/package.json index 09928ab1a..f92e57afd 100644 --- a/packages/vscode/package.json +++ b/packages/vscode/package.json @@ -3,7 +3,7 @@ "private": true, "displayName": "Agentuity VSCode Extension", "description": "Build, deploy, and manage AI agents with Agentuity", - "version": "3.1.16", + "version": "3.1.17", "publisher": "agentuity", "license": "Apache-2.0", "repository": { diff --git a/packages/webhook/package.json b/packages/webhook/package.json index 242ef54b0..38995801a 100644 --- a/packages/webhook/package.json +++ b/packages/webhook/package.json @@ -1,6 +1,6 @@ { "name": "@agentuity/webhook", - "version": "3.1.16", + "version": "3.1.17", "license": "Apache-2.0", "author": "Agentuity employees and contributors", "type": "module",