@adep/cli 0.4.0 → 0.4.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/README.md CHANGED
@@ -126,7 +126,7 @@ adep --json whoami
126
126
  ```bash
127
127
  adep login # 打开浏览器;终端会同时打印验证码与链接
128
128
  adep login --no-browser # 不开浏览器:手工访问打印出来的链接(SSH / 无桌面)
129
- adep login -s <url> # 指定平台(缺省 ADEP_SERVER,再缺省 https://adep.jajabjbj.top)
129
+ adep login -s <url> # 指定平台(缺省 ADEP_SERVER,再缺省 https://adep.top)
130
130
  adep login -e <邮箱> -p <密码> # 邮箱密码直登,不发起浏览器流程(CI / 老平台)
131
131
  adep whoami # 查看当前身份(读凭据并向平台确认会话仍有效)
132
132
  adep logout # 通知平台注销并清除本地凭据
@@ -554,7 +554,7 @@ adep app build --offline # 消费服务端 .adepack:离线
554
554
 
555
555
  | 变量 | 说明 | 缺省 |
556
556
  | ----------------- | --------------------------------- | ----------------------------- |
557
- | `ADEP_SERVER` | 平台地址(CLI 请求的默认 server) | `https://adep.jajabjbj.top` |
557
+ | `ADEP_SERVER` | 平台地址(CLI 请求的默认 server) | `https://adep.top` |
558
558
  | `ADEP_HOME` | 凭据目录(测试隔离用) | `~/.adep` |
559
559
  | `ADEP_NO_BROWSER` | `1` = `adep login` 不打开浏览器 | 未设置(TTY 下自动打开) |
560
560
 
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ function resolvePaths(env = process.env) {
15
15
  return { home, credentialsFile: `${home}/credentials` };
16
16
  }
17
17
  function resolveServer(env = process.env) {
18
- return (env["ADEP_SERVER"] ?? "https://adep.jajabjbj.top").replace(/\/+$/, "");
18
+ return (env["ADEP_SERVER"] ?? "https://adep.top").replace(/\/+$/, "");
19
19
  }
