@howone/sdk 0.7.0 → 0.7.1

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/index.js CHANGED
@@ -298,6 +298,7 @@ __export(index_exports, {
298
298
  AUTH_TOKEN_KEY: () => AUTH_TOKEN_KEY,
299
299
  ClayxButton: () => ClayxButton,
300
300
  ClayxToast: () => ClayxToast,
301
+ DEFAULT_LIMIT_EXCEEDED_MESSAGE: () => DEFAULT_LIMIT_EXCEEDED_MESSAGE,
301
302
  DefaultErrorFallback: () => DefaultErrorFallback,
302
303
  ElementSelector: () => ElementSelector,
303
304
  ElementSelectorProvider: () => ElementSelectorProvider,
@@ -327,11 +328,13 @@ __export(index_exports, {
327
328
  getEnvs: () => getEnvs,
328
329
  getGlobalEnvironment: () => getGlobalEnvironment,
329
330
  getToken: () => getToken,
331
+ handleLimitExceeded: () => handleLimitExceeded,
330
332
  howone: () => client_default,
331
333
  iframeNavigation: () => iframeNavigation,
332
334
  initIframeNavigation: () => initIframeNavigation,
333
335
  isTokenValid: () => isTokenValid,
334
336
  loginWithEmailCode: () => loginWithEmailCode,
337
+ mergeLimitExceededOptions: () => mergeLimitExceededOptions,
335
338
  onAuthStateChanged: () => onAuthStateChanged,
336
339
  parseUserFromToken: () => parseUserFromToken,
337
340
  sendElementSelectionToParent: () => sendElementSelectionToParent,
@@ -831,6 +834,355 @@ function getCodeStatus(email) {
831
834
  return unifiedAuth.getCodeStatus(email);
832
835
  }
833
836
 
837
+ // src/services/ai-workflow.ts
838
+ var AIWorkflowClient = class {
839
+ constructor(options = {}) {
840
+ this.baseUrl = options.baseUrl?.replace(/\/+$/, "") || "";
841
+ this.apiKey = options.apiKey;
842
+ this.headers = { "Content-Type": "application/json", ...options.headers || {} };
843
+ this.fetchImpl = options.fetchImpl || fetch.bind(globalThis);
844
+ }
845
+ buildHeaders(extra) {
846
+ const h = { ...this.headers, ...extra || {} };
847
+ if (this.apiKey && !h["Authorization"]) {
848
+ h["Authorization"] = `Bearer ${this.apiKey}`;
849
+ }
850
+ return h;
851
+ }
852
+ async safeJson(resp) {
853
+ try {
854
+ return await resp.json();
855
+ } catch (_e) {
856
+ return null;
857
+ }
858
+ }
859
+ /**
860
+ * 按 ID 执行工作流:POST {baseUrl}/workflow/{workflowId}/execute
861
+ * body: { input, options }
862
+ */
863
+ async executeWorkflow(workflowId, inputs, options) {
864
+ if (!this.baseUrl) {
865
+ throw new Error("AI workflow client requires a baseUrl (e.g. https://evoagentx-server.fly.dev)");
866
+ }
867
+ const url = `${this.baseUrl}/workflow/${workflowId}/execute`;
868
+ try {
869
+ const res = await this.fetchImpl(url, {
870
+ method: "POST",
871
+ headers: this.buildHeaders(),
872
+ body: JSON.stringify({ inputs, options })
873
+ });
874
+ const data = await this.safeJson(res);
875
+ if (!res.ok) {
876
+ return { success: false, error: data?.error || `HTTP ${res.status}` };
877
+ }
878
+ return data || { success: true };
879
+ } catch (error) {
880
+ return { success: false, error: error instanceof Error ? error.message : "Network error" };
881
+ }
882
+ }
883
+ };
884
+ function createAIWorkflowClient(options = {}) {
885
+ return new AIWorkflowClient(options);
886
+ }
887
+ var aiWorkflow = createAIWorkflowClient({ baseUrl: "https://evoagentx-server" });
888
+
889
+ // src/services/request/index.ts
890
+ var import_axios = __toESM(require("axios"));
891
+ var Request = class {
892
+ constructor(config) {
893
+ this.abortControllers = /* @__PURE__ */ new Map();
894
+ this.instance = import_axios.default.create({
895
+ ...config,
896
+ withCredentials: true,
897
+ validateStatus: (status) => {
898
+ return status >= 200 && status < 300;
899
+ }
900
+ });
901
+ this.interceptors = config.interceptors;
902
+ this.instance.interceptors.request.use(
903
+ this.interceptors?.requestInterceptor,
904
+ this.interceptors?.requestInterceptorCatch
905
+ );
906
+ this.instance.interceptors.response.use(
907
+ this.interceptors?.responseInterceptor,
908
+ this.interceptors?.responseInterceptorCatch
909
+ );
910
+ this.instance.interceptors.request.use(
911
+ (config2) => {
912
+ return config2;
913
+ },
914
+ (err) => {
915
+ return Promise.reject(err);
916
+ }
917
+ );
918
+ this.instance.interceptors.response.use(
919
+ (res) => {
920
+ return res.data;
921
+ },
922
+ (err) => {
923
+ if (import_axios.default.isCancel(err)) {
924
+ return Promise.reject({
925
+ isCanceled: true,
926
+ message: "request canceled",
927
+ originalError: err
928
+ });
929
+ }
930
+ if (err.response?.data?.error) {
931
+ return Promise.reject(err.response.data.error);
932
+ }
933
+ return Promise.reject(err);
934
+ }
935
+ );
936
+ }
937
+ cancelRequest(url) {
938
+ this.abortControllers.forEach((controller, key) => {
939
+ if (key.includes(url)) {
940
+ controller.abort();
941
+ this.abortControllers.delete(key);
942
+ }
943
+ });
944
+ }
945
+ cancelAllRequests() {
946
+ this.abortControllers.forEach((controller) => {
947
+ controller.abort();
948
+ });
949
+ this.abortControllers.clear();
950
+ }
951
+ request(config) {
952
+ const controller = new AbortController();
953
+ const url = config.url || "";
954
+ const method = config.method || "GET";
955
+ const key = `${method}:${url}`;
956
+ this.abortControllers.set(key, controller);
957
+ config.signal = controller.signal;
958
+ return new Promise((resolve, reject) => {
959
+ if (config.interceptors?.requestInterceptor) {
960
+ config = config.interceptors.requestInterceptor(config);
961
+ }
962
+ this.instance.request(config).then((res) => {
963
+ this.abortControllers.delete(key);
964
+ if (config.interceptors?.responseInterceptor) {
965
+ res = config.interceptors.responseInterceptor(res);
966
+ }
967
+ resolve(res);
968
+ }).catch((err) => {
969
+ this.abortControllers.delete(key);
970
+ reject(err);
971
+ });
972
+ });
973
+ }
974
+ get(config) {
975
+ return this.request({ ...config, method: "GET" });
976
+ }
977
+ post(config) {
978
+ return this.request({ ...config, method: "POST" });
979
+ }
980
+ delete(config) {
981
+ return this.request({ ...config, method: "DELETE" });
982
+ }
983
+ put(config) {
984
+ return this.request({ ...config, method: "PUT" });
985
+ }
986
+ patch(config) {
987
+ return this.request({ ...config, method: "PATCH" });
988
+ }
989
+ };
990
+ var request_default = Request;
991
+
992
+ // src/services/ai-workflow-axios.ts
993
+ function createAIWorkflowClientAxios(options = {}) {
994
+ const basePath = options.basePath ?? "/api";
995
+ const baseUrl = (options.baseUrl || "https://evoagentx-server.fly.dev").replace(/\/+$/, "");
996
+ const baseAPI = `${baseUrl}${basePath.startsWith("/") ? "" : "/"}${basePath}`.replace(/\/+$/, "");
997
+ const client = options.requestInstance || new request_default({
998
+ baseURL: baseAPI,
999
+ timeout: options.timeout ?? 6e4,
1000
+ interceptors: {
1001
+ requestInterceptor: (config) => {
1002
+ config.headers = config.headers || {};
1003
+ if (options.apiKey && !config.headers["Authorization"]) {
1004
+ config.headers["Authorization"] = `Bearer ${options.apiKey}`;
1005
+ }
1006
+ if (options.headers) {
1007
+ config.headers = { ...config.headers || {}, ...options.headers };
1008
+ }
1009
+ return config;
1010
+ },
1011
+ requestInterceptorCatch: (err) => Promise.reject(err),
1012
+ responseInterceptor: (res) => res,
1013
+ responseInterceptorCatch: (err) => Promise.reject(err)
1014
+ }
1015
+ });
1016
+ return {
1017
+ async executeWorkflow(workflowId, inputs, opts) {
1018
+ const url = `${baseUrl}/workflow/${workflowId}/execute`;
1019
+ const data = await client.post({ url, data: { inputs, options: opts } });
1020
+ return data;
1021
+ }
1022
+ };
1023
+ }
1024
+
1025
+ // src/services/artifact-types.ts
1026
+ function canAccessArtifact(a, ctx = {}) {
1027
+ if (!a) return false;
1028
+ if (a.visibility === "public") return true;
1029
+ if (a.visibility === "project") {
1030
+ if (ctx.projectId && a.projectId && ctx.projectId === a.projectId) return true;
1031
+ if (ctx.tokenScopes && ctx.tokenScopes.includes("project:read")) return true;
1032
+ return false;
1033
+ }
1034
+ if (a.visibility === "private") {
1035
+ if (ctx.userId && a.ownerId && ctx.userId === a.ownerId) return true;
1036
+ if (ctx.tokenScopes && (ctx.tokenScopes.includes("artifact:read:all") || ctx.tokenScopes.includes("admin"))) return true;
1037
+ return false;
1038
+ }
1039
+ if (a.visibility === "shared") {
1040
+ if (ctx.userId && a.sharedWith && a.sharedWith.includes(ctx.userId)) return true;
1041
+ if (ctx.tokenScopes && (ctx.tokenScopes.includes("artifact:read:all") || ctx.tokenScopes.includes("admin"))) return true;
1042
+ return false;
1043
+ }
1044
+ return false;
1045
+ }
1046
+
1047
+ // src/services/artifacts-client.ts
1048
+ function createArtifactsClient(req) {
1049
+ return {
1050
+ async create(input) {
1051
+ return await req.post({ url: "/artifacts", data: input });
1052
+ },
1053
+ async list(query) {
1054
+ return await req.get({ url: "/artifacts", params: query });
1055
+ },
1056
+ async get(id) {
1057
+ return await req.get({ url: `/artifacts/${encodeURIComponent(id)}` });
1058
+ },
1059
+ async setVisibility(id, visibility) {
1060
+ await req.patch({ url: `/artifacts/${encodeURIComponent(id)}/visibility`, data: { visibility } });
1061
+ },
1062
+ async delete(id) {
1063
+ await req.delete({ url: `/artifacts/${encodeURIComponent(id)}` });
1064
+ },
1065
+ // convenience local check (server is authoritative)
1066
+ canAccessLocal(a, ctx) {
1067
+ if (!a) return false;
1068
+ if (a.visibility === "public") return true;
1069
+ if (a.visibility === "project") return Boolean(ctx.projectId && a.projectId && ctx.projectId === a.projectId);
1070
+ if (a.visibility === "private") return Boolean(ctx.userId && a.ownerId && ctx.userId === a.ownerId);
1071
+ if (a.visibility === "shared") return Boolean(ctx.userId && a.sharedWith && a.sharedWith.includes(ctx.userId));
1072
+ return false;
1073
+ }
1074
+ };
1075
+ }
1076
+
1077
+ // src/services/upload-client.ts
1078
+ function createUploadClient(req, projectId) {
1079
+ const uploadUrl = projectId ? `/entities/apps/${projectId}/files` : "/files";
1080
+ return {
1081
+ /**
1082
+ * 上传单个文件
1083
+ *
1084
+ * @example
1085
+ * ```typescript
1086
+ * const { url } = await client.upload.file(imageFile)
1087
+ * console.log('文件地址:', url)
1088
+ * ```
1089
+ */
1090
+ async file(file, options) {
1091
+ const formData = new FormData();
1092
+ if (typeof file === "string") {
1093
+ if (file.startsWith("data:")) {
1094
+ const response2 = await fetch(file);
1095
+ const blob = await response2.blob();
1096
+ formData.append("file", blob, "upload.bin");
1097
+ } else {
1098
+ formData.append("fileUrl", file);
1099
+ }
1100
+ } else if (file instanceof File) {
1101
+ formData.append("file", file, file.name);
1102
+ } else {
1103
+ formData.append("file", file, "upload.bin");
1104
+ }
1105
+ if (options?.metadata) {
1106
+ formData.append("metadata", JSON.stringify(options.metadata));
1107
+ }
1108
+ const response = await req.post({
1109
+ url: uploadUrl,
1110
+ data: formData,
1111
+ signal: options?.signal
1112
+ });
1113
+ return {
1114
+ url: response.data.publicUrl
1115
+ };
1116
+ },
1117
+ /**
1118
+ * 上传图片(快捷方法,专为 AI 设计)
1119
+ *
1120
+ * @example
1121
+ * ```typescript
1122
+ * const { url } = await client.upload.image(imageFile)
1123
+ * ```
1124
+ */
1125
+ async image(file) {
1126
+ const result = await this.file(file);
1127
+ return { url: result.url };
1128
+ },
1129
+ /**
1130
+ * 批量上传文件
1131
+ *
1132
+ * @example
1133
+ * ```typescript
1134
+ * const result = await client.upload.batch({
1135
+ * files: [file1, file2, file3],
1136
+ * concurrent: 3,
1137
+ * onProgress: (completed, total) => {
1138
+ * console.log(`${completed}/${total}`)
1139
+ * }
1140
+ * })
1141
+ * ```
1142
+ */
1143
+ async batch(options) {
1144
+ const { files, concurrent = 3, onProgress, onFileComplete, signal } = options;
1145
+ const success = [];
1146
+ const failed = [];
1147
+ let completed = 0;
1148
+ for (let i = 0; i < files.length; i += concurrent) {
1149
+ if (signal?.aborted) break;
1150
+ const batch = files.slice(i, Math.min(i + concurrent, files.length));
1151
+ const promises = batch.map(async (file, batchIndex) => {
1152
+ const globalIndex = i + batchIndex;
1153
+ try {
1154
+ const result = await this.file(file, { signal });
1155
+ success.push(result);
1156
+ if (onFileComplete) {
1157
+ onFileComplete(result, globalIndex);
1158
+ }
1159
+ } catch (error) {
1160
+ const errorMsg = error instanceof Error ? error.message : String(error);
1161
+ failed.push({ index: globalIndex, error: errorMsg });
1162
+ if (onFileComplete) {
1163
+ onFileComplete(
1164
+ error instanceof Error ? error : new Error(errorMsg),
1165
+ globalIndex
1166
+ );
1167
+ }
1168
+ } finally {
1169
+ completed++;
1170
+ if (onProgress) {
1171
+ onProgress(completed, files.length);
1172
+ }
1173
+ }
1174
+ });
1175
+ await Promise.all(promises);
1176
+ }
1177
+ return {
1178
+ success,
1179
+ failed,
1180
+ total: files.length
1181
+ };
1182
+ }
1183
+ };
1184
+ }
1185
+
834
1186
  // src/components/auth/LoginForm.tsx
835
1187
  var import_react2 = require("react");
836
1188
  var import_react3 = require("@iconify/react");
@@ -2251,437 +2603,105 @@ var LimitToastContainer = ({ message, onUpgrade, closeToast }) => {
2251
2603
  backgroundImage: `linear-gradient(135deg, rgba(168,85,247,0.6) 0%, rgba(168,85,247,0.4) 5%, transparent 22%)`,
2252
2604
  backgroundOrigin: "border-box",
2253
2605
  backgroundClip: "border-box",
2254
- WebkitMask: "linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0)",
2255
- WebkitMaskComposite: "xor",
2256
- mask: "linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0)",
2257
- maskComposite: "exclude"
2258
- }
2259
- }
2260
- ),
2261
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute -top-16 -right-16 h-32 w-32 rounded-full bg-gradient-to-br from-purple-500/20 via-pink-500/10 to-transparent blur-3xl animate-pulse" }),
2262
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute -bottom-16 -left-16 h-32 w-32 rounded-full bg-gradient-to-tr from-blue-500/10 to-transparent blur-2xl animate-pulse", style: { animationDelay: "1s" } }),
2263
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "relative z-10 flex items-start gap-4 p-4", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-1 flex-col gap-3", children: [
2264
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center justify-between", children: [
2265
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", children: [
2266
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "text-lg font-bold text-white", children: "Upgrade Required" }),
2267
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "px-2 py-0.5 text-xs font-bold bg-purple-500/20 text-purple-400 rounded-md border border-purple-500/30", children: "Premium" })
2268
- ] }),
2269
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2270
- ClayxButton,
2271
- {
2272
- onClick: closeToast,
2273
- isIconOnly: true,
2274
- size: "sm",
2275
- onMouseEnter: () => setCloseHover(true),
2276
- onMouseLeave: () => setCloseHover(false),
2277
- style: {
2278
- height: "1.5rem",
2279
- width: "1.5rem",
2280
- minWidth: "1.5rem",
2281
- borderRadius: "9999px",
2282
- backgroundColor: closeHover ? "rgba(255,255,255,0.1)" : "rgba(255,255,255,0.05)",
2283
- transition: "background-color 150ms ease",
2284
- cursor: "pointer"
2285
- },
2286
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react14.Icon, { icon: "iconamoon:close", className: "w-4 h-4 text-gray-400" })
2287
- }
2288
- )
2289
- ] }),
2290
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm text-gray-300 leading-relaxed", children: message }),
2291
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "mt-1 flex items-center gap-3", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2292
- ClayxButton,
2293
- {
2294
- onClick: () => {
2295
- onUpgrade();
2296
- closeToast?.();
2297
- },
2298
- onMouseEnter: () => setHover(true),
2299
- onMouseLeave: () => setHover(false),
2300
- style: {
2301
- flex: 1,
2302
- color: "#ffffff",
2303
- fontWeight: 600,
2304
- cursor: "pointer",
2305
- transition: "all 300ms ease-in-out",
2306
- backgroundImage: hover ? "linear-gradient(to right, #9333ea, #db2777)" : "linear-gradient(to right, #a855f7, #ec4899)",
2307
- boxShadow: hover ? "0 10px 15px -3px rgba(168,85,247,0.3), 0 4px 6px -2px rgba(168,85,247,0.3)" : "none"
2308
- },
2309
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "flex items-center gap-2", children: [
2310
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react14.Icon, { icon: "solar:rocket-2-bold", className: "w-4 h-4" }),
2311
- "Upgrade Now"
2312
- ] })
2313
- }
2314
- ) })
2315
- ] }) })
2316
- ] });
2317
- };
2318
- function showLimitUpgradeToast(message, onUpgrade) {
2319
- ClayxToast.default({
2320
- render: (closeToast) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(LimitToastContainer, { message, onUpgrade, closeToast }),
2321
- options: {
2322
- autoClose: false
2323
- }
2324
- });
2325
- }
2326
-
2327
- // src/services/ai-workflow.ts
2328
- var AIWorkflowClient = class {
2329
- constructor(options = {}) {
2330
- this.baseUrl = options.baseUrl?.replace(/\/+$/, "") || "";
2331
- this.apiKey = options.apiKey;
2332
- this.headers = { "Content-Type": "application/json", ...options.headers || {} };
2333
- this.fetchImpl = options.fetchImpl || fetch.bind(globalThis);
2334
- }
2335
- buildHeaders(extra) {
2336
- const h = { ...this.headers, ...extra || {} };
2337
- if (this.apiKey && !h["Authorization"]) {
2338
- h["Authorization"] = `Bearer ${this.apiKey}`;
2339
- }
2340
- return h;
2341
- }
2342
- async safeJson(resp) {
2343
- try {
2344
- return await resp.json();
2345
- } catch (_e) {
2346
- return null;
2347
- }
2348
- }
2349
- /**
2350
- * 按 ID 执行工作流:POST {baseUrl}/workflow/{workflowId}/execute
2351
- * body: { input, options }
2352
- */
2353
- async executeWorkflow(workflowId, inputs, options) {
2354
- if (!this.baseUrl) {
2355
- throw new Error("AI workflow client requires a baseUrl (e.g. https://evoagentx-server.fly.dev)");
2356
- }
2357
- const url = `${this.baseUrl}/workflow/${workflowId}/execute`;
2358
- try {
2359
- const res = await this.fetchImpl(url, {
2360
- method: "POST",
2361
- headers: this.buildHeaders(),
2362
- body: JSON.stringify({ inputs, options })
2363
- });
2364
- const data = await this.safeJson(res);
2365
- if (!res.ok) {
2366
- return { success: false, error: data?.error || `HTTP ${res.status}` };
2367
- }
2368
- if (data && data.status && data.status === 4003) {
2369
- showLimitUpgradeToast(
2370
- "Your credits are exhausted. Please upgrade your plan to continue generating projects.",
2371
- () => window.open("https://clayx.ai/pricing", "_blank")
2372
- );
2373
- return null;
2374
- }
2375
- return data || { success: true };
2376
- } catch (error) {
2377
- return { success: false, error: error instanceof Error ? error.message : "Network error" };
2378
- }
2379
- }
2380
- };
2381
- function createAIWorkflowClient(options = {}) {
2382
- return new AIWorkflowClient(options);
2383
- }
2384
- var aiWorkflow = createAIWorkflowClient({ baseUrl: "https://evoagentx-server" });
2385
-
2386
- // src/services/request/index.ts
2387
- var import_axios = __toESM(require("axios"));
2388
- var Request = class {
2389
- constructor(config) {
2390
- this.abortControllers = /* @__PURE__ */ new Map();
2391
- this.instance = import_axios.default.create({
2392
- ...config,
2393
- withCredentials: true,
2394
- validateStatus: (status) => {
2395
- return status >= 200 && status < 300;
2396
- }
2397
- });
2398
- this.interceptors = config.interceptors;
2399
- this.instance.interceptors.request.use(
2400
- this.interceptors?.requestInterceptor,
2401
- this.interceptors?.requestInterceptorCatch
2402
- );
2403
- this.instance.interceptors.response.use(
2404
- this.interceptors?.responseInterceptor,
2405
- this.interceptors?.responseInterceptorCatch
2406
- );
2407
- this.instance.interceptors.request.use(
2408
- (config2) => {
2409
- return config2;
2410
- },
2411
- (err) => {
2412
- return Promise.reject(err);
2413
- }
2414
- );
2415
- this.instance.interceptors.response.use(
2416
- (res) => {
2417
- return res.data;
2418
- },
2419
- (err) => {
2420
- if (import_axios.default.isCancel(err)) {
2421
- return Promise.reject({
2422
- isCanceled: true,
2423
- message: "request canceled",
2424
- originalError: err
2425
- });
2426
- }
2427
- if (err.response?.data?.error) {
2428
- return Promise.reject(err.response.data.error);
2429
- }
2430
- return Promise.reject(err);
2431
- }
2432
- );
2433
- }
2434
- cancelRequest(url) {
2435
- this.abortControllers.forEach((controller, key) => {
2436
- if (key.includes(url)) {
2437
- controller.abort();
2438
- this.abortControllers.delete(key);
2439
- }
2440
- });
2441
- }
2442
- cancelAllRequests() {
2443
- this.abortControllers.forEach((controller) => {
2444
- controller.abort();
2445
- });
2446
- this.abortControllers.clear();
2447
- }
2448
- request(config) {
2449
- const controller = new AbortController();
2450
- const url = config.url || "";
2451
- const method = config.method || "GET";
2452
- const key = `${method}:${url}`;
2453
- this.abortControllers.set(key, controller);
2454
- config.signal = controller.signal;
2455
- return new Promise((resolve, reject) => {
2456
- if (config.interceptors?.requestInterceptor) {
2457
- config = config.interceptors.requestInterceptor(config);
2458
- }
2459
- this.instance.request(config).then((res) => {
2460
- this.abortControllers.delete(key);
2461
- if (config.interceptors?.responseInterceptor) {
2462
- res = config.interceptors.responseInterceptor(res);
2463
- }
2464
- resolve(res);
2465
- }).catch((err) => {
2466
- this.abortControllers.delete(key);
2467
- reject(err);
2468
- });
2469
- });
2470
- }
2471
- get(config) {
2472
- return this.request({ ...config, method: "GET" });
2473
- }
2474
- post(config) {
2475
- return this.request({ ...config, method: "POST" });
2476
- }
2477
- delete(config) {
2478
- return this.request({ ...config, method: "DELETE" });
2479
- }
2480
- put(config) {
2481
- return this.request({ ...config, method: "PUT" });
2482
- }
2483
- patch(config) {
2484
- return this.request({ ...config, method: "PATCH" });
2485
- }
2486
- };
2487
- var request_default = Request;
2488
-
2489
- // src/services/ai-workflow-axios.ts
2490
- function createAIWorkflowClientAxios(options = {}) {
2491
- const basePath = options.basePath ?? "/api";
2492
- const baseUrl = (options.baseUrl || "https://evoagentx-server.fly.dev").replace(/\/+$/, "");
2493
- const baseAPI = `${baseUrl}${basePath.startsWith("/") ? "" : "/"}${basePath}`.replace(/\/+$/, "");
2494
- const client = options.requestInstance || new request_default({
2495
- baseURL: baseAPI,
2496
- timeout: options.timeout ?? 6e4,
2497
- interceptors: {
2498
- requestInterceptor: (config) => {
2499
- config.headers = config.headers || {};
2500
- if (options.apiKey && !config.headers["Authorization"]) {
2501
- config.headers["Authorization"] = `Bearer ${options.apiKey}`;
2606
+ WebkitMask: "linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0)",
2607
+ WebkitMaskComposite: "xor",
2608
+ mask: "linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0)",
2609
+ maskComposite: "exclude"
2502
2610
  }
2503
- if (options.headers) {
2504
- config.headers = { ...config.headers || {}, ...options.headers };
2611
+ }
2612
+ ),
2613
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute -top-16 -right-16 h-32 w-32 rounded-full bg-gradient-to-br from-purple-500/20 via-pink-500/10 to-transparent blur-3xl animate-pulse" }),
2614
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute -bottom-16 -left-16 h-32 w-32 rounded-full bg-gradient-to-tr from-blue-500/10 to-transparent blur-2xl animate-pulse", style: { animationDelay: "1s" } }),
2615
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "relative z-10 flex items-start gap-4 p-4", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-1 flex-col gap-3", children: [
2616
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center justify-between", children: [
2617
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", children: [
2618
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "text-lg font-bold text-white", children: "Upgrade Required" }),
2619
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "px-2 py-0.5 text-xs font-bold bg-purple-500/20 text-purple-400 rounded-md border border-purple-500/30", children: "Premium" })
2620
+ ] }),
2621
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2622
+ ClayxButton,
2623
+ {
2624
+ onClick: closeToast,
2625
+ isIconOnly: true,
2626
+ size: "sm",
2627
+ onMouseEnter: () => setCloseHover(true),
2628
+ onMouseLeave: () => setCloseHover(false),
2629
+ style: {
2630
+ height: "1.5rem",
2631
+ width: "1.5rem",
2632
+ minWidth: "1.5rem",
2633
+ borderRadius: "9999px",
2634
+ backgroundColor: closeHover ? "rgba(255,255,255,0.1)" : "rgba(255,255,255,0.05)",
2635
+ transition: "background-color 150ms ease",
2636
+ cursor: "pointer"
2637
+ },
2638
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react14.Icon, { icon: "iconamoon:close", className: "w-4 h-4 text-gray-400" })
2639
+ }
2640
+ )
2641
+ ] }),
2642
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm text-gray-300 leading-relaxed", children: message }),
2643
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "mt-1 flex items-center gap-3", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2644
+ ClayxButton,
2645
+ {
2646
+ onClick: () => {
2647
+ onUpgrade();
2648
+ closeToast?.();
2649
+ },
2650
+ onMouseEnter: () => setHover(true),
2651
+ onMouseLeave: () => setHover(false),
2652
+ style: {
2653
+ flex: 1,
2654
+ color: "#ffffff",
2655
+ fontWeight: 600,
2656
+ cursor: "pointer",
2657
+ transition: "all 300ms ease-in-out",
2658
+ backgroundImage: hover ? "linear-gradient(to right, #9333ea, #db2777)" : "linear-gradient(to right, #a855f7, #ec4899)",
2659
+ boxShadow: hover ? "0 10px 15px -3px rgba(168,85,247,0.3), 0 4px 6px -2px rgba(168,85,247,0.3)" : "none"
2660
+ },
2661
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "flex items-center gap-2", children: [
2662
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react14.Icon, { icon: "solar:rocket-2-bold", className: "w-4 h-4" }),
2663
+ "Upgrade Now"
2664
+ ] })
2505
2665
  }
