@adep/cli 0.1.15 → 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 +338 -16
  2. package/dist/vite.js +14 -2
  3. package/package.json +4 -3
package/dist/index.js CHANGED
@@ -2693,6 +2693,14 @@ function hmacSha256(key, message) {
2693
2693
  function hmacSha256Hex(key, message) {
2694
2694
  return [...hmacSha256(key, message)].map((b) => b.toString(16).padStart(2, "0")).join("");
2695
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
+ }
2696
2704
  var K, rotr;
2697
2705
  var init_hmac_sha256 = __esm({
2698
2706
  "packages/runtime/src/storage/hmac-sha256.ts"() {
@@ -2778,6 +2786,22 @@ function signDownloadUrl(config, projectId, path, ttlSeconds = DEFAULT_SIGN_TTL_
2778
2786
  const signature = sign(config.secret, projectId, path, expires);
2779
2787
  return `${base}?${SIGN_EXPIRES_KEY}=${expires}&${SIGN_SIGNATURE_KEY}=${signature}`;
2780
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
+ }
2781
2805
  var DEFAULT_SIGN_TTL_SECONDS, SIGN_EXPIRES_KEY, SIGN_SIGNATURE_KEY;
2782
2806
  var init_signature = __esm({
2783
2807
  "packages/runtime/src/storage/signature.ts"() {
@@ -3006,7 +3030,7 @@ function createLocalStorageDriver(options) {
3006
3030
  assertStoragePrefix(prefix);
3007
3031
  const root = bucketDir(projectId);
3008
3032
  const metas = [];
3009
- const walk = async (dir, relative6) => {
3033
+ const walk = async (dir, relative7) => {
3010
3034
  let entries;
3011
3035
  try {
3012
3036
  entries = await readdir3(dir, { withFileTypes: true });
@@ -3017,7 +3041,7 @@ function createLocalStorageDriver(options) {
3017
3041
  for (const entry of entries) {
3018
3042
  if (entry.name.endsWith(META_SUFFIX)) continue;
3019
3043
  const full = join6(dir, entry.name);
3020
- const rel = relative6 === "" ? entry.name : `${relative6}/${entry.name}`;
3044
+ const rel = relative7 === "" ? entry.name : `${relative7}/${entry.name}`;
3021
3045
  if (entry.isFile()) {
3022
3046
  if (prefix !== void 0 && !rel.startsWith(prefix)) continue;
3023
3047
  tasks.push(
@@ -3071,17 +3095,19 @@ function createSimStorageCapability(options) {
3071
3095
  const dir = join7(options.cwd, ".adep", "sim", "storage");
3072
3096
  const driver = createLocalStorageDriver({ dir });
3073
3097
  const signer = {
3074
- secret: options.secret ?? "adep-sim-secret",
3098
+ secret: options.secret ?? SIM_SIGN_SECRET,
3075
3099
  baseUrl: options.baseUrl ?? "http://127.0.0.1:8787"
3076
3100
  };
3077
3101
  const bundle = createStorageCapability(driver, signer, options.projectId ?? "sim-project");
3078
3102
  return { bundle, driver };
3079
3103
  }
3104
+ var SIM_SIGN_SECRET;
3080
3105
  var init_storage = __esm({
3081
3106
  "packages/cli/src/sim/storage.ts"() {
3082
3107
  "use strict";
3083
3108
  init_cloud2();
3084
3109
  init_local();
3110
+ SIM_SIGN_SECRET = "adep-sim-secret";
3085
3111
  }
3086
3112
  });
3087
3113
 
@@ -3281,6 +3307,8 @@ async function createSimRuntime(options) {
3281
3307
  bundle,
3282
3308
  realtime: simRealtime.realtime,
3283
3309
  db,
3310
+ /** 存储句柄(serve 的签名文件下载路由用它读对象;bundle 里的 capability 是同一驱动的 RPC 面)。 */
3311
+ storage,
3284
3312
  dispose: async () => {
3285
3313
  await db.driver.close();
3286
3314
  }
@@ -3395,15 +3423,19 @@ var init_invoke = __esm({
3395
3423
  });
3396
3424
 
3397
3425
  // shared/builtin-deps.ts
3398
- 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;
3399
3427
  var init_builtin_deps = __esm({
3400
3428
  "shared/builtin-deps.ts"() {
3401
3429
  "use strict";
3402
3430
  CF_COMPAT_SPECIFIER = "@adep/cf-compat";
3403
3431
  STATE_OBJECTS_SPECIFIER = "@adep/runtime/state-objects";
3432
+ CRYPTO_SPECIFIER = "@adep/crypto";
3433
+ ROUTE_SPECIFIER = "@adep/runtime/route";
3404
3434
  BUILTIN_DEPS = [
3405
3435
  CF_COMPAT_SPECIFIER,
3406
3436
  STATE_OBJECTS_SPECIFIER,
3437
+ CRYPTO_SPECIFIER,
3438
+ ROUTE_SPECIFIER,
3407
3439
  "axios",
3408
3440
  "lodash",
3409
3441
  "dayjs",
@@ -3419,7 +3451,12 @@ var init_builtin_deps2 = __esm({
3419
3451
  "packages/cli/src/builtin-deps.ts"() {
3420
3452
  "use strict";
3421
3453
  init_builtin_deps();
3422
- 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
+ ];
3423
3460
  }
3424
3461
  });
3425
3462
 
@@ -5338,7 +5375,7 @@ function splitSqlStatements(source) {
5338
5375
  const lines = source.split("\n");
5339
5376
  for (const rawLine of lines) {
5340
5377
  const trimmed = rawLine.trim();
5341
- if (trimmed.length === 0 || trimmed.startsWith("--")) continue;
5378
+ if (!inQuote && (trimmed.length === 0 || trimmed.startsWith("--"))) continue;
5342
5379
  for (let i = 0; i < rawLine.length; i++) {
5343
5380
  const ch = rawLine[i];
5344
5381
  if (inQuote) {
@@ -5367,7 +5404,7 @@ function splitSqlStatements(source) {
5367
5404
  }
5368
5405
  buffer += ch;
5369
5406
  }
5370
- buffer += " ";
5407
+ buffer += inQuote ? "\n" : " ";
5371
5408
  }
5372
5409
  if (inQuote) {
5373
5410
  throw new Error(`SQL \u8FC1\u79FB\u6587\u4EF6\u5F15\u53F7\u672A\u95ED\u5408\uFF1A${buffer.trim().slice(0, 80)}\u2026`);
@@ -5844,9 +5881,66 @@ async function startServeServer(options) {
5844
5881
  return false;
5845
5882
  }
5846
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\/([^/]+)$/;
5847
5930
  const handle = async (req, res) => {
5848
5931
  const url = new URL(req.url ?? "/", `http://${host}:${port}`);
5849
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
+ }
5850
5944
  const normalizedPrefix = config.functionsPrefix.replace(/^\/+|\/+$/g, "");
5851
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;
5852
5946
  if (fnRest !== null) {
@@ -5994,9 +6088,12 @@ var init_server = __esm({
5994
6088
  init_executor();
5995
6089
  init_http_envelope();
5996
6090
  init_worker_executor();
6091
+ init_signature();
6092
+ init_driver();
5997
6093
  init_builtin_deps2();
5998
6094
  init_db2();
5999
6095
  init_runtime();
6096
+ init_storage();
6000
6097
  init_env();
6001
6098
  init_invoke();
6002
6099
  init_dev();
@@ -7172,6 +7269,189 @@ var init_storage2 = __esm({
7172
7269
  }
7173
7270
  });
7174
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
+
7175
7455
  // packages/cli/src/doctor.ts
7176
7456
  var doctor_exports = {};
7177
7457
  __export(doctor_exports, {
@@ -7202,12 +7482,13 @@ async function credentialMode(file) {
7202
7482
  return void 0;
7203
7483
  }
7204
7484
  }
7205
- async function runDoctor(paths) {
7485
+ async function runDoctor(paths, cwd = process.cwd()) {
7206
7486
  const credentials = await loadCredentials(paths);
7207
7487
  const server = credentials?.server ?? resolveServer();
7208
7488
  const mode = await credentialMode(paths.credentialsFile);
7209
7489
  const offline = await isOffline();
7210
7490
  const health = await probeServer(server);
7491
+ const sandbox = await scanSandbox(cwd);
7211
7492
  const report = {
7212
7493
  server,
7213
7494
  credentials: {
@@ -7218,7 +7499,8 @@ async function runDoctor(paths) {
7218
7499
  network: { offline },
7219
7500
  serverHealth: health,
7220
7501
  offlineCommands: [...OFFLINE_COMMANDS],
7221
- 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
7222
7504
  };
7223
7505
  if (credentials !== null) report.credentials.email = credentials.email;
7224
7506
  if (mode !== void 0 && mode !== "600") {
@@ -7226,6 +7508,22 @@ async function runDoctor(paths) {
7226
7508
  }
7227
7509
  return report;
7228
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
+ }
7229
7527
  function formatDoctorReport(report) {
7230
7528
  const lines = ["adep doctor \xB7 \u73AF\u5883\u81EA\u68C0", ""];
7231
7529
  lines.push("\u51ED\u636E\u4E0E\u8EAB\u4EFD");
@@ -7253,6 +7551,28 @@ function formatDoctorReport(report) {
7253
7551
  }
7254
7552
  lines.push("", "\u79BB\u7EBF\u53EF\u7528\u8303\u56F4\uFF08\u65AD\u7F51\u65F6\u4ECD\u53EF\u6267\u884C\uFF09");
7255
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
+ }
7256
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");
7257
7577
  for (const boundary of report.boundaries) {
7258
7578
  lines.push(` \xB7 [${boundary.kind}] ${boundary.title}\uFF1A${boundary.detail}`);
@@ -7267,6 +7587,8 @@ var init_doctor = __esm({
7267
7587
  init_config();
7268
7588
  init_boundary();
7269
7589
  init_net();
7590
+ init_sandbox_scan();
7591
+ init_ts_config();
7270
7592
  SERVER_PROBE_TIMEOUT_MS = 5e3;
7271
7593
  OFFLINE_COMMANDS = [
7272
7594
  "adep init",
@@ -7639,8 +7961,8 @@ __export(hosting_exports, {
7639
7961
  hostingInfo: () => hostingInfo,
7640
7962
  hostingPull: () => hostingPull
7641
7963
  });
7642
- import { readdir as readdir7, readFile as readFile16, stat as stat13 } from "node:fs/promises";
7643
- 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";
7644
7966
  async function open4(paths, options) {
7645
7967
  const client = await createClient(paths);
7646
7968
  const project2 = await resolveSlug(resolve14(options.cwd), options.slug);
@@ -7661,16 +7983,16 @@ async function collectSiteFiles(dir) {
7661
7983
  const walk = async (sub) => {
7662
7984
  let entries;
7663
7985
  try {
7664
- entries = await readdir7(sub, { withFileTypes: true });
7986
+ entries = await readdir8(sub, { withFileTypes: true });
7665
7987
  } catch {
7666
7988
  return;
7667
7989
  }
7668
7990
  for (const entry of entries) {
7669
- const full = join20(sub, entry.name);
7991
+ const full = join21(sub, entry.name);
7670
7992
  if (entry.isDirectory()) {
7671
7993
  await walk(full);
7672
7994
  } else if (entry.isFile()) {
7673
- const rel = relative5(root, full).split(sep3).join("/");
7995
+ const rel = relative6(root, full).split(sep4).join("/");
7674
7996
  files.set(rel, full);
7675
7997
  }
7676
7998
  }
@@ -7690,7 +8012,7 @@ async function hostingDeploy(paths, options) {
7690
8012
  for (const [rel, full] of files) {
7691
8013
  log(`[adep] \u4E0A\u4F20 site/${rel}`);
7692
8014
  const info2 = await stat13(full);
7693
- const bytes = await readFile16(full);
8015
+ const bytes = await readFile17(full);
7694
8016
  const form = new FormData();
7695
8017
  form.set("path", `site/${rel}`);
7696
8018
  form.set("visibility", "public");
@@ -7728,7 +8050,7 @@ async function hostingPull(paths, options) {
7728
8050
  const response = await client.download(url, "HOSTING_DOWNLOAD_FAILED");
7729
8051
  const bytes = Buffer.from(await response.arrayBuffer());
7730
8052
  const rel = entry.path.replace(/^site\//, "");
7731
- const target = join20(outputDir, ...rel.split("/"));
8053
+ const target = join21(outputDir, ...rel.split("/"));
7732
8054
  await mkdir12(dirname11(target), { recursive: true });
7733
8055
  await writeFile10(target, bytes);
7734
8056
  files.push({ path: entry.path, size: bytes.length });
package/dist/vite.js CHANGED
@@ -2438,11 +2438,12 @@ function createLocalStorageDriver(options) {
2438
2438
  }
2439
2439
 
2440
2440
  // packages/cli/src/sim/storage.ts
2441
+ var SIM_SIGN_SECRET = "adep-sim-secret";
2441
2442
  function createSimStorageCapability(options) {
2442
2443
  const dir = join3(options.cwd, ".adep", "sim", "storage");
2443
2444
  const driver = createLocalStorageDriver({ dir });
2444
2445
  const signer = {
2445
- secret: options.secret ?? "adep-sim-secret",
2446
+ secret: options.secret ?? SIM_SIGN_SECRET,
2446
2447
  baseUrl: options.baseUrl ?? "http://127.0.0.1:8787"
2447
2448
  };
2448
2449
  const bundle = createStorageCapability(driver, signer, options.projectId ?? "sim-project");
@@ -2631,6 +2632,8 @@ async function createSimRuntime(options) {
2631
2632
  bundle,
2632
2633
  realtime: simRealtime.realtime,
2633
2634
  db,
2635
+ /** 存储句柄(serve 的签名文件下载路由用它读对象;bundle 里的 capability 是同一驱动的 RPC 面)。 */
2636
+ storage,
2634
2637
  dispose: async () => {
2635
2638
  await db.driver.close();
2636
2639
  }
@@ -2726,9 +2729,13 @@ function createSimInvokeHandler(options) {
2726
2729
  // shared/builtin-deps.ts
2727
2730
  var CF_COMPAT_SPECIFIER = "@adep/cf-compat";
2728
2731
  var STATE_OBJECTS_SPECIFIER = "@adep/runtime/state-objects";
2732
+ var CRYPTO_SPECIFIER = "@adep/crypto";
2733
+ var ROUTE_SPECIFIER = "@adep/runtime/route";
2729
2734
  var BUILTIN_DEPS = [
2730
2735
  CF_COMPAT_SPECIFIER,
2731
2736
  STATE_OBJECTS_SPECIFIER,
2737
+ CRYPTO_SPECIFIER,
2738
+ ROUTE_SPECIFIER,
2732
2739
  "axios",
2733
2740
  "lodash",
2734
2741
  "dayjs",
@@ -2737,7 +2744,12 @@ var BUILTIN_DEPS = [
2737
2744
  var BUILTIN_DEPS_CSV = BUILTIN_DEPS.join(",");
2738
2745
 
2739
2746
  // packages/cli/src/builtin-deps.ts
2740
- 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
+ ];
2741
2753
 
2742
2754
  // packages/cli/src/sim/boundary.ts
2743
2755
  var SIM_BOUNDARIES = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adep/cli",
3
- "version": "0.1.15",
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,10 +21,11 @@
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.6"
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/runtime": "0.2.6",
28
29
  "@adep/types": "0.2.3"
29
30
  },
30
31
  "scripts": {