20
20
  function resolveElectronMirror(env = process.env) {
21
21
  return env["ELECTRON_MIRROR"] ?? DEFAULT_ELECTRON_MIRROR;
@@ -23904,6 +23904,28 @@ function transpile(code) {
23904
23904
  function transformExports(code) {
23905
23905
  return transformModule(code);
23906
23906
  }
23907
+ function realmValue(value) {
23908
+ return { [REALM_VALUE_KEY]: value };
23909
+ }
23910
+ function isRealmValue(value) {
23911
+ return typeof value === "object" && value !== null && REALM_VALUE_KEY in value;
23912
+ }
23913
+ function createSandboxContext(sandboxGlobals) {
23914
+ const direct = {};
23915
+ const realmEntries = {};
23916
+ for (const [key, value] of Object.entries(sandboxGlobals)) {
23917
+ if (isRealmValue(value)) realmEntries[key] = value[REALM_VALUE_KEY];
23918
+ else direct[key] = value;
23919
+ }
23920
+ const context = vm.createContext(direct, { codeGeneration: { strings: false, wasm: false } });
23921
+ if (Object.keys(realmEntries).length > 0) {
23922
+ const literal2 = JSON.stringify(JSON.stringify(realmEntries));
23923
+ vm.runInContext(`Object.assign(globalThis, JSON.parse(${literal2}))`, context, {
23924
+ filename: "<sandbox-realm-globals>"
23925
+ });
23926
+ }
23927
+ return context;
23928
+ }
23907
23929
  function resolveModulePath(fromDir, request, files) {
23908
23930
  if (!request.startsWith("./") && !request.startsWith("../")) return null;
23909
23931
  const base = joinPosix(fromDir, request);
@@ -23977,12 +23999,13 @@ function loadModule(path, files, sandboxGlobals, cache, resolveBare, bareCache)
23977
23999
  } catch (error51) {
23978
24000
  throw syntaxErrorWithLocation(error51, path);
23979
24001
  }
24002
+ const context = createSandboxContext(sandboxGlobals);
23980
24003
  let wrapper;
23981
24004
  try {
23982
- wrapper = vm.runInNewContext(
24005
+ wrapper = vm.runInContext(
23983
24006
  `(function (module, exports, require) { ${transformed}
23984
24007
  })`,
23985
- vm.createContext({ ...sandboxGlobals }),
24008
+ context,
23986
24009
  { filename: path }
23987
24010
  );
23988
24011
  } catch (error51) {
@@ -24004,13 +24027,14 @@ function loadFunctionModule(files, entry, sandboxGlobals, resolveBare) {
24004
24027
  }
24005
24028
  return { handler };
24006
24029
  }
24007
- var bunTranspiler, SANDBOX_BUILTIN_REJECTED;
24030
+ var bunTranspiler, SANDBOX_BUILTIN_REJECTED, REALM_VALUE_KEY;
24008
24031
  var init_sandbox = __esm({
24009
24032
  "packages/runtime/src/functions/runtime/sandbox.ts"() {
24010
24033
  "use strict";
24011
24034
  init_exports();
24012
24035
  init_regexp();
24013
24036
  SANDBOX_BUILTIN_REJECTED = "FN_SANDBOX_BUILTIN_BLOCKED";
24037
+ REALM_VALUE_KEY = "__adepRealmValue";
24014
24038
  }
24015
24039
  });
24016
24040
 
@@ -24209,18 +24233,24 @@ function headersToRecord(headers) {
24209
24233
  }
24210
24234
  return out;
24211
24235
  }
24212
- function contentLength(res) {
24213
- const headers = res.headers;
24236
+ function headerValue(headers, name) {
24214
24237
  if (headers === void 0 || headers === null) return null;
24238
+ let raw;
24215
24239
  if (typeof headers.get === "function") {
24216
- const raw2 = headers.get?.("content-length");
24217
- if (raw2 === void 0 || raw2 === null || raw2.trim() === "") return null;
24218
- const n2 = Number(raw2);
24219
- return Number.isFinite(n2) && n2 >= 0 ? n2 : null;
24220
- }
24221
- const record2 = headers;
24222
- const raw = record2["content-length"] ?? record2["Content-Length"];
24223
- if (raw === void 0 || raw === null) return null;
24240
+ raw = headers.get?.(name);
24241
+ } else {
24242
+ const record2 = headers;
24243
+ raw = record2[name.toLocaleLowerCase()] ?? record2[name];
24244
+ }
24245
+ if (raw === void 0 || raw === null || raw.trim() === "") return null;
24246
+ return raw;
24247
+ }
24248
+ function contentLength(res) {
24249
+ const raw = headerValue(
24250
+ res.headers,
24251
+ "content-length"
24252
+ );
24253
+ if (raw === null) return null;
24224
24254
  const n = Number(raw);
24225
24255
  return Number.isFinite(n) && n >= 0 ? n : null;
24226
24256
  }
@@ -24298,46 +24328,74 @@ function createSandboxFetch(options) {
24298
24328
  const fetchImpl = options.fetchImpl ?? defaultFetch;
24299
24329
  const { allowlist, timeoutMs } = options;
24300
24330
  const maxBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
24301
- return async (url2, init) => {
24331
+ const maxRedirects = Math.max(0, options.maxRedirects ?? DEFAULT_MAX_REDIRECTS);
24332
+ const guard = (raw) => {
24302
24333
  let parsed;
24303
24334
  try {
24304
- parsed = new URL(url2);
24335
+ parsed = new URL(raw);
24305
24336
  } catch {
24306
- throw new SandboxFetchError("FN_FETCH_INVALID_URL", `cloud.fetch \u6536\u5230\u975E\u6CD5 URL"${url2}"`);
24337
+ throw new SandboxFetchError("FN_FETCH_INVALID_URL", `cloud.fetch \u6536\u5230\u975E\u6CD5 URL"${raw}"`);
24307
24338
  }
24308
- if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw FORBIDDEN(url2);
24309
- if (!isHostAllowed(parsed.hostname, allowlist)) throw FORBIDDEN(url2);
24339
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw FORBIDDEN(raw);
24340
+ if (!isHostAllowed(parsed.hostname, allowlist)) throw FORBIDDEN(raw);
24341
+ return parsed;
24342
+ };
24343
+ const fetchOnce = async (url2, init) => {
24310
24344
  const controller = new AbortController();
24311
24345
  const timer = setTimeout(() => controller.abort(), timeoutMs);
24312
24346
  const signals = [controller.signal];
24313
24347
  if (init?.signal !== void 0) signals.push(init.signal);
24314
24348
  const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
24349
+ const request = {
24350
+ ...init,
24351
+ signal,
24352
+ redirect: "manual"
24353
+ };
24315
24354
  try {
24316
- let res;
24317
- try {
24318
- res = await fetchImpl(url2, { ...init, signal });
24319
- } catch (error51) {
24320
- if (controller.signal.aborted) {
24321
- throw new SandboxFetchError(
24322
- "FN_FETCH_TIMEOUT",
24323
- `cloud.fetch \u8BF7\u6C42\u8D85\u65F6\uFF08\u8D85\u8FC7 ${timeoutMs}ms\uFF09`
24324
- );
24325
- }
24326
- if (init?.signal?.aborted === true) {
24327
- throw new SandboxFetchError("FN_FETCH_ABORTED", "cloud.fetch \u8BF7\u6C42\u88AB\u8C03\u7528\u65B9\u4E2D\u6B62");
24328
- }
24329
- throw new SandboxFetchError(
24330
- "FN_FETCH_FAILED",
24331
- `cloud.fetch \u8BF7\u6C42\u5931\u8D25\uFF1A${errorMessageOf(error51)}`
24332
- );
24355
+ return await fetchImpl(url2, request);
24356
+ } catch (error51) {
24357
+ if (controller.signal.aborted) {
24358
+ throw new SandboxFetchError("FN_FETCH_TIMEOUT", `cloud.fetch \u8BF7\u6C42\u8D85\u65F6\uFF08\u8D85\u8FC7 ${timeoutMs}ms\uFF09`);
24359
+ }
24360
+ if (init?.signal?.aborted === true) {
24361
+ throw new SandboxFetchError("FN_FETCH_ABORTED", "cloud.fetch \u8BF7\u6C42\u88AB\u8C03\u7528\u65B9\u4E2D\u6B62");
24333
24362
  }
24334
- return normalizeResponse(res, maxBytes);
24363
+ throw new SandboxFetchError(
24364
+ "FN_FETCH_FAILED",
24365
+ `cloud.fetch \u8BF7\u6C42\u5931\u8D25\uFF1A${errorMessageOf(error51)}`
24366
+ );
24335
24367
  } finally {
24336
24368
  clearTimeout(timer);
24337
24369
  }
24338
24370
  };
24371
+ return async (url2, init) => {
24372
+ let target = guard(url2);
24373
+ let currentInit = init === void 0 ? void 0 : { ...init };
24374
+ let lastRes = null;
24375
+ for (let hop = 0; hop <= maxRedirects; hop++) {
24376
+ const res = await fetchOnce(target.href, currentInit);
24377
+ const location = REDIRECT_STATUSES.has(res.status) ? headerValue(res.headers, "location") : null;
24378
+ if (location === null || hop === maxRedirects) {
24379
+ lastRes = res;
24380
+ break;
24381
+ }
24382
+ let next;
24383
+ try {
24384
+ next = new URL(location, target.href);
24385
+ } catch {
24386
+ lastRes = res;
24387
+ break;
24388
+ }
24389
+ if (res.status !== 307 && res.status !== 308 && currentInit !== void 0) {
24390
+ currentInit.method = "GET";
24391
+ delete currentInit.body;
24392
+ }
24393
+ target = guard(next.href);
24394
+ }
24395
+ return normalizeResponse(lastRes, maxBytes);
24396
+ };
24339
24397
  }
24340
- var SandboxFetchError, DEFAULT_MAX_RESPONSE_BYTES, defaultFetch, FORBIDDEN, TOO_LARGE, encoder, decoder;
24398
+ var SandboxFetchError, DEFAULT_MAX_RESPONSE_BYTES, DEFAULT_MAX_REDIRECTS, REDIRECT_STATUSES, defaultFetch, FORBIDDEN, TOO_LARGE, encoder, decoder;
24341
24399
  var init_sandbox_fetch = __esm({
24342
24400
  "packages/runtime/src/functions/runtime/sandbox-fetch.ts"() {
24343
24401
  "use strict";
@@ -24350,6 +24408,8 @@ var init_sandbox_fetch = __esm({
24350
24408
  }
24351
24409
  };
24352
24410
  DEFAULT_MAX_RESPONSE_BYTES = 100 * 1024 * 1024;
24411
+ DEFAULT_MAX_REDIRECTS = 5;
24412
+ REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
24353
24413
  defaultFetch = (url2, init) => (
24354
24414
  // 真实 Response 与内部 ResponseLike 结构性一致;getReader 的多态签名差异属纯类型问题,运行语义等价。
24355
24415
  globalThis.fetch(url2, init)
@@ -24543,8 +24603,8 @@ async function normalizeResultBody(value) {
24543
24603
  }
24544
24604
  }
24545
24605
  const headers = {};
24546
- value.headers.forEach((headerValue, key) => {
24547
- headers[key] = headerValue;
24606
+ value.headers.forEach((headerValue2, key) => {
24607
+ headers[key] = headerValue2;
24548
24608
  });
24549
24609
  return { __adepHttp: { status: value.status, body: payload, headers } };
24550
24610
  }
@@ -24651,7 +24711,7 @@ function runWorker(input, port) {
24651
24711
  structuredClone
24652
24712
  };
24653
24713
  if (input.env !== void 0) {
24654
- sandboxGlobals["process"] = { env: input.env };
24714
+ sandboxGlobals["process"] = realmValue({ env: input.env });
24655
24715
  }
24656
24716
  if (input.fetch !== void 0) {
24657
24717
  const sandboxFetch = createSandboxFetch({
@@ -28445,7 +28505,7 @@ function createCliContext(deps) {
28445
28505
  init_auth();
28446
28506
  init_config();
28447
28507
  function registerAuth(program2, ctx) {
28448
- program2.command("login").description("\u767B\u5F55\u5E73\u53F0\uFF1A\u7F3A\u7701\u6253\u5F00\u6D4F\u89C8\u5668\u7528\u7F51\u9875\u6388\u6743\u767B\u5F55\uFF1B-e/-p \u8D70\u90AE\u7BB1\u5BC6\u7801\u76F4\u767B\uFF08CI / \u65E0\u6D4F\u89C8\u5668\uFF09").option("-s, --server <url>", "\u5E73\u53F0\u5730\u5740\uFF08\u7F3A\u7701\u8BFB\u53D6 ADEP_SERVER \u6216 https://adep.jajabjbj.top\uFF09").option("-e, --email <email>", "\u767B\u5F55\u90AE\u7BB1\uFF08\u4E0E -p \u4E00\u8D77\u8D70\u5BC6\u7801\u76F4\u767B\uFF0C\u4E0D\u53D1\u8D77\u6D4F\u89C8\u5668\u6D41\u7A0B\uFF09").option("-p, --password <password>", "\u767B\u5F55\u5BC6\u7801\uFF08\u6CE8\u610F shell \u5386\u53F2\uFF1B\u7F3A\u7701\u4EA4\u4E92\u8F93\u5165\uFF09").option("--no-browser", "\u4E0D\u81EA\u52A8\u6253\u5F00\u6D4F\u89C8\u5668\uFF1A\u53EA\u6253\u5370\u9A8C\u8BC1\u7801\u4E0E\u94FE\u63A5\uFF0C\u7531\u4F60\u624B\u5DE5\u8BBF\u95EE\uFF08SSH / \u65E0\u684C\u9762\uFF09").action(
28508
+ program2.command("login").description("\u767B\u5F55\u5E73\u53F0\uFF1A\u7F3A\u7701\u6253\u5F00\u6D4F\u89C8\u5668\u7528\u7F51\u9875\u6388\u6743\u767B\u5F55\uFF1B-e/-p \u8D70\u90AE\u7BB1\u5BC6\u7801\u76F4\u767B\uFF08CI / \u65E0\u6D4F\u89C8\u5668\uFF09").option("-s, --server <url>", "\u5E73\u53F0\u5730\u5740\uFF08\u7F3A\u7701\u8BFB\u53D6 ADEP_SERVER \u6216 https://adep.top\uFF09").option("-e, --email <email>", "\u767B\u5F55\u90AE\u7BB1\uFF08\u4E0E -p \u4E00\u8D77\u8D70\u5BC6\u7801\u76F4\u767B\uFF0C\u4E0D\u53D1\u8D77\u6D4F\u89C8\u5668\u6D41\u7A0B\uFF09").option("-p, --password <password>", "\u767B\u5F55\u5BC6\u7801\uFF08\u6CE8\u610F shell \u5386\u53F2\uFF1B\u7F3A\u7701\u4EA4\u4E92\u8F93\u5165\uFF09").option("--no-browser", "\u4E0D\u81EA\u52A8\u6253\u5F00\u6D4F\u89C8\u5668\uFF1A\u53EA\u6253\u5370\u9A8C\u8BC1\u7801\u4E0E\u94FE\u63A5\uFF0C\u7531\u4F60\u624B\u5DE5\u8BBF\u95EE\uFF08SSH / \u65E0\u684C\u9762\uFF09").action(
28449
28509
  async (flags) => {
28450
28510
  const command = ctx.io("login");
28451
28511
  await command.run(async () => {
@@ -29118,7 +29178,7 @@ function registerApp(program2, ctx) {
29118
29178
  const app = program2.command("app").description("\u684C\u9762 / \u79FB\u52A8\u5E94\u7528\u58F3\u5DE5\u7A0B\uFF08\u5F53\u524D\u652F\u6301\uFF1Ainit / build\uFF09");
29119
29179
  app.command("init").description("\u751F\u6210\u58F3\u5DE5\u7A0B\uFF1A--platform desktop\uFF08\u7F3A\u7701\uFF0CElectron\uFF09| mobile\uFF08Capacitor \u79FB\u52A8\u58F3\uFF09").requiredOption("-p, --project <slug>", "\u5E73\u53F0\u9879\u76EE slug\uFF08\u5FC5\u586B\uFF1B\u4E91\u51FD\u6570\u7ECF\u8BE5\u9879\u76EE\u7684 /api/* \u6267\u884C\uFF09").requiredOption("-n, --name <\u5E94\u7528\u540D>", "\u5E94\u7528\u663E\u793A\u540D\uFF08\u5FC5\u586B\uFF1B\u7A97\u53E3\u6807\u9898 / \u5B89\u88C5\u5305 productName / \u79FB\u52A8\u7AEF\u663E\u793A\u540D\uFF09").option("--dir <\u76EE\u5F55>", "\u76EE\u6807\u76EE\u5F55\uFF08\u7F3A\u7701 ./<slug>-app\uFF09").option("--platform <\u58F3>", "desktop\uFF08\u7F3A\u7701\uFF0CElectron \u684C\u9762\u58F3\uFF09| mobile\uFF08Capacitor \u79FB\u52A8\u58F3\uFF0CAPP-009\uFF09").option(
29120
29180
  "-s, --server <url>",
29121
- "\u5E73\u53F0\u5730\u5740\uFF08\u7F3A\u7701\u5F53\u524D\u767B\u5F55\u5E73\u53F0\uFF0C\u6216 ADEP_SERVER / https://adep.jajabjbj.top\uFF09"
29181
+ "\u5E73\u53F0\u5730\u5740\uFF08\u7F3A\u7701\u5F53\u524D\u767B\u5F55\u5E73\u53F0\uFF0C\u6216 ADEP_SERVER / https://adep.top\uFF09"
29122
29182
  ).action(
29123
29183
  async (flags) => {
29124
29184
  const command = ctx.io("app init");
package/dist/vite.js CHANGED
@@ -3717,6 +3717,29 @@ function transformExports(code) {
3717
3717
  return transformModule(code);
3718
3718
  }
3719
3719
  var SANDBOX_BUILTIN_REJECTED = "FN_SANDBOX_BUILTIN_BLOCKED";
3720
+ var REALM_VALUE_KEY = "__adepRealmValue";
3721
+ function realmValue(value) {
3722
+ return { [REALM_VALUE_KEY]: value };
3723
+ }
3724
+ function isRealmValue(value) {
3725
+ return typeof value === "object" && value !== null && REALM_VALUE_KEY in value;
3726
+ }
3727
+ function createSandboxContext(sandboxGlobals) {
3728
+ const direct = {};
3729
+ const realmEntries = {};
3730
+ for (const [key, value] of Object.entries(sandboxGlobals)) {
3731
+ if (isRealmValue(value)) realmEntries[key] = value[REALM_VALUE_KEY];
3732
+ else direct[key] = value;
3733
+ }
3734
+ const context = vm.createContext(direct, { codeGeneration: { strings: false, wasm: false } });
3735
+ if (Object.keys(realmEntries).length > 0) {
3736
+ const literal = JSON.stringify(JSON.stringify(realmEntries));
3737
+ vm.runInContext(`Object.assign(globalThis, JSON.parse(${literal}))`, context, {
3738
+ filename: "<sandbox-realm-globals>"
3739
+ });
3740
+ }
3741
+ return context;
3742
+ }
3720
3743
  function resolveModulePath(fromDir, request, files) {
3721
3744
  if (!request.startsWith("./") && !request.startsWith("../")) return null;
3722
3745
  const base = joinPosix(fromDir, request);
@@ -3790,12 +3813,13 @@ function loadModule(path, files, sandboxGlobals, cache, resolveBare, bareCache)
3790
3813
  } catch (error) {
3791
3814
  throw syntaxErrorWithLocation(error, path);
3792
3815
  }
3816
+ const context = createSandboxContext(sandboxGlobals);
3793
3817
  let wrapper;
3794
3818
  try {
3795
- wrapper = vm.runInNewContext(
3819
+ wrapper = vm.runInContext(
3796
3820
  `(function (module, exports, require) { ${transformed}
3797
3821
  })`,
3798
- vm.createContext({ ...sandboxGlobals }),
3822
+ context,
3799
3823
  { filename: path }
3800
3824
  );
3801
3825
  } catch (error) {
@@ -3975,6 +3999,8 @@ var SandboxFetchError = class extends Error {
3975
3999
  }
3976
4000
  };
3977
4001
  var DEFAULT_MAX_RESPONSE_BYTES = 100 * 1024 * 1024;
4002
+ var DEFAULT_MAX_REDIRECTS = 5;
4003
+ var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
3978
4004
  var defaultFetch = (url, init) => (
3979
4005
  // 真实 Response 与内部 ResponseLike 结构性一致;getReader 的多态签名差异属纯类型问题,运行语义等价。
3980
4006
  globalThis.fetch(url, init)
@@ -4013,18 +4039,24 @@ function headersToRecord(headers) {
4013
4039
  }
4014
4040
  return out;
4015
4041
  }
4016
- function contentLength(res) {
4017
- const headers = res.headers;
4042
+ function headerValue(headers, name) {
4018
4043
  if (headers === void 0 || headers === null) return null;
4044
+ let raw;
4019
4045
  if (typeof headers.get === "function") {
4020
- const raw2 = headers.get?.("content-length");
4021
- if (raw2 === void 0 || raw2 === null || raw2.trim() === "") return null;
4022
- const n2 = Number(raw2);
4023
- return Number.isFinite(n2) && n2 >= 0 ? n2 : null;
4024
- }
4025
- const record = headers;
4026
- const raw = record["content-length"] ?? record["Content-Length"];
4027
- if (raw === void 0 || raw === null) return null;
4046
+ raw = headers.get?.(name);
4047
+ } else {
4048
+ const record = headers;
4049
+ raw = record[name.toLocaleLowerCase()] ?? record[name];
4050
+ }
4051
+ if (raw === void 0 || raw === null || raw.trim() === "") return null;
4052
+ return raw;
4053
+ }
4054
+ function contentLength(res) {
4055
+ const raw = headerValue(
4056
+ res.headers,
4057
+ "content-length"
4058
+ );
4059
+ if (raw === null) return null;
4028
4060
  const n = Number(raw);
4029
4061
  return Number.isFinite(n) && n >= 0 ? n : null;
4030
4062
  }
@@ -4104,44 +4136,72 @@ function createSandboxFetch(options) {
4104
4136
  const fetchImpl = options.fetchImpl ?? defaultFetch;
4105
4137
  const { allowlist, timeoutMs } = options;
4106
4138
  const maxBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
4107
- return async (url, init) => {
4139
+ const maxRedirects = Math.max(0, options.maxRedirects ?? DEFAULT_MAX_REDIRECTS);
4140
+ const guard = (raw) => {
4108
4141
  let parsed;
4109
4142
  try {
4110
- parsed = new URL(url);
4143
+ parsed = new URL(raw);
4111
4144
  } catch {
4112
- throw new SandboxFetchError("FN_FETCH_INVALID_URL", `cloud.fetch \u6536\u5230\u975E\u6CD5 URL"${url}"`);
4145
+ throw new SandboxFetchError("FN_FETCH_INVALID_URL", `cloud.fetch \u6536\u5230\u975E\u6CD5 URL"${raw}"`);
4113
4146
  }
4114
- if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw FORBIDDEN(url);
4115
- if (!isHostAllowed(parsed.hostname, allowlist)) throw FORBIDDEN(url);
4147
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw FORBIDDEN(raw);
4148
+ if (!isHostAllowed(parsed.hostname, allowlist)) throw FORBIDDEN(raw);
4149
+ return parsed;
4150
+ };
4151
+ const fetchOnce = async (url, init) => {
4116
4152
  const controller = new AbortController();
4117
4153
  const timer = setTimeout(() => controller.abort(), timeoutMs);
4118
4154
  const signals = [controller.signal];
4119
4155
  if (init?.signal !== void 0) signals.push(init.signal);
4120
4156
  const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
4157
+ const request = {
4158
+ ...init,
4159
+ signal,
4160
+ redirect: "manual"
4161
+ };
4121
4162
  try {
4122
- let res;
4123
- try {
4124
- res = await fetchImpl(url, { ...init, signal });
4125
- } catch (error) {
4126
- if (controller.signal.aborted) {
4127
- throw new SandboxFetchError(
4128
- "FN_FETCH_TIMEOUT",
4129
- `cloud.fetch \u8BF7\u6C42\u8D85\u65F6\uFF08\u8D85\u8FC7 ${timeoutMs}ms\uFF09`
4130
- );
4131
- }
4132
- if (init?.signal?.aborted === true) {
4133
- throw new SandboxFetchError("FN_FETCH_ABORTED", "cloud.fetch \u8BF7\u6C42\u88AB\u8C03\u7528\u65B9\u4E2D\u6B62");
4134
- }
4135
- throw new SandboxFetchError(
4136
- "FN_FETCH_FAILED",
4137
- `cloud.fetch \u8BF7\u6C42\u5931\u8D25\uFF1A${errorMessageOf(error)}`
4138
- );
4163
+ return await fetchImpl(url, request);
4164
+ } catch (error) {
4165
+ if (controller.signal.aborted) {
4166
+ throw new SandboxFetchError("FN_FETCH_TIMEOUT", `cloud.fetch \u8BF7\u6C42\u8D85\u65F6\uFF08\u8D85\u8FC7 ${timeoutMs}ms\uFF09`);
4139
4167
  }
4140
- return normalizeResponse(res, maxBytes);
4168
+ if (init?.signal?.aborted === true) {
4169
+ throw new SandboxFetchError("FN_FETCH_ABORTED", "cloud.fetch \u8BF7\u6C42\u88AB\u8C03\u7528\u65B9\u4E2D\u6B62");
4170
+ }
4171
+ throw new SandboxFetchError(
4172
+ "FN_FETCH_FAILED",
4173
+ `cloud.fetch \u8BF7\u6C42\u5931\u8D25\uFF1A${errorMessageOf(error)}`
4174
+ );
4141
4175
  } finally {
4142
4176
  clearTimeout(timer);
4143
4177
  }
4144
4178
  };
4179
+ return async (url, init) => {
4180
+ let target = guard(url);
4181
+ let currentInit = init === void 0 ? void 0 : { ...init };
4182
+ let lastRes = null;
4183
+ for (let hop = 0; hop <= maxRedirects; hop++) {
4184
+ const res = await fetchOnce(target.href, currentInit);
4185
+ const location = REDIRECT_STATUSES.has(res.status) ? headerValue(res.headers, "location") : null;
4186
+ if (location === null || hop === maxRedirects) {
4187
+ lastRes = res;
4188
+ break;
4189
+ }
4190
+ let next;
4191
+ try {
4192
+ next = new URL(location, target.href);
4193
+ } catch {
4194
+ lastRes = res;
4195
+ break;
4196
+ }
4197
+ if (res.status !== 307 && res.status !== 308 && currentInit !== void 0) {
4198
+ currentInit.method = "GET";
4199
+ delete currentInit.body;
4200
+ }
4201
+ target = guard(next.href);
4202
+ }
4203
+ return normalizeResponse(lastRes, maxBytes);
4204
+ };
4145
4205
  }
4146
4206
 
4147
4207
  // packages/runtime/src/functions/runtime/flat-rpc-proxy.ts
@@ -4292,8 +4352,8 @@ async function normalizeResultBody(value) {
4292
4352
  }
4293
4353
  }
4294
4354
  const headers = {};
4295
- value.headers.forEach((headerValue, key) => {
4296
- headers[key] = headerValue;
4355
+ value.headers.forEach((headerValue2, key) => {
4356
+ headers[key] = headerValue2;
4297
4357
  });
4298
4358
  return { __adepHttp: { status: value.status, body: payload, headers } };
4299
4359
  }
@@ -4400,7 +4460,7 @@ function runWorker(input, port) {
4400
4460
  structuredClone
4401
4461
  };
4402
4462
  if (input.env !== void 0) {
4403
- sandboxGlobals["process"] = { env: input.env };
4463
+ sandboxGlobals["process"] = realmValue({ env: input.env });
4404
4464
  }
4405
4465
  if (input.fetch !== void 0) {
4406
4466
  const sandboxFetch = createSandboxFetch({
@@ -246,6 +246,29 @@ function transformExports(code) {
246
246
  return transformModule(code);
247
247
  }
248
248
  var SANDBOX_BUILTIN_REJECTED = "FN_SANDBOX_BUILTIN_BLOCKED";
249
+ var REALM_VALUE_KEY = "__adepRealmValue";
250
+ function realmValue(value) {
251
+ return { [REALM_VALUE_KEY]: value };
252
+ }
253
+ function isRealmValue(value) {
254
+ return typeof value === "object" && value !== null && REALM_VALUE_KEY in value;
255
+ }
256
+ function createSandboxContext(sandboxGlobals) {
257
+ const direct = {};
258
+ const realmEntries = {};
259
+ for (const [key, value] of Object.entries(sandboxGlobals)) {
260
+ if (isRealmValue(value)) realmEntries[key] = value[REALM_VALUE_KEY];
261
+ else direct[key] = value;
262
+ }
263
+ const context = vm.createContext(direct, { codeGeneration: { strings: false, wasm: false } });
264
+ if (Object.keys(realmEntries).length > 0) {
265
+ const literal = JSON.stringify(JSON.stringify(realmEntries));
266
+ vm.runInContext(`Object.assign(globalThis, JSON.parse(${literal}))`, context, {
267
+ filename: "<sandbox-realm-globals>"
268
+ });
269
+ }
270
+ return context;
271
+ }
249
272
  function resolveModulePath(fromDir, request, files) {
250
273
  if (!request.startsWith("./") && !request.startsWith("../")) return null;
251
274
  const base = joinPosix(fromDir, request);
@@ -319,12 +342,13 @@ function loadModule(path, files, sandboxGlobals, cache, resolveBare, bareCache)
319
342
  } catch (error) {
320
343
  throw syntaxErrorWithLocation(error, path);
321
344
  }
345
+ const context = createSandboxContext(sandboxGlobals);
322
346
  let wrapper;
323
347
  try {
324
- wrapper = vm.runInNewContext(
348
+ wrapper = vm.runInContext(
325
349
  `(function (module, exports, require) { ${transformed}
326
350
  })`,
327
- vm.createContext({ ...sandboxGlobals }),
351
+ context,
328
352
  { filename: path }
329
353
  );
330
354
  } catch (error) {
@@ -357,6 +381,8 @@ var SandboxFetchError = class extends Error {
357
381
  }
358
382
  };
359
383
  var DEFAULT_MAX_RESPONSE_BYTES = 100 * 1024 * 1024;
384
+ var DEFAULT_MAX_REDIRECTS = 5;
385
+ var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
360
386
  var defaultFetch = (url, init) => (
361
387
  // 真实 Response 与内部 ResponseLike 结构性一致;getReader 的多态签名差异属纯类型问题,运行语义等价。
362
388
  globalThis.fetch(url, init)
@@ -395,18 +421,24 @@ function headersToRecord(headers) {
395
421
  }
396
422
  return out;
397
423
  }
398
- function contentLength(res) {
399
- const headers = res.headers;
424
+ function headerValue(headers, name) {
400
425
  if (headers === void 0 || headers === null) return null;
426
+ let raw;
401
427
  if (typeof headers.get === "function") {
402
- const raw2 = headers.get?.("content-length");
403
- if (raw2 === void 0 || raw2 === null || raw2.trim() === "") return null;
404
- const n2 = Number(raw2);
405
- return Number.isFinite(n2) && n2 >= 0 ? n2 : null;
406
- }
407
- const record = headers;
408
- const raw = record["content-length"] ?? record["Content-Length"];
409
- if (raw === void 0 || raw === null) return null;
428
+ raw = headers.get?.(name);
429
+ } else {
430
+ const record = headers;
431
+ raw = record[name.toLocaleLowerCase()] ?? record[name];
432
+ }
433
+ if (raw === void 0 || raw === null || raw.trim() === "") return null;
434
+ return raw;
435
+ }
436
+ function contentLength(res) {
437
+ const raw = headerValue(
438
+ res.headers,
439
+ "content-length"
440
+ );
441
+ if (raw === null) return null;
410
442
  const n = Number(raw);
411
443
  return Number.isFinite(n) && n >= 0 ? n : null;
412
444
  }
@@ -486,44 +518,72 @@ function createSandboxFetch(options) {
486
518
  const fetchImpl = options.fetchImpl ?? defaultFetch;
487
519
  const { allowlist, timeoutMs } = options;
488
520
  const maxBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
489
- return async (url, init) => {
521
+ const maxRedirects = Math.max(0, options.maxRedirects ?? DEFAULT_MAX_REDIRECTS);
522
+ const guard = (raw) => {
490
523
  let parsed;
491
524
  try {
492
- parsed = new URL(url);
525
+ parsed = new URL(raw);
493
526
  } catch {
494
- throw new SandboxFetchError("FN_FETCH_INVALID_URL", `cloud.fetch \u6536\u5230\u975E\u6CD5 URL"${url}"`);
527
+ throw new SandboxFetchError("FN_FETCH_INVALID_URL", `cloud.fetch \u6536\u5230\u975E\u6CD5 URL"${raw}"`);
495
528
  }
496
- if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw FORBIDDEN(url);
497
- if (!isHostAllowed(parsed.hostname, allowlist)) throw FORBIDDEN(url);
529
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw FORBIDDEN(raw);
530
+ if (!isHostAllowed(parsed.hostname, allowlist)) throw FORBIDDEN(raw);
531
+ return parsed;
532
+ };
533
+ const fetchOnce = async (url, init) => {
498
534
  const controller = new AbortController();
499
535
  const timer = setTimeout(() => controller.abort(), timeoutMs);
500
536
  const signals = [controller.signal];
501
537
  if (init?.signal !== void 0) signals.push(init.signal);
502
538
  const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
539
+ const request = {
540
+ ...init,
541
+ signal,
542
+ redirect: "manual"
543
+ };
503
544
  try {
504
- let res;
505
- try {
506
- res = await fetchImpl(url, { ...init, signal });
507
- } catch (error) {
508
- if (controller.signal.aborted) {
509
- throw new SandboxFetchError(
510
- "FN_FETCH_TIMEOUT",
511
- `cloud.fetch \u8BF7\u6C42\u8D85\u65F6\uFF08\u8D85\u8FC7 ${timeoutMs}ms\uFF09`
512
- );
513
- }
514
- if (init?.signal?.aborted === true) {
515
- throw new SandboxFetchError("FN_FETCH_ABORTED", "cloud.fetch \u8BF7\u6C42\u88AB\u8C03\u7528\u65B9\u4E2D\u6B62");
516
- }
517
- throw new SandboxFetchError(
518
- "FN_FETCH_FAILED",
519
- `cloud.fetch \u8BF7\u6C42\u5931\u8D25\uFF1A${errorMessageOf(error)}`
520
- );
545
+ return await fetchImpl(url, request);
546
+ } catch (error) {
547
+ if (controller.signal.aborted) {
548
+ throw new SandboxFetchError("FN_FETCH_TIMEOUT", `cloud.fetch \u8BF7\u6C42\u8D85\u65F6\uFF08\u8D85\u8FC7 ${timeoutMs}ms\uFF09`);
549
+ }
550
+ if (init?.signal?.aborted === true) {
551
+ throw new SandboxFetchError("FN_FETCH_ABORTED", "cloud.fetch \u8BF7\u6C42\u88AB\u8C03\u7528\u65B9\u4E2D\u6B62");
521
552
  }
522
- return normalizeResponse(res, maxBytes);
553
+ throw new SandboxFetchError(
554
+ "FN_FETCH_FAILED",
555
+ `cloud.fetch \u8BF7\u6C42\u5931\u8D25\uFF1A${errorMessageOf(error)}`
556
+ );
523
557
  } finally {
524
558
  clearTimeout(timer);
525
559
  }
526
560
  };
561
+ return async (url, init) => {
562
+ let target = guard(url);
563
+ let currentInit = init === void 0 ? void 0 : { ...init };
564
+ let lastRes = null;
565
+ for (let hop = 0; hop <= maxRedirects; hop++) {
566
+ const res = await fetchOnce(target.href, currentInit);
567
+ const location = REDIRECT_STATUSES.has(res.status) ? headerValue(res.headers, "location") : null;
568
+ if (location === null || hop === maxRedirects) {
569
+ lastRes = res;
570
+ break;
571
+ }
572
+ let next;
573
+ try {
574
+ next = new URL(location, target.href);
575
+ } catch {
576
+ lastRes = res;
577
+ break;
578
+ }
579
+ if (res.status !== 307 && res.status !== 308 && currentInit !== void 0) {
580
+ currentInit.method = "GET";
581
+ delete currentInit.body;
582
+ }
583
+ target = guard(next.href);
584
+ }
585
+ return normalizeResponse(lastRes, maxBytes);
586
+ };
527
587
  }
528
588
 
529
589
  // packages/runtime/src/shared/capability-keys.ts
@@ -703,8 +763,8 @@ async function normalizeResultBody(value) {
703
763
  }
704
764
  }
705
765
  const headers = {};
706
- value.headers.forEach((headerValue, key) => {
707
- headers[key] = headerValue;
766
+ value.headers.forEach((headerValue2, key) => {
767
+ headers[key] = headerValue2;
708
768
  });
709
769
  return { __adepHttp: { status: value.status, body: payload, headers } };
710
770
  }
@@ -811,7 +871,7 @@ function runWorker(input, port) {
811
871
  structuredClone
812
872
  };
813
873
  if (input.env !== void 0) {
814
- sandboxGlobals["process"] = { env: input.env };
874
+ sandboxGlobals["process"] = realmValue({ env: input.env });
815
875
  }
816
876
  if (input.fetch !== void 0) {
817
877
  const sandboxFetch = createSandboxFetch({
package/package.json CHANGED
@@ -5,9 +5,9 @@
5
5
  "type": "git",
6
6
  "url": "git+https://gitcode.com/yunqiang_wu/ai-yunke.git"
7
7
  },
8
- "homepage": "https://adep.jajabjbj.top",
8
+ "homepage": "https://adep.top",
9
9
  "bugs": "https://gitcode.com/yunqiang_wu/ai-yunke/issues",
10
- "version": "0.4.0",
10
+ "version": "0.4.1",
11
11
  "type": "module",
12
12
  "description": "`adep` 命令行工具包位:登录 / 初始化 / 本地调试 / 部署 / 数据库维护 / 云存储 / 静态托管 / 微前端组件(widget),实现见任务单 CLI-001 ~ CLI-003 与 FE-002;子路径 `@adep/cli/vite` 提供云函数 vite 插件(CLI-014)",
13
13
  "bin": {
@@ -30,13 +30,13 @@
30
30
  "esbuild": "0.25.12",
31
31
  "yaml": "2.9.1",
32
32
  "zod": "^4.4.3",
33
- "@adep/crypto": "0.1.0",
34
- "@adep/cf-compat": "0.1.1",
35
- "@adep/runtime": "0.3.2",
36
- "@adep/vite-plugin": "0.1.8"
33
+ "@adep/cf-compat": "0.1.2",
34
+ "@adep/runtime": "0.3.3",
35
+ "@adep/crypto": "0.1.1",
36
+ "@adep/vite-plugin": "0.1.9"
37
37
  },
38
38
  "devDependencies": {
39
- "@adep/types": "0.3.0"
39
+ "@adep/types": "0.3.1"
40
40
  },
41
41
  "scripts": {
42
42
  "test": "vitest run",
@@ -3,10 +3,10 @@ Pod::Spec.new do |s|
3
3
  s.version = '0.1.0'
4
4
  s.summary = 'AgentDeploy mobile shell native bridge (APP-009)'
5
5
  s.description = 'Native-side permission gate + ownerKey Keychain storage + native capability invoke routing'
6
- s.homepage = 'https://adep.jajabjbj.top'
6
+ s.homepage = 'https://adep.top'
7
7
  s.license = 'MIT'
8
8
  s.author = 'AgentDeploy'
9
- s.source = { :git => 'https://adep.jajabjbj.top/adep-bridge.git', :tag => s.version.to_s }
9
+ s.source = { :git => 'https://adep.top/adep-bridge.git', :tag => s.version.to_s }
10
10
  s.ios.deployment_target = '13.0'
11
11
  s.source_files = 'Sources/**/*.swift'
12
12
  s.swift_version = '5.1'