@adep/cli 0.1.14 → 0.1.16

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 (3) hide show
  1. package/dist/index.js +354 -19
  2. package/dist/vite.js +34 -4
  3. package/package.json +5 -4
package/dist/index.js CHANGED
@@ -577,11 +577,23 @@ var init_http_envelope = __esm({
577
577
  });
578
578
 
579
579
  // packages/runtime/src/functions/domain.ts
580
- var MAX_TOTAL_SOURCE_BYTES, FnError;
580
+ function flatEntryCandidates(name) {
581
+ return ENTRY_SOURCE_EXTS.map((ext) => `${name}${ext}`);
582
+ }
583
+ function resolveDraftEntry(name, files) {
584
+ if (files[ENTRY_FILE] !== void 0) return ENTRY_FILE;
585
+ for (const candidate of flatEntryCandidates(name)) {
586
+ if (files[candidate] !== void 0) return candidate;
587
+ }
588
+ return null;
589
+ }
590
+ var MAX_TOTAL_SOURCE_BYTES, ENTRY_FILE, ENTRY_SOURCE_EXTS, FnError;
581
591
  var init_domain = __esm({
582
592
  "packages/runtime/src/functions/domain.ts"() {
583
593
  "use strict";
584
594
  MAX_TOTAL_SOURCE_BYTES = 256 * 1024;
595
+ ENTRY_FILE = "index.ts";
596
+ ENTRY_SOURCE_EXTS = [".ts", ".js", ".mjs", ".cjs"];
585
597
  FnError = class extends Error {
586
598
  status;
587
599
  code;
@@ -759,6 +771,7 @@ var init_worker_executor = __esm({
759
771
  "use strict";
760
772
  init_executor();
761
773
  init_resolve();
774
+ init_domain();
762
775
  WorkerFunctionExecutor = class {
763
776
  depsConfig;
764
777
  fetchConfig;
@@ -769,7 +782,7 @@ var init_worker_executor = __esm({
769
782
  async execute(input) {
770
783
  const timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS;
771
784
  const memoryLimitMb = input.memoryLimitMb ?? DEFAULT_MEMORY_LIMIT_MB;
772
- const entry = input.entry ?? "index.ts";
785
+ const entry = input.entry ?? resolveDraftEntry(input.fn.name, input.files) ?? "index.ts";
773
786
  const deps = this.depsConfig === void 0 ? void 0 : resolveExecutionDeps(this.depsConfig, input.project.id, input.files["package.json"]);
774
787
  const logs = [];
775
788
  return new Promise((resolve15, reject) => {
@@ -2680,6 +2693,14 @@ function hmacSha256(key, message) {
2680
2693
  function hmacSha256Hex(key, message) {
2681
2694
  return [...hmacSha256(key, message)].map((b) => b.toString(16).padStart(2, "0")).join("");
2682
2695
  }
2696
+ function constantTimeEqual(a, b) {
2697
+ if (a.length !== b.length) return false;
2698
+ let diff = 0;
2699
+ for (let i = 0; i < a.length; i += 1) {
2700
+ diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
2701
+ }
2702
+ return diff === 0;
2703
+ }
2683
2704
  var K, rotr;
2684
2705
  var init_hmac_sha256 = __esm({
2685
2706
  "packages/runtime/src/storage/hmac-sha256.ts"() {
@@ -2765,6 +2786,22 @@ function signDownloadUrl(config, projectId, path, ttlSeconds = DEFAULT_SIGN_TTL_
2765
2786
  const signature = sign(config.secret, projectId, path, expires);
2766
2787
  return `${base}?${SIGN_EXPIRES_KEY}=${expires}&${SIGN_SIGNATURE_KEY}=${signature}`;
2767
2788
  }
2789
+ function verifyDownloadSignature(config, projectId, path, query, nowSeconds2 = Math.floor(Date.now() / 1e3)) {
2790
+ const expiresRaw = query.expires;
2791
+ const signatureRaw = query.signature;
2792
+ if (typeof expiresRaw !== "string" || typeof signatureRaw !== "string") {
2793
+ return { ok: false, reason: "missing" };
2794
+ }
2795
+ if (!/^\d{1,15}$/.test(expiresRaw)) return { ok: false, reason: "malformed" };
2796
+ const expires = Number(expiresRaw);
2797
+ if (!Number.isFinite(expires)) return { ok: false, reason: "malformed" };
2798
+ if (nowSeconds2 > expires) return { ok: false, reason: "expired" };
2799
+ const expected = sign(config.secret, projectId, path, expires);
2800
+ if (!constantTimeEqual(signatureRaw, expected)) {
2801
+ return { ok: false, reason: "invalid" };
2802
+ }
2803
+ return { ok: true };
2804
+ }
2768
2805
  var DEFAULT_SIGN_TTL_SECONDS, SIGN_EXPIRES_KEY, SIGN_SIGNATURE_KEY;
2769
2806
  var init_signature = __esm({
2770
2807
  "packages/runtime/src/storage/signature.ts"() {
@@ -2993,7 +3030,7 @@ function createLocalStorageDriver(options) {
2993
3030
  assertStoragePrefix(prefix);
2994
3031
  const root = bucketDir(projectId);
2995
3032
  const metas = [];
2996
- const walk = async (dir, relative6) => {
3033
+ const walk = async (dir, relative7) => {
2997
3034
  let entries;
2998
3035
  try {
2999
3036
  entries = await readdir3(dir, { withFileTypes: true });
@@ -3004,7 +3041,7 @@ function createLocalStorageDriver(options) {
3004
3041
  for (const entry of entries) {
3005
3042
  if (entry.name.endsWith(META_SUFFIX)) continue;
3006
3043
  const full = join6(dir, entry.name);
3007
- const rel = relative6 === "" ? entry.name : `${relative6}/${entry.name}`;
3044
+ const rel = relative7 === "" ? entry.name : `${relative7}/${entry.name}`;
3008
3045
  if (entry.isFile()) {
3009
3046
  if (prefix !== void 0 && !rel.startsWith(prefix)) continue;
3010
3047
  tasks.push(
@@ -3058,17 +3095,19 @@ function createSimStorageCapability(options) {
3058
3095
  const dir = join7(options.cwd, ".adep", "sim", "storage");
3059
3096
  const driver = createLocalStorageDriver({ dir });
3060
3097
  const signer = {
3061
- secret: options.secret ?? "adep-sim-secret",
3098
+ secret: options.secret ?? SIM_SIGN_SECRET,
3062
3099
  baseUrl: options.baseUrl ?? "http://127.0.0.1:8787"
3063
3100
  };
3064
3101
  const bundle = createStorageCapability(driver, signer, options.projectId ?? "sim-project");
3065
3102
  return { bundle, driver };
3066
3103
  }
3104
+ var SIM_SIGN_SECRET;
3067
3105
  var init_storage = __esm({
3068
3106
  "packages/cli/src/sim/storage.ts"() {
3069
3107
  "use strict";
3070
3108
  init_cloud2();
3071
3109
  init_local();
3110
+ SIM_SIGN_SECRET = "adep-sim-secret";
3072
3111
  }
3073
3112
  });
3074
3113
 
@@ -3268,6 +3307,8 @@ async function createSimRuntime(options) {
3268
3307
  bundle,
3269
3308
  realtime: simRealtime.realtime,
3270
3309
  db,
3310
+ /** 存储句柄(serve 的签名文件下载路由用它读对象;bundle 里的 capability 是同一驱动的 RPC 面)。 */
3311
+ storage,
3271
3312
  dispose: async () => {
3272
3313
  await db.driver.close();
3273
3314
  }
@@ -3382,15 +3423,19 @@ var init_invoke = __esm({
3382
3423
  });
3383
3424
 
3384
3425
  // shared/builtin-deps.ts
3385
- var CF_COMPAT_SPECIFIER, STATE_OBJECTS_SPECIFIER, BUILTIN_DEPS, BUILTIN_DEPS_CSV;
3426
+ var CF_COMPAT_SPECIFIER, STATE_OBJECTS_SPECIFIER, CRYPTO_SPECIFIER, ROUTE_SPECIFIER, BUILTIN_DEPS, BUILTIN_DEPS_CSV;
3386
3427
  var init_builtin_deps = __esm({
3387
3428
  "shared/builtin-deps.ts"() {
3388
3429
  "use strict";
3389
3430
  CF_COMPAT_SPECIFIER = "@adep/cf-compat";
3390
3431
  STATE_OBJECTS_SPECIFIER = "@adep/runtime/state-objects";
3432
+ CRYPTO_SPECIFIER = "@adep/crypto";
3433
+ ROUTE_SPECIFIER = "@adep/runtime/route";
3391
3434
  BUILTIN_DEPS = [
3392
3435
  CF_COMPAT_SPECIFIER,
3393
3436
  STATE_OBJECTS_SPECIFIER,
3437
+ CRYPTO_SPECIFIER,
3438
+ ROUTE_SPECIFIER,
3394
3439
  "axios",
3395
3440
  "lodash",
3396
3441
  "dayjs",
@@ -3406,7 +3451,12 @@ var init_builtin_deps2 = __esm({
3406
3451
  "packages/cli/src/builtin-deps.ts"() {
3407
3452
  "use strict";
3408
3453
  init_builtin_deps();
3409
- LOCAL_BUILTIN_DEPS = [CF_COMPAT_SPECIFIER, STATE_OBJECTS_SPECIFIER];
3454
+ LOCAL_BUILTIN_DEPS = [
3455
+ CF_COMPAT_SPECIFIER,
3456
+ STATE_OBJECTS_SPECIFIER,
3457
+ CRYPTO_SPECIFIER,
3458
+ ROUTE_SPECIFIER
3459
+ ];
3410
3460
  }
3411
3461
  });
3412
3462
 
@@ -5325,7 +5375,7 @@ function splitSqlStatements(source) {
5325
5375
  const lines = source.split("\n");
5326
5376
  for (const rawLine of lines) {
5327
5377
  const trimmed = rawLine.trim();
5328
- if (trimmed.length === 0 || trimmed.startsWith("--")) continue;
5378
+ if (!inQuote && (trimmed.length === 0 || trimmed.startsWith("--"))) continue;
5329
5379
  for (let i = 0; i < rawLine.length; i++) {
5330
5380
  const ch = rawLine[i];
5331
5381
  if (inQuote) {
@@ -5354,7 +5404,7 @@ function splitSqlStatements(source) {
5354
5404
  }
5355
5405
  buffer += ch;
5356
5406
  }
5357
- buffer += " ";
5407
+ buffer += inQuote ? "\n" : " ";
5358
5408
  }
5359
5409
  if (inQuote) {
5360
5410
  throw new Error(`SQL \u8FC1\u79FB\u6587\u4EF6\u5F15\u53F7\u672A\u95ED\u5408\uFF1A${buffer.trim().slice(0, 80)}\u2026`);
@@ -5831,9 +5881,66 @@ async function startServeServer(options) {
5831
5881
  return false;
5832
5882
  }
5833
5883
  };
5884
+ const serveSignedFile = async (req, res, url, projectId, path) => {
5885
+ const driver = runtime.storage.driver;
5886
+ try {
5887
+ const meta = await driver.getMeta(projectId, path);
5888
+ if (meta.visibility === "private") {
5889
+ const verification = verifyDownloadSignature(
5890
+ { secret: SIM_SIGN_SECRET, baseUrl: "" },
5891
+ projectId,
5892
+ path,
5893
+ {
5894
+ expires: url.searchParams.get(SIGN_EXPIRES_KEY) ?? void 0,
5895
+ signature: url.searchParams.get(SIGN_SIGNATURE_KEY) ?? void 0
5896
+ }
5897
+ );
5898
+ if (!verification.ok) {
5899
+ const message = verification.reason === "expired" ? "\u7B7E\u540D URL \u5DF2\u8FC7\u671F\uFF1A\u8BF7\u91CD\u65B0\u751F\u6210\uFF0815 \u5206\u949F\u6709\u6548\uFF09" : verification.reason === "missing" ? "\u79C1\u6709\u6587\u4EF6\u9700\u8981\u7B7E\u540D URL \u624D\u80FD\u8BBF\u95EE" : "\u7B7E\u540D\u65E0\u6548\uFF1AURL \u53EF\u80FD\u88AB\u7BE1\u6539";
5900
+ writeJson(res, 403, { error: { code: "STORAGE_SIGNATURE_INVALID", message } });
5901
+ log(`[serve] ${req.method ?? "GET"} ${url.pathname} -> 403 (${verification.reason})`);
5902
+ return true;
5903
+ }
5904
+ }
5905
+ const { data } = await driver.get(projectId, path);
5906
+ res.writeHead(200, {
5907
+ "content-type": meta.contentType ?? "application/octet-stream",
5908
+ "content-length": String(meta.size),
5909
+ "cache-control": meta.visibility === "public" ? "public, max-age=3600" : "private, no-store"
5910
+ });
5911
+ res.end(data);
5912
+ log(`[serve] ${req.method ?? "GET"} ${url.pathname} -> 200\uFF08\u6587\u4EF6\u4E0B\u8F7D ${meta.size}B\uFF09`);
5913
+ } catch (error) {
5914
+ if (error instanceof StorageError) {
5915
+ writeJson(res, error.status, { error: { code: error.code, message: error.message } });
5916
+ log(`[serve] ${req.method ?? "GET"} ${url.pathname} -> ${error.status} (${error.code})`);
5917
+ return true;
5918
+ }
5919
+ writeJson(res, 500, {
5920
+ error: {
5921
+ code: "FILE_DOWNLOAD_ERROR",
5922
+ message: error instanceof Error ? error.message : "\u6587\u4EF6\u4E0B\u8F7D\u5931\u8D25"
5923
+ }
5924
+ });
5925
+ log(`[serve] ${req.method ?? "GET"} ${url.pathname} -> 500\uFF08\u6587\u4EF6\u4E0B\u8F7D\u5F02\u5E38\uFF09`);
5926
+ }
5927
+ return true;
5928
+ };
5929
+ const SIGNED_FILES_ROUTE = /^\/api\/v1\/projects\/([^/]+)\/files\/([^/]+)$/;
5834
5930
  const handle = async (req, res) => {
5835
5931
  const url = new URL(req.url ?? "/", `http://${host}:${port}`);
5836
5932
  const pathname = url.pathname;
5933
+ const filesMatch = SIGNED_FILES_ROUTE.exec(pathname);
5934
+ if (filesMatch !== null && (req.method === "GET" || req.method === "HEAD")) {
5935
+ await serveSignedFile(
5936
+ req,
5937
+ res,
5938
+ url,
5939
+ decodeURIComponent(filesMatch[1]),
5940
+ decodeURIComponent(filesMatch[2])
5941
+ );
5942
+ return;
5943
+ }
5837
5944
  const normalizedPrefix = config.functionsPrefix.replace(/^\/+|\/+$/g, "");
5838
5945
  const fnRest = normalizedPrefix.length > 0 ? pathname === `/${normalizedPrefix}` || pathname.startsWith(`/${normalizedPrefix}/`) ? pathname.slice(normalizedPrefix.length + 1) : null : pathname === "/api" ? "" : pathname.startsWith("/api/") ? pathname.slice("/api/".length) : null;
5839
5946
  if (fnRest !== null) {
@@ -5981,9 +6088,12 @@ var init_server = __esm({
5981
6088
  init_executor();
5982
6089
  init_http_envelope();
5983
6090
  init_worker_executor();
6091
+ init_signature();
6092
+ init_driver();
5984
6093
  init_builtin_deps2();
5985
6094
  init_db2();
5986
6095
  init_runtime();
6096
+ init_storage();
5987
6097
  init_env();
5988
6098
  init_invoke();
5989
6099
  init_dev();
@@ -6051,7 +6161,7 @@ async function collectFunctionFiles(functionsDir) {
6051
6161
  if (conflicting.length > 0) throw new AmbiguousFunctionError(functionsDir, conflicting);
6052
6162
  const out = {};
6053
6163
  for (const name of flatNames) {
6054
- out[name] = { "index.ts": await readFile12(join15(functionsDir, `${name}.ts`), "utf8") };
6164
+ out[name] = { [`${name}.ts`]: await readFile12(join15(functionsDir, `${name}.ts`), "utf8") };
6055
6165
  }
6056
6166
  for (const name of dirNames) {
6057
6167
  const files = await collectFunctionDir(join15(functionsDir, name));
@@ -7159,6 +7269,189 @@ var init_storage2 = __esm({
7159
7269
  }
7160
7270
  });
7161
7271
 
7272
+ // packages/cli/src/sandbox-scan.ts
7273
+ import { readdir as readdir7, readFile as readFile16 } from "node:fs/promises";
7274
+ import { join as join20, relative as relative5, sep as sep3 } from "node:path";
7275
+ function extractSpecifiers(line) {
7276
+ const out = [];
7277
+ const patterns = [
7278
+ /\bfrom\s+['"]([^'"]+)['"]/g,
7279
+ // import x from '<spec>' / export … from '<spec>'
7280
+ /\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
7281
+ /\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
7282
+ /^\s*import\s+['"]([^'"]+)['"]/g
7283
+ // 副作用导入 import '<spec>'
7284
+ ];
7285
+ for (const pattern of patterns) {
7286
+ for (const match of line.matchAll(pattern)) {
7287
+ const spec = match[1];
7288
+ if (spec !== void 0) out.push(spec);
7289
+ }
7290
+ }
7291
+ return out;
7292
+ }
7293
+ function classify(specifier) {
7294
+ if (specifier.startsWith("node:")) {
7295
+ const bare = specifier.slice("node:".length);
7296
+ const root = bare.split("/")[0];
7297
+ const suggestion = BUILTIN_SUGGESTIONS[specifier] ?? BUILTIN_SUGGESTIONS[`node:${root}`];
7298
+ return {
7299
+ code: "NODE_BUILTIN",
7300
+ message: `\u6C99\u7BB1\u6CA1\u6709 node \u5185\u7F6E\u6A21\u5757\uFF0C'${specifier}' \u5728\u53D1\u5E03\u540E\u8FD0\u884C\u65F6\u4F1A\u629B ERR_MODULE_NOT_FOUND\uFF08\u672C\u5730 adep dev \u80FD\u8DD1\uFF0C\u56E0\u4E3A\u5BBF\u4E3B\u5C31\u662F Node\uFF09\u3002${suggestion ?? `\u8BF7\u79FB\u9664\u5BF9 ${root} \u7684\u4F9D\u8D56\u3002`}`
7301
+ };
7302
+ }
7303
+ if (NODE_BUILTINS.has(specifier)) {
7304
+ return {
7305
+ code: "NODE_BUILTIN",
7306
+ message: `\u6C99\u7BB1\u6CA1\u6709 node \u5185\u7F6E\u6A21\u5757\uFF0C'${specifier}' \u5728\u53D1\u5E03\u540E\u8FD0\u884C\u65F6\u4F1A\u629B ERR_MODULE_NOT_FOUND\u3002${BUILTIN_SUGGESTIONS[specifier] ?? `\u8BF7\u79FB\u9664\u5BF9 ${specifier} \u7684\u4F9D\u8D56\u3002`}`
7307
+ };
7308
+ }
7309
+ if (NATIVE_ADDON_PACKAGES.has(specifier)) {
7310
+ return {
7311
+ code: "NATIVE_ADDON",
7312
+ message: `'${specifier}' \u9700\u8981\u539F\u751F C++ \u6269\u5C55\uFF0C\u4E91\u51FD\u6570\u6C99\u7BB1\u65E0\u6CD5\u52A0\u8F7D\uFF08\u4E5F\u4E0D\u662F registry \u767D\u540D\u5355\u80FD\u89E3\u51B3\u7684\u95EE\u9898\uFF09\u3002\u8BF7\u6362\u7EAF JS \u5B9E\u73B0\uFF08\u5982 bcrypt\u2192@adep/crypto \u7684 hashPassword\u3001sharp\u2192\u6784\u5EFA\u671F\u9884\u5904\u7406\u56FE\u7247\uFF09\u3002`
7313
+ };
7314
+ }
7315
+ return null;
7316
+ }
7317
+ async function scanFunctionSources(rootDir, functionsDir) {
7318
+ const base = join20(rootDir, functionsDir);
7319
+ const findings = [];
7320
+ async function walk(current) {
7321
+ let entries;
7322
+ try {
7323
+ entries = await readdir7(current, { withFileTypes: true });
7324
+ } catch {
7325
+ return;
7326
+ }
7327
+ for (const entry of entries) {
7328
+ const abs = join20(current, entry.name);
7329
+ if (entry.isDirectory()) {
7330
+ if (SKIP_DIRS.has(entry.name)) continue;
7331
+ await walk(abs);
7332
+ continue;
7333
+ }
7334
+ if (!entry.isFile()) continue;
7335
+ if (!SOURCE_EXTENSIONS.some((ext) => entry.name.endsWith(ext))) continue;
7336
+ if (entry.name.endsWith(".d.ts")) continue;
7337
+ let content;
7338
+ try {
7339
+ content = await readFile16(abs, "utf8");
7340
+ } catch {
7341
+ continue;
7342
+ }
7343
+ if (content.length > MAX_SCAN_BYTES) continue;
7344
+ const relPath = relative5(rootDir, abs).split(sep3).join("/");
7345
+ const lines = content.split("\n");
7346
+ for (const [index, line] of lines.entries()) {
7347
+ const trimmed = line.trim();
7348
+ if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*")) continue;
7349
+ for (const specifier of extractSpecifiers(line)) {
7350
+ const hit = classify(specifier);
7351
+ if (hit !== null) {
7352
+ findings.push({
7353
+ file: relPath,
7354
+ line: index + 1,
7355
+ code: hit.code,
7356
+ specifier,
7357
+ message: hit.message
7358
+ });
7359
+ }
7360
+ }
7361
+ }
7362
+ }
7363
+ }
7364
+ await walk(base);
7365
+ return findings;
7366
+ }
7367
+ var NODE_BUILTINS, NATIVE_ADDON_PACKAGES, BUILTIN_SUGGESTIONS, SOURCE_EXTENSIONS, SKIP_DIRS, MAX_SCAN_BYTES;
7368
+ var init_sandbox_scan = __esm({
7369
+ "packages/cli/src/sandbox-scan.ts"() {
7370
+ "use strict";
7371
+ NODE_BUILTINS = /* @__PURE__ */ new Set([
7372
+ "assert",
7373
+ "async_hooks",
7374
+ "buffer",
7375
+ "child_process",
7376
+ "cluster",
7377
+ "console",
7378
+ "constants",
7379
+ "crypto",
7380
+ "dgram",
7381
+ "diagnostics_channel",
7382
+ "dns",
7383
+ "domain",
7384
+ "events",
7385
+ "fs",
7386
+ "http",
7387
+ "http2",
7388
+ "https",
7389
+ "inspector",
7390
+ "module",
7391
+ "net",
7392
+ "os",
7393
+ "path",
7394
+ "perf_hooks",
7395
+ "process",
7396
+ "punycode",
7397
+ "querystring",
7398
+ "readline",
7399
+ "repl",
7400
+ "stream",
7401
+ "string_decoder",
7402
+ "sys",
7403
+ "timers",
7404
+ "tls",
7405
+ "trace_events",
7406
+ "tty",
7407
+ "url",
7408
+ "util",
7409
+ "v8",
7410
+ "vm",
7411
+ "wasi",
7412
+ "worker_threads",
7413
+ "zlib"
7414
+ ]);
7415
+ NATIVE_ADDON_PACKAGES = /* @__PURE__ */ new Set([
7416
+ "bcrypt",
7417
+ "better-sqlite3",
7418
+ "sqlite3",
7419
+ "sharp",
7420
+ "canvas",
7421
+ "argon2",
7422
+ "node-gyp",
7423
+ "farmhash",
7424
+ "xxhash-addon",
7425
+ "zeromq",
7426
+ "grpc",
7427
+ "@grpc/grpc-js",
7428
+ "leveldown",
7429
+ "rocksdb",
7430
+ "node-canvas",
7431
+ "microtime",
7432
+ "sodium-native",
7433
+ "re2"
7434
+ ]);
7435
+ BUILTIN_SUGGESTIONS = {
7436
+ "node:crypto": "\u7528 WebCrypto\uFF08\u6C99\u7BB1\u5DF2\u6CE8\u5165 crypto\uFF09\u6216 @adep/crypto \u7684 signJwt / hashPassword / encryptSecret",
7437
+ crypto: "\u7528 WebCrypto\uFF08\u6C99\u7BB1\u5DF2\u6CE8\u5165 crypto\uFF09\u6216 @adep/crypto",
7438
+ "node:fs": "\u6C99\u7BB1\u65E0\u6587\u4EF6\u7CFB\u7EDF\uFF1B\u9879\u76EE\u6570\u636E\u8D70 ctx.cloud.db\uFF0C\u6587\u4EF6\u8D70 ctx.cloud.storage",
7439
+ fs: "\u6C99\u7BB1\u65E0\u6587\u4EF6\u7CFB\u7EDF\uFF1B\u9879\u76EE\u6570\u636E\u8D70 ctx.cloud.db\uFF0C\u6587\u4EF6\u8D70 ctx.cloud.storage",
7440
+ "node:path": "\u624B\u5199\u5B57\u7B26\u4E32\u62FC\u63A5\uFF08\u6C99\u7BB1\u91CC\u7684\u8DEF\u5F84\u90FD\u662F POSIX \u5F62\u5F0F\uFF09",
7441
+ path: "\u624B\u5199\u5B57\u7B26\u4E32\u62FC\u63A5\uFF08\u6C99\u7BB1\u91CC\u7684\u8DEF\u5F84\u90FD\u662F POSIX \u5F62\u5F0F\uFF09",
7442
+ "node:buffer": "\u7528 Uint8Array + TextEncoder/TextDecoder\uFF08\u6C99\u7BB1\u5DF2\u6CE8\u5165\uFF09",
7443
+ buffer: "\u7528 Uint8Array + TextEncoder/TextDecoder\uFF08\u6C99\u7BB1\u5DF2\u6CE8\u5165\uFF09",
7444
+ "node:process": "\u73AF\u5883\u53D8\u91CF\u8D70 ctx.env\uFF08adep.config.ts \u7684 environment\uFF09",
7445
+ process: "\u73AF\u5883\u53D8\u91CF\u8D70 ctx.env\uFF08adep.config.ts \u7684 environment\uFF09",
7446
+ "node:stream": "\u7528\u6C99\u7BB1\u6CE8\u5165\u7684 ReadableStream / WritableStream / TransformStream",
7447
+ stream: "\u7528\u6C99\u7BB1\u6CE8\u5165\u7684 ReadableStream / WritableStream / TransformStream"
7448
+ };
7449
+ SOURCE_EXTENSIONS = [".ts", ".js", ".mjs", ".cjs", ".tsx", ".jsx"];
7450
+ SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "__tests__", "dist", ".git", ".adep", "coverage"]);
7451
+ MAX_SCAN_BYTES = 512 * 1024;
7452
+ }
7453
+ });
7454
+
7162
7455
  // packages/cli/src/doctor.ts
7163
7456
  var doctor_exports = {};
7164
7457
  __export(doctor_exports, {
@@ -7189,12 +7482,13 @@ async function credentialMode(file) {
7189
7482
  return void 0;
7190
7483
  }
7191
7484
  }
7192
- async function runDoctor(paths) {
7485
+ async function runDoctor(paths, cwd = process.cwd()) {
7193
7486
  const credentials = await loadCredentials(paths);
7194
7487
  const server = credentials?.server ?? resolveServer();
7195
7488
  const mode = await credentialMode(paths.credentialsFile);
7196
7489
  const offline = await isOffline();
7197
7490
  const health = await probeServer(server);
7491
+ const sandbox = await scanSandbox(cwd);
7198
7492
  const report = {
7199
7493
  server,
7200
7494
  credentials: {
@@ -7205,7 +7499,8 @@ async function runDoctor(paths) {
7205
7499
  network: { offline },
7206
7500
  serverHealth: health,
7207
7501
  offlineCommands: [...OFFLINE_COMMANDS],
7208
- boundaries: SIM_BOUNDARIES.map((b) => ({ kind: b.kind, title: b.title, detail: b.detail }))
7502
+ boundaries: SIM_BOUNDARIES.map((b) => ({ kind: b.kind, title: b.title, detail: b.detail })),
7503
+ sandbox
7209
7504
  };
7210
7505
  if (credentials !== null) report.credentials.email = credentials.email;
7211
7506
  if (mode !== void 0 && mode !== "600") {
@@ -7213,6 +7508,22 @@ async function runDoctor(paths) {
7213
7508
  }
7214
7509
  return report;
7215
7510
  }
7511
+ async function scanSandbox(cwd) {
7512
+ let functionsDir = "functions";
7513
+ try {
7514
+ const config = await loadAdepConfigModule(cwd);
7515
+ if (config?.functionsDir !== void 0 && config.functionsDir.length > 0) {
7516
+ functionsDir = config.functionsDir;
7517
+ }
7518
+ } catch {
7519
+ }
7520
+ try {
7521
+ const findings = await scanFunctionSources(cwd, functionsDir);
7522
+ return { functionsDir, findings };
7523
+ } catch {
7524
+ return { functionsDir: null, findings: [] };
7525
+ }
7526
+ }
7216
7527
  function formatDoctorReport(report) {
7217
7528
  const lines = ["adep doctor \xB7 \u73AF\u5883\u81EA\u68C0", ""];
7218
7529
  lines.push("\u51ED\u636E\u4E0E\u8EAB\u4EFD");
@@ -7240,6 +7551,28 @@ function formatDoctorReport(report) {
7240
7551
  }
7241
7552
  lines.push("", "\u79BB\u7EBF\u53EF\u7528\u8303\u56F4\uFF08\u65AD\u7F51\u65F6\u4ECD\u53EF\u6267\u884C\uFF09");
7242
7553
  for (const command of report.offlineCommands) lines.push(` \xB7 ${command}`);
7554
+ lines.push("", "\u4E91\u51FD\u6570\u6C99\u7BB1\u517C\u5BB9\u6027\uFF08\u53D1\u5E03\u524D\u5C31\u80FD\u6539\uFF0C\u522B\u7B49\u8FD0\u884C\u65F6\u70B8\uFF09");
7555
+ if (report.sandbox.functionsDir === null) {
7556
+ lines.push(" \xB7 \u672A\u627E\u5230\u51FD\u6570\u76EE\u5F55\uFF0C\u8DF3\u8FC7\u626B\u63CF");
7557
+ } else if (report.sandbox.findings.length === 0) {
7558
+ lines.push(` \u2713 ${report.sandbox.functionsDir}/ \u5185\u672A\u53D1\u73B0 node \u5185\u7F6E\u6A21\u5757 / \u539F\u751F addon \u4F9D\u8D56`);
7559
+ } else {
7560
+ const byFile = /* @__PURE__ */ new Map();
7561
+ for (const finding of report.sandbox.findings) {
7562
+ const list = byFile.get(finding.file) ?? [];
7563
+ list.push(finding);
7564
+ byFile.set(finding.file, list);
7565
+ }
7566
+ lines.push(
7567
+ ` \u2717 \u53D1\u73B0 ${report.sandbox.findings.length} \u5904\u6C99\u7BB1\u4E0D\u517C\u5BB9\uFF08\u672C\u5730 adep dev \u80FD\u8DD1\uFF0C\u53D1\u5E03\u540E\u4F1A\u5931\u8D25\uFF09\uFF1A`
7568
+ );
7569
+ for (const [file, findingList] of byFile) {
7570
+ for (const finding of findingList) {
7571
+ lines.push(` ${file}:${finding.line} [${finding.code}] ${finding.specifier}`);
7572
+ lines.push(` ${finding.message}`);
7573
+ }
7574
+ }
7575
+ }
7243
7576
  lines.push("", "\u672C\u5730\u6A21\u62DF\u8FD0\u884C\u65F6\u7684\u80FD\u529B\u8FB9\u754C\uFF08\u4E0E adep dev \u542F\u52A8\u6A2A\u5E45\u540C\u6E90\uFF09");
7244
7577
  for (const boundary of report.boundaries) {
7245
7578
  lines.push(` \xB7 [${boundary.kind}] ${boundary.title}\uFF1A${boundary.detail}`);
@@ -7254,6 +7587,8 @@ var init_doctor = __esm({
7254
7587
  init_config();
7255
7588
  init_boundary();
7256
7589
  init_net();
7590
+ init_sandbox_scan();
7591
+ init_ts_config();
7257
7592
  SERVER_PROBE_TIMEOUT_MS = 5e3;
7258
7593
  OFFLINE_COMMANDS = [
7259
7594
  "adep init",
@@ -7626,8 +7961,8 @@ __export(hosting_exports, {
7626
7961
  hostingInfo: () => hostingInfo,
7627
7962
  hostingPull: () => hostingPull
7628
7963
  });
7629
- import { readdir as readdir7, readFile as readFile16, stat as stat13 } from "node:fs/promises";
7630
- import { dirname as dirname11, join as join20, relative as relative5, resolve as resolve14, sep as sep3 } from "node:path";
7964
+ import { readdir as readdir8, readFile as readFile17, stat as stat13 } from "node:fs/promises";
7965
+ import { dirname as dirname11, join as join21, relative as relative6, resolve as resolve14, sep as sep4 } from "node:path";
7631
7966
  async function open4(paths, options) {
7632
7967
  const client = await createClient(paths);
7633
7968
  const project2 = await resolveSlug(resolve14(options.cwd), options.slug);
@@ -7648,16 +7983,16 @@ async function collectSiteFiles(dir) {
7648
7983
  const walk = async (sub) => {
7649
7984
  let entries;
7650
7985
  try {
7651
- entries = await readdir7(sub, { withFileTypes: true });
7986
+ entries = await readdir8(sub, { withFileTypes: true });
7652
7987
  } catch {
7653
7988
  return;
7654
7989
  }
7655
7990
  for (const entry of entries) {
7656
- const full = join20(sub, entry.name);
7991
+ const full = join21(sub, entry.name);
7657
7992
  if (entry.isDirectory()) {
7658
7993
  await walk(full);
7659
7994
  } else if (entry.isFile()) {
7660
- const rel = relative5(root, full).split(sep3).join("/");
7995
+ const rel = relative6(root, full).split(sep4).join("/");
7661
7996
  files.set(rel, full);
7662
7997
  }
7663
7998
  }
@@ -7677,7 +8012,7 @@ async function hostingDeploy(paths, options) {
7677
8012
  for (const [rel, full] of files) {
7678
8013
  log(`[adep] \u4E0A\u4F20 site/${rel}`);
7679
8014
  const info2 = await stat13(full);
7680
- const bytes = await readFile16(full);
8015
+ const bytes = await readFile17(full);
7681
8016
  const form = new FormData();
7682
8017
  form.set("path", `site/${rel}`);
7683
8018
  form.set("visibility", "public");
@@ -7715,7 +8050,7 @@ async function hostingPull(paths, options) {
7715
8050
  const response = await client.download(url, "HOSTING_DOWNLOAD_FAILED");
7716
8051
  const bytes = Buffer.from(await response.arrayBuffer());
7717
8052
  const rel = entry.path.replace(/^site\//, "");
7718
- const target = join20(outputDir, ...rel.split("/"));
8053
+ const target = join21(outputDir, ...rel.split("/"));
7719
8054
  await mkdir12(dirname11(target), { recursive: true });
7720
8055
  await writeFile10(target, bytes);
7721
8056
  files.push({ path: entry.path, size: bytes.length });
package/dist/vite.js CHANGED
@@ -94,6 +94,18 @@ import { createHash } from "node:crypto";
94
94
 
95
95
  // packages/runtime/src/functions/domain.ts
96
96
  var MAX_TOTAL_SOURCE_BYTES = 256 * 1024;
97
+ var ENTRY_FILE = "index.ts";
98
+ var ENTRY_SOURCE_EXTS = [".ts", ".js", ".mjs", ".cjs"];
99
+ function flatEntryCandidates(name) {
100
+ return ENTRY_SOURCE_EXTS.map((ext) => `${name}${ext}`);
101
+ }
102
+ function resolveDraftEntry(name, files) {
103
+ if (files[ENTRY_FILE] !== void 0) return ENTRY_FILE;
104
+ for (const candidate of flatEntryCandidates(name)) {
105
+ if (files[candidate] !== void 0) return candidate;
106
+ }
107
+ return null;
108
+ }
97
109
  var FnError = class extends Error {
98
110
  status;
99
111
  code;
@@ -254,7 +266,7 @@ var WorkerFunctionExecutor = class {
254
266
  async execute(input) {
255
267
  const timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS;
256
268
  const memoryLimitMb = input.memoryLimitMb ?? DEFAULT_MEMORY_LIMIT_MB;
257
- const entry = input.entry ?? "index.ts";
269
+ const entry = input.entry ?? resolveDraftEntry(input.fn.name, input.files) ?? "index.ts";
258
270
  const deps = this.depsConfig === void 0 ? void 0 : resolveExecutionDeps(this.depsConfig, input.project.id, input.files["package.json"]);
259
271
  const logs = [];
260
272
  return new Promise((resolve3, reject) => {
@@ -2426,11 +2438,12 @@ function createLocalStorageDriver(options) {
2426
2438
  }
2427
2439
 
2428
2440
  // packages/cli/src/sim/storage.ts
2441
+ var SIM_SIGN_SECRET = "adep-sim-secret";
2429
2442
  function createSimStorageCapability(options) {
2430
2443
  const dir = join3(options.cwd, ".adep", "sim", "storage");
2431
2444
  const driver = createLocalStorageDriver({ dir });
2432
2445
  const signer = {
2433
- secret: options.secret ?? "adep-sim-secret",
2446
+ secret: options.secret ?? SIM_SIGN_SECRET,
2434
2447
  baseUrl: options.baseUrl ?? "http://127.0.0.1:8787"
2435
2448
  };
2436
2449
  const bundle = createStorageCapability(driver, signer, options.projectId ?? "sim-project");
@@ -2619,6 +2632,8 @@ async function createSimRuntime(options) {
2619
2632
  bundle,
2620
2633
  realtime: simRealtime.realtime,
2621
2634
  db,
2635
+ /** 存储句柄(serve 的签名文件下载路由用它读对象;bundle 里的 capability 是同一驱动的 RPC 面)。 */
2636
+ storage,
2622
2637
  dispose: async () => {
2623
2638
  await db.driver.close();
2624
2639
  }
@@ -2714,9 +2729,13 @@ function createSimInvokeHandler(options) {
2714
2729
  // shared/builtin-deps.ts
2715
2730
  var CF_COMPAT_SPECIFIER = "@adep/cf-compat";
2716
2731
  var STATE_OBJECTS_SPECIFIER = "@adep/runtime/state-objects";
2732
+ var CRYPTO_SPECIFIER = "@adep/crypto";
2733
+ var ROUTE_SPECIFIER = "@adep/runtime/route";
2717
2734
  var BUILTIN_DEPS = [
2718
2735
  CF_COMPAT_SPECIFIER,
2719
2736
  STATE_OBJECTS_SPECIFIER,
2737
+ CRYPTO_SPECIFIER,
2738
+ ROUTE_SPECIFIER,
2720
2739
  "axios",
2721
2740
  "lodash",
2722
2741
  "dayjs",
@@ -2725,7 +2744,12 @@ var BUILTIN_DEPS = [
2725
2744
  var BUILTIN_DEPS_CSV = BUILTIN_DEPS.join(",");
2726
2745
 
2727
2746
  // packages/cli/src/builtin-deps.ts
2728
- var LOCAL_BUILTIN_DEPS = [CF_COMPAT_SPECIFIER, STATE_OBJECTS_SPECIFIER];
2747
+ var LOCAL_BUILTIN_DEPS = [
2748
+ CF_COMPAT_SPECIFIER,
2749
+ STATE_OBJECTS_SPECIFIER,
2750
+ CRYPTO_SPECIFIER,
2751
+ ROUTE_SPECIFIER
2752
+ ];
2729
2753
 
2730
2754
  // packages/cli/src/sim/boundary.ts
2731
2755
  var SIM_BOUNDARIES = [
@@ -4443,12 +4467,15 @@ async function startDevServer(options) {
4443
4467
 
4444
4468
  // packages/cli/src/vite.ts
4445
4469
  import {
4470
+ createAdepAutomationScriptMiddleware,
4446
4471
  createAdepFnProxyScriptMiddleware,
4447
4472
  createAdepProxyMiddleware,
4448
4473
  FN_FETCH_INTERCEPTOR_SCRIPT,
4474
+ injectAutomationScriptTag,
4449
4475
  injectFnProxyScriptTag,
4450
4476
  isAddrInUse,
4451
- normalizeFunctionPrefix
4477
+ normalizeFunctionPrefix,
4478
+ PREVIEW_AUTOMATION_SCRIPT
4452
4479
  } from "@adep/vite-plugin";
4453
4480
  var createDevServer = (opts) => startDevServer({ cwd: opts.cwd, port: opts.port, prefix: opts.prefix, log: opts.log });
4454
4481
  var loadConfig2 = (cwd) => loadAdepConfigModule(cwd);
@@ -4458,10 +4485,13 @@ function adepVitePlugin(options = {}) {
4458
4485
  var vite_default = adepVitePlugin;
4459
4486
  export {
4460
4487
  FN_FETCH_INTERCEPTOR_SCRIPT,
4488
+ PREVIEW_AUTOMATION_SCRIPT,
4461
4489
  adepVitePlugin,
4490
+ createAdepAutomationScriptMiddleware,
4462
4491
  createAdepFnProxyScriptMiddleware,
4463
4492
  createAdepProxyMiddleware,
4464
4493
  vite_default as default,
4494
+ injectAutomationScriptTag,
4465
4495
  injectFnProxyScriptTag,
4466
4496
  isAddrInUse,
4467
4497
  normalizeFunctionPrefix
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adep/cli",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "type": "module",
5
5
  "description": "`adep` 命令行工具包位:登录 / 初始化 / 本地调试 / 部署 / 数据库维护 / 云存储 / 静态托管 / 微前端组件(widget),实现见任务单 CLI-001 ~ CLI-003 与 FE-002;子路径 `@adep/cli/vite` 提供云函数 vite 插件(CLI-014)",
6
6
  "bin": {
@@ -21,11 +21,12 @@
21
21
  "commander": "14.0.2",
22
22
  "esbuild": "0.25.12",
23
23
  "@adep/cf-compat": "0.1.1",
24
- "@adep/vite-plugin": "0.1.5"
24
+ "@adep/runtime": "0.2.7",
25
+ "@adep/crypto": "0.1.0",
26
+ "@adep/vite-plugin": "0.1.7"
25
27
  },
26
28
  "devDependencies": {
27
- "@adep/types": "0.2.3",
28
- "@adep/runtime": "0.2.6"
29
+ "@adep/types": "0.2.3"
29
30
  },
30
31
  "scripts": {
31
32
  "test": "vitest run",