@fastgpt-plugin/cli 0.2.0-alpha.1 → 0.2.0-alpha.2

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 (2) hide show
  1. package/dist/index.js +600 -41
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { registerHooks, stripTypeScriptTypes } from "node:module";
2
+ import { createRequire, registerHooks, stripTypeScriptTypes } from "node:module";
3
3
  import fs from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -526,7 +526,7 @@ var CheckCommand = class extends BaseCommand {
526
526
  //#endregion
527
527
  //#region package.json
528
528
  var name = "@fastgpt-plugin/cli";
529
- var version = "0.2.0-alpha.1";
529
+ var version = "0.2.0-alpha.2";
530
530
 
531
531
  //#endregion
532
532
  //#region src/constants.ts
@@ -686,6 +686,40 @@ var CreateCommand = class extends BaseCommand {
686
686
  }
687
687
  };
688
688
 
689
+ //#endregion
690
+ //#region ../../packages/domain/src/value-objects/plugin-debug-session.vo.ts
691
+ const PLUGIN_DEBUG_SOURCE_KIND = "debug";
692
+ const PLUGIN_DEBUG_SESSION_SOURCE_TMB_ID_KEY = "tmbId";
693
+ const PLUGIN_DEBUG_SOURCE_PREFIX = `${PLUGIN_DEBUG_SOURCE_KIND}:`;
694
+ const PLUGIN_DEBUG_SESSION_SOURCE_FORMAT = `${PLUGIN_DEBUG_SOURCE_KIND}:${PLUGIN_DEBUG_SESSION_SOURCE_TMB_ID_KEY}:{tmbId}`;
695
+ const PluginDebugSessionIdSchema = z.string().min(1);
696
+ const PluginDebugSessionTmbIdSchema = z.string().min(1);
697
+ const PluginDebugSessionSourceSchema = z.string().min(1).refine((source) => parsePluginDebugSessionSource(source) !== null, { message: "Invalid plugin debug session source" });
698
+ const PluginDebugSessionStatusSchema = z.enum([
699
+ "enabled",
700
+ "connected",
701
+ "disconnected",
702
+ "revoked"
703
+ ]);
704
+ const PluginDebugSessionSchema = z.object({
705
+ tmbId: PluginDebugSessionTmbIdSchema,
706
+ source: PluginDebugSessionSourceSchema,
707
+ status: PluginDebugSessionStatusSchema,
708
+ enabled: z.boolean(),
709
+ keyId: z.string().min(1),
710
+ connectionKeyHash: z.string().min(1),
711
+ connectionKey: z.string().min(1).optional(),
712
+ createdAt: z.number().int().positive(),
713
+ updatedAt: z.number().int().positive(),
714
+ refreshedAt: z.number().int().positive().optional(),
715
+ revokedAt: z.number().int().positive().optional()
716
+ });
717
+ function parsePluginDebugSessionSource(source) {
718
+ const parts = source.split(":");
719
+ if (parts.length !== 3 || parts[0] !== PLUGIN_DEBUG_SOURCE_KIND || parts[1] !== PLUGIN_DEBUG_SESSION_SOURCE_TMB_ID_KEY || !parts[2]) return null;
720
+ return { tmbId: parts[2] };
721
+ }
722
+
689
723
  //#endregion
690
724
  //#region src/debug/discover.ts
