everything-dev 0.0.15 → 0.0.17

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/src/plugin.ts CHANGED
@@ -3,6 +3,7 @@ import { Effect } from "every-plugin/effect";
3
3
  import { z } from "every-plugin/zod";
4
4
  import { Graph } from "near-social-js";
5
5
 
6
+ import { createProcessRegistry } from "./lib/process-registry";
6
7
  import {
7
8
  type AppConfig,
8
9
  type BosConfig as BosConfigType,
@@ -34,6 +35,7 @@ import {
34
35
  verifyNovaCredentials
35
36
  } from "./lib/nova";
36
37
  import { type AppOrchestrator, startApp } from "./lib/orchestrator";
38
+ import { syncFiles } from "./lib/sync";
37
39
  import { run } from "./utils/run";
38
40
  import { colors, icons } from "./utils/theme";
39
41
 
@@ -54,6 +56,27 @@ function getGatewayDomain(config: BosConfigType): string {
54
56
  throw new Error("bos.config.json must have a 'gateway' field with production URL");
55
57
  }
56
58
 
59
+ function getAccountForNetwork(config: BosConfigType, network: "mainnet" | "testnet"): string {
60
+ if (network === "testnet") {
61
+ if (!config.testnet) {
62
+ throw new Error("bos.config.json must have a 'testnet' field to use testnet network");
63
+ }
64
+ return config.testnet;
65
+ }
66
+ return config.account;
67
+ }
68
+
69
+ function getSocialContract(network: "mainnet" | "testnet"): string {
70
+ return network === "testnet" ? "v1.social08.testnet" : "social.near";
71
+ }
72
+
73
+ function getSocialExplorerUrl(network: "mainnet" | "testnet", path: string): string {
74
+ const baseUrl = network === "testnet"
75
+ ? "https://test.near.social"
76
+ : "https://near.social";
77
+ return `${baseUrl}/${path}`;
78
+ }
79
+
57
80
  function buildSocialSetArgs(account: string, gatewayDomain: string, config: BosConfigType): object {
58
81
  return {
59
82
  data: {
@@ -201,6 +224,7 @@ export default createPlugin({
201
224
  env,
202
225
  description,
203
226
  appConfig,
227
+ bosConfig: deps.bosConfig ?? undefined,
204
228
  port: input.port,
205
229
  interactive: input.interactive,
206
230
  };
@@ -236,7 +260,9 @@ export default createPlugin({
236
260
  }
237
261
  if (typeof current === "string") {
238
262
  remoteConfig = JSON.parse(current) as BosConfigType;
239
- setConfig(remoteConfig);
263
+ const configFilePath = `${process.cwd()}/bos.config.json`;
264
+ await Bun.write(configFilePath, JSON.stringify(remoteConfig, null, 2));
265
+ setConfig(remoteConfig, process.cwd());
240
266
  }
241
267
  }
242
268
  } catch (error) {
@@ -297,6 +323,7 @@ export default createPlugin({
297
323
  ui: "remote",
298
324
  api: "remote",
299
325
  },
326
+ bosConfig: config,
300
327
  port,
301
328
  interactive: input.interactive,
302
329
  noLogs: true,
@@ -312,6 +339,7 @@ export default createPlugin({
312
339
 
313
340
  serve: builder.serve.handler(async ({ input }) => {
314
341
  const port = input.port;
342
+
315
343
  return {
316
344
  status: "serving" as const,
317
345
  url: `http://localhost:${port}`,
@@ -381,7 +409,7 @@ export default createPlugin({
381
409
  }),
382
410
 
383
411
  publish: builder.publish.handler(async ({ input: publishInput }) => {
384
- const { configDir, bosConfig, nearPrivateKey } = deps;
412
+ const { bosConfig, nearPrivateKey } = deps;
385
413
 
386
414
  if (!bosConfig) {
387
415
  return {
@@ -392,45 +420,49 @@ export default createPlugin({
392
420
  };
393
421
  }
394
422
 
395
- const gatewayDomain = getGatewayDomain(bosConfig);
396
- const socialPath = `${bosConfig.account}/bos/gateways/${gatewayDomain}/bos.config.json`;
423
+ const network = publishInput.network;
397
424
 
398
- const publishEffect = Effect.gen(function* () {
399
- yield* ensureNearCli;
425
+ try {
426
+ const account = getAccountForNetwork(bosConfig, network);
427
+ const gatewayDomain = getGatewayDomain(bosConfig);
428
+ const socialContract = getSocialContract(network);
429
+ const socialPath = `${account}/bos/gateways/${gatewayDomain}/bos.config.json`;
400
430
 
401
- const bosEnv = yield* loadBosEnv;
402
- const privateKey = nearPrivateKey || bosEnv.NEAR_PRIVATE_KEY;
431
+ const publishEffect = Effect.gen(function* () {
432
+ yield* ensureNearCli;
433
+
434
+ const bosEnv = yield* loadBosEnv;
435
+ const privateKey = nearPrivateKey || bosEnv.NEAR_PRIVATE_KEY;
436
+
437
+ const socialArgs = buildSocialSetArgs(account, gatewayDomain, bosConfig);
438
+ const argsBase64 = Buffer.from(JSON.stringify(socialArgs)).toString("base64");
439
+
440
+ if (publishInput.dryRun) {
441
+ return {
442
+ status: "dry-run" as const,
443
+ txHash: "",
444
+ registryUrl: getSocialExplorerUrl(network, socialPath),
445
+ };
446
+ }
403
447
 
404
- const socialArgs = buildSocialSetArgs(bosConfig.account, gatewayDomain, bosConfig);
405
- const argsBase64 = Buffer.from(JSON.stringify(socialArgs)).toString("base64");
448
+ const result = yield* executeTransaction({
449
+ account,
450
+ contract: socialContract,
451
+ method: "set",
452
+ argsBase64,
453
+ network,
454
+ privateKey,
455
+ gas: "300Tgas",
456
+ deposit: "0.05NEAR",
457
+ });
406
458
 
407
- if (publishInput.dryRun) {
408
459
  return {
409
- status: "dry-run" as const,
410
- txHash: "",
411
- registryUrl: `https://near.social/${socialPath}`,
460
+ status: "published" as const,
461
+ txHash: result.txHash || "unknown",
462
+ registryUrl: getSocialExplorerUrl(network, socialPath),
412
463
  };
413
- }
414
-
415
- const result = yield* executeTransaction({
416
- account: bosConfig.account,
417
- contract: "social.near",
418
- method: "set",
419
- argsBase64,
420
- network: publishInput.network,
421
- privateKey,
422
- gas: "300Tgas",
423
- deposit: "0.05NEAR",
424
464
  });
425
465
 
426
- return {
427
- status: "published" as const,
428
- txHash: result.txHash || "unknown",
429
- registryUrl: `https://near.social/${socialPath}`,
430
- };
431
- });
432
-
433
- try {
434
466
  return await Effect.runPromise(publishEffect);
435
467
  } catch (error) {
436
468
  return {
@@ -456,7 +488,16 @@ export default createPlugin({
456
488
  gateway: "near-everything/every-plugin/demo/gateway",
457
489
  };
458
490
 
459
- const template = input.template || deps.bosConfig?.create?.[input.type] || DEFAULT_TEMPLATES[input.type];
491
+ const getTemplate = (): string => {
492
+ if (input.template) return input.template;
493
+ if (input.type === "project") {
494
+ return deps.bosConfig?.template || DEFAULT_TEMPLATES.project;
495
+ }
496
+ const appConfig = deps.bosConfig?.app[input.type] as { template?: string } | undefined;
497
+ return appConfig?.template || DEFAULT_TEMPLATES[input.type];
498
+ };
499
+
500
+ const template = getTemplate();
460
501
  const dest = input.type === "project" ? input.name! : input.type;
461
502
 
462
503
  try {
@@ -465,11 +506,11 @@ export default createPlugin({
465
506
  if (input.type === "project" && input.name) {
466
507
  const newConfig = {
467
508
  account: `${input.name}.near`,
509
+ template: DEFAULT_TEMPLATES.project,
468
510
  gateway: {
469
511
  development: "http://localhost:8787",
470
512
  production: `https://gateway.${input.name}.example.com`,
471
513
  },
472
- create: DEFAULT_TEMPLATES,
473
514
  app: {
474
515
  host: {
475
516
  title: input.name,
@@ -630,41 +671,44 @@ export default createPlugin({
630
671
  };
631
672
  }
632
673
 
633
- const registerEffect = Effect.gen(function* () {
634
- yield* ensureNearCli;
674
+ const network = input.network;
635
675
 
636
- const bosEnv = yield* loadBosEnv;
637
- const gatewayPrivateKey = bosEnv.GATEWAY_PRIVATE_KEY;
676
+ try {
677
+ const parentAccount = getAccountForNetwork(bosConfig, network);
678
+ const fullAccount = `${input.name}.${parentAccount}`;
638
679
 
639
- const fullAccount = `${input.name}.${bosConfig.account}`;
640
- const parentAccount = bosConfig.account;
680
+ const registerEffect = Effect.gen(function* () {
681
+ yield* ensureNearCli;
641
682
 
642
- yield* createSubaccount({
643
- newAccount: fullAccount,
644
- parentAccount,
645
- initialBalance: "0.1NEAR",
646
- network: input.network,
647
- privateKey: gatewayPrivateKey,
648
- });
683
+ const bosEnv = yield* loadBosEnv;
684
+ const gatewayPrivateKey = bosEnv.GATEWAY_PRIVATE_KEY;
649
685
 
650
- const novaConfig = yield* getNovaConfig;
651
- const nova = createNovaClient(novaConfig);
686
+ yield* createSubaccount({
687
+ newAccount: fullAccount,
688
+ parentAccount,
689
+ initialBalance: "0.1NEAR",
690
+ network,
691
+ privateKey: gatewayPrivateKey,
692
+ });
652
693
 
653
- yield* registerSecretsGroup(nova, fullAccount, parentAccount);
694
+ const novaConfig = yield* getNovaConfig;
695
+ const nova = createNovaClient(novaConfig);
654
696
 
655
- return {
656
- status: "registered" as const,
657
- account: fullAccount,
658
- novaGroup: getSecretsGroupId(fullAccount),
659
- };
660
- });
697
+ yield* registerSecretsGroup(nova, fullAccount, parentAccount);
698
+
699
+ return {
700
+ status: "registered" as const,
701
+ account: fullAccount,
702
+ novaGroup: getSecretsGroupId(fullAccount),
703
+ };
704
+ });
661
705
 
662
- try {
663
706
  return await Effect.runPromise(registerEffect);
664
707
  } catch (error) {
708
+ const parentAccount = network === "testnet" ? bosConfig.testnet : bosConfig.account;
665
709
  return {
666
710
  status: "error" as const,
667
- account: `${input.name}.${bosConfig.account}`,
711
+ account: `${input.name}.${parentAccount || bosConfig.account}`,
668
712
  error: error instanceof Error ? error.message : "Unknown error",
669
713
  };
670
714
  }
@@ -1145,7 +1189,7 @@ export default createPlugin({
1145
1189
  }
1146
1190
  }),
1147
1191
 
1148
- depsSync: builder.depsSync.handler(async ({ input }) => {
1192
+ filesSync: builder.filesSync.handler(async ({ input }) => {
1149
1193
  const { configDir, bosConfig } = deps;
1150
1194
 
1151
1195
  if (!bosConfig) {
@@ -1156,62 +1200,26 @@ export default createPlugin({
1156
1200
  };
1157
1201
  }
1158
1202
 
1159
- const category = input.category;
1160
- const sharedDeps = bosConfig.shared?.[category];
1161
-
1162
- if (!sharedDeps || Object.keys(sharedDeps).length === 0) {
1163
- return {
1164
- status: "error" as const,
1165
- synced: [],
1166
- error: `No shared.${category} dependencies found in bos.config.json`,
1167
- };
1168
- }
1169
-
1170
- try {
1171
- const rootPkgPath = `${configDir}/package.json`;
1172
- const rootPkg = await Bun.file(rootPkgPath).json() as {
1173
- workspaces?: { packages?: string[]; catalog?: Record<string, string> };
1174
- };
1175
-
1176
- if (!rootPkg.workspaces) {
1177
- rootPkg.workspaces = {};
1178
- }
1179
- if (!rootPkg.workspaces.catalog) {
1180
- rootPkg.workspaces.catalog = {};
1181
- }
1182
-
1183
- const synced: string[] = [];
1184
-
1185
- for (const [name, config] of Object.entries(sharedDeps)) {
1186
- const version = (config as { requiredVersion?: string }).requiredVersion;
1187
- if (version) {
1188
- const cleanVersion = version.replace(/^[\^~]/, "");
1189
- rootPkg.workspaces.catalog[name] = cleanVersion;
1190
- synced.push(name);
1191
- }
1192
- }
1203
+ const rootPkgPath = `${configDir}/package.json`;
1204
+ const rootPkg = await Bun.file(rootPkgPath).json() as {
1205
+ workspaces?: { catalog?: Record<string, string> };
1206
+ };
1207
+ const catalog = rootPkg.workspaces?.catalog ?? {};
1193
1208
 
1194
- await Bun.write(rootPkgPath, JSON.stringify(rootPkg, null, 2));
1209
+ const packages = input.packages || Object.keys(bosConfig.app);
1195
1210
 
1196
- if (input.install) {
1197
- const { execa } = await import("execa");
1198
- await execa("bun", ["install"], {
1199
- cwd: configDir,
1200
- stdio: "inherit",
1201
- });
1202
- }
1211
+ const synced = await syncFiles({
1212
+ configDir,
1213
+ packages,
1214
+ bosConfig,
1215
+ catalog,
1216
+ force: input.force,
1217
+ });
1203
1218
 
1204
- return {
1205
- status: "synced" as const,
1206
- synced,
1207
- };
1208
- } catch (error) {
1209
- return {
1210
- status: "error" as const,
1211
- synced: [],
1212
- error: error instanceof Error ? error.message : "Unknown error",
1213
- };
1214
- }
1219
+ return {
1220
+ status: "synced" as const,
1221
+ synced,
1222
+ };
1215
1223
  }),
1216
1224
 
1217
1225
  sync: builder.sync.handler(async ({ input }) => {
@@ -1348,6 +1356,22 @@ export default createPlugin({
1348
1356
  }
1349
1357
  }
1350
1358
 
1359
+ let filesSynced: Array<{ package: string; files: string[] }> | undefined;
1360
+
1361
+ if (input.files) {
1362
+ const results = await syncFiles({
1363
+ configDir,
1364
+ packages: Object.keys(bosConfig.app),
1365
+ bosConfig,
1366
+ catalog: rootPkg.workspaces?.catalog ?? {},
1367
+ force: input.force,
1368
+ });
1369
+
1370
+ if (results.length > 0) {
1371
+ filesSynced = results.map(r => ({ package: r.package, files: r.files }));
1372
+ }
1373
+ }
1374
+
1351
1375
  return {
1352
1376
  status: "synced" as const,
1353
1377
  account,
@@ -1355,6 +1379,7 @@ export default createPlugin({
1355
1379
  hostUrl,
1356
1380
  catalogUpdated: true,
1357
1381
  packagesUpdated,
1382
+ filesSynced,
1358
1383
  };
1359
1384
  } catch (error) {
1360
1385
  return {
@@ -1368,5 +1393,227 @@ export default createPlugin({
1368
1393
  };
1369
1394
  }
1370
1395
  }),
1396
+
1397
+ kill: builder.kill.handler(async ({ input }) => {
1398
+ const killEffect = Effect.gen(function* () {
1399
+ const registry = yield* createProcessRegistry();
1400
+ const result = yield* registry.killAll(input.force);
1401
+ return {
1402
+ status: "killed" as const,
1403
+ killed: result.killed,
1404
+ failed: result.failed,
1405
+ };
1406
+ });
1407
+
1408
+ try {
1409
+ return await Effect.runPromise(killEffect);
1410
+ } catch (error) {
1411
+ return {
1412
+ status: "error" as const,
1413
+ killed: [],
1414
+ failed: [],
1415
+ error: error instanceof Error ? error.message : "Unknown error",
1416
+ };
1417
+ }
1418
+ }),
1419
+
1420
+ ps: builder.ps.handler(async () => {
1421
+ const psEffect = Effect.gen(function* () {
1422
+ const registry = yield* createProcessRegistry();
1423
+ const processes = yield* registry.getAll();
1424
+ return {
1425
+ status: "listed" as const,
1426
+ processes,
1427
+ };
1428
+ });
1429
+
1430
+ try {
1431
+ return await Effect.runPromise(psEffect);
1432
+ } catch (error) {
1433
+ return {
1434
+ status: "error" as const,
1435
+ processes: [],
1436
+ error: error instanceof Error ? error.message : "Unknown error",
1437
+ };
1438
+ }
1439
+ }),
1440
+
1441
+ dockerBuild: builder.dockerBuild.handler(async ({ input }) => {
1442
+ const { configDir, bosConfig } = deps;
1443
+
1444
+ const dockerEffect = Effect.gen(function* () {
1445
+ const { execa } = yield* Effect.tryPromise({
1446
+ try: () => import("execa"),
1447
+ catch: (e) => new Error(`Failed to import execa: ${e}`),
1448
+ });
1449
+
1450
+ const dockerfile = input.target === "development" ? "Dockerfile.dev" : "Dockerfile";
1451
+ const imageName = bosConfig?.account?.replace(/\./g, "-") || "bos-app";
1452
+ const tag = input.tag || (input.target === "development" ? "dev" : "latest");
1453
+ const fullTag = `${imageName}:${tag}`;
1454
+
1455
+ const args = ["build", "-f", dockerfile, "-t", fullTag];
1456
+ if (input.noCache) {
1457
+ args.push("--no-cache");
1458
+ }
1459
+ args.push(".");
1460
+
1461
+ yield* Effect.tryPromise({
1462
+ try: () => execa("docker", args, {
1463
+ cwd: configDir,
1464
+ stdio: "inherit",
1465
+ }),
1466
+ catch: (e) => new Error(`Docker build failed: ${e}`),
1467
+ });
1468
+
1469
+ return {
1470
+ status: "built" as const,
1471
+ image: imageName,
1472
+ tag: fullTag,
1473
+ };
1474
+ });
1475
+
1476
+ try {
1477
+ return await Effect.runPromise(dockerEffect);
1478
+ } catch (error) {
1479
+ return {
1480
+ status: "error" as const,
1481
+ image: "",
1482
+ tag: "",
1483
+ error: error instanceof Error ? error.message : "Unknown error",
1484
+ };
1485
+ }
1486
+ }),
1487
+
1488
+ dockerRun: builder.dockerRun.handler(async ({ input }) => {
1489
+ const { bosConfig } = deps;
1490
+
1491
+ const dockerEffect = Effect.gen(function* () {
1492
+ const { execa } = yield* Effect.tryPromise({
1493
+ try: () => import("execa"),
1494
+ catch: (e) => new Error(`Failed to import execa: ${e}`),
1495
+ });
1496
+
1497
+ const imageName = bosConfig?.account?.replace(/\./g, "-") || "bos-app";
1498
+ const tag = input.target === "development" ? "dev" : "latest";
1499
+ const fullTag = `${imageName}:${tag}`;
1500
+ const port = input.port || (input.target === "development" ? 4000 : 3000);
1501
+
1502
+ const args = ["run"];
1503
+
1504
+ if (input.detach) {
1505
+ args.push("-d");
1506
+ }
1507
+
1508
+ args.push("-p", `${port}:${port}`);
1509
+ args.push("-e", `PORT=${port}`);
1510
+
1511
+ if (input.target === "development") {
1512
+ args.push("-e", `MODE=${input.mode}`);
1513
+ }
1514
+
1515
+ if (input.env) {
1516
+ for (const [key, value] of Object.entries(input.env)) {
1517
+ args.push("-e", `${key}=${value}`);
1518
+ }
1519
+ }
1520
+
1521
+ if (bosConfig) {
1522
+ args.push("-e", `BOS_ACCOUNT=${bosConfig.account}`);
1523
+ const gateway = bosConfig.gateway as { production?: string } | string | undefined;
1524
+ if (gateway) {
1525
+ const domain = typeof gateway === "string"
1526
+ ? gateway
1527
+ : gateway.production?.replace(/^https?:\/\//, "") || "";
1528
+ if (domain) {
1529
+ args.push("-e", `GATEWAY_DOMAIN=${domain}`);
1530
+ }
1531
+ }
1532
+ }
1533
+
1534
+ args.push(fullTag);
1535
+
1536
+ const result = yield* Effect.tryPromise({
1537
+ try: () => execa("docker", args, {
1538
+ stdio: input.detach ? "pipe" : "inherit",
1539
+ }),
1540
+ catch: (e) => new Error(`Docker run failed: ${e}`),
1541
+ });
1542
+
1543
+ const containerId = input.detach && result.stdout ? result.stdout.trim().slice(0, 12) : "attached";
1544
+
1545
+ return {
1546
+ status: "running" as const,
1547
+ containerId,
1548
+ url: `http://localhost:${port}`,
1549
+ };
1550
+ });
1551
+
1552
+ try {
1553
+ return await Effect.runPromise(dockerEffect);
1554
+ } catch (error) {
1555
+ return {
1556
+ status: "error" as const,
1557
+ containerId: "",
1558
+ url: "",
1559
+ error: error instanceof Error ? error.message : "Unknown error",
1560
+ };
1561
+ }
1562
+ }),
1563
+
1564
+ dockerStop: builder.dockerStop.handler(async ({ input }) => {
1565
+ const { bosConfig } = deps;
1566
+
1567
+ const dockerEffect = Effect.gen(function* () {
1568
+ const { execa } = yield* Effect.tryPromise({
1569
+ try: () => import("execa"),
1570
+ catch: (e) => new Error(`Failed to import execa: ${e}`),
1571
+ });
1572
+
1573
+ const stopped: string[] = [];
1574
+
1575
+ if (input.containerId) {
1576
+ yield* Effect.tryPromise({
1577
+ try: () => execa("docker", ["stop", input.containerId!]),
1578
+ catch: (e) => new Error(`Failed to stop container: ${e}`),
1579
+ });
1580
+ stopped.push(input.containerId!);
1581
+ } else if (input.all) {
1582
+ const imageName = bosConfig?.account?.replace(/\./g, "-") || "bos-app";
1583
+
1584
+ const psResult = yield* Effect.tryPromise({
1585
+ try: () => execa("docker", ["ps", "-q", "--filter", `ancestor=${imageName}`]),
1586
+ catch: () => new Error("Failed to list containers"),
1587
+ });
1588
+
1589
+ const containerIds = psResult.stdout.trim().split("\n").filter(Boolean);
1590
+
1591
+ for (const id of containerIds) {
1592
+ yield* Effect.tryPromise({
1593
+ try: () => execa("docker", ["stop", id]),
1594
+ catch: () => new Error(`Failed to stop container ${id}`),
1595
+ }).pipe(Effect.catchAll(() => Effect.void));
1596
+ stopped.push(id);
1597
+ }
1598
+ }
1599
+
1600
+ return {
1601
+ status: "stopped" as const,
1602
+ stopped,
1603
+ };
1604
+ });
1605
+
1606
+ try {
1607
+ return await Effect.runPromise(dockerEffect);
1608
+ } catch (error) {
1609
+ return {
1610
+ status: "error" as const,
1611
+ stopped: [],
1612
+ error: error instanceof Error ? error.message : "Unknown error",
1613
+ };
1614
+ }
1615
+ }),
1371
1616
  }),
1372
1617
  });
1618
+
1619
+
package/src/types.ts CHANGED
@@ -9,6 +9,9 @@ export const HostConfigSchema = z.object({
9
9
  development: z.string(),
10
10
  production: z.string(),
11
11
  secrets: z.array(z.string()).optional(),
12
+ template: z.string().optional(),
13
+ files: z.array(z.string()).optional(),
14
+ sync: z.lazy(() => SyncConfigSchema).optional(),
12
15
  });
13
16
  export type HostConfig = z.infer<typeof HostConfigSchema>;
14
17
 
@@ -21,6 +24,9 @@ export const RemoteConfigSchema = z.object({
21
24
  exposes: z.record(z.string(), z.string()).optional(),
22
25
  variables: z.record(z.string(), z.string()).optional(),
23
26
  secrets: z.array(z.string()).optional(),
27
+ template: z.string().optional(),
28
+ files: z.array(z.string()).optional(),
29
+ sync: z.lazy(() => SyncConfigSchema).optional(),
24
30
  });
25
31
  export type RemoteConfig = z.infer<typeof RemoteConfigSchema>;
26
32
 
@@ -38,11 +44,18 @@ export const SharedDepConfigSchema = z.object({
38
44
  });
39
45
  export type SharedDepConfig = z.infer<typeof SharedDepConfigSchema>;
40
46
 
47
+ export const SyncConfigSchema = z.object({
48
+ scripts: z.union([z.array(z.string()), z.literal(true)]).optional(),
49
+ dependencies: z.boolean().default(true),
50
+ devDependencies: z.boolean().default(true),
51
+ });
52
+ export type SyncConfig = z.infer<typeof SyncConfigSchema>;
53
+
41
54
  export const BosConfigSchema = z.object({
42
55
  account: z.string(),
56
+ testnet: z.string().optional(),
43
57
  gateway: GatewayConfigSchema,
44
- templates: z.record(z.string(), z.string()).optional(),
45
- create: z.record(z.string(), z.string()).optional(),
58
+ template: z.string().optional(),
46
59
  cli: z.object({
47
60
  version: z.string().optional(),
48
61
  }).optional(),