everything-dev 1.16.3 → 1.19.0

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 (83) hide show
  1. package/dist/cli/init.cjs +167 -87
  2. package/dist/cli/init.cjs.map +1 -1
  3. package/dist/cli/init.d.cts +14 -2
  4. package/dist/cli/init.d.cts.map +1 -1
  5. package/dist/cli/init.d.mts +14 -2
  6. package/dist/cli/init.d.mts.map +1 -1
  7. package/dist/cli/init.mjs +166 -88
  8. package/dist/cli/init.mjs.map +1 -1
  9. package/dist/cli/prompts.cjs +12 -14
  10. package/dist/cli/prompts.cjs.map +1 -1
  11. package/dist/cli/prompts.mjs +12 -14
  12. package/dist/cli/prompts.mjs.map +1 -1
  13. package/dist/cli/sync.cjs +3 -5
  14. package/dist/cli/sync.cjs.map +1 -1
  15. package/dist/cli/sync.mjs +3 -5
  16. package/dist/cli/sync.mjs.map +1 -1
  17. package/dist/cli/upgrade.cjs +214 -2
  18. package/dist/cli/upgrade.cjs.map +1 -1
  19. package/dist/cli/upgrade.mjs +214 -2
  20. package/dist/cli/upgrade.mjs.map +1 -1
  21. package/dist/config.cjs +125 -74
  22. package/dist/config.cjs.map +1 -1
  23. package/dist/config.d.cts +9 -2
  24. package/dist/config.d.cts.map +1 -1
  25. package/dist/config.d.mts +9 -2
  26. package/dist/config.d.mts.map +1 -1
  27. package/dist/config.mjs +126 -76
  28. package/dist/config.mjs.map +1 -1
  29. package/dist/contract.cjs +1 -4
  30. package/dist/contract.cjs.map +1 -1
  31. package/dist/contract.d.cts +40 -22
  32. package/dist/contract.d.cts.map +1 -1
  33. package/dist/contract.d.mts +40 -22
  34. package/dist/contract.d.mts.map +1 -1
  35. package/dist/contract.meta.cjs +5 -5
  36. package/dist/contract.meta.cjs.map +1 -1
  37. package/dist/contract.meta.d.cts +9 -9
  38. package/dist/contract.meta.d.mts +9 -9
  39. package/dist/contract.meta.mjs +5 -5
  40. package/dist/contract.meta.mjs.map +1 -1
  41. package/dist/contract.mjs +1 -4
  42. package/dist/contract.mjs.map +1 -1
  43. package/dist/index.cjs +2 -0
  44. package/dist/index.d.cts +3 -3
  45. package/dist/index.d.mts +3 -3
  46. package/dist/index.mjs +3 -3
  47. package/dist/merge.cjs +1 -0
  48. package/dist/merge.mjs +1 -1
  49. package/dist/plugin.cjs +70 -114
  50. package/dist/plugin.cjs.map +1 -1
  51. package/dist/plugin.d.cts +38 -17
  52. package/dist/plugin.d.cts.map +1 -1
  53. package/dist/plugin.d.mts +38 -17
  54. package/dist/plugin.d.mts.map +1 -1
  55. package/dist/plugin.mjs +71 -115
  56. package/dist/plugin.mjs.map +1 -1
  57. package/dist/sidebar.cjs +6 -14
  58. package/dist/sidebar.cjs.map +1 -1
  59. package/dist/sidebar.d.cts +3 -3
  60. package/dist/sidebar.d.cts.map +1 -1
  61. package/dist/sidebar.d.mts +3 -3
  62. package/dist/sidebar.d.mts.map +1 -1
  63. package/dist/sidebar.mjs +6 -14
  64. package/dist/sidebar.mjs.map +1 -1
  65. package/dist/types.cjs +10 -16
  66. package/dist/types.cjs.map +1 -1
  67. package/dist/types.d.cts +56 -12
  68. package/dist/types.d.cts.map +1 -1
  69. package/dist/types.d.mts +56 -12
  70. package/dist/types.d.mts.map +1 -1
  71. package/dist/types.mjs +10 -17
  72. package/dist/types.mjs.map +1 -1
  73. package/package.json +1 -1
  74. package/src/cli/init.ts +225 -131
  75. package/src/cli/prompts.ts +17 -22
  76. package/src/cli/sync.ts +5 -8
  77. package/src/cli/upgrade.ts +326 -2
  78. package/src/config.ts +250 -107
  79. package/src/contract.meta.ts +6 -8
  80. package/src/contract.ts +1 -4
  81. package/src/plugin.ts +120 -183
  82. package/src/sidebar.ts +9 -31
  83. package/src/types.ts +10 -15