691
725
  const IGNORED_DIRS = new Set([
@@ -997,7 +1031,11 @@ const RESOLVE_EXTENSIONS = [
997
1031
  ".cjs",
998
1032
  ".json"
999
1033
  ];
1034
+ const TRANSFORM_PROBE_SOURCE = "const __fastgptPluginTransformProbe__: number = 1;";
1000
1035
  let hooksRegistered = false;
1036
+ let transformModeSupported;
1037
+ let cachedTypeScriptCompiler;
1038
+ const localRequire = createRequire(import.meta.url);
1001
1039
  function ensureDebugImportHooks() {
1002
1040
  if (hooksRegistered) return;
1003
1041
  hooksRegistered = true;
@@ -1021,15 +1059,90 @@ function ensureDebugImportHooks() {
1021
1059
  if (SOURCE_EXTENSIONS.includes(ext)) return {
1022
1060
  format: "module",
1023
1061
  shortCircuit: true,
1024
- source: stripTypeScriptTypes(fs$1.readFileSync(fileURLToPath(parsed), "utf-8"), {
1025
- mode: "transform",
1026
- sourceMap: false
1062
+ source: compileTypeScriptModuleSource(fs$1.readFileSync(fileURLToPath(parsed), "utf-8"), {
1063
+ filePath: fileURLToPath(parsed),
1064
+ workspaceRoot
1027
1065
  })
1028
1066
  };
1029
1067
  return nextLoad(url, context);
1030
1068
  }
1031
1069
  });
1032
1070
  }
1071
+ function compileTypeScriptModuleSource(sourceText, { filePath, workspaceRoot, stripTypeScriptTypesFn = stripTypeScriptTypes, typeScriptCompiler }) {
1072
+ if (supportsTransformMode(stripTypeScriptTypesFn)) try {
1073
+ return stripTypeScriptTypesFn(sourceText, {
1074
+ mode: "transform",
1075
+ sourceMap: false
1076
+ });
1077
+ } catch {}
1078
+ return transpileTypeScriptModuleSource(sourceText, {
1079
+ filePath,
1080
+ workspaceRoot,
1081
+ typeScriptCompiler
1082
+ });
1083
+ }
1084
+ function supportsTransformMode(stripTypeScriptTypesFn) {
1085
+ if (stripTypeScriptTypesFn === stripTypeScriptTypes) {
1086
+ if (transformModeSupported !== void 0) return transformModeSupported;
1087
+ transformModeSupported = canTransformTypeScript(stripTypeScriptTypesFn);
1088
+ return transformModeSupported;
1089
+ }
1090
+ return canTransformTypeScript(stripTypeScriptTypesFn);
1091
+ }
1092
+ function canTransformTypeScript(stripTypeScriptTypesFn) {
1093
+ try {
1094
+ stripTypeScriptTypesFn(TRANSFORM_PROBE_SOURCE, {
1095
+ mode: "transform",
1096
+ sourceMap: false
1097
+ });
1098
+ return true;
1099
+ } catch {
1100
+ return false;
1101
+ }
1102
+ }
1103
+ function transpileTypeScriptModuleSource(sourceText, { filePath, workspaceRoot, typeScriptCompiler }) {
1104
+ const compiler = typeScriptCompiler ?? loadTypeScriptCompiler(filePath, workspaceRoot);
1105
+ const transpiled = compiler.transpileModule(sourceText, {
1106
+ fileName: filePath,
1107
+ reportDiagnostics: true,
1108
+ compilerOptions: {
1109
+ module: compiler.ModuleKind.ESNext,
1110
+ target: compiler.ScriptTarget.ES2022,
1111
+ moduleResolution: compiler.ModuleResolutionKind.Bundler,
1112
+ jsx: compiler.JsxEmit.ReactJSX,
1113
+ sourceMap: false,
1114
+ inlineSourceMap: false,
1115
+ inlineSources: false,
1116
+ isolatedModules: true,
1117
+ esModuleInterop: true,
1118
+ allowImportingTsExtensions: true,
1119
+ verbatimModuleSyntax: true
1120
+ }
1121
+ });
1122
+ const errors = transpiled.diagnostics?.filter((diagnostic) => diagnostic.category === compiler.DiagnosticCategory.Error) ?? [];
1123
+ if (errors.length > 0) {
1124
+ const formatted = errors.map((diagnostic) => formatDiagnosticMessage(compiler, diagnostic)).join("\n");
1125
+ throw new Error(`Failed to transpile TypeScript debug module: ${filePath}\n${formatted}`);
1126
+ }
1127
+ return transpiled.outputText;
1128
+ }
1129
+ function loadTypeScriptCompiler(filePath, workspaceRoot) {
1130
+ if (cachedTypeScriptCompiler) return cachedTypeScriptCompiler;
1131
+ const resolvePaths = [
1132
+ path.dirname(filePath),
1133
+ process.cwd(),
1134
+ workspaceRoot,
1135
+ path.dirname(fileURLToPath(import.meta.url))
1136
+ ].filter((value) => Boolean(value));
1137
+ cachedTypeScriptCompiler = localRequire(localRequire.resolve("typescript", { paths: resolvePaths }));
1138
+ return cachedTypeScriptCompiler;
1139
+ }
1140
+ function formatDiagnosticMessage(compiler, diagnostic) {
1141
+ const message = compiler.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
1142
+ if (!diagnostic.file || diagnostic.start === void 0) return message;
1143
+ const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
1144
+ return `${line + 1}:${character + 1} ${message}`;
1145
+ }
1033
1146
  function resolveSpecifier({ specifier, parentURL, workspaceRoot }) {
1034
1147
  if (specifier.startsWith("node:")) return null;
1035
1148
  if (specifier.startsWith("file:")) return toFileURL(resolveExistingPath(fileURLToPath(new URL(specifier))));
@@ -1191,9 +1304,188 @@ const InvokeMethodEnumSchema = z.enum([
1191
1304
  ]);
1192
1305
  const InvokeMethodEnum = InvokeMethodEnumSchema.enum;
1193
1306
 
1307
+ //#endregion
1308
+ //#region ../../packages/domain/src/value-objects/error.vo.ts
1309
+ const DEFAULT_INTERNAL_REASON = {
1310
+ en: "Internal Server Error",
1311
+ "zh-CN": "服务器内部错误"
1312
+ };
1313
+ var RegisteredError = class extends Error {
1314
+ code;
1315
+ reason;
1316
+ httpStatus;
1317
+ telemetryKind;
1318
+ visibility;
1319
+ severity;
1320
+ data;
1321
+ constructor(definition, options = {}) {
1322
+ super(options.message ?? definition.message, { cause: normalizeErrorCause(options.cause) });
1323
+ this.name = "RegisteredError";
1324
+ this.code = definition.code;
1325
+ this.reason = options.reason ?? definition.reason;
1326
+ this.httpStatus = definition.httpStatus ?? 500;
1327
+ this.telemetryKind = definition.telemetryKind;
1328
+ this.visibility = definition.visibility ?? "public";
1329
+ this.severity = definition.severity ?? "expected";
1330
+ this.data = options.data;
1331
+ }
1332
+ };
1333
+ function createReasonError(reason, options = {}) {
1334
+ const cause = normalizeErrorCause(options.cause);
1335
+ const error = cause === void 0 ? new Error(reason.en) : new Error(reason.en, { cause });
1336
+ return Object.assign(error, { reason });
1337
+ }
1338
+ function normalizeToError(error, fallbackMessage = "Unknown error") {
1339
+ if (error instanceof Error) return error;
1340
+ if (typeof error === "string") return new Error(error);
1341
+ if (isI18nString(error)) return createReasonError(error);
1342
+ if (isResultFailureLike(error)) {
1343
+ const reason = isI18nString(error.reason) ? error.reason : void 0;
1344
+ const cause = normalizeToError(error.error, reason?.en ?? fallbackMessage);
1345
+ if (reason && isSameI18nString(reason, getErrorReason(cause))) return cause;
1346
+ return reason ? createReasonError(reason, { cause }) : new Error(fallbackMessage, { cause });
1347
+ }
1348
+ return new Error(getUnknownErrorMessage(error, fallbackMessage), { cause: error });
1349
+ }
1350
+ function getErrorReason(error, fallback) {
1351
+ if (error instanceof RegisteredError) return error.visibility === "internal" ? DEFAULT_INTERNAL_REASON : error.reason;
1352
+ if (isI18nString(error)) return error;
1353
+ const record = isRecord(error) ? error : void 0;
1354
+ if (record && isI18nString(record.reason)) return record.reason;
1355
+ if (record && isI18nString(record.error)) return record.error;
1356
+ if (fallback) return fallback;
1357
+ if (error instanceof Error) return {
1358
+ en: error.message,
1359
+ "zh-CN": error.message
1360
+ };
1361
+ if (typeof error === "string") return {
1362
+ en: error,
1363
+ "zh-CN": error
1364
+ };
1365
+ return DEFAULT_INTERNAL_REASON;
1366
+ }
1367
+ function serializeError(error, options) {
1368
+ return serializeErrorInner(error, {
1369
+ includeStack: options?.includeStack ?? false,
1370
+ depth: 0,
1371
+ seen: /* @__PURE__ */ new WeakSet()
1372
+ });
1373
+ }
1374
+ function normalizeErrorCause(cause) {
1375
+ if (cause === void 0 || cause instanceof Error) return cause;
1376
+ if (typeof cause === "string") return new Error(cause);
1377
+ if (isI18nString(cause)) return createReasonError(cause);
1378
+ if (isResultFailureLike(cause)) return normalizeToError(cause);
1379
+ return cause;
1380
+ }
1381
+ function serializeErrorInner(error, state) {
1382
+ if (state.depth > 5) return {
1383
+ name: "Error",
1384
+ message: "Error cause depth exceeded"
1385
+ };
1386
+ if (error instanceof RegisteredError) return {
1387
+ name: error.name,
1388
+ code: error.code,
1389
+ message: error.message,
1390
+ reason: error.reason,
1391
+ httpStatus: error.httpStatus,
1392
+ telemetryKind: error.telemetryKind,
1393
+ visibility: error.visibility,
1394
+ data: error.data,
1395
+ ...state.includeStack && error.stack !== void 0 ? { stack: error.stack } : {},
1396
+ ...error.cause !== void 0 ? { cause: serializeErrorInner(error.cause, nextSerializeState(state, error)) } : {}
1397
+ };
1398
+ if (error instanceof Error) {
1399
+ const reason = getI18nStringField(error, "reason");
1400
+ return {
1401
+ name: error.name,
1402
+ code: getStringField(error, "code"),
1403
+ message: error.message,
1404
+ ...reason !== void 0 ? { reason } : {},
1405
+ data: getUnknownField(error, "data"),
1406
+ ...state.includeStack && error.stack !== void 0 ? { stack: error.stack } : {},
1407
+ ...error.cause !== void 0 ? { cause: serializeErrorInner(error.cause, nextSerializeState(state, error)) } : {}
1408
+ };
1409
+ }
1410
+ if (typeof error === "string") return {
1411
+ name: "Error",
1412
+ message: error
1413
+ };
1414
+ return {
1415
+ name: "Error",
1416
+ message: getUnknownErrorMessage(error, "Unknown error")
1417
+ };
1418
+ }
1419
+ function nextSerializeState(state, error) {
1420
+ if (state.seen.has(error)) return {
1421
+ ...state,
1422
+ depth: 6
1423
+ };
1424
+ state.seen.add(error);
1425
+ return {
1426
+ ...state,
1427
+ depth: state.depth + 1
1428
+ };
1429
+ }
1430
+ function getUnknownErrorMessage(error, fallback) {
1431
+ if (error === null || error === void 0) return fallback;
1432
+ if (isRecord(error)) {
1433
+ const message = error.message ?? error.msg;
1434
+ if (typeof message === "string" && message.length > 0) return message;
1435
+ }
1436
+ return String(error);
1437
+ }
1438
+ function getStringField(error, field) {
1439
+ const value = error[field];
1440
+ return typeof value === "string" ? value : void 0;
1441
+ }
1442
+ function getI18nStringField(error, field) {
1443
+ const value = error[field];
1444
+ return isI18nString(value) ? value : void 0;
1445
+ }
1446
+ function getUnknownField(error, field) {
1447
+ return error[field];
1448
+ }
1449
+ function isI18nString(value) {
1450
+ return isRecord(value) && typeof value.en === "string" && (value["zh-CN"] === void 0 || typeof value["zh-CN"] === "string") && (value["zh-Hant"] === void 0 || typeof value["zh-Hant"] === "string");
1451
+ }
1452
+ function isSameI18nString(left, right) {
1453
+ return left.en === right.en && left["zh-CN"] === right["zh-CN"] && left["zh-Hant"] === right["zh-Hant"];
1454
+ }
1455
+ function isRecord(value) {
1456
+ return Boolean(value && typeof value === "object");
1457
+ }
1458
+ function isResultFailureLike(value) {
1459
+ return isRecord(value) && "error" in value && ("reason" in value || isRecord(value.error));
1460
+ }
1461
+
1194
1462
  //#endregion
1195
1463
  //#region ../../packages/domain/src/value-objects/result.vo.ts
1196
1464
  const successResult = (data) => [data, null];
1465
+ function failureResult(reasonOrFailure, error) {
1466
+ if (isResultFailure(reasonOrFailure)) return [null, toResultFailure(reasonOrFailure.reason, reasonOrFailure.error, reasonOrFailure)];
1467
+ if (reasonOrFailure instanceof Error) return [null, toResultFailure(getErrorReason(reasonOrFailure), reasonOrFailure)];
1468
+ return [null, toResultFailure(reasonOrFailure, error === void 0 ? createReasonError(reasonOrFailure) : createLegacyFailureError(reasonOrFailure, error))];
1469
+ }
1470
+ function isResultFailure(value) {
1471
+ return Boolean(value && typeof value === "object" && "reason" in value && "error" in value);
1472
+ }
1473
+ function createLegacyFailureError(reason, cause) {
1474
+ return createReasonError(reason, { cause: normalizeToError(cause, reason.en) });
1475
+ }
1476
+ function toResultFailure(reason, error, existing) {
1477
+ const serialized = serializeError(error);
1478
+ const code = existing?.code ?? serialized.code;
1479
+ const message = existing?.message ?? serialized.message;
1480
+ const data = existing && "data" in existing ? existing.data : serialized.data;
1481
+ return {
1482
+ reason,
1483
+ error,
1484
+ ...code !== void 0 ? { code } : {},
1485
+ ...message !== void 0 ? { message } : {},
1486
+ ...data !== void 0 ? { data } : {}
1487
+ };
1488
+ }
1197
1489
 
1198
1490
  //#endregion
1199
1491
  //#region ../../packages/domain/src/value-objects/stream.vo.ts
@@ -1690,7 +1982,7 @@ function createIncomingStream({ source, streamName, streamId, traceId, meta }) {
1690
1982
  };
1691
1983
  }
1692
1984
  function toStreamData(source) {
1693
- if (isStreamDataLike$1(source)) return source;
1985
+ if (isStreamDataLike$2(source)) return source;
1694
1986
  const output = StreamData.create();
1695
1987
  (async () => {
1696
1988
  try {
@@ -1702,7 +1994,7 @@ function toStreamData(source) {
1702
1994
  })();
1703
1995
  return output;
1704
1996
  }
1705
- function isStreamDataLike$1(source) {
1997
+ function isStreamDataLike$2(source) {
1706
1998
  return source instanceof StreamData || typeof source === "object" && source !== null && typeof source.values === "function" && typeof source.consume === "function";
1707
1999
  }
1708
2000
 
@@ -1737,7 +2029,7 @@ async function loadDebugSession({ entryDir, uploadDir }) {
1737
2029
  else process.env.RUNTIME_MODE = previousRuntimeMode;
1738
2030
  }
1739
2031
  }
1740
- async function runDebugTool({ runtime, snapshot, toolId, input, secrets, systemVar }) {
2032
+ async function runDebugTool({ runtime, snapshot, toolId, input, secrets, systemVar, traceId }) {
1741
2033
  const targetTool = pickTargetTool(snapshot, toolId);
1742
2034
  const mergedSystemVar = createDebugSystemVar(snapshot, targetTool.id, systemVar);
1743
2035
  const response = await runtime.invokePlugin("run", {
@@ -1745,7 +2037,7 @@ async function runDebugTool({ runtime, snapshot, toolId, input, secrets, systemV
1745
2037
  systemVar: mergedSystemVar,
1746
2038
  ...snapshot.isToolSet ? { childId: targetTool.id } : {},
1747
2039
  ...secrets ? { secrets } : {}
1748
- }, { traceId: randomUUID() });
2040
+ }, { traceId: traceId ?? randomUUID() });
1749
2041
  if (!response.output) throw new Error("调试运行未返回输出流。");
1750
2042
  const streamMessages = [];
1751
2043
  let finalResponse;
@@ -1805,7 +2097,7 @@ function createDebugSnapshot(tool, location) {
1805
2097
  author: getOptionalString(userManifest.author),
1806
2098
  tags: toStringArray(userManifest.tags),
1807
2099
  permissions: toStringArray(userManifest.permission),
1808
- secretSchema: ensurePlainObject(secretSchema),
2100
+ secretSchema: ensurePlainObject$1(secretSchema),
1809
2101
  isToolSet: children.length > 0,
1810
2102
  tools
1811
2103
  };
@@ -1818,8 +2110,8 @@ function createSingleToolSnapshot(tool, userManifest) {
1818
2110
  name: pickLocalizedText(userManifest.name),
1819
2111
  description: pickLocalizedText(userManifest.description),
1820
2112
  toolDescription: pickToolDescription(getOptionalString(userManifest.toolDescription), userManifest.description),
1821
- inputSchema: ensurePlainObject(z.toJSONSchema(handler.inputSchema)),
1822
- outputSchema: ensurePlainObject(z.toJSONSchema(handler.outputSchema))
2113
+ inputSchema: ensurePlainObject$1(z.toJSONSchema(handler.inputSchema)),
2114
+ outputSchema: ensurePlainObject$1(z.toJSONSchema(handler.outputSchema))
1823
2115
  };
1824
2116
  }
1825
2117
  function createChildToolSnapshot(tool, child) {
@@ -1830,8 +2122,8 @@ function createChildToolSnapshot(tool, child) {
1830
2122
  name: pickLocalizedText(child.name),
1831
2123
  description: pickLocalizedText(child.description),
1832
2124
  toolDescription: pickToolDescription(child.toolDescription, child.description),
1833
- inputSchema: ensurePlainObject(z.toJSONSchema(handler.inputSchema)),
1834
- outputSchema: ensurePlainObject(z.toJSONSchema(handler.outputSchema))
2125
+ inputSchema: ensurePlainObject$1(z.toJSONSchema(handler.inputSchema)),
2126
+ outputSchema: ensurePlainObject$1(z.toJSONSchema(handler.outputSchema))
1835
2127
  };
1836
2128
  }
1837
2129
  function pickTargetTool(snapshot, toolId) {
@@ -1843,10 +2135,10 @@ function pickTargetTool(snapshot, toolId) {
1843
2135
  }
1844
2136
  function createVirtualHostHandler(uploadDir) {
1845
2137
  return async ({ method, args, input }) => {
1846
- const invokeArgs = ensurePlainObject(args);
2138
+ const invokeArgs = ensurePlainObject$1(args);
1847
2139
  switch (method) {
1848
2140
  case InvokeMethodEnum.uploadFile: {
1849
- const buffer = await readSourceToBuffer(input);
2141
+ const buffer = await readSourceToBuffer$1(input);
1850
2142
  const fileName = sanitizeFileName(typeof invokeArgs.fileName === "string" && invokeArgs.fileName.trim().length > 0 ? invokeArgs.fileName : "debug-upload.bin");
1851
2143
  const contentType = typeof invokeArgs.contentType === "string" && invokeArgs.contentType.trim().length > 0 ? invokeArgs.contentType : "application/octet-stream";
1852
2144
  const saveDir = path.resolve(uploadDir);
@@ -1866,17 +2158,17 @@ function createVirtualHostHandler(uploadDir) {
1866
2158
  }
1867
2159
  };
1868
2160
  }
1869
- async function readSourceToBuffer(source) {
2161
+ async function readSourceToBuffer$1(source) {
1870
2162
  if (!source) return Buffer.alloc(0);
1871
2163
  const chunks = [];
1872
- const iterable = isStreamDataLike(source) ? source.values() : source;
1873
- for await (const chunk of iterable) chunks.push(toBuffer(chunk));
2164
+ const iterable = isStreamDataLike$1(source) ? source.values() : source;
2165
+ for await (const chunk of iterable) chunks.push(toBuffer$1(chunk));
1874
2166
  return Buffer.concat(chunks);
1875
2167
  }
1876
- function isStreamDataLike(source) {
2168
+ function isStreamDataLike$1(source) {
1877
2169
  return source instanceof StreamData || typeof source === "object" && source !== null && typeof source.values === "function" && typeof source.consume === "function";
1878
2170
  }
1879
- function toBuffer(value) {
2171
+ function toBuffer$1(value) {
1880
2172
  if (Buffer.isBuffer(value)) return value;
1881
2173
  if (value instanceof Uint8Array) return Buffer.from(value);
1882
2174
  if (typeof value === "string") return Buffer.from(value);
@@ -1914,7 +2206,7 @@ function toStringArray(value) {
1914
2206
  const items = value.filter((item) => typeof item === "string");
1915
2207
  return items.length > 0 ? items : void 0;
1916
2208
  }
1917
- function ensurePlainObject(value) {
2209
+ function ensurePlainObject$1(value) {
1918
2210
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1919
2211
  }
1920
2212
  function sanitizeFileName(fileName) {
@@ -1945,6 +2237,7 @@ async function assertPathExists(targetPath, message) {
1945
2237
 
1946
2238
  //#endregion
1947
2239
  //#region src/debug/gateway.ts
2240
+ const GATEWAY_DUPLICATE_CONNECTION_CODE = "connection_gateway.session_already_bound";
1948
2241
  async function connectDebugGateway({ targets, options, onLog }) {
1949
2242
  if (targets.length === 0) throw new Error("Gateway debug targets cannot be empty");
1950
2243
  if (options.reconnect) return connectReconnectingDebugGateway({
@@ -1963,19 +2256,32 @@ async function connectReconnectingDebugGateway({ targets, options, onLog }) {
1963
2256
  let current = null;
1964
2257
  let currentSession = null;
1965
2258
  let latestTargets = targets;
2259
+ let hasAttemptedConnection = false;
2260
+ let currentOptions = options;
2261
+ const resolveReconnectOptions = options.resolveReconnectOptions;
1966
2262
  const intervalMs = Math.max(100, options.reconnectIntervalMs ?? 2e3);
1967
2263
  const closedPromise = (async () => {
1968
2264
  while (!closed) {
1969
2265
  try {
2266
+ const attemptOptions = hasAttemptedConnection && resolveReconnectOptions ? await resolveReconnectOptions() : currentOptions;
2267
+ hasAttemptedConnection = true;
2268
+ currentOptions = {
2269
+ ...attemptOptions,
2270
+ resolveReconnectOptions
2271
+ };
1970
2272
  current = await connectSingleDebugGateway({
1971
2273
  targets: latestTargets,
1972
- options,
2274
+ options: currentOptions,
1973
2275
  onLog
1974
2276
  });
1975
2277
  currentSession = current.session;
1976
2278
  await current.closed;
1977
2279
  } catch (error) {
1978
2280
  if (closed) return;
2281
+ if (isDuplicateConnectionError(error)) {
2282
+ closed = true;
2283
+ throw error;
2284
+ }
1979
2285
  onLog?.(`Connection Gateway 调试通道断开: ${formatErrorMessage(error)}`);
1980
2286
  } finally {
1981
2287
  current = null;
@@ -2030,7 +2336,7 @@ async function connectSingleDebugGateway({ targets, options, onLog }) {
2030
2336
  }
2031
2337
  if (message.type === "heartbeat") return;
2032
2338
  if (message.type === "error") {
2033
- const error = new Error(message.message);
2339
+ const error = createGatewayError(message.code, message.message);
2034
2340
  if (!session) boundReject(error);
2035
2341
  onLog?.(`Connection Gateway 错误: ${message.code} ${message.message}`);
2036
2342
  return;
@@ -2088,6 +2394,7 @@ async function handleGatewayEnvelope({ socket, session, targetsByPluginId, envel
2088
2394
  if (!pluginId) throw new Error("Gateway debug request missing pluginId");
2089
2395
  const target = targetsByPluginId.get(pluginId);
2090
2396
  if (!target) throw new Error(`Gateway debug target not found: ${source} ${pluginId}`);
2397
+ const traceId = envelope.traceId ?? requiredRequestId(envelope);
2091
2398
  onLog?.(`收到远程调试请求: ${source} ${pluginId} ${envelope.requestId ?? "-"}`);
2092
2399
  await sendEnvelope(socket, {
2093
2400
  protocol: "connection-gateway.v1",
@@ -2101,15 +2408,21 @@ async function handleGatewayEnvelope({ socket, session, targetsByPluginId, envel
2101
2408
  createdAt: Date.now(),
2102
2409
  payload: { kind: "plugin-debug.accepted" }
2103
2410
  });
2104
- const result = await runDebugTool({
2105
- runtime: target.runtime,
2106
- snapshot: target.snapshot,
2107
- toolId: request.payload.childId,
2108
- input: request.payload.input,
2109
- secrets: request.payload.secrets,
2110
- systemVar: request.payload.systemVar
2111
- });
2112
- for (const message of result.streamMessages) await sendStreamChunk(socket, session, envelope, message);
2411
+ target.invokeBridge?.bind(traceId, { invokeToken: request.payload.systemVar.invokeToken });
2412
+ try {
2413
+ const result = await runDebugTool({
2414
+ runtime: target.runtime,
2415
+ snapshot: target.snapshot,
2416
+ toolId: request.payload.childId,
2417
+ input: request.payload.input,
2418
+ secrets: request.payload.secrets,
2419
+ systemVar: request.payload.systemVar,
2420
+ traceId
2421
+ });
2422
+ for (const message of result.streamMessages) await sendStreamChunk(socket, session, envelope, message);
2423
+ } finally {
2424
+ target.invokeBridge?.release(traceId);
2425
+ }
2113
2426
  await sendEnvelope(socket, {
2114
2427
  protocol: "connection-gateway.v1",
2115
2428
  sessionId: session.id,
@@ -2212,11 +2525,244 @@ function delay(ms) {
2212
2525
  function formatErrorMessage(error) {
2213
2526
  return error instanceof Error ? error.message : String(error);
2214
2527
  }
2528
+ function createGatewayError(code, message) {
2529
+ const error = new Error(message);
2530
+ Object.assign(error, { code });
2531
+ return error;
2532
+ }
2533
+ function isDuplicateConnectionError(error) {
2534
+ return error instanceof Error && ("code" in error ? error.code === GATEWAY_DUPLICATE_CONNECTION_CODE : false);
2535
+ }
2215
2536
  function parseWsServerMessage(data) {
2216
2537
  const text = typeof data === "string" ? data : data instanceof ArrayBuffer ? Buffer.from(data).toString("utf8") : ArrayBuffer.isView(data) ? Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString("utf8") : String(data);
2217
2538
  return ConnectionGatewayWsServerMessageSchema.parse(JSON.parse(text));
2218
2539
  }
2219
2540
 
2541
+ //#endregion
2542
+ //#region ../../packages/infrastructure/src/plugin/invoke/invoke.impl.ts
2543
+ var InvokeManager = class {
2544
+ constructor(deps) {
2545
+ this.deps = deps;
2546
+ }
2547
+ async getWecomCorpToken() {
2548
+ const [result, responseErr] = await this.requestFastGPT({
2549
+ path: "/api/proApi/support/wecom/getCorpToken",
2550
+ init: { method: "GET" },
2551
+ failureReason: {
2552
+ en: "Get wecom corp token failed",
2553
+ "zh-CN": "获取企业微信企业令牌失败"
2554
+ },
2555
+ invalidJsonReason: {
2556
+ en: "Invalid wecom corp token output",
2557
+ "zh-CN": "获取企业微信企业令牌返回数据格式错误"
2558
+ }
2559
+ });
2560
+ if (responseErr) return failureResult(responseErr);
2561
+ return successResult(result);
2562
+ }
2563
+ async userInfo() {
2564
+ const [result, responseErr] = await this.requestFastGPT({
2565
+ path: "/api/invoke/userInfo",
2566
+ init: { method: "GET" },
2567
+ failureReason: {
2568
+ en: "Invoke user info failed",
2569
+ "zh-CN": "反向调用用户信息失败"
2570
+ },
2571
+ invalidJsonReason: {
2572
+ en: "Invalid invoke user info output",
2573
+ "zh-CN": "反向调用用户信息返回数据格式错误"
2574
+ }
2575
+ });
2576
+ if (responseErr) return failureResult(responseErr);
2577
+ try {
2578
+ return successResult(InvokeUserInfoOutputSchema.parse(result));
2579
+ } catch (error) {
2580
+ return failureResult({
2581
+ en: "Invalid invoke user info output",
2582
+ "zh-CN": "反向调用用户信息返回数据格式错误"
2583
+ }, error);
2584
+ }
2585
+ }
2586
+ async uploadFile(input) {
2587
+ let request;
2588
+ try {
2589
+ request = await this.buildUploadFileRequest(input);
2590
+ } catch (error) {
2591
+ return failureResult({
2592
+ en: "Invoke upload file failed",
2593
+ "zh-CN": "反向调用上传文件失败"
2594
+ }, error);
2595
+ }
2596
+ const [result, responseErr] = await this.requestFastGPT({
2597
+ path: "/api/invoke/fileUpload",
2598
+ init: {
2599
+ method: "POST",
2600
+ body: request.formData
2601
+ },
2602
+ failureReason: {
2603
+ en: "Invoke upload file failed",
2604
+ "zh-CN": "反向调用上传文件失败"
2605
+ },
2606
+ invalidJsonReason: {
2607
+ en: "Invalid invoke upload file output",
2608
+ "zh-CN": "反向调用上传文件返回数据格式错误"
2609
+ }
2610
+ });
2611
+ if (responseErr) return failureResult(responseErr);
2612
+ try {
2613
+ return successResult(InvokeUploadFileOutputSchema.parse({ accessURL: result.url }));
2614
+ } catch (error) {
2615
+ return failureResult({
2616
+ en: "Invalid invoke upload file output",
2617
+ "zh-CN": "反向调用上传文件返回数据格式错误"
2618
+ }, error);
2619
+ }
2620
+ }
2621
+ buildUrl(path) {
2622
+ return new URL(path, this.deps.fastgptBaseUrl).toString();
2623
+ }
2624
+ async buildUploadFileRequest(input) {
2625
+ const formData = new FormData();
2626
+ const fileBuffer = await this.toBuffer(input.file);
2627
+ const fileName = input.fileName ?? randomUUID();
2628
+ const contentType = input.contentType ?? "application/octet-stream";
2629
+ const blobPart = new Uint8Array(fileBuffer.byteLength);
2630
+ blobPart.set(fileBuffer);
2631
+ formData.append("file", new Blob([blobPart], { type: contentType }), fileName);
2632
+ if (input.fileName) formData.append("fileName", input.fileName);
2633
+ if (input.contentType) formData.append("contentType", input.contentType);
2634
+ return {
2635
+ formData,
2636
+ meta: {
2637
+ fileName,
2638
+ contentType
2639
+ }
2640
+ };
2641
+ }
2642
+ async toBuffer(file) {
2643
+ if (Buffer.isBuffer(file)) return file;
2644
+ if (file instanceof Readable$1) {
2645
+ const chunks = [];
2646
+ for await (const chunk of file) chunks.push(this.toBufferChunk(chunk));
2647
+ return Buffer.concat(chunks);
2648
+ }
2649
+ return Buffer.from(file);
2650
+ }
2651
+ toBufferChunk(chunk) {
2652
+ if (Buffer.isBuffer(chunk)) return chunk;
2653
+ if (chunk instanceof Uint8Array) return Buffer.from(chunk);
2654
+ if (chunk instanceof ArrayBuffer) return Buffer.from(chunk);
2655
+ if (ArrayBuffer.isView(chunk)) return Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);
2656
+ if (typeof chunk === "string") return Buffer.from(chunk);
2657
+ if (typeof chunk === "number") return Buffer.from([chunk]);
2658
+ if (this.isSerializedBufferChunk(chunk)) return Buffer.from(chunk.data);
2659
+ throw new TypeError(`Unsupported upload file stream chunk type: ${Object.prototype.toString.call(chunk)}`);
2660
+ }
2661
+ isSerializedBufferChunk(value) {
2662
+ return this.isRecord(value) && value.type === "Buffer" && Array.isArray(value.data) && value.data.every((item) => Number.isInteger(item) && Number.isSafeInteger(item) && item >= 0 && item <= 255);
2663
+ }
2664
+ async requestFastGPT({ path, init, failureReason, invalidJsonReason }) {
2665
+ const headers = new Headers(init?.headers);
2666
+ headers.set("Authorization", `Bearer ${this.deps.token}`);
2667
+ let response;
2668
+ try {
2669
+ response = await fetch(this.buildUrl(path), {
2670
+ ...init,
2671
+ headers
2672
+ });
2673
+ } catch (error) {
2674
+ return failureResult(failureReason, error);
2675
+ }
2676
+ if (!response.ok) return failureResult(failureReason, await this.readErrorResponse(response));
2677
+ let payload;
2678
+ try {
2679
+ payload = await response.json();
2680
+ } catch (error) {
2681
+ return failureResult(invalidJsonReason, error);
2682
+ }
2683
+ return this.parseResponse(payload, failureReason);
2684
+ }
2685
+ parseResponse(payload, fallbackReason) {
2686
+ if (!this.isRecord(payload)) return successResult(payload);
2687
+ if (payload.success === false) return failureResult({
2688
+ en: this.getResponseMessage(payload) ?? fallbackReason.en,
2689
+ "zh-CN": this.getResponseMessage(payload) ?? fallbackReason["zh-CN"]
2690
+ }, payload);
2691
+ if (typeof payload.code === "number" && payload.code >= 400) return failureResult({
2692
+ en: this.getResponseMessage(payload) ?? fallbackReason.en,
2693
+ "zh-CN": this.getResponseMessage(payload) ?? fallbackReason["zh-CN"]
2694
+ }, payload);
2695
+ return successResult("data" in payload ? payload.data : payload);
2696
+ }
2697
+ async readErrorResponse(response) {
2698
+ return {
2699
+ status: response.status,
2700
+ statusText: response.statusText,
2701
+ body: (await response.text().catch(() => "")).slice(0, 1e3)
2702
+ };
2703
+ }
2704
+ getResponseMessage(payload) {
2705
+ return typeof payload.message === "string" ? payload.message : void 0;
2706
+ }
2707
+ isRecord(value) {
2708
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
2709
+ }
2710
+ };
2711
+
2712
+ //#endregion
2713
+ //#region src/debug/remote-invoke.ts
2714
+ var RemoteDebugInvokeBridge = class {
2715
+ sessions = /* @__PURE__ */ new Map();
2716
+ constructor(fastgptBaseUrl) {
2717
+ this.fastgptBaseUrl = fastgptBaseUrl;
2718
+ }
2719
+ attach(runtime) {
2720
+ runtime.setHostRequestHandler((request) => this.handleHostRequest(request));
2721
+ }
2722
+ bind(traceId, input) {
2723
+ this.sessions.set(traceId, { invokeToken: input.invokeToken });
2724
+ }
2725
+ release(traceId) {
2726
+ this.sessions.delete(traceId);
2727
+ }
2728
+ async handleHostRequest({ method, args, input, traceId }) {
2729
+ if (!traceId) throw new Error("远程调试反向调用缺少 traceId。");
2730
+ const session = this.sessions.get(traceId);
2731
+ if (!session) throw new Error("远程调试反向调用会话不存在或已结束。");
2732
+ const invoke = new InvokeManager({
2733
+ token: session.invokeToken,
2734
+ fastgptBaseUrl: this.fastgptBaseUrl
2735
+ });
2736
+ switch (method) {
2737
+ case InvokeMethodEnum.uploadFile: return invoke.uploadFile({
2738
+ ...ensurePlainObject(args),
2739
+ file: input ? await readSourceToBuffer(input) : Buffer.alloc(0)
2740
+ });
2741
+ case InvokeMethodEnum.userInfo: return invoke.userInfo();
2742
+ case InvokeMethodEnum.wecomCorpToken: return invoke.getWecomCorpToken();
2743
+ default: throw new Error(`远程调试宿主暂不支持反向调用: ${String(method)}`);
2744
+ }
2745
+ }
2746
+ };
2747
+ function ensurePlainObject(value) {
2748
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
2749
+ }
2750
+ async function readSourceToBuffer(source) {
2751
+ const chunks = [];
2752
+ const iterable = isStreamDataLike(source) ? source.values() : source;
2753
+ for await (const chunk of iterable) chunks.push(toBuffer(chunk));
2754
+ return Buffer.concat(chunks);
2755
+ }
2756
+ function isStreamDataLike(source) {
2757
+ return source instanceof StreamData || typeof source === "object" && source !== null && typeof source.values === "function" && typeof source.consume === "function";
2758
+ }
2759
+ function toBuffer(value) {
2760
+ if (Buffer.isBuffer(value)) return value;
2761
+ if (value instanceof Uint8Array) return Buffer.from(value);
2762
+ if (typeof value === "string") return Buffer.from(value);
2763
+ return Buffer.from(JSON.stringify(value));
2764
+ }
2765
+
2220
2766
  //#endregion
2221
2767
  //#region src/debug/remote-session.ts
2222
2768
  async function runRemoteDebugSession({ entriesInput, options, commandName, migrationHint }) {
@@ -2247,6 +2793,7 @@ async function runRemoteDebugSessionOnce({ entriesInput, options, commandName })
2247
2793
  commandName,
2248
2794
  targets
2249
2795
  });
2796
+ attachRemoteInvokeBridges(targets, gatewayOptions.fastgptBaseUrl);
2250
2797
  const reporter = createRemoteDebugReporter({
2251
2798
  commandName,
2252
2799
  options,
@@ -2284,6 +2831,7 @@ async function runWatchRemoteDebugSession({ entriesInput, options, commandName }
2284
2831
  commandName,
2285
2832
  targets: initialTargets
2286
2833
  });
2834
+ attachRemoteInvokeBridges(initialTargets, gatewayOptions.fastgptBaseUrl);
2287
2835
  const reporter = createRemoteDebugReporter({
2288
2836
  commandName,
2289
2837
  options,
@@ -2305,6 +2853,7 @@ async function runWatchRemoteDebugSession({ entriesInput, options, commandName }
2305
2853
  reloadPending = false;
2306
2854
  try {
2307
2855
  const nextTargets = await loadTargets(entries, options);
2856
+ attachRemoteInvokeBridges(nextTargets, gatewayOptions.fastgptBaseUrl);
2308
2857
  gateway?.updateTargets(nextTargets);
2309
2858
  reporter.log(`检测到插件文件变化,已热更新 ${nextTargets.length} 个本地插件。`);
2310
2859
  } catch (error) {
@@ -2369,6 +2918,13 @@ async function loadTargets(entries, options) {
2369
2918
  if (duplicatePluginIds.length > 0) throw new Error(`gateway pluginId 重复: ${duplicatePluginIds.join(", ")}`);
2370
2919
  return targets;
2371
2920
  }
2921
+ function attachRemoteInvokeBridges(targets, fastgptBaseUrl) {
2922
+ targets.forEach((target) => {
2923
+ const invokeBridge = new RemoteDebugInvokeBridge(fastgptBaseUrl);
2924
+ invokeBridge.attach(target.runtime);
2925
+ target.invokeBridge = invokeBridge;
2926
+ });
2927
+ }
2372
2928
  function watchDebugEntries(entries, onChange) {
2373
2929
  const watchers = entries.map((entry) => fsWatch(entry, {
2374
2930
  recursive: true,
@@ -2428,12 +2984,12 @@ async function promptConnectLink({ commandName, targets, defaultValue, errorMess
2428
2984
  "",
2429
2985
  ...errorMessage ? [`Last error ${errorMessage}`, ""] : [],
2430
2986
  "Paste the FastGPT connection key or connect link to start the WSS debug session.",
2987
+ ...defaultValue ? ["Press Enter to reuse the current value, or type a new one to replace it.", ""] : [],
2431
2988
  ""
2432
2989
  ].join("\n"));
2433
2990
  return (await input({
2434
2991
  message: "FastGPT connection key",
2435
2992
  default: defaultValue,
2436
- prefill: defaultValue ? "editable" : void 0,
2437
2993
  validate(value) {
2438
2994
  return value.trim().length > 0 || "请输入 FastGPT connection key";
2439
2995
  }
@@ -2778,9 +3334,11 @@ async function resolveConnectGatewayOptions(options) {
2778
3334
  connectToken: info.connectToken,
2779
3335
  userId: info.tmbId,
2780
3336
  source: info.source,
3337
+ fastgptBaseUrl: info.fastgptBaseUrl,
2781
3338
  tokenTtlMs: Math.max(1, info.expiresAt - Date.now()),
2782
3339
  reconnect: options.noReconnect ? false : options.reconnect ?? true,
2783
- reconnectIntervalMs: toPositiveInt(options.reconnectIntervalMs ?? process.env.CONNECTION_GATEWAY_RECONNECT_INTERVAL_MS ?? "2000", "reconnect-interval-ms")
3340
+ reconnectIntervalMs: toPositiveInt(options.reconnectIntervalMs ?? process.env.CONNECTION_GATEWAY_RECONNECT_INTERVAL_MS ?? "2000", "reconnect-interval-ms"),
3341
+ resolveReconnectOptions: () => resolveConnectGatewayOptions(options)
2784
3342
  };
2785
3343
  }
2786
3344
  function resolveUploadDir({ entryDir, index, total, uploadDir }) {
@@ -2793,10 +3351,12 @@ const ConnectInfoSchema = z.object({
2793
3351
  transport: z.literal("websocket"),
2794
3352
  source: z.string().min(1),
2795
3353
  connectToken: z.string().min(1),
3354
+ fastgptBaseUrl: z.string().min(1).optional(),
2796
3355
  expiresAt: z.number().int().positive()
2797
3356
  });
2798
3357
  async function exchangeConnectLink(connectInput) {
2799
- const response = isHttpUrl(connectInput) ? await fetch(connectInput) : await fetch(resolveConnectionKeyExchangeUrl(), {
3358
+ const exchangeUrl = isHttpUrl(connectInput) ? connectInput : resolveConnectionKeyExchangeUrl();
3359
+ const response = isHttpUrl(connectInput) ? await fetch(exchangeUrl) : await fetch(exchangeUrl, {
2800
3360
  method: "POST",
2801
3361
  headers: { "Content-Type": "application/json" },
2802
3362
  body: JSON.stringify({ connectionKey: connectInput })
@@ -2807,6 +3367,7 @@ async function exchangeConnectLink(connectInput) {
2807
3367
  const info = ConnectInfoSchema.parse(payload.data ?? payload);
2808
3368
  return {
2809
3369
  ...info,
3370
+ fastgptBaseUrl: info.fastgptBaseUrl ?? new URL(exchangeUrl).origin,
2810
3371
  tmbId: parseTmbIdFromDebugSource(info.source)
2811
3372
  };
2812
3373
  }
@@ -2826,11 +3387,9 @@ function resolveConnectionKeyExchangeUrl() {
2826
3387
  return new URL("/api/plugin/debug-sessions/connection-key:exchange", baseUrl).toString();
2827
3388
  }
2828
3389
  function parseTmbIdFromDebugSource(source) {
2829
- const parts = source.split(":");
2830
- const index = parts.indexOf("tmbId");
2831
- const tmbId = index >= 0 ? parts[index + 1] : void 0;
2832
- if (!tmbId) throw new Error(`debug source 缺少 tmbId: ${source}`);
2833
- return tmbId;
3390
+ const parsed = parsePluginDebugSessionSource(source);
3391
+ if (!parsed) throw new Error(`debug source 缺少 tmbId: ${source}`);
3392
+ return parsed.tmbId;
2834
3393
  }
2835
3394
  function normalizeGatewayWsUrl(gatewayUrl) {
2836
3395
  const parsed = new URL(gatewayUrl);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastgpt-plugin/cli",
3
- "version": "0.2.0-alpha.1",
3
+ "version": "0.2.0-alpha.2",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "publishConfig": {