@agentuity/cli 0.1.43 → 0.1.44
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.
- package/dist/auth.d.ts +2 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +7 -5
- package/dist/auth.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +24 -12
- package/dist/cli.js.map +1 -1
- package/dist/cmd/build/entry-generator.d.ts.map +1 -1
- package/dist/cmd/build/entry-generator.js +26 -17
- package/dist/cmd/build/entry-generator.js.map +1 -1
- package/dist/cmd/build/vite/public-asset-path-plugin.d.ts +17 -20
- package/dist/cmd/build/vite/public-asset-path-plugin.d.ts.map +1 -1
- package/dist/cmd/build/vite/public-asset-path-plugin.js +62 -43
- package/dist/cmd/build/vite/public-asset-path-plugin.js.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server-config.js +3 -1
- package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
- package/dist/cmd/cloud/env/org-util.d.ts +2 -1
- package/dist/cmd/cloud/env/org-util.d.ts.map +1 -1
- package/dist/cmd/cloud/env/org-util.js +4 -2
- package/dist/cmd/cloud/env/org-util.js.map +1 -1
- package/dist/cmd/cloud/stream/create.d.ts +3 -0
- package/dist/cmd/cloud/stream/create.d.ts.map +1 -0
- package/dist/cmd/cloud/stream/create.js +227 -0
- package/dist/cmd/cloud/stream/create.js.map +1 -0
- package/dist/cmd/cloud/stream/delete.d.ts.map +1 -1
- package/dist/cmd/cloud/stream/delete.js +2 -1
- package/dist/cmd/cloud/stream/delete.js.map +1 -1
- package/dist/cmd/cloud/stream/get.d.ts.map +1 -1
- package/dist/cmd/cloud/stream/get.js +2 -1
- package/dist/cmd/cloud/stream/get.js.map +1 -1
- package/dist/cmd/cloud/stream/index.d.ts.map +1 -1
- package/dist/cmd/cloud/stream/index.js +10 -1
- package/dist/cmd/cloud/stream/index.js.map +1 -1
- package/dist/cmd/cloud/stream/list.d.ts.map +1 -1
- package/dist/cmd/cloud/stream/list.js +2 -1
- package/dist/cmd/cloud/stream/list.js.map +1 -1
- package/dist/cmd/cloud/stream/util.d.ts +6 -5
- package/dist/cmd/cloud/stream/util.d.ts.map +1 -1
- package/dist/cmd/cloud/stream/util.js +26 -5
- package/dist/cmd/cloud/stream/util.js.map +1 -1
- package/dist/cmd/upgrade/index.d.ts.map +1 -1
- package/dist/cmd/upgrade/index.js +23 -0
- package/dist/cmd/upgrade/index.js.map +1 -1
- package/dist/cmd/upgrade/npm-availability.d.ts +44 -0
- package/dist/cmd/upgrade/npm-availability.d.ts.map +1 -0
- package/dist/cmd/upgrade/npm-availability.js +73 -0
- package/dist/cmd/upgrade/npm-availability.js.map +1 -0
- package/dist/tui.d.ts +9 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +39 -14
- package/dist/tui.js.map +1 -1
- package/dist/version-check.d.ts.map +1 -1
- package/dist/version-check.js +13 -2
- package/dist/version-check.js.map +1 -1
- package/package.json +6 -6
- package/src/auth.ts +9 -5
- package/src/cli.ts +44 -12
- package/src/cmd/build/entry-generator.ts +26 -17
- package/src/cmd/build/vite/public-asset-path-plugin.ts +73 -51
- package/src/cmd/build/vite/vite-asset-server-config.ts +3 -1
- package/src/cmd/cloud/env/org-util.ts +5 -2
- package/src/cmd/cloud/stream/create.ts +248 -0
- package/src/cmd/cloud/stream/delete.ts +2 -1
- package/src/cmd/cloud/stream/get.ts +2 -1
- package/src/cmd/cloud/stream/index.ts +10 -1
- package/src/cmd/cloud/stream/list.ts +2 -1
- package/src/cmd/cloud/stream/util.ts +39 -12
- package/src/cmd/upgrade/index.ts +25 -0
- package/src/cmd/upgrade/npm-availability.ts +105 -0
- package/src/tui.ts +42 -14
- package/src/version-check.ts +19 -3
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Vite plugin to fix incorrect public asset paths
|
|
3
3
|
*
|
|
4
|
-
* Developers
|
|
5
|
-
*
|
|
4
|
+
* Developers should use /public/ paths for static assets from src/web/public/.
|
|
5
|
+
* In production, these paths are transformed to CDN URLs.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* This plugin:
|
|
8
|
+
* 1. During build: Rewrites /public/* paths to CDN URLs
|
|
9
|
+
* 2. During dev: Warns only about incorrect source paths (src/web/public/)
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
* - '/
|
|
12
|
-
* - './
|
|
13
|
-
* - 'src/web/public/foo.svg' → '/foo.svg'
|
|
14
|
-
* - './public/foo.svg' → '/foo.svg'
|
|
15
|
-
* - '/public/foo.svg' → '/foo.svg'
|
|
11
|
+
* Supported patterns (work in dev, rewritten to CDN in production):
|
|
12
|
+
* - '/public/foo.svg' → CDN URL (recommended)
|
|
13
|
+
* - './public/foo.svg' → CDN URL
|
|
16
14
|
*
|
|
17
|
-
*
|
|
15
|
+
* Incorrect patterns (warned in dev, rewritten in production):
|
|
16
|
+
* - '/src/web/public/foo.svg' → CDN URL
|
|
17
|
+
* - './src/web/public/foo.svg' → CDN URL
|
|
18
|
+
* - 'src/web/public/foo.svg' → CDN URL
|
|
18
19
|
*/
|
|
19
20
|
|
|
20
21
|
import type { Plugin } from 'vite';
|
|
@@ -29,23 +30,41 @@ export interface PublicAssetPathPluginOptions {
|
|
|
29
30
|
interface PathPattern {
|
|
30
31
|
regex: RegExp;
|
|
31
32
|
description: string;
|
|
33
|
+
/** Replacement template - use {base} for the target base URL */
|
|
34
|
+
replacement: string;
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
/**
|
|
35
|
-
*
|
|
36
|
-
* (RegExp with global flag maintains state via lastIndex)
|
|
38
|
+
* Patterns that are incorrect - reference source paths directly
|
|
37
39
|
*/
|
|
38
|
-
function
|
|
40
|
+
function createIncorrectPatterns(): PathPattern[] {
|
|
39
41
|
return [
|
|
40
42
|
// '/src/web/public/...' or './src/web/public/...' or 'src/web/public/...'
|
|
41
43
|
{
|
|
42
44
|
regex: /(['"`])(?:\.?\/)?src\/web\/public\//g,
|
|
43
45
|
description: 'src/web/public/',
|
|
46
|
+
replacement: '$1{base}',
|
|
44
47
|
},
|
|
45
|
-
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Patterns that need rewriting for production CDN
|
|
53
|
+
* Both patterns simply replace the prefix while preserving the rest of the path naturally.
|
|
54
|
+
*/
|
|
55
|
+
function createPublicPatterns(): PathPattern[] {
|
|
56
|
+
return [
|
|
57
|
+
// './public/...' (relative public path) - replace prefix, keep rest
|
|
46
58
|
{
|
|
47
59
|
regex: /(['"`])\.\/public\//g,
|
|
48
60
|
description: './public/',
|
|
61
|
+
replacement: '$1{base}',
|
|
62
|
+
},
|
|
63
|
+
// '/public/...' (absolute public path) - replace prefix, keep rest
|
|
64
|
+
{
|
|
65
|
+
regex: /(['"`])\/public\//g,
|
|
66
|
+
description: '/public/',
|
|
67
|
+
replacement: '$1{base}',
|
|
49
68
|
},
|
|
50
69
|
];
|
|
51
70
|
}
|
|
@@ -53,21 +72,17 @@ function createPatterns(): PathPattern[] {
|
|
|
53
72
|
/**
|
|
54
73
|
* Vite plugin that fixes public asset paths and rewrites to CDN URLs
|
|
55
74
|
*
|
|
56
|
-
* Rewrites all public asset paths to CDN URLs in production
|
|
57
|
-
* if no CDN base URL is provided.
|
|
75
|
+
* Rewrites all public asset paths to CDN URLs in production.
|
|
58
76
|
*
|
|
59
77
|
* @example
|
|
60
78
|
* // In vite config:
|
|
61
79
|
* plugins: [publicAssetPathPlugin({ cdnBaseUrl: 'https://cdn.example.com/deploy/client/' })]
|
|
62
80
|
*
|
|
63
|
-
* //
|
|
64
|
-
*
|
|
65
|
-
* // './src/web/public/logo.svg' → 'https://cdn.example.com/deploy/client/logo.svg'
|
|
66
|
-
* // '/public/logo.svg' → 'https://cdn.example.com/deploy/client/logo.svg'
|
|
81
|
+
* // In code, use /public/ paths:
|
|
82
|
+
* <img src="/public/logo.svg" />
|
|
67
83
|
*
|
|
68
|
-
* // Transforms in production
|
|
69
|
-
* // '/
|
|
70
|
-
* // '/public/logo.svg' → '/logo.svg'
|
|
84
|
+
* // Transforms in production:
|
|
85
|
+
* // '/public/logo.svg' → 'https://cdn.example.com/deploy/client/logo.svg'
|
|
71
86
|
*/
|
|
72
87
|
export function publicAssetPathPlugin(options: PublicAssetPathPluginOptions = {}): Plugin {
|
|
73
88
|
const { warnInDev = true, cdnBaseUrl } = options;
|
|
@@ -89,17 +104,18 @@ export function publicAssetPathPlugin(options: PublicAssetPathPluginOptions = {}
|
|
|
89
104
|
}
|
|
90
105
|
|
|
91
106
|
// Quick check: does the code contain any patterns we care about?
|
|
92
|
-
const
|
|
93
|
-
const hasPublicPaths = code.includes('/public/');
|
|
107
|
+
const hasIncorrectSourcePaths = code.includes('src/web/public/');
|
|
108
|
+
const hasPublicPaths = code.includes('/public/') || code.includes('./public/');
|
|
94
109
|
|
|
95
|
-
if (!
|
|
110
|
+
if (!hasIncorrectSourcePaths && !hasPublicPaths) {
|
|
96
111
|
return null;
|
|
97
112
|
}
|
|
98
113
|
|
|
99
|
-
// In dev mode,
|
|
114
|
+
// In dev mode, only warn about incorrect source paths (src/web/public/)
|
|
115
|
+
// /public/ and ./public/ paths work correctly in dev mode
|
|
100
116
|
if (isDev) {
|
|
101
|
-
if (warnInDev &&
|
|
102
|
-
const patterns =
|
|
117
|
+
if (warnInDev && hasIncorrectSourcePaths) {
|
|
118
|
+
const patterns = createIncorrectPatterns();
|
|
103
119
|
const foundPatterns: string[] = [];
|
|
104
120
|
|
|
105
121
|
for (const { regex, description } of patterns) {
|
|
@@ -121,37 +137,43 @@ export function publicAssetPathPlugin(options: PublicAssetPathPluginOptions = {}
|
|
|
121
137
|
this.warn(
|
|
122
138
|
`Found incorrect asset path(s) in ${id}:\n` +
|
|
123
139
|
newWarnings.map((p) => ` - '${p}' should be '/public/'`).join('\n') +
|
|
124
|
-
`\nUse
|
|
140
|
+
`\nUse '/public/...' paths for static assets.`
|
|
125
141
|
);
|
|
126
142
|
}
|
|
127
143
|
}
|
|
128
144
|
}
|
|
129
145
|
// In dev mode, never transform - Vite serves from source paths
|
|
146
|
+
// and the Bun server proxies /public/* to Vite
|
|
130
147
|
return null;
|
|
131
148
|
}
|
|
132
149
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
150
|
+
// Build mode: transform paths to CDN URLs
|
|
151
|
+
let transformed = code;
|
|
152
|
+
|
|
153
|
+
// Determine target URL: CDN base if provided, otherwise root
|
|
154
|
+
const targetBase = cdnBaseUrl
|
|
155
|
+
? cdnBaseUrl.endsWith('/')
|
|
156
|
+
? cdnBaseUrl
|
|
157
|
+
: `${cdnBaseUrl}/`
|
|
158
|
+
: '/';
|
|
159
|
+
|
|
160
|
+
// Transform incorrect source paths (src/web/public/) → CDN
|
|
161
|
+
if (hasIncorrectSourcePaths) {
|
|
162
|
+
const patterns = createIncorrectPatterns();
|
|
163
|
+
for (const { regex, replacement } of patterns) {
|
|
164
|
+
const replaceRegex = new RegExp(regex.source, regex.flags);
|
|
165
|
+
transformed = transformed.replace(replaceRegex, replacement.replace('{base}', targetBase));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
138
168
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
169
|
+
// Transform public paths → CDN
|
|
170
|
+
if (hasPublicPaths) {
|
|
171
|
+
const publicPatterns = createPublicPatterns();
|
|
172
|
+
for (const { regex, replacement } of publicPatterns) {
|
|
173
|
+
const replaceRegex = new RegExp(regex.source, regex.flags);
|
|
174
|
+
transformed = transformed.replace(replaceRegex, replacement.replace('{base}', targetBase));
|
|
175
|
+
}
|
|
145
176
|
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Then, rewrite /public/foo → {targetBase}foo
|
|
149
|
-
if (hasPublicPaths) {
|
|
150
|
-
// Match '/public/...' paths in strings (single, double, or backtick quotes)
|
|
151
|
-
// Captures: $1 = quote char, $2 = path after /public/
|
|
152
|
-
const publicPathRegex = /(['"`])\/public\/([^'"`\s]+)/g;
|
|
153
|
-
transformed = transformed.replace(publicPathRegex, `$1${targetBase}$2`);
|
|
154
|
-
}
|
|
155
177
|
|
|
156
178
|
// Return transformed code if changed
|
|
157
179
|
if (transformed !== code) {
|
|
@@ -68,7 +68,9 @@ export async function generateAssetServerConfig(
|
|
|
68
68
|
root: rootDir,
|
|
69
69
|
base: '/',
|
|
70
70
|
clearScreen: false,
|
|
71
|
-
|
|
71
|
+
// Serve public assets from src/web/public/ at root path (e.g., /favicon.png)
|
|
72
|
+
// The Bun server proxies /public/* requests to Vite, rewriting to root paths
|
|
73
|
+
publicDir: join(rootDir, 'src', 'web', 'public'),
|
|
72
74
|
|
|
73
75
|
resolve: {
|
|
74
76
|
alias,
|
|
@@ -9,12 +9,14 @@ import { listOrganizations } from '@agentuity/server';
|
|
|
9
9
|
* @param apiClient - The API client
|
|
10
10
|
* @param config - The CLI config (may be null)
|
|
11
11
|
* @param orgOption - The --org option value (true for default/prompt, or explicit org ID)
|
|
12
|
+
* @param autoSelect - If true, auto-select preferred org without prompting (for --confirm)
|
|
12
13
|
* @returns The resolved organization ID
|
|
13
14
|
*/
|
|
14
15
|
export async function resolveOrgId(
|
|
15
16
|
apiClient: APIClient,
|
|
16
17
|
config: Config | null,
|
|
17
|
-
orgOption: boolean | string
|
|
18
|
+
orgOption: boolean | string,
|
|
19
|
+
autoSelect?: boolean
|
|
18
20
|
): Promise<string> {
|
|
19
21
|
// If an explicit org ID was provided (string), use it directly
|
|
20
22
|
if (typeof orgOption === 'string' && orgOption !== 'true') {
|
|
@@ -25,8 +27,9 @@ export async function resolveOrgId(
|
|
|
25
27
|
const orgs = await tui.spinner('Fetching organizations', () => listOrganizations(apiClient));
|
|
26
28
|
|
|
27
29
|
// Use preference if available, otherwise prompt
|
|
30
|
+
// Pass autoSelect to skip prompting when --confirm is used
|
|
28
31
|
const preferredOrgId = config?.preferences?.orgId;
|
|
29
|
-
return tui.selectOrganization(orgs, preferredOrgId);
|
|
32
|
+
return tui.selectOrganization(orgs, preferredOrgId, autoSelect);
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
/**
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { basename } from 'path';
|
|
3
|
+
import { createCommand } from '../../../types';
|
|
4
|
+
import * as tui from '../../../tui';
|
|
5
|
+
import { createStorageAdapter } from './util';
|
|
6
|
+
import { getCommand } from '../../../command-prefix';
|
|
7
|
+
|
|
8
|
+
const StreamCreateResponseSchema = z.object({
|
|
9
|
+
id: z.string().describe('Stream ID'),
|
|
10
|
+
namespace: z.string().describe('Stream namespace'),
|
|
11
|
+
url: z.string().describe('Public URL'),
|
|
12
|
+
sizeBytes: z.number().describe('Size in bytes'),
|
|
13
|
+
metadata: z.record(z.string(), z.string()).describe('Stream metadata'),
|
|
14
|
+
expiresAt: z.string().optional().describe('Expiration timestamp'),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const createSubcommand = createCommand({
|
|
18
|
+
name: 'create',
|
|
19
|
+
aliases: ['new'],
|
|
20
|
+
description: 'Create a new stream and upload content',
|
|
21
|
+
tags: ['mutating', 'creates-resource', 'slow', 'requires-auth', 'uses-stdin'],
|
|
22
|
+
requires: { auth: true, region: true },
|
|
23
|
+
optional: { project: true },
|
|
24
|
+
idempotent: false,
|
|
25
|
+
examples: [
|
|
26
|
+
{
|
|
27
|
+
command: getCommand('cloud stream create memory-share ./notes.md'),
|
|
28
|
+
description: 'Create stream from file',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
command: getCommand(
|
|
32
|
+
'cloud stream create memory-share ./data.json --content-type application/json'
|
|
33
|
+
),
|
|
34
|
+
description: 'Create stream with explicit content type',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
command: `cat summary.md | ${getCommand('cloud stream create memory-share -')}`,
|
|
38
|
+
description: 'Create stream from stdin',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
command: getCommand('cloud stream create memory-share ./notes.md --ttl 3600'),
|
|
42
|
+
description: 'Create stream with 1 hour TTL',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
command: getCommand(
|
|
46
|
+
'cloud stream create memory-share ./notes.md --metadata type=summary,source=session'
|
|
47
|
+
),
|
|
48
|
+
description: 'Create stream with metadata',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
command: getCommand('cloud stream create memory-share ./large.json --compress'),
|
|
52
|
+
description: 'Create compressed stream',
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
schema: {
|
|
56
|
+
args: z.object({
|
|
57
|
+
namespace: z.string().min(1).max(254).describe('Stream namespace (1-254 characters)'),
|
|
58
|
+
filename: z.string().describe('File path to upload or "-" for STDIN'),
|
|
59
|
+
}),
|
|
60
|
+
options: z.object({
|
|
61
|
+
metadata: z
|
|
62
|
+
.string()
|
|
63
|
+
.optional()
|
|
64
|
+
.describe('Metadata key=value pairs (comma-separated: key1=value1,key2=value2)'),
|
|
65
|
+
contentType: z
|
|
66
|
+
.string()
|
|
67
|
+
.optional()
|
|
68
|
+
.describe('Content type (auto-detected from extension if not provided)'),
|
|
69
|
+
compress: z.boolean().optional().describe('Enable gzip compression'),
|
|
70
|
+
ttl: z.coerce
|
|
71
|
+
.number()
|
|
72
|
+
.optional()
|
|
73
|
+
.describe('TTL in seconds (60-7776000, or 0/null for never expires)'),
|
|
74
|
+
}),
|
|
75
|
+
response: StreamCreateResponseSchema,
|
|
76
|
+
},
|
|
77
|
+
webUrl: '/services/stream',
|
|
78
|
+
|
|
79
|
+
async handler(ctx) {
|
|
80
|
+
const { args, opts, options } = ctx;
|
|
81
|
+
const started = Date.now();
|
|
82
|
+
const storage = await createStorageAdapter(ctx);
|
|
83
|
+
|
|
84
|
+
// Parse metadata if provided
|
|
85
|
+
let metadata: Record<string, string> | undefined;
|
|
86
|
+
if (opts.metadata) {
|
|
87
|
+
const validPairs: Record<string, string> = {};
|
|
88
|
+
const malformed: string[] = [];
|
|
89
|
+
const pairs = opts.metadata.split(',');
|
|
90
|
+
|
|
91
|
+
for (const pair of pairs) {
|
|
92
|
+
const trimmedPair = pair.trim();
|
|
93
|
+
if (!trimmedPair) continue;
|
|
94
|
+
|
|
95
|
+
const firstEqualIdx = trimmedPair.indexOf('=');
|
|
96
|
+
if (firstEqualIdx === -1) {
|
|
97
|
+
malformed.push(trimmedPair);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const key = trimmedPair.substring(0, firstEqualIdx).trim();
|
|
102
|
+
const value = trimmedPair.substring(firstEqualIdx + 1).trim();
|
|
103
|
+
|
|
104
|
+
if (!key || !value) {
|
|
105
|
+
malformed.push(trimmedPair);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
validPairs[key] = value;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (malformed.length > 0) {
|
|
113
|
+
ctx.logger.warn(`Skipping malformed metadata pairs: ${malformed.join(', ')}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (Object.keys(validPairs).length > 0) {
|
|
117
|
+
metadata = validPairs;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Determine content type
|
|
122
|
+
let contentType = opts.contentType;
|
|
123
|
+
if (!contentType) {
|
|
124
|
+
// Auto-detect from filename extension
|
|
125
|
+
const filename = args.filename === '-' ? 'stdin' : args.filename;
|
|
126
|
+
const dotIndex = filename.lastIndexOf('.');
|
|
127
|
+
const ext = dotIndex > 0 ? filename.substring(dotIndex + 1).toLowerCase() : undefined;
|
|
128
|
+
// Text-based types should include charset=utf-8 for proper browser rendering
|
|
129
|
+
const mimeTypes: Record<string, string> = {
|
|
130
|
+
txt: 'text/plain; charset=utf-8',
|
|
131
|
+
md: 'text/markdown; charset=utf-8',
|
|
132
|
+
html: 'text/html; charset=utf-8',
|
|
133
|
+
css: 'text/css; charset=utf-8',
|
|
134
|
+
yaml: 'application/x-yaml; charset=utf-8',
|
|
135
|
+
yml: 'application/x-yaml; charset=utf-8',
|
|
136
|
+
js: 'application/javascript; charset=utf-8',
|
|
137
|
+
ts: 'application/typescript; charset=utf-8',
|
|
138
|
+
json: 'application/json; charset=utf-8',
|
|
139
|
+
xml: 'application/xml; charset=utf-8',
|
|
140
|
+
pdf: 'application/pdf',
|
|
141
|
+
zip: 'application/zip',
|
|
142
|
+
jpg: 'image/jpeg',
|
|
143
|
+
jpeg: 'image/jpeg',
|
|
144
|
+
png: 'image/png',
|
|
145
|
+
gif: 'image/gif',
|
|
146
|
+
svg: 'image/svg+xml; charset=utf-8',
|
|
147
|
+
mp4: 'video/mp4',
|
|
148
|
+
mp3: 'audio/mpeg',
|
|
149
|
+
};
|
|
150
|
+
contentType = ext ? mimeTypes[ext] : 'application/octet-stream';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Create the stream
|
|
154
|
+
const stream = await storage.create(args.namespace, {
|
|
155
|
+
metadata,
|
|
156
|
+
contentType,
|
|
157
|
+
compress: opts.compress ? true : undefined,
|
|
158
|
+
ttl: opts.ttl,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Read and write content
|
|
162
|
+
let inputStream: ReadableStream<Uint8Array>;
|
|
163
|
+
|
|
164
|
+
if (args.filename === '-') {
|
|
165
|
+
// Stream from STDIN
|
|
166
|
+
inputStream = Bun.stdin.stream();
|
|
167
|
+
} else {
|
|
168
|
+
// Stream from file
|
|
169
|
+
const file = Bun.file(args.filename);
|
|
170
|
+
if (!(await file.exists())) {
|
|
171
|
+
tui.fatal(`File not found: ${args.filename}`);
|
|
172
|
+
}
|
|
173
|
+
inputStream = file.stream();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Write content to stream in chunks
|
|
177
|
+
const reader = inputStream.getReader();
|
|
178
|
+
const MAX_CHUNK_SIZE = 5 * 1024 * 1024; // 5MB max per write
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
while (true) {
|
|
182
|
+
const { done, value } = await reader.read();
|
|
183
|
+
if (done) break;
|
|
184
|
+
|
|
185
|
+
// If chunk is larger than 5MB, split it
|
|
186
|
+
if (value.length > MAX_CHUNK_SIZE) {
|
|
187
|
+
let offset = 0;
|
|
188
|
+
while (offset < value.length) {
|
|
189
|
+
const chunk = value.slice(offset, offset + MAX_CHUNK_SIZE);
|
|
190
|
+
await stream.write(chunk);
|
|
191
|
+
offset += MAX_CHUNK_SIZE;
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
await stream.write(value);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
} finally {
|
|
198
|
+
reader.releaseLock();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Close the stream
|
|
202
|
+
await stream.close();
|
|
203
|
+
|
|
204
|
+
const durationMs = Date.now() - started;
|
|
205
|
+
const sizeBytes = stream.bytesWritten;
|
|
206
|
+
|
|
207
|
+
// Get stream info to retrieve expiresAt
|
|
208
|
+
let expiresAt: string | undefined;
|
|
209
|
+
try {
|
|
210
|
+
const info = await storage.get(stream.id);
|
|
211
|
+
expiresAt = info.expiresAt;
|
|
212
|
+
} catch {
|
|
213
|
+
// expiresAt is optional, ignore errors
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (!options.json) {
|
|
217
|
+
const sourceLabel = args.filename === '-' ? 'stdin' : basename(args.filename);
|
|
218
|
+
console.log(`Namespace: ${tui.bold(args.namespace)}`);
|
|
219
|
+
console.log(`ID: ${stream.id}`);
|
|
220
|
+
console.log(`Size: ${tui.formatBytes(sizeBytes)}`);
|
|
221
|
+
console.log(`URL: ${tui.link(stream.url)}`);
|
|
222
|
+
if (expiresAt) {
|
|
223
|
+
console.log(`Expires: ${expiresAt}`);
|
|
224
|
+
}
|
|
225
|
+
if (metadata && Object.keys(metadata).length > 0) {
|
|
226
|
+
console.log(`Metadata:`);
|
|
227
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
228
|
+
console.log(` ${key}: ${value}`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (opts.compress) {
|
|
232
|
+
console.log(`Compressed: yes`);
|
|
233
|
+
}
|
|
234
|
+
tui.success(`created stream from ${sourceLabel} in ${durationMs.toFixed(1)}ms`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
id: stream.id,
|
|
239
|
+
namespace: args.namespace,
|
|
240
|
+
url: stream.url,
|
|
241
|
+
sizeBytes,
|
|
242
|
+
metadata: metadata ?? {},
|
|
243
|
+
expiresAt,
|
|
244
|
+
};
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
export default createSubcommand;
|
|
@@ -13,7 +13,8 @@ export const deleteSubcommand = createCommand({
|
|
|
13
13
|
description: 'Delete a stream by ID (soft delete)',
|
|
14
14
|
tags: ['destructive', 'deletes-resource', 'slow', 'requires-auth'],
|
|
15
15
|
idempotent: true,
|
|
16
|
-
requires: { auth: true,
|
|
16
|
+
requires: { auth: true, region: true },
|
|
17
|
+
optional: { project: true },
|
|
17
18
|
examples: [
|
|
18
19
|
{ command: getCommand('stream delete stream-id-123'), description: 'Delete a stream' },
|
|
19
20
|
{
|
|
@@ -16,7 +16,8 @@ export const getSubcommand = createCommand({
|
|
|
16
16
|
name: 'get',
|
|
17
17
|
description: 'Get detailed information about a specific stream',
|
|
18
18
|
tags: ['read-only', 'slow', 'requires-auth'],
|
|
19
|
-
requires: { auth: true,
|
|
19
|
+
requires: { auth: true, region: true },
|
|
20
|
+
optional: { project: true },
|
|
20
21
|
idempotent: true,
|
|
21
22
|
examples: [
|
|
22
23
|
{ command: getCommand('stream get stream-id-123'), description: 'Get stream details' },
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createCommand } from '../../../types';
|
|
2
|
+
import createSubcommand from './create';
|
|
2
3
|
import listSubcommand from './list';
|
|
3
4
|
import getSubcommand from './get';
|
|
4
5
|
import deleteSubcommand from './delete';
|
|
@@ -10,10 +11,18 @@ export const streamCommand = createCommand({
|
|
|
10
11
|
description: 'Manage durable streams',
|
|
11
12
|
tags: ['slow', 'requires-auth'],
|
|
12
13
|
examples: [
|
|
14
|
+
{
|
|
15
|
+
command: getCommand('cloud stream create memory-share ./notes.md'),
|
|
16
|
+
description: 'Create stream from file',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
command: `cat data.json | ${getCommand('cloud stream create memory-share -')}`,
|
|
20
|
+
description: 'Create stream from stdin',
|
|
21
|
+
},
|
|
13
22
|
{ command: getCommand('cloud stream list'), description: 'List all streams' },
|
|
14
23
|
{ command: getCommand('cloud stream get <id>'), description: 'Get stream details' },
|
|
15
24
|
],
|
|
16
|
-
subcommands: [listSubcommand, getSubcommand, deleteSubcommand],
|
|
25
|
+
subcommands: [createSubcommand, listSubcommand, getSubcommand, deleteSubcommand],
|
|
17
26
|
});
|
|
18
27
|
|
|
19
28
|
export default streamCommand;
|
|
@@ -22,7 +22,8 @@ export const listSubcommand = createCommand({
|
|
|
22
22
|
aliases: ['ls'],
|
|
23
23
|
description: 'List recent streams with optional filtering',
|
|
24
24
|
tags: ['read-only', 'slow', 'requires-auth'],
|
|
25
|
-
requires: { auth: true,
|
|
25
|
+
requires: { auth: true, region: true },
|
|
26
|
+
optional: { project: true },
|
|
26
27
|
idempotent: true,
|
|
27
28
|
examples: [
|
|
28
29
|
{ command: getCommand('cloud stream list'), description: 'List all streams' },
|
|
@@ -1,35 +1,62 @@
|
|
|
1
|
-
import { StreamStorageService, Logger } from '@agentuity/core';
|
|
1
|
+
import { StreamStorageService, type Logger } from '@agentuity/core';
|
|
2
2
|
import { createServerFetchAdapter, getServiceUrls } from '@agentuity/server';
|
|
3
3
|
import { loadProjectSDKKey } from '../../../config';
|
|
4
|
-
import {
|
|
5
|
-
import type { Config } from '../../../types';
|
|
4
|
+
import type { AuthData, Config, GlobalOptions, ProjectConfig } from '../../../types';
|
|
6
5
|
import * as tui from '../../../tui';
|
|
7
6
|
|
|
8
7
|
export async function createStorageAdapter(ctx: {
|
|
9
8
|
logger: Logger;
|
|
10
9
|
projectDir: string;
|
|
10
|
+
auth: AuthData;
|
|
11
|
+
region: string;
|
|
12
|
+
project?: ProjectConfig;
|
|
11
13
|
config: Config | null;
|
|
12
|
-
|
|
14
|
+
options: GlobalOptions;
|
|
13
15
|
}) {
|
|
16
|
+
// Try to get SDK key from project context first (preferred for project-based auth)
|
|
14
17
|
const sdkKey = await loadProjectSDKKey(ctx.logger, ctx.projectDir);
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
|
|
19
|
+
let authToken: string;
|
|
20
|
+
let queryParams: Record<string, string> | undefined;
|
|
21
|
+
|
|
22
|
+
if (sdkKey) {
|
|
23
|
+
// Use SDK key auth (project context available)
|
|
24
|
+
authToken = sdkKey;
|
|
25
|
+
ctx.logger.trace('using SDK key auth for stream');
|
|
26
|
+
} else {
|
|
27
|
+
// Use CLI key auth with orgId query param
|
|
28
|
+
// Pulse server expects orgId as query param for CLI tokens (ck_*)
|
|
29
|
+
// IMPORTANT: For CLI key auth, prefer user's org ID over project's org ID
|
|
30
|
+
// because the CLI key is validated against the user's orgs, not the project's org
|
|
31
|
+
const orgId =
|
|
32
|
+
ctx.options.orgId ??
|
|
33
|
+
process.env.AGENTUITY_CLOUD_ORG_ID ??
|
|
34
|
+
ctx.config?.preferences?.orgId ??
|
|
35
|
+
ctx.project?.orgId;
|
|
36
|
+
|
|
37
|
+
if (!orgId) {
|
|
38
|
+
tui.fatal(
|
|
39
|
+
'Organization ID is required. Either run from a project directory, use --org-id flag, or set AGENTUITY_CLOUD_ORG_ID environment variable.'
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
authToken = ctx.auth.apiKey;
|
|
44
|
+
queryParams = { orgId };
|
|
45
|
+
ctx.logger.trace('using CLI key auth with orgId query param for stream');
|
|
20
46
|
}
|
|
21
47
|
|
|
48
|
+
const baseUrl = getServiceUrls(ctx.region).stream;
|
|
49
|
+
|
|
22
50
|
const adapter = createServerFetchAdapter(
|
|
23
51
|
{
|
|
24
52
|
headers: {
|
|
25
|
-
Authorization: `Bearer ${
|
|
53
|
+
Authorization: `Bearer ${authToken}`,
|
|
26
54
|
},
|
|
55
|
+
queryParams,
|
|
27
56
|
},
|
|
28
57
|
ctx.logger
|
|
29
58
|
);
|
|
30
59
|
|
|
31
|
-
const baseUrl = getServiceUrls(ctx.project.region).stream;
|
|
32
|
-
|
|
33
60
|
ctx.logger.trace('using stream url: %s', baseUrl);
|
|
34
61
|
|
|
35
62
|
return new StreamStorageService(baseUrl, adapter);
|
package/src/cmd/upgrade/index.ts
CHANGED
|
@@ -185,6 +185,31 @@ export const command = createCommand({
|
|
|
185
185
|
};
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
+
// Verify the version is available on npm before proceeding
|
|
189
|
+
const isAvailable = await tui.spinner({
|
|
190
|
+
message: 'Verifying npm availability...',
|
|
191
|
+
clearOnSuccess: true,
|
|
192
|
+
callback: async () => {
|
|
193
|
+
const { waitForNpmAvailability } = await import('./npm-availability');
|
|
194
|
+
return await waitForNpmAvailability(latestVersion, {
|
|
195
|
+
maxAttempts: 6,
|
|
196
|
+
initialDelayMs: 2000,
|
|
197
|
+
});
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
if (!isAvailable) {
|
|
202
|
+
tui.warning('The new version is not yet available on npm.');
|
|
203
|
+
tui.info('This can happen right after a release. Please try again in a few minutes.');
|
|
204
|
+
tui.info(`You can also run: ${tui.muted('bun add -g @agentuity/cli@latest')}`);
|
|
205
|
+
return {
|
|
206
|
+
upgraded: false,
|
|
207
|
+
from: currentVersion,
|
|
208
|
+
to: latestVersion,
|
|
209
|
+
message: 'Version not yet available on npm',
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
188
213
|
// Show version info
|
|
189
214
|
if (!force) {
|
|
190
215
|
tui.info(`Current version: ${tui.muted(normalizedCurrent)}`);
|