@agent-foundry/studio 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +96 -0
  2. package/dist/db/client.d.ts +59 -0
  3. package/dist/db/client.d.ts.map +1 -0
  4. package/dist/db/client.js +51 -0
  5. package/dist/db/client.js.map +1 -0
  6. package/dist/db/deployments.d.ts +65 -0
  7. package/dist/db/deployments.d.ts.map +1 -0
  8. package/dist/db/deployments.js +249 -0
  9. package/dist/db/deployments.js.map +1 -0
  10. package/dist/db/index.d.ts +7 -0
  11. package/dist/db/index.d.ts.map +1 -0
  12. package/dist/db/index.js +7 -0
  13. package/dist/db/index.js.map +1 -0
  14. package/dist/db/projects.d.ts +48 -0
  15. package/dist/db/projects.d.ts.map +1 -0
  16. package/dist/db/projects.js +192 -0
  17. package/dist/db/projects.js.map +1 -0
  18. package/dist/index.d.ts +11 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +15 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/oss/client.d.ts +65 -0
  23. package/dist/oss/client.d.ts.map +1 -0
  24. package/dist/oss/client.js +146 -0
  25. package/dist/oss/client.js.map +1 -0
  26. package/dist/oss/index.d.ts +7 -0
  27. package/dist/oss/index.d.ts.map +1 -0
  28. package/dist/oss/index.js +7 -0
  29. package/dist/oss/index.js.map +1 -0
  30. package/dist/oss/types.d.ts +96 -0
  31. package/dist/oss/types.d.ts.map +1 -0
  32. package/dist/oss/types.js +5 -0
  33. package/dist/oss/types.js.map +1 -0
  34. package/dist/oss/uploader.d.ts +72 -0
  35. package/dist/oss/uploader.d.ts.map +1 -0
  36. package/dist/oss/uploader.js +185 -0
  37. package/dist/oss/uploader.js.map +1 -0
  38. package/dist/types/deployment.d.ts +112 -0
  39. package/dist/types/deployment.d.ts.map +1 -0
  40. package/dist/types/deployment.js +7 -0
  41. package/dist/types/deployment.js.map +1 -0
  42. package/dist/types/index.d.ts +8 -0
  43. package/dist/types/index.d.ts.map +1 -0
  44. package/dist/types/index.js +8 -0
  45. package/dist/types/index.js.map +1 -0
  46. package/dist/types/project.d.ts +90 -0
  47. package/dist/types/project.d.ts.map +1 -0
  48. package/dist/types/project.js +8 -0
  49. package/dist/types/project.js.map +1 -0
  50. package/dist/types/user.d.ts +71 -0
  51. package/dist/types/user.d.ts.map +1 -0
  52. package/dist/types/user.js +8 -0
  53. package/dist/types/user.js.map +1 -0
  54. package/dist/types/workspace.d.ts +88 -0
  55. package/dist/types/workspace.d.ts.map +1 -0
  56. package/dist/types/workspace.js +27 -0
  57. package/dist/types/workspace.js.map +1 -0
  58. package/dist/utils/build.d.ts +78 -0
  59. package/dist/utils/build.d.ts.map +1 -0
  60. package/dist/utils/build.js +148 -0
  61. package/dist/utils/build.js.map +1 -0
  62. package/dist/utils/index.d.ts +6 -0
  63. package/dist/utils/index.d.ts.map +1 -0
  64. package/dist/utils/index.js +6 -0
  65. package/dist/utils/index.js.map +1 -0
  66. package/dist/utils/manifest.d.ts +106 -0
  67. package/dist/utils/manifest.d.ts.map +1 -0
  68. package/dist/utils/manifest.js +109 -0
  69. package/dist/utils/manifest.js.map +1 -0
  70. package/package.json +62 -0
  71. package/src/db/client.ts +92 -0
  72. package/src/db/deployments.ts +316 -0
  73. package/src/db/index.ts +7 -0
  74. package/src/db/projects.ts +246 -0
  75. package/src/db/schema.sql +156 -0
  76. package/src/index.ts +18 -0
  77. package/src/oss/client.ts +183 -0
  78. package/src/oss/index.ts +7 -0
  79. package/src/oss/types.ts +126 -0
  80. package/src/oss/uploader.ts +254 -0
  81. package/src/types/deployment.ts +147 -0
  82. package/src/types/index.ts +8 -0
  83. package/src/types/project.ts +114 -0
  84. package/src/types/user.ts +91 -0
  85. package/src/types/workspace.ts +124 -0
  86. package/src/utils/build.ts +199 -0
  87. package/src/utils/index.ts +6 -0
  88. package/src/utils/manifest.ts +224 -0