2506
- return config;
2507
- },
2508
- requestInterceptorCatch: (err) => Promise.reject(err),
2509
- responseInterceptor: (res) => res,
2510
- responseInterceptorCatch: (err) => Promise.reject(err)
2666
+ ) })
2667
+ ] }) })
2668
+ ] });
2669
+ };
2670
+ function showLimitUpgradeToast(message, onUpgrade) {
2671
+ ClayxToast.default({
2672
+ render: (closeToast) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(LimitToastContainer, { message, onUpgrade, closeToast }),
2673
+ options: {
2674
+ autoClose: false
2511
2675
  }
2512
2676
  });
2513
- return {
2514
- async executeWorkflow(workflowId, inputs, opts) {
2515
- const url = `${baseUrl}/workflow/${workflowId}/execute`;
2516
- const data = await client.post({ url, data: { inputs, options: opts } });
2517
- return data;
2518
- }
2519
- };
2520
2677
  }
2521
2678
 
2522
- // src/services/artifact-types.ts
2523
- function canAccessArtifact(a, ctx = {}) {
2524
- if (!a) return false;
2525
- if (a.visibility === "public") return true;
2526
- if (a.visibility === "project") {
2527
- if (ctx.projectId && a.projectId && ctx.projectId === a.projectId) return true;
2528
- if (ctx.tokenScopes && ctx.tokenScopes.includes("project:read")) return true;
2529
- return false;
2530
- }
2531
- if (a.visibility === "private") {
2532
- if (ctx.userId && a.ownerId && ctx.userId === a.ownerId) return true;
2533
- if (ctx.tokenScopes && (ctx.tokenScopes.includes("artifact:read:all") || ctx.tokenScopes.includes("admin"))) return true;
2534
- return false;
2535
- }
2536
- if (a.visibility === "shared") {
2537
- if (ctx.userId && a.sharedWith && a.sharedWith.includes(ctx.userId)) return true;
2538
- if (ctx.tokenScopes && (ctx.tokenScopes.includes("artifact:read:all") || ctx.tokenScopes.includes("admin"))) return true;
2539
- return false;
2679
+ // src/services/limit-exceeded.ts
2680
+ init_config();
2681
+ var DEFAULT_LIMIT_EXCEEDED_MESSAGE = "Your credits are exhausted. Please upgrade your plan to continue generating projects.";
2682
+ function handleLimitExceeded(context, options) {
2683
+ options?.onLimitExceeded?.(context);
2684
+ if (options?.showUpgradeToast === false) {
2685
+ return;
2540
2686
  }
2541
- return false;
2542
- }
2543
-
2544
- // src/services/artifacts-client.ts
2545
- function createArtifactsClient(req) {
2546
- return {
2547
- async create(input) {
2548
- return await req.post({ url: "/artifacts", data: input });
2549
- },
2550
- async list(query) {
2551
- return await req.get({ url: "/artifacts", params: query });
2552
- },
2553
- async get(id) {
2554
- return await req.get({ url: `/artifacts/${encodeURIComponent(id)}` });
2555
- },
2556
- async setVisibility(id, visibility) {
2557
- await req.patch({ url: `/artifacts/${encodeURIComponent(id)}/visibility`, data: { visibility } });
2558
- },
2559
- async delete(id) {
2560
- await req.delete({ url: `/artifacts/${encodeURIComponent(id)}` });
2561
- },
2562
- // convenience local check (server is authoritative)
2563
- canAccessLocal(a, ctx) {
2564
- if (!a) return false;
2565
- if (a.visibility === "public") return true;
2566
- if (a.visibility === "project") return Boolean(ctx.projectId && a.projectId && ctx.projectId === a.projectId);
2567
- if (a.visibility === "private") return Boolean(ctx.userId && a.ownerId && ctx.userId === a.ownerId);
2568
- if (a.visibility === "shared") return Boolean(ctx.userId && a.sharedWith && a.sharedWith.includes(ctx.userId));
2569
- return false;
2570
- }
2571
- };
2687
+ const upgradeUrl = options?.upgradeUrl || `${getEnvs().AUTH_ROOT_VALUE}/price`;
2688
+ showLimitUpgradeToast(
2689
+ context.message || DEFAULT_LIMIT_EXCEEDED_MESSAGE,
2690
+ () => window.open(upgradeUrl, "_blank")
2691
+ );
2572
2692
  }
2573
-
2574
- // src/services/upload-client.ts
2575
- function createUploadClient(req, projectId) {
2576
- const uploadUrl = projectId ? `/entities/apps/${projectId}/files` : "/files";
2693
+ function mergeLimitExceededOptions(base, override) {
2694
+ if (!base && !override) {
2695
+ return void 0;
2696
+ }
2577
2697
  return {
2578
- /**
2579
- * 上传单个文件
2580
- *
2581
- * @example
2582
- * ```typescript
2583
- * const { url } = await client.upload.file(imageFile)
2584
- * console.log('文件地址:', url)
2585
- * ```
2586
- */
2587
- async file(file, options) {
2588
- const formData = new FormData();
2589
- if (typeof file === "string") {
2590
- if (file.startsWith("data:")) {
2591
- const response2 = await fetch(file);
2592
- const blob = await response2.blob();
2593
- formData.append("file", blob, "upload.bin");
2594
- } else {
2595
- formData.append("fileUrl", file);
2596
- }
2597
- } else if (file instanceof File) {
2598
- formData.append("file", file, file.name);
2599
- } else {
2600
- formData.append("file", file, "upload.bin");
2601
- }
2602
- if (options?.metadata) {
2603
- formData.append("metadata", JSON.stringify(options.metadata));
2604
- }
2605
- const response = await req.post({
2606
- url: uploadUrl,
2607
- data: formData,
2608
- signal: options?.signal
2609
- });
2610
- return {
2611
- url: response.data.publicUrl
2612
- };
2613
- },
2614
- /**
2615
- * 上传图片(快捷方法,专为 AI 设计)
2616
- *
2617
- * @example
2618
- * ```typescript
2619
- * const { url } = await client.upload.image(imageFile)
2620
- * ```
2621
- */
2622
- async image(file) {
2623
- const result = await this.file(file);
2624
- return { url: result.url };
2625
- },
2626
- /**
2627
- * 批量上传文件
2628
- *
2629
- * @example
2630
- * ```typescript
2631
- * const result = await client.upload.batch({
2632
- * files: [file1, file2, file3],
2633
- * concurrent: 3,
2634
- * onProgress: (completed, total) => {
2635
- * console.log(`${completed}/${total}`)
2636
- * }
2637
- * })
2638
- * ```
2639
- */
2640
- async batch(options) {
2641
- const { files, concurrent = 3, onProgress, onFileComplete, signal } = options;
2642
- const success = [];
2643
- const failed = [];
2644
- let completed = 0;
2645
- for (let i = 0; i < files.length; i += concurrent) {
2646
- if (signal?.aborted) break;
2647
- const batch = files.slice(i, Math.min(i + concurrent, files.length));
2648
- const promises = batch.map(async (file, batchIndex) => {
2649
- const globalIndex = i + batchIndex;
2650
- try {
2651
- const result = await this.file(file, { signal });
2652
- success.push(result);
2653
- if (onFileComplete) {
2654
- onFileComplete(result, globalIndex);
2655
- }
2656
- } catch (error) {
2657
- const errorMsg = error instanceof Error ? error.message : String(error);
2658
- failed.push({ index: globalIndex, error: errorMsg });
2659
- if (onFileComplete) {
2660
- onFileComplete(
2661
- error instanceof Error ? error : new Error(errorMsg),
2662
- globalIndex
2663
- );
2664
- }
2665
- } finally {
2666
- completed++;
2667
- if (onProgress) {
2668
- onProgress(completed, files.length);
2669
- }
2670
- }
2671
- });
2672
- await Promise.all(promises);
2673
- }
2674
- return {
2675
- success,
2676
- failed,
2677
- total: files.length
2678
- };
2679
- }
2698
+ ...base,
2699
+ ...override,
2700
+ onLimitExceeded: override?.onLimitExceeded ?? base?.onLimitExceeded
2680
2701
  };
2681
2702
  }
2682
2703
 
2683
2704
  // src/services/sse-executor.ts
2684
- init_config();
2685
2705
  async function executeSSEWorkflow(request, options = {}) {
2686
2706
  const startTime = Date.now();
2687
2707
  const result = {
@@ -2782,11 +2802,16 @@ async function executeSSEWorkflow(request, options = {}) {
2782
2802
  options.onStreamContent(rawEvent.delta);
2783
2803
  }
2784
2804
  if (rawEvent.type === "key_limit_exceed") {
2785
- const errorMsg = rawEvent.data.content;
2805
+ const errorMsg = rawEvent.data?.content || DEFAULT_LIMIT_EXCEEDED_MESSAGE;
2786
2806
  result.errors.push(errorMsg);
2787
- showLimitUpgradeToast(
2788
- "Your credits are exhausted. Please upgrade your plan to continue generating projects.",
2789
- () => window.open(`${getEnvs().AUTH_ROOT_VALUE}/price`, "_blank")
2807
+ handleLimitExceeded(
2808
+ {
2809
+ source: "sse-executor",
2810
+ message: errorMsg,
2811
+ eventType: rawEvent.type,
2812
+ rawData: rawEvent.data
2813
+ },
2814
+ options.limitExceeded
2790
2815
  );
2791
2816
  if (options.onError) {
2792
2817
  options.onError(new Error(errorMsg));
@@ -2997,7 +3022,7 @@ function createEventIterable(streamFactory, config) {
2997
3022
  };
2998
3023
  }
2999
3024
  function createSSEClient(config) {
3000
- const { baseUrl, projectId, getAuthToken } = config;
3025
+ const { baseUrl, projectId, getAuthToken, limitExceeded } = config;
3001
3026
  const stream = (streamConfig) => {
3002
3027
  const { signal: externalSignal, ...callbacks } = streamConfig;
3003
3028
  const request = resolveRequest(baseUrl, projectId, streamConfig);
@@ -3013,7 +3038,11 @@ function createSSEClient(config) {
3013
3038
  {
3014
3039
  ...callbacks,
3015
3040
  authToken,
3016
- signal: controller.signal
3041
+ signal: controller.signal,
3042
+ limitExceeded: mergeLimitExceededOptions(
3043
+ limitExceeded,
3044
+ streamConfig.limitExceeded
3045
+ )
3017
3046
  }
3018
3047
  ).finally(() => {
3019
3048
  controller.cleanup();
@@ -3189,6 +3218,19 @@ async function executeWorkflowStreamPost(baseUrl, projectId, workflowId, request
3189
3218
  break;
3190
3219
  case "error":
3191
3220
  throw new Error(event.message || "Workflow execution failed");
3221
+ case "key_limit_exceed": {
3222
+ const errorMessage = event?.data?.content || event.message || DEFAULT_LIMIT_EXCEEDED_MESSAGE;
3223
+ handleLimitExceeded(
3224
+ {
3225
+ source: "sse-executor",
3226
+ message: errorMessage,
3227
+ eventType: event.type,
3228
+ rawData: event.data
3229
+ },
3230
+ options.limitExceeded
3231
+ );
3232
+ throw new Error(errorMessage);
3233
+ }
3192
3234
  }
3193
3235
  } catch (err) {
3194
3236
  console.error("Failed to parse SSE event:", err);
@@ -3207,12 +3249,12 @@ async function executeWorkflowStreamPost(baseUrl, projectId, workflowId, request
3207
3249
  }
3208
3250
 
3209
3251
  // src/services/workflow-executor.ts
3210
- init_config();
3211
3252
  var WorkflowExecutor = class {
3212
- constructor(baseUrl, projectId, authToken) {
3253
+ constructor(baseUrl, projectId, authToken, limitExceeded) {
3213
3254
  this.baseUrl = baseUrl.replace(/\/$/, "");
3214
3255
  this.projectId = projectId;
3215
3256
  this.authToken = authToken;
3257
+ this.limitExceeded = limitExceeded;
3216
3258
  }
3217
3259
  /**
3218
3260
  * 执行工作流 (SSE 模式)
@@ -3262,6 +3304,10 @@ var WorkflowExecutor = class {
3262
3304
  errors: []
3263
3305
  };
3264
3306
  let streamContent = "";
3307
+ const limitExceededOptions = mergeLimitExceededOptions(
3308
+ this.limitExceeded,
3309
+ options?.limitExceeded
3310
+ );
3265
3311
  try {
3266
3312
  const response = await fetch(url, {
3267
3313
  method: "POST",
@@ -3314,11 +3360,16 @@ var WorkflowExecutor = class {
3314
3360
  }
3315
3361
  }
3316
3362
  } else if (event.type === "key_limit_exceed") {
3317
- const errorMsg = event.data?.content;
3363
+ const errorMsg = event.data?.content || DEFAULT_LIMIT_EXCEEDED_MESSAGE;
3318
3364
  result.errors.push(errorMsg);
3319
- showLimitUpgradeToast(
3320
- "Your credits are exhausted. Please upgrade your plan to continue generating projects.",
3321
- () => window.open(`${getEnvs().AUTH_ROOT_VALUE}/price`, "_blank")
3365
+ handleLimitExceeded(
3366
+ {
3367
+ source: "workflow-executor-sse",
3368
+ message: errorMsg,
3369
+ eventType: event.type,
3370
+ rawData: event.data
3371
+ },
3372
+ limitExceededOptions
3322
3373
  );
3323
3374
  if (options?.onError) {
3324
3375
  options.onError(new Error(errorMsg));
@@ -3402,6 +3453,17 @@ var WorkflowExecutor = class {
3402
3453
  body: JSON.stringify({ inputs })
3403
3454
  });
3404
3455
  const data = await response.json();
3456
+ if (data && data.status === 4003) {
3457
+ handleLimitExceeded(
3458
+ {
3459
+ source: "workflow-executor-rest",
3460
+ message: data?.error || data?.message || DEFAULT_LIMIT_EXCEEDED_MESSAGE,
3461
+ status: response.status,
3462
+ rawData: data
3463
+ },
3464
+ this.limitExceeded
3465
+ );
3466
+ }
3405
3467
  return {
3406
3468
  status: response.status,
3407
3469
  data
@@ -3483,14 +3545,6 @@ function createClient(opts) {
3483
3545
  },
3484
3546
  requestInterceptorCatch: (err) => Promise.reject(err),
3485
3547
  responseInterceptor: (res) => {
3486
- const data = res.data;
3487
- if (data && data.status && data.status === 4003) {
3488
- showLimitUpgradeToast(
3489
- "Your credits are exhausted. Please upgrade your plan to continue generating projects.",
3490
- () => window.open(`${getEnvs().AUTH_ROOT_VALUE}/price`, "_blank")
3491
- );
3492
- return res;
3493
- }
3494
3548
  return res;
3495
3549
  },
3496
3550
  responseInterceptorCatch: (err) => Promise.reject(err)
@@ -3598,7 +3652,8 @@ function createClient(opts) {
3598
3652
  void fetchExternalToken();
3599
3653
  }
3600
3654
  return token ?? cachedExternalToken ?? getToken() ?? null;
3601
- }
3655
+ },
3656
+ limitExceeded: opts.limitExceeded
3602
3657
  });
3603
3658
  const assignMethod = (config, method) => method ? { ...config, method } : config;
3604
3659
  function createMethodInvoker(invoker) {
@@ -3660,7 +3715,8 @@ function createClient(opts) {
3660
3715
  const executor = new WorkflowExecutor(
3661
3716
  actualAiBaseUrl,
3662
3717
  opts.projectId,
3663
- getCachedOrGlobalToken() ?? void 0
3718
+ getCachedOrGlobalToken() ?? void 0,
3719
+ opts.limitExceeded
3664
3720
  );
3665
3721
  return {
3666
3722
  executeSse: (inputs, options) => {
@@ -4291,6 +4347,7 @@ var elementSelector = {
4291
4347
  AUTH_TOKEN_KEY,
4292
4348
  ClayxButton,
4293
4349
  ClayxToast,
4350
+ DEFAULT_LIMIT_EXCEEDED_MESSAGE,
4294
4351
  DefaultErrorFallback,
4295
4352
  ElementSelector,
4296
4353
  ElementSelectorProvider,
@@ -4320,11 +4377,13 @@ var elementSelector = {
4320
4377
  getEnvs,
4321
4378
  getGlobalEnvironment,
4322
4379
  getToken,
4380
+ handleLimitExceeded,
4323
4381
  howone,
4324
4382
  iframeNavigation,
4325
4383
  initIframeNavigation,
4326
4384
  isTokenValid,
4327
4385
  loginWithEmailCode,
4386
+ mergeLimitExceededOptions,
4328
4387
  onAuthStateChanged,
4329
4388
  parseUserFromToken,
4330
4389
  sendElementSelectionToParent,