package/src/config.ts CHANGED
@@ -3,6 +3,7 @@ import { dirname, isAbsolute, join, resolve } from "node:path";
3
3
  import { fetchBosConfigFromFastKv } from "./fastkv";
4
4
  import {
5
5
  type BosEnv,
6
+ bosConfigMerger,
6
7
  isPlainObject,
7
8
  mergeBosConfigWithExtends,
8
9
  type ResolvedConfigMeta,
@@ -18,7 +19,6 @@ import type {
18
19
  PluginEntryValue,
19
20
  RuntimeConfig,
20
21
  RuntimePluginConfig,
21
- SharedDepConfig,
22
22
  } from "./types";
23
23
  import { BosConfigSchema } from "./types";
24
24
 
@@ -74,6 +74,18 @@ export interface ConfigResult {
74
74
  };
75
75
  }
76
76
 
77
+ export interface ResolvedComposableReference {
78
+ entry: BosPluginRef;
79
+ providerBaseDir: string;
80
+ targetPath: string;
81
+ associatedUi?: Record<string, unknown>;
82
+ }
83
+
84
+ interface ParsedExtendsTarget {
85
+ configPath: string;
86
+ targetPath?: string;
87
+ }
88
+
77
89
  export async function loadConfig(options?: {
78
90
  cwd?: string;
79
91
  path?: string;
@@ -98,7 +110,11 @@ export async function loadConfig(options?: {
98
110
  extendedChain,
99
111
  env,
100
112
  );
101
- const config = BosConfigSchema.parse(parsed);
113
+ const config = await resolveRootComposableEntries(
114
+ BosConfigSchema.parse(parsed),
115
+ baseDir,
116
+ runtimeEnv,
117
+ );
102
118
 
103
119
  cachedConfig = config;
104
120
  projectRoot = baseDir;
@@ -144,6 +160,31 @@ export async function buildRuntimePluginsForConfig(
144
160
  return Object.keys(plugins).length > 0 ? plugins : undefined;
145
161
  }
146
162
 
163
+ async function resolveRootComposableEntries(
164
+ config: BosConfig,
165
+ baseDir: string,
166
+ env: BosEnv,
167
+ ): Promise<BosConfig> {
168
+ const resolvedApi = await resolveComposableReference(
169
+ config.app.api as BosPluginRef,
170
+ baseDir,
171
+ env,
172
+ "app.api",
173
+ );
174
+ const resolvedAuth = config.app.auth
175
+ ? await resolveComposableReference(config.app.auth as BosPluginRef, baseDir, env, "app.auth")
176
+ : undefined;
177
+
178
+ return {
179
+ ...config,
180
+ app: {
181
+ ...config.app,
182
+ api: resolvedApi.entry,
183
+ auth: resolvedAuth?.entry,
184
+ },
185
+ };
186
+ }
187
+
147
188
  export function getResolvedConfigPath(configDir: string): string {
148
189
  return join(configDir, ".bos", RESOLVED_CONFIG_FILENAME);
149
190
  }
@@ -216,6 +257,173 @@ export function readBosConfigForBuild(configDir: string): Record<string, unknown
216
257
  return JSON.parse(readFileSync(bosConfigPath, "utf-8")) as Record<string, unknown>;
217
258
  }
218
259
 
260
+ function parseExtendsTarget(ref: string): ParsedExtendsTarget {
261
+ const hashIndex = ref.indexOf("#");
262
+ if (hashIndex === -1) {
263
+ return { configPath: ref };
264
+ }
265
+
266
+ const configPath = ref.slice(0, hashIndex);
267
+ const targetPath = ref.slice(hashIndex + 1);
268
+ return {
269
+ configPath,
270
+ targetPath: targetPath.length > 0 ? targetPath : undefined,
271
+ };
272
+ }
273
+
274
+ function getConfigBaseDir(configPath: string, baseDir: string): string {
275
+ if (configPath.startsWith("bos://")) return baseDir;
276
+ return dirname(isAbsolute(configPath) ? configPath : resolve(baseDir, configPath));
277
+ }
278
+
279
+ function asComposableEntry(value: unknown): BosPluginRef {
280
+ if (typeof value === "string") {
281
+ return { extends: value };
282
+ }
283
+ if (!isPlainObject(value)) {
284
+ throw new Error(`Expected config entry object, received ${typeof value}`);
285
+ }
286
+ return value as BosPluginRef;
287
+ }
288
+
289
+ function getTargetedEntry(config: BosConfigInput, targetPath: string): BosPluginRef {
290
+ if (targetPath === "app.api") {
291
+ return asComposableEntry(config.app?.api);
292
+ }
293
+
294
+ if (targetPath === "app.auth") {
295
+ return asComposableEntry(config.app?.auth);
296
+ }
297
+
298
+ if (targetPath.startsWith("plugins.")) {
299
+ const pluginId = targetPath.slice("plugins.".length);
300
+ if (pluginId.length === 0) {
301
+ throw new Error(`Invalid plugin target path: ${targetPath}`);
302
+ }
303
+ return asComposableEntry(config.plugins?.[pluginId]);
304
+ }
305
+
306
+ throw new Error(`Unsupported extends target path: ${targetPath}`);
307
+ }
308
+
309
+ function getAssociatedUi(
310
+ config: BosConfigInput,
311
+ _targetPath: string,
312
+ ): Record<string, unknown> | undefined {
313
+ return isPlainObject(config.app?.ui) ? (config.app.ui as Record<string, unknown>) : undefined;
314
+ }
315
+
316
+ function mergeComposableEntries(
317
+ parent: Partial<BosPluginRef>,
318
+ child: Partial<BosPluginRef>,
319
+ ): BosPluginRef {
320
+ return bosConfigMerger({ ...child }, parent) as BosPluginRef;
321
+ }
322
+
323
+ function stripUnsafeLocalDevelopment<T extends Record<string, unknown> | undefined>(
324
+ entry: T,
325
+ allowLocalPaths: boolean,
326
+ ): T {
327
+ if (!entry || allowLocalPaths) {
328
+ return entry;
329
+ }
330
+
331
+ if (typeof entry.development === "string" && entry.development.startsWith(LOCAL_PREFIX)) {
332
+ const { development: _ignored, ...rest } = entry;
333
+ return rest as T;
334
+ }
335
+
336
+ return entry;
337
+ }
338
+
339
+ export async function resolveComposableReference(
340
+ source: BosPluginRef,
341
+ baseDir: string,
342
+ env: BosEnv,
343
+ defaultTargetPath: string,
344
+ ): Promise<ResolvedComposableReference> {
345
+ let resolvedEntry: BosPluginRef = {};
346
+ let providerBaseDir = baseDir;
347
+ let targetPath = defaultTargetPath;
348
+ let associatedUi: Record<string, unknown> | undefined;
349
+ let allowLocalPaths = false;
350
+ let extendsError: unknown;
351
+
352
+ const extendsRef = source.extends ? resolveExtendsRef(source.extends, env) : undefined;
353
+ if (extendsRef) {
354
+ const parsed = parseExtendsTarget(extendsRef);
355
+ targetPath = parsed.targetPath ?? defaultTargetPath;
356
+ const extendsBaseDir = getConfigBaseDir(parsed.configPath, baseDir);
357
+ try {
358
+ const extendedConfig = await resolveConfigWithExtends(
359
+ parsed.configPath,
360
+ extendsBaseDir,
361
+ new Set(),
362
+ [],
363
+ env,
364
+ );
365
+ resolvedEntry = mergeComposableEntries(
366
+ resolvedEntry,
367
+ getTargetedEntry(extendedConfig, targetPath),
368
+ );
369
+ providerBaseDir = extendsBaseDir;
370
+ associatedUi = getAssociatedUi(extendedConfig, targetPath);
371
+ } catch (error) {
372
+ extendsError = error;
373
+ }
374
+ }
375
+
376
+ const localDevelopment =
377
+ typeof source.development === "string" && source.development.startsWith(LOCAL_PREFIX)
378
+ ? source.development
379
+ : undefined;
380
+
381
+ if (localDevelopment) {
382
+ const localPath = resolve(baseDir, localDevelopment.slice(LOCAL_PREFIX.length).trim());
383
+ const localConfigPath = join(localPath, "bos.config.json");
384
+ if (existsSync(localConfigPath)) {
385
+ const localConfig = await resolveConfigWithExtends(
386
+ localConfigPath,
387
+ localPath,
388
+ new Set(),
389
+ [],
390
+ env,
391
+ );
392
+ resolvedEntry = mergeComposableEntries(
393
+ resolvedEntry,
394
+ getTargetedEntry(localConfig, targetPath),
395
+ );
396
+ providerBaseDir = localPath;
397
+ associatedUi = getAssociatedUi(localConfig, targetPath);
398
+ allowLocalPaths = true;
399
+ }
400
+ }
401
+
402
+ const sourceOverrides = { ...source };
403
+ if (allowLocalPaths && localDevelopment) {
404
+ delete sourceOverrides.development;
405
+ }
406
+
407
+ resolvedEntry = mergeComposableEntries(resolvedEntry, sourceOverrides);
408
+
409
+ if (
410
+ extendsError &&
411
+ !allowLocalPaths &&
412
+ typeof resolvedEntry.development !== "string" &&
413
+ typeof resolvedEntry.production !== "string" &&
414
+ typeof resolvedEntry.name !== "string"
415
+ ) {
416
+ throw extendsError;
417
+ }
418
+
419
+ return {
420
+ entry: stripUnsafeLocalDevelopment(resolvedEntry, allowLocalPaths || Boolean(localDevelopment)),
421
+ providerBaseDir,
422
+ targetPath,
423
+ associatedUi: stripUnsafeLocalDevelopment(associatedUi, allowLocalPaths),
424
+ };
425
+ }
426
+
219
427
  function resolveDevelopmentTarget(
220
428
  development: string | undefined,
221
429
  production: string | undefined,
@@ -225,6 +433,9 @@ function resolveDevelopmentTarget(
225
433
  if (forceSource === "remote") {
226
434
  return resolveRuntimeTarget(production, baseDir, "remote");
227
435
  }
436
+ if (!development) {
437
+ return resolveRuntimeTarget(production, baseDir, "remote");
438
+ }
228
439
  const devTarget = resolveRuntimeTarget(development, baseDir);
229
440
  if (devTarget.source === "local" && (!devTarget.localPath || !existsSync(devTarget.localPath))) {
230
441
  return resolveRuntimeTarget(production, baseDir, "remote");
@@ -298,6 +509,7 @@ export function buildRuntimeConfig(
298
509
  const hostIsRemote = hostRuntime.source === "remote";
299
510
  const uiIsRemote = uiRuntime.source === "remote";
300
511
  const apiIsRemote = apiRuntime.source === "remote";
512
+ const resolvedApiName = resolvePluginRuntimeName(apiConfig.name, apiRuntime.localPath, "api");
301
513
 
302
514
  return {
303
515
  env,
@@ -331,7 +543,7 @@ export function buildRuntimeConfig(
331
543
  source: uiRuntime.source,
332
544
  },
333
545
  api: {
334
- name: apiConfig.name,
546
+ name: resolvedApiName,
335
547
  url: apiRuntime.url,
336
548
  entry: apiRuntime.url ? `${apiRuntime.url}/mf-manifest.json` : "/mf-manifest.json",
337
549
  localPath: apiRuntime.localPath,
@@ -345,7 +557,7 @@ export function buildRuntimeConfig(
345
557
  auth: (() => {
346
558
  if (!authConfig || !authRuntime) return undefined;
347
559
  return {
348
- name: resolvePluginRuntimeName(undefined, authRuntime.localPath, authConfig.name),
560
+ name: resolvePluginRuntimeName(authConfig.name, authRuntime.localPath, "auth"),
349
561
  url: authRuntime.url,
350
562
  entry: authRuntime.url ? `${authRuntime.url}/mf-manifest.json` : "/mf-manifest.json",
351
563
  localPath: authRuntime.localPath,
@@ -399,14 +611,18 @@ async function resolveConfigWithExtends(
399
611
  return config;
400
612
  }
401
613
 
614
+ const parsedParentRef = parseExtendsTarget(extendsRef);
615
+
402
616
  const nextVisited = new Set(visited);
403
617
  nextVisited.add(configPath);
404
- const parentBaseDir = extendsRef.startsWith("bos://")
405
- ? baseDir
406
- : isAbsolute(extendsRef)
407
- ? dirname(extendsRef)
408
- : baseDir;
409
- const parent = await resolveConfigWithExtends(extendsRef, parentBaseDir, nextVisited, chain, env);
618
+ const parentBaseDir = getConfigBaseDir(parsedParentRef.configPath, baseDir);
619
+ const parent = await resolveConfigWithExtends(
620
+ parsedParentRef.configPath,
621
+ parentBaseDir,
622
+ nextVisited,
623
+ chain,
624
+ env,
625
+ );
410
626
 
411
627
  return mergeBosConfigWithExtends(parent, config);
412
628
  }
@@ -432,82 +648,20 @@ async function resolveRuntimePlugins(
432
648
  const normalized = normalizePluginEntry(rawInput);
433
649
  if (normalized === null || normalized === false) continue;
434
650
 
435
- let resolvedConfig: BosConfigInput = {};
436
- let pluginBaseDir = baseDir;
437
-
438
- if (normalized.extends) {
439
- try {
440
- const extendsUrl = resolveExtendsRef(normalized.extends, env);
441
- if (extendsUrl) {
442
- const remoteConfig = await fetchBosConfigFromFastKv<BosConfigInput>(extendsUrl);
443
- resolvedConfig = remoteConfig;
444
- }
445
- } catch {
446
- resolvedConfig = {};
447
- }
448
- }
449
-
450
- if (normalized.development?.startsWith(LOCAL_PREFIX)) {
451
- const localPath = resolve(baseDir, normalized.development.slice(LOCAL_PREFIX.length).trim());
452
- if (existsSync(localPath)) {
453
- const localConfigPath = join(localPath, "bos.config.json");
454
- if (existsSync(localConfigPath)) {
455
- try {
456
- const localRaw = JSON.parse(readFileSync(localConfigPath, "utf-8")) as BosConfigInput;
457
- resolvedConfig = mergeBosConfigWithExtends(resolvedConfig, localRaw);
458
- pluginBaseDir = localPath;
459
- } catch {}
460
- }
461
- }
462
- }
463
-
464
- if (normalized.app && isPlainObject(normalized.app)) {
465
- const mergedApp: Record<string, unknown> = {
466
- ...((resolvedConfig.app as Record<string, unknown>) ?? {}),
467
- ...(normalized.app as Record<string, unknown>),
468
- };
469
- resolvedConfig = { ...resolvedConfig, app: mergedApp as BosConfigInput["app"] };
470
- }
471
- if (normalized.shared && isPlainObject(normalized.shared)) {
472
- const mergedShared: Record<string, Record<string, SharedDepConfig>> = {
473
- ...(resolvedConfig.shared ?? {}),
474
- ...(normalized.shared as Record<string, Record<string, SharedDepConfig>>),
475
- };
476
- resolvedConfig = { ...resolvedConfig, shared: mergedShared };
477
- }
478
- if (normalized.sidebar) {
479
- resolvedConfig = { ...resolvedConfig, sidebar: normalized.sidebar };
480
- }
481
- if (normalized.routes) {
482
- resolvedConfig = { ...resolvedConfig, routes: normalized.routes };
483
- }
484
-
485
- const pluginRuntime = await buildRuntimePluginConfig(
486
- pluginId,
487
- resolvedConfig,
488
- pluginBaseDir,
489
- env,
651
+ const resolvedReference = await resolveComposableReference(
490
652
  normalized,
653
+ baseDir,
654
+ env,
655
+ `plugins.${pluginId}`,
491
656
  );
492
- if (
493
- normalized.name &&
494
- typeof normalized.name === "string" &&
495
- !pluginRuntime.name.includes("/")
496
- ) {
497
- pluginRuntime.name = normalized.name;
498
- }
499
657
 
500
- const integrity = normalized.integrity;
501
- if (env === "production" && integrity) {
502
- pluginRuntime.integrity = integrity;
503
- }
658
+ const pluginRuntime = buildRuntimePluginConfig(pluginId, env, resolvedReference);
504
659
 
505
660
  if (
506
661
  pluginRuntime.source === "remote" &&
507
662
  pluginRuntime.url &&
508
663
  !pluginRuntime.localPath &&
509
- typeof resolvedConfig.app?.api?.name !== "string" &&
510
- !normalized.name
664
+ typeof resolvedReference.entry.name !== "string"
511
665
  ) {
512
666
  pluginRuntime.name = await resolveRemotePluginRuntimeName(
513
667
  pluginRuntime.url,
@@ -546,22 +700,14 @@ async function resolveRemotePluginRuntimeName(baseUrl: string, fallback: string)
546
700
  }
547
701
  }
548
702
 
549
- async function buildRuntimePluginConfig(
703
+ function buildRuntimePluginConfig(
550
704
  pluginId: string,
551
- config: BosConfigInput,
552
- baseDir: string,
553
705
  env: BosEnv,
554
- source: BosPluginRef,
555
- ): Promise<RuntimePluginConfig> {
556
- const apiConfig = config.app?.api ?? {};
557
- const apiDevelopment =
558
- typeof apiConfig.development === "string" ? apiConfig.development : undefined;
559
- const apiProduction = typeof apiConfig.production === "string" ? apiConfig.production : undefined;
560
- const sourceDevelopment = typeof source.development === "string" ? source.development : undefined;
561
- const sourceProduction = typeof source.production === "string" ? source.production : undefined;
562
- const proxy = typeof apiConfig.proxy === "string" ? apiConfig.proxy : undefined;
563
- const development = apiDevelopment ?? sourceDevelopment;
564
- const production = apiProduction ?? sourceProduction;
706
+ resolved: ResolvedComposableReference,
707
+ ): RuntimePluginConfig {
708
+ const source = resolved.entry;
709
+ const development = typeof source.development === "string" ? source.development : undefined;
710
+ const production = typeof source.production === "string" ? source.production : undefined;
565
711
 
566
712
  if (production?.startsWith("bos://")) {
567
713
  throw new Error(
@@ -571,32 +717,28 @@ async function buildRuntimePluginConfig(
571
717
 
572
718
  const runtimeTarget =
573
719
  env === "development"
574
- ? resolveDevelopmentTarget(development, production, baseDir)
575
- : resolveRuntimeTarget(production, baseDir, "remote");
576
- const apiName = resolvePluginRuntimeName(
577
- typeof apiConfig.name === "string" ? apiConfig.name : undefined,
578
- runtimeTarget.localPath,
579
- pluginId,
580
- );
720
+ ? resolveDevelopmentTarget(development, production, resolved.providerBaseDir)
721
+ : resolveRuntimeTarget(production, resolved.providerBaseDir, "remote");
722
+ const apiName = resolvePluginRuntimeName(source.name, runtimeTarget.localPath, pluginId);
581
723
 
582
- const uiConfig = config.app?.ui;
724
+ const uiConfig = resolved.associatedUi;
583
725
  const uiDevelopment =
584
726
  typeof uiConfig?.development === "string" ? uiConfig.development : undefined;
585
727
  const uiProduction = typeof uiConfig?.production === "string" ? uiConfig.production : undefined;
586
728
  const uiRuntime =
587
729
  uiConfig && (uiDevelopment || uiProduction)
588
730
  ? env === "development"
589
- ? resolveDevelopmentTarget(uiDevelopment, uiProduction, baseDir)
590
- : resolveRuntimeTarget(uiProduction, baseDir, "remote")
731
+ ? resolveDevelopmentTarget(uiDevelopment, uiProduction, resolved.providerBaseDir)
732
+ : resolveRuntimeTarget(uiProduction, resolved.providerBaseDir, "remote")
591
733
  : undefined;
592
734
 
593
- const sidebar = (config.sidebar ?? source.sidebar)?.map((item) => ({
735
+ const sidebar = source.sidebar?.map((item) => ({
594
736
  ...item,
595
737
  to: item.to ?? `/${pluginId}`,
596
738
  roleRequired: item.roleRequired ?? ("member" as const),
597
739
  }));
598
740
 
599
- const routes = config.routes ?? source.routes;
741
+ const routes = source.routes;
600
742
 
601
743
  return {
602
744
  name: apiName,
@@ -607,9 +749,10 @@ async function buildRuntimePluginConfig(
607
749
  source: runtimeTarget.source,
608
750
  localPath: runtimeTarget.localPath,
609
751
  port: runtimeTarget.port,
610
- proxy: proxy ?? (typeof source.proxy === "string" ? source.proxy : undefined),
611
- variables: normalizeStringRecord(apiConfig.variables ?? source.variables),
612
- secrets: normalizeStringArray(apiConfig.secrets ?? source.secrets),
752
+ proxy: typeof source.proxy === "string" ? source.proxy : undefined,
753
+ variables: normalizeStringRecord(source.variables),
754
+ secrets: normalizeStringArray(source.secrets),
755
+ integrity: runtimeTarget.source === "remote" ? source.integrity : undefined,
613
756
  ui: uiRuntime
614
757
  ? {
615
758
  name: typeof uiConfig?.name === "string" ? uiConfig.name : `${apiName}-ui`,
@@ -79,22 +79,20 @@ export const cliCommandMeta = {
79
79
  },
80
80
  init: {
81
81
  commandPath: ["init"],
82
- summary: "Scaffold a new project from a bos template",
82
+ summary: "Scaffold a new project by extending a deployed app or template",
83
83
  interactive: true,
84
84
  fields: {
85
85
  domain: {
86
86
  positional: true,
87
- description: "New project domain (e.g. ironclaw.everything.dev)",
87
+ description: "New project domain (e.g. myapp.everything.dev)",
88
88
  },
89
- account: { description: "New project NEAR account (auto-derived from domain)" },
90
- extendsAccount: {
91
- description: "Parent NEAR account to extend from (defaults to dev.everything.near)",
92
- },
93
- extendsGateway: {
94
- description: "Parent gateway to extend from (defaults to everything.dev)",
89
+ extends: {
90
+ description: "Parent to extend from (e.g. bos://account/gateway or account/gateway)",
95
91
  },
92
+ account: { description: "New project NEAR account (auto-derived from extends)" },
96
93
  directory: { description: "Target directory (auto-derived from domain)" },
97
94
  source: { description: "Local source dir (skips GitHub download)" },
95
+ plugins: { description: "Comma-separated plugin keys to include" },
98
96
  withHost: { description: "Include host/ in template output" },
99
97
  noInteractive: { description: "Skip prompts, use flags only" },
100
98
  noInstall: { description: "Skip bun install" },
package/src/contract.ts CHANGED
@@ -144,8 +144,6 @@ export const KeyPublishResultSchema = z.object({
144
144
 
145
145
  export const InitOptionsSchema = z.object({
146
146
  extends: z.string().optional(),
147
- extendsAccount: z.string().optional(),
148
- extendsGateway: z.string().optional(),
149
147
  directory: z.string().optional(),
150
148
  account: z.string().optional(),
151
149
  domain: z.string().optional(),
@@ -164,8 +162,7 @@ export const PhaseTimingSchema = z.object({
164
162
  export const InitResultSchema = z.object({
165
163
  status: z.enum(["initialized", "error"]),
166
164
  directory: z.string(),
167
- extendsAccount: z.string(),
168
- extendsGateway: z.string(),
165
+ extendsRef: z.string(),
169
166
  account: z.string().optional(),
170
167
  domain: z.string().optional(),
171
168
  extends: z.string(),