@@ -0,0 +1,156 @@
1
+ -- =============================================================================
2
+ -- Agent Foundry Studio Database Schema
3
+ -- =============================================================================
4
+ -- This schema defines tables for managing Studio projects and deployments.
5
+ -- Run this migration against your Supabase PostgreSQL database.
6
+ --
7
+ -- Prerequisites:
8
+ -- 1. Enable uuid-ossp extension: CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
9
+ -- 2. Supabase Auth must be configured (auth.users table exists)
10
+ -- =============================================================================
11
+
12
+ -- Enable UUID extension if not already enabled
13
+ CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
14
+
15
+ -- =============================================================================
16
+ -- Studio Projects
17
+ -- =============================================================================
18
+ -- Represents a React/Vite project created and managed in Build Studio.
19
+ -- Each project belongs to a user and can optionally be forked from another project.
20
+
21
+ CREATE TABLE IF NOT EXISTS studio_projects (
22
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
23
+
24
+ -- Owner (references Supabase Auth user)
25
+ user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
26
+
27
+ -- Project identity
28
+ name VARCHAR(255) NOT NULL,
29
+ slug VARCHAR(255) NOT NULL,
30
+ description TEXT,
31
+
32
+ -- Local development
33
+ root_path TEXT NOT NULL, -- Local filesystem path to project root
34
+
35
+ -- Framework and configuration
36
+ framework VARCHAR(50) NOT NULL DEFAULT 'vite-react',
37
+ config JSONB NOT NULL DEFAULT '{}',
38
+
39
+ -- Fork relationship
40
+ parent_project_id UUID REFERENCES studio_projects(id) ON DELETE SET NULL,
41
+
42
+ -- Timestamps
43
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
44
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
45
+
46
+ -- Ensure slug is unique per user
47
+ CONSTRAINT unique_user_slug UNIQUE (user_id, slug)
48
+ );
49
+
50
+ -- Index for user's projects
51
+ CREATE INDEX IF NOT EXISTS idx_studio_projects_user_id ON studio_projects(user_id);
52
+
53
+ -- Index for finding forks
54
+ CREATE INDEX IF NOT EXISTS idx_studio_projects_parent ON studio_projects(parent_project_id);
55
+
56
+ -- Trigger to auto-update updated_at
57
+ CREATE OR REPLACE FUNCTION update_studio_projects_updated_at()
58
+ RETURNS TRIGGER AS $$
59
+ BEGIN
60
+ NEW.updated_at = NOW();
61
+ RETURN NEW;
62
+ END;
63
+ $$ LANGUAGE plpgsql;
64
+
65
+ DROP TRIGGER IF EXISTS trigger_studio_projects_updated_at ON studio_projects;
66
+ CREATE TRIGGER trigger_studio_projects_updated_at
67
+ BEFORE UPDATE ON studio_projects
68
+ FOR EACH ROW
69
+ EXECUTE FUNCTION update_studio_projects_updated_at();
70
+
71
+ -- =============================================================================
72
+ -- Studio Deployments
73
+ -- =============================================================================
74
+ -- Represents a build and upload of a project to Alibaba Cloud OSS.
75
+ -- Tracks the full lifecycle: pending -> building -> uploading -> published/failed
76
+
77
+ CREATE TABLE IF NOT EXISTS studio_deployments (
78
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
79
+
80
+ -- References
81
+ project_id UUID NOT NULL REFERENCES studio_projects(id) ON DELETE CASCADE,
82
+ user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
83
+
84
+ -- Version info
85
+ version VARCHAR(50) NOT NULL,
86
+
87
+ -- Deployment status
88
+ status VARCHAR(20) NOT NULL DEFAULT 'pending'
89
+ CHECK (status IN ('pending', 'building', 'uploading', 'published', 'failed')),
90
+
91
+ -- OSS storage info (populated after successful upload)
92
+ oss_bucket VARCHAR(255),
93
+ oss_key TEXT,
94
+ oss_url TEXT,
95
+
96
+ -- Metrics
97
+ bundle_size_bytes BIGINT,
98
+
99
+ -- Logs and errors
100
+ build_log TEXT,
101
+ error_message TEXT,
102
+
103
+ -- Additional metadata (commit hash, build duration, etc.)
104
+ metadata JSONB NOT NULL DEFAULT '{}',
105
+
106
+ -- Timestamps
107
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
108
+ published_at TIMESTAMPTZ
109
+ );
110
+
111
+ -- Index for project's deployments
112
+ CREATE INDEX IF NOT EXISTS idx_studio_deployments_project_id ON studio_deployments(project_id);
113
+
114
+ -- Index for user's deployments
115
+ CREATE INDEX IF NOT EXISTS idx_studio_deployments_user_id ON studio_deployments(user_id);
116
+
117
+ -- Index for filtering by status
118
+ CREATE INDEX IF NOT EXISTS idx_studio_deployments_status ON studio_deployments(status);
119
+
120
+ -- Composite index for recent deployments by project
121
+ CREATE INDEX IF NOT EXISTS idx_studio_deployments_project_created
122
+ ON studio_deployments(project_id, created_at DESC);
123
+
124
+ -- =============================================================================
125
+ -- Row Level Security (RLS) Policies
126
+ -- =============================================================================
127
+ -- Enable RLS to ensure users can only access their own data.
128
+
129
+ -- Enable RLS on both tables
130
+ ALTER TABLE studio_projects ENABLE ROW LEVEL SECURITY;
131
+ ALTER TABLE studio_deployments ENABLE ROW LEVEL SECURITY;
132
+
133
+ -- Projects: Users can only see/modify their own projects
134
+ DROP POLICY IF EXISTS studio_projects_user_policy ON studio_projects;
135
+ CREATE POLICY studio_projects_user_policy ON studio_projects
136
+ FOR ALL
137
+ USING (auth.uid() = user_id)
138
+ WITH CHECK (auth.uid() = user_id);
139
+
140
+ -- Deployments: Users can only see/modify their own deployments
141
+ DROP POLICY IF EXISTS studio_deployments_user_policy ON studio_deployments;
142
+ CREATE POLICY studio_deployments_user_policy ON studio_deployments
143
+ FOR ALL
144
+ USING (auth.uid() = user_id)
145
+ WITH CHECK (auth.uid() = user_id);
146
+
147
+ -- =============================================================================
148
+ -- Comments
149
+ -- =============================================================================
150
+
151
+ COMMENT ON TABLE studio_projects IS 'React/Vite projects created in Agent Foundry Build Studio';
152
+ COMMENT ON COLUMN studio_projects.root_path IS 'Local filesystem path - only valid on the machine where project was created';
153
+ COMMENT ON COLUMN studio_projects.config IS 'JSON config: buildCommand, outputDir, envVars, baseUrl, viteConfig';
154
+
155
+ COMMENT ON TABLE studio_deployments IS 'Build and deployment history to Alibaba Cloud OSS';
156
+ COMMENT ON COLUMN studio_deployments.metadata IS 'JSON metadata: commitHash, branch, buildDurationMs, uploadDurationMs, fileCount';
package/src/index.ts ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @agent-foundry/studio
3
+ *
4
+ * Full SDK for Agent Foundry Build Studio.
5
+ * Provides types, Alibaba Cloud OSS upload utilities, and Supabase database client.
6
+ */
7
+
8
+ // Re-export all types
9
+ export * from './types';
10
+
11
+ // Re-export OSS utilities
12
+ export * from './oss';
13
+
14
+ // Re-export database client
15
+ export * from './db';
16
+
17
+ // Re-export utilities
18
+ export * from './utils';
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Alibaba Cloud OSS Client
3
+ *
4
+ * Wrapper around ali-oss SDK for browser/WebView usage.
5
+ */
6
+
7
+ import OSS from 'ali-oss';
8
+ import type { OSSClientConfig, STSCredentials, UploadFile } from './types';
9
+
10
+ /**
11
+ * Progress callback type
12
+ */
13
+ export type ProgressCallback = (percent: number, checkpoint?: unknown) => void;
14
+
15
+ /**
16
+ * AliOSSClient - wrapper for ali-oss SDK
17
+ *
18
+ * Provides simplified interface for uploading files to Alibaba Cloud OSS
19
+ * using STS credentials.
20
+ */
21
+ export class AliOSSClient {
22
+ private client: OSS;
23
+ private credentials: STSCredentials;
24
+
25
+ constructor(config: OSSClientConfig) {
26
+ this.credentials = config.credentials;
27
+
28
+ this.client = new OSS({
29
+ region: config.region,
30
+ bucket: config.bucket,
31
+ accessKeyId: config.credentials.accessKeyId,
32
+ accessKeySecret: config.credentials.accessKeySecret,
33
+ stsToken: config.credentials.securityToken,
34
+ timeout: config.timeout ?? 60000,
35
+ // Enable for browser usage
36
+ secure: true,
37
+ });
38
+ }
39
+
40
+ /**
41
+ * Check if credentials are expired
42
+ */
43
+ isExpired(): boolean {
44
+ const expiration = new Date(this.credentials.expiration);
45
+ // Consider expired 5 minutes before actual expiration
46
+ const buffer = 5 * 60 * 1000;
47
+ return Date.now() > expiration.getTime() - buffer;
48
+ }
49
+
50
+ /**
51
+ * Upload a single file
52
+ *
53
+ * @param key - Object key (path in bucket)
54
+ * @param content - File content
55
+ * @param contentType - MIME type
56
+ * @param _onProgress - Progress callback (not supported for simple uploads)
57
+ */
58
+ async uploadFile(
59
+ key: string,
60
+ content: Blob | ArrayBuffer | string,
61
+ contentType?: string,
62
+ _onProgress?: ProgressCallback
63
+ ): Promise<{ url: string; etag: string }> {
64
+ if (this.isExpired()) {
65
+ throw new Error('STS credentials have expired. Please refresh credentials.');
66
+ }
67
+
68
+ // Convert content to appropriate format
69
+ let body: Blob | Buffer | string;
70
+ if (content instanceof ArrayBuffer) {
71
+ body = new Blob([content]);
72
+ } else {
73
+ body = content;
74
+ }
75
+
76
+ const options: OSS.PutObjectOptions = {
77
+ headers: contentType ? { 'Content-Type': contentType } : undefined,
78
+ };
79
+
80
+ // Note: Progress callback is not supported for simple put operations
81
+ // Only multipart uploads support progress tracking
82
+
83
+ const result = await this.client.put(key, body, options);
84
+
85
+ return {
86
+ url: result.url,
87
+ etag: (result.res.headers as Record<string, string>)['etag'] as string,
88
+ };
89
+ }
90
+
91
+ /**
92
+ * Upload multiple files with progress tracking
93
+ *
94
+ * @param files - Array of files to upload
95
+ * @param keyPrefix - Prefix for all object keys
96
+ * @param onProgress - Progress callback for overall progress
97
+ */
98
+ async uploadFiles(
99
+ files: UploadFile[],
100
+ keyPrefix: string,
101
+ onProgress?: (uploadedCount: number, totalCount: number, currentFile: string) => void
102
+ ): Promise<{ url: string; totalBytes: number }> {
103
+ if (this.isExpired()) {
104
+ throw new Error('STS credentials have expired. Please refresh credentials.');
105
+ }
106
+
107
+ let totalBytes = 0;
108
+ const totalCount = files.length;
109
+
110
+ for (let i = 0; i < files.length; i++) {
111
+ const file = files[i];
112
+ const key = `${keyPrefix}/${file.path}`.replace(/\/+/g, '/');
113
+
114
+ // Get content size
115
+ let size = 0;
116
+ if (file.content instanceof Blob) {
117
+ size = file.content.size;
118
+ } else if (file.content instanceof ArrayBuffer) {
119
+ size = file.content.byteLength;
120
+ } else if (typeof file.content === 'string') {
121
+ size = new Blob([file.content]).size;
122
+ }
123
+
124
+ await this.uploadFile(key, file.content, file.contentType);
125
+ totalBytes += size;
126
+
127
+ if (onProgress) {
128
+ onProgress(i + 1, totalCount, file.path);
129
+ }
130
+ }
131
+
132
+ // Return URL to index.html or root
133
+ const baseUrl = this.getPublicUrl(keyPrefix);
134
+
135
+ return {
136
+ url: baseUrl,
137
+ totalBytes,
138
+ };
139
+ }
140
+
141
+ /**
142
+ * Get public URL for an object
143
+ */
144
+ getPublicUrl(key: string): string {
145
+ // Construct public URL based on bucket and region
146
+ const bucket = (this.client as unknown as { options: { bucket: string } }).options.bucket;
147
+ const region = (this.client as unknown as { options: { region: string } }).options.region;
148
+
149
+ // Standard OSS URL format
150
+ return `https://${bucket}.${region}.aliyuncs.com/${key}`;
151
+ }
152
+
153
+ /**
154
+ * Check if an object exists
155
+ */
156
+ async exists(key: string): Promise<boolean> {
157
+ try {
158
+ await this.client.head(key);
159
+ return true;
160
+ } catch {
161
+ return false;
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Delete an object
167
+ */
168
+ async delete(key: string): Promise<void> {
169
+ await this.client.delete(key);
170
+ }
171
+
172
+ /**
173
+ * List objects with prefix
174
+ */
175
+ async list(prefix: string, maxKeys = 100): Promise<string[]> {
176
+ const result = await this.client.list({
177
+ prefix,
178
+ 'max-keys': maxKeys,
179
+ }, {});
180
+
181
+ return (result.objects || []).map((obj) => obj.name);
182
+ }
183
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * OSS module - Alibaba Cloud OSS upload utilities
3
+ */
4
+
5
+ export * from './types';
6
+ export { AliOSSClient } from './client';
7
+ export { DirectUploader, type DirectUploaderConfig } from './uploader';
@@ -0,0 +1,126 @@
1
+ /**
2
+ * OSS-specific types for Alibaba Cloud integration
3
+ */
4
+
5
+ /**
6
+ * STS (Security Token Service) credentials for temporary access
7
+ */
8
+ export interface STSCredentials {
9
+ /** Temporary access key ID */
10
+ accessKeyId: string;
11
+
12
+ /** Temporary access key secret */
13
+ accessKeySecret: string;
14
+
15
+ /** Security token */
16
+ securityToken: string;
17
+
18
+ /** Token expiration timestamp (ISO string) */
19
+ expiration: string;
20
+ }
21
+
22
+ /**
23
+ * Response from BFF when requesting upload credentials
24
+ */
25
+ export interface UploadTokenResponse {
26
+ /** STS credentials for OSS access */
27
+ credentials: STSCredentials;
28
+
29
+ /** OSS bucket name */
30
+ bucket: string;
31
+
32
+ /** Region endpoint (e.g., "oss-cn-hangzhou.aliyuncs.com") */
33
+ region: string;
34
+
35
+ /** Object key prefix for this upload */
36
+ keyPrefix: string;
37
+
38
+ /** Deployment ID for tracking */
39
+ deploymentId: string;
40
+ }
41
+
42
+ /**
43
+ * File to upload
44
+ */
45
+ export interface UploadFile {
46
+ /** Relative path within the bundle (e.g., "index.html", "assets/main.js") */
47
+ path: string;
48
+
49
+ /** File content */
50
+ content: Blob | ArrayBuffer | string;
51
+
52
+ /** Content type (MIME type) */
53
+ contentType?: string;
54
+ }
55
+
56
+ /**
57
+ * Upload progress event
58
+ */
59
+ export interface UploadProgress {
60
+ /** Current file being uploaded */
61
+ currentFile: string;
62
+
63
+ /** Number of files uploaded so far */
64
+ uploadedCount: number;
65
+
66
+ /** Total number of files to upload */
67
+ totalCount: number;
68
+
69
+ /** Bytes uploaded for current file */
70
+ uploadedBytes: number;
71
+
72
+ /** Total bytes for current file */
73
+ totalBytes: number;
74
+
75
+ /** Overall progress percentage (0-100) */
76
+ percent: number;
77
+
78
+ /** Current stage */
79
+ stage: 'preparing' | 'uploading' | 'finalizing';
80
+ }
81
+
82
+ /**
83
+ * Upload result
84
+ */
85
+ export interface UploadResult {
86
+ /** Whether upload was successful */
87
+ success: boolean;
88
+
89
+ /** Deployment ID */
90
+ deploymentId: string;
91
+
92
+ /** Public URL of the deployed app */
93
+ url?: string;
94
+
95
+ /** OSS bucket */
96
+ bucket?: string;
97
+
98
+ /** OSS key prefix */
99
+ keyPrefix?: string;
100
+
101
+ /** Total bytes uploaded */
102
+ totalBytes: number;
103
+
104
+ /** Number of files uploaded */
105
+ fileCount: number;
106
+
107
+ /** Error message if failed */
108
+ error?: string;
109
+ }
110
+
111
+ /**
112
+ * OSS client configuration
113
+ */
114
+ export interface OSSClientConfig {
115
+ /** Region (e.g., "oss-cn-hangzhou") */
116
+ region: string;
117
+
118
+ /** Bucket name */
119
+ bucket: string;
120
+
121
+ /** STS credentials */
122
+ credentials: STSCredentials;
123
+
124
+ /** Request timeout in milliseconds */
125
+ timeout?: number;
126
+ }