everything-dev 0.0.3 → 0.0.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "everything-dev",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "bos": "./src/cli.ts"
@@ -23,6 +23,7 @@
23
23
  "degit": "^2.8.4",
24
24
  "effect": "^3.19.14",
25
25
  "every-plugin": "0.8.8",
26
+ "everything-dev": "^0.0.4",
26
27
  "execa": "^9.6.1",
27
28
  "gradient-string": "^3.0.0",
28
29
  "ink": "^5.2.1",
package/src/cli.ts CHANGED
@@ -1,43 +1,73 @@
1
1
  #!/usr/bin/env bun
2
2
  import { program } from "commander";
3
3
  import { createPluginRuntime } from "every-plugin";
4
- import { getConfigDir, getConfigPath, getPackages, getTitle, loadConfig } from "./config";
4
+ import { getConfigDir, getConfigPath, getPackages, getTitle, loadConfig, type BosConfig } from "./config";
5
5
  import BosPlugin from "./plugin";
6
6
  import { printBanner } from "./utils/banner";
7
7
  import { colors, frames, gradients, icons } from "./utils/theme";
8
8
 
9
- async function main() {
10
- let config: ReturnType<typeof loadConfig>;
9
+ function getHelpHeader(config: BosConfig | null, configPath: string): string {
10
+ const host = config?.app.host;
11
+ const lines: string[] = [];
12
+
13
+ lines.push("");
14
+ lines.push(colors.cyan(frames.top(52)));
15
+ lines.push(` ${icons.config} ${gradients.cyber("BOS CLI")} ${colors.dim("v1.0.0")}`);
16
+ lines.push(colors.cyan(frames.bottom(52)));
17
+ lines.push("");
18
+
19
+ if (config) {
20
+ lines.push(` ${colors.dim("Account")} ${colors.cyan(config.account)}`);
21
+ lines.push(` ${colors.dim("Gateway")} ${colors.white(config.gateway?.production ?? "not configured")}`);
22
+ lines.push(` ${colors.dim("Config ")} ${colors.dim(configPath)}`);
23
+ if (host?.description) {
24
+ lines.push(` ${colors.dim("About ")} ${colors.white(host.description)}`);
25
+ }
26
+ } else {
27
+ lines.push(` ${colors.dim("No project config found")}`);
28
+ lines.push(` ${colors.dim("Run")} ${colors.cyan("bos create project <name>")} ${colors.dim("to get started")}`);
29
+ }
30
+
31
+ lines.push("");
32
+ lines.push(colors.cyan(frames.top(52)));
33
+ lines.push("");
34
+
35
+ return lines.join("\n");
36
+ }
11
37
 
12
- try {
13
- config = loadConfig();
14
- } catch {
38
+ function requireConfig(config: BosConfig | null): asserts config is BosConfig {
39
+ if (!config) {
15
40
  console.error(colors.error(`${icons.err} Could not find bos.config.json`));
16
41
  console.log(colors.dim(" Run 'bos create project <name>' to create a new project"));
17
42
  process.exit(1);
18
43
  }
44
+ }
19
45
 
20
- const envPath = `${getConfigDir()}/.env.bos`;
21
- const envFile = Bun.file(envPath);
22
- if (await envFile.exists()) {
23
- const content = await envFile.text();
24
- for (const line of content.split("\n")) {
25
- const trimmed = line.trim();
26
- if (!trimmed || trimmed.startsWith("#")) continue;
27
- const eqIndex = trimmed.indexOf("=");
28
- if (eqIndex === -1) continue;
29
- const key = trimmed.slice(0, eqIndex).trim();
30
- const value = trimmed.slice(eqIndex + 1).trim();
31
- if (key && !process.env[key]) {
32
- process.env[key] = value;
46
+ async function main() {
47
+ const config = loadConfig();
48
+ const configPath = config ? getConfigPath() : process.cwd();
49
+ const packages = config ? getPackages() : [];
50
+ const title = config ? getTitle() : "BOS CLI";
51
+
52
+ if (config) {
53
+ const envPath = `${getConfigDir()}/.env.bos`;
54
+ const envFile = Bun.file(envPath);
55
+ if (await envFile.exists()) {
56
+ const content = await envFile.text();
57
+ for (const line of content.split("\n")) {
58
+ const trimmed = line.trim();
59
+ if (!trimmed || trimmed.startsWith("#")) continue;
60
+ const eqIndex = trimmed.indexOf("=");
61
+ if (eqIndex === -1) continue;
62
+ const key = trimmed.slice(0, eqIndex).trim();
63
+ const value = trimmed.slice(eqIndex + 1).trim();
64
+ if (key && !process.env[key]) {
65
+ process.env[key] = value;
66
+ }
33
67
  }
34
68
  }
35
69
  }
36
70
 
37
- const packages = getPackages();
38
- const title = getTitle();
39
- const configPath = getConfigPath();
40
-
41
71
  printBanner(title);
42
72
 
43
73
  const runtime = createPluginRuntime({
@@ -59,32 +89,10 @@ async function main() {
59
89
 
60
90
  const client = result.createClient();
61
91
 
62
- function getHelpHeader(): string {
63
- const host = config.app.host;
64
- const lines: string[] = [];
65
-
66
- lines.push("");
67
- lines.push(colors.cyan(frames.top(52)));
68
- lines.push(` ${icons.config} ${gradients.cyber("BOS CLI")} ${colors.dim("v1.0.0")}`);
69
- lines.push(colors.cyan(frames.bottom(52)));
70
- lines.push("");
71
- lines.push(` ${colors.dim("Account")} ${colors.cyan(config.account)}`);
72
- lines.push(` ${colors.dim("Gateway")} ${colors.white(config.gateway.production)}`);
73
- lines.push(` ${colors.dim("Config ")} ${colors.dim(configPath)}`);
74
- if (host.description) {
75
- lines.push(` ${colors.dim("About ")} ${colors.white(host.description)}`);
76
- }
77
- lines.push("");
78
- lines.push(colors.cyan(frames.top(52)));
79
- lines.push("");
80
-
81
- return lines.join("\n");
82
- }
83
-
84
92
  program
85
93
  .name("bos")
86
94
  .version("1.0.0")
87
- .addHelpText("before", getHelpHeader());
95
+ .addHelpText("before", getHelpHeader(config, configPath));
88
96
 
89
97
  program
90
98
  .command("info")
@@ -263,7 +271,7 @@ Zephyr Configuration:
263
271
  .action(async (options) => {
264
272
  console.log();
265
273
  console.log(` ${icons.pkg} Publishing to Near Social...`);
266
- console.log(colors.dim(` Account: ${config.account}`));
274
+ console.log(colors.dim(` Account: ${config?.account}`));
267
275
  console.log(colors.dim(` Network: ${options.network}`));
268
276
 
269
277
  if (options.dryRun) {
@@ -486,7 +494,7 @@ Zephyr Configuration:
486
494
  program
487
495
  .command("register")
488
496
  .description("Register a new tenant on the gateway")
489
- .argument("<name>", `Account name (will create <name>.${config.account})`)
497
+ .argument("<name>", `Account name (will create <name>.${config?.account})`)
490
498
  .option("--network <network>", "Network: mainnet | testnet", "mainnet")
491
499
  .action(async (name: string, options: { network: string }) => {
492
500
  console.log();
@@ -1,5 +1,6 @@
1
1
  import { Box, render, Text, useApp, useInput } from "ink";
2
2
  import { useEffect, useState } from "react";
3
+ import { linkify } from "../utils/linkify";
3
4
  import { colors, divider, gradients, icons, frames } from "../utils/theme";
4
5
 
5
6
  export type ProcessStatus = "pending" | "starting" | "ready" | "error";
@@ -81,7 +82,7 @@ function LogLine({ entry }: { entry: LogEntry }) {
81
82
  return (
82
83
  <Box>
83
84
  <Text color={color}>[{entry.source}]</Text>
84
- <Text color={entry.isError ? "#ff3366" : undefined}> {entry.line}</Text>
85
+ <Text color={entry.isError ? "#ff3366" : undefined}> {linkify(entry.line)}</Text>
85
86
  </Box>
86
87
  );
87
88
  }
@@ -1,3 +1,4 @@
1
+ import { linkify } from "../utils/linkify";
1
2
  import { colors, icons } from "../utils/theme";
2
3
  import type { ProcessState, ProcessStatus } from "./dev-view";
3
4
 
@@ -93,7 +94,7 @@ export function renderStreamingView(
93
94
  const addLog = (source: string, line: string, isError = false) => {
94
95
  const color = getServiceColor(source);
95
96
  const logColor = isError ? colors.error : colors.dim;
96
- write(`${colors.dim(`[${getTimestamp()}]`)} ${color(`[${source.toUpperCase()}]`)} ${colors.dim("│")} ${logColor(line)}`);
97
+ write(`${colors.dim(`[${getTimestamp()}]`)} ${color(`[${source.toUpperCase()}]`)} ${colors.dim("│")} ${logColor(linkify(line))}`);
97
98
  };
98
99
 
99
100
  const unmount = () => {
package/src/config.ts CHANGED
@@ -55,6 +55,7 @@ export interface BosConfig {
55
55
 
56
56
  let cachedConfig: BosConfig | null = null;
57
57
  let configDir: string | null = null;
58
+ let configLoaded = false;
58
59
 
59
60
  export function findConfigPath(startDir: string): string | null {
60
61
  let dir = startDir;
@@ -86,25 +87,33 @@ function findConfigPathSync(startDir: string): string | null {
86
87
  return null;
87
88
  }
88
89
 
89
- export function loadConfig(cwd?: string): BosConfig {
90
- if (cachedConfig) return cachedConfig;
90
+ export function loadConfig(cwd?: string): BosConfig | null {
91
+ if (configLoaded) return cachedConfig;
91
92
 
92
93
  const startDir = cwd ?? process.cwd();
93
94
  const configPath = findConfigPathSync(startDir);
94
95
 
95
96
  if (!configPath) {
96
- throw new Error("Could not find bos.config.json in current directory or parents");
97
+ configLoaded = true;
98
+ configDir = startDir;
99
+ return null;
97
100
  }
98
101
 
99
102
  configDir = dirname(configPath);
100
- const file = Bun.file(configPath);
101
103
  const content = require(configPath);
102
104
  cachedConfig = content as BosConfig;
105
+ configLoaded = true;
103
106
  return cachedConfig;
104
107
  }
105
108
 
109
+ export function setConfig(config: BosConfig, dir?: string): void {
110
+ cachedConfig = config;
111
+ configDir = dir ?? process.cwd();
112
+ configLoaded = true;
113
+ }
114
+
106
115
  export function getConfigDir(): string {
107
- if (!configDir) {
116
+ if (!configLoaded) {
108
117
  loadConfig();
109
118
  }
110
119
  return configDir!;
@@ -112,16 +121,19 @@ export function getConfigDir(): string {
112
121
 
113
122
  export function getRemotes(): string[] {
114
123
  const config = loadConfig();
124
+ if (!config) return [];
115
125
  return Object.keys(config.app).filter((k) => k !== "host");
116
126
  }
117
127
 
118
128
  export function getPackages(): string[] {
119
129
  const config = loadConfig();
130
+ if (!config) return [];
120
131
  return Object.keys(config.app);
121
132
  }
122
133
 
123
134
  export function getRemote(name: string): RemoteConfig | undefined {
124
135
  const config = loadConfig();
136
+ if (!config) return undefined;
125
137
  const remote = config.app[name];
126
138
  if (remote && "name" in remote) {
127
139
  return remote as RemoteConfig;
@@ -131,6 +143,9 @@ export function getRemote(name: string): RemoteConfig | undefined {
131
143
 
132
144
  export function getHost(): HostConfig {
133
145
  const config = loadConfig();
146
+ if (!config) {
147
+ throw new Error("No bos.config.json found");
148
+ }
134
149
  return config.app.host;
135
150
  }
136
151
 
@@ -139,6 +154,7 @@ export function getUrl(
139
154
  env: "development" | "production" = "development"
140
155
  ): string | undefined {
141
156
  const config = loadConfig();
157
+ if (!config) return undefined;
142
158
  const pkg = config.app[packageName];
143
159
  if (!pkg) return undefined;
144
160
  return pkg[env];
@@ -146,11 +162,17 @@ export function getUrl(
146
162
 
147
163
  export function getAccount(): string {
148
164
  const config = loadConfig();
165
+ if (!config) {
166
+ throw new Error("No bos.config.json found");
167
+ }
149
168
  return config.account;
150
169
  }
151
170
 
152
171
  export function getTitle(): string {
153
172
  const config = loadConfig();
173
+ if (!config) {
174
+ throw new Error("No bos.config.json found");
175
+ }
154
176
  return config.app.host.title;
155
177
  }
156
178
 
@@ -159,6 +181,9 @@ export function getComponentUrl(
159
181
  source: SourceMode
160
182
  ): string {
161
183
  const config = loadConfig();
184
+ if (!config) {
185
+ throw new Error("No bos.config.json found");
186
+ }
162
187
 
163
188
  if (component === "host") {
164
189
  return source === "remote" ? config.app.host.production : config.app.host.development;
@@ -189,6 +214,9 @@ export interface PortConfig {
189
214
 
190
215
  export function getPortsFromConfig(): PortConfig {
191
216
  const config = loadConfig();
217
+ if (!config) {
218
+ return { host: 3000, ui: 3002, api: 3014 };
219
+ }
192
220
  return {
193
221
  host: parsePort(config.app.host.development),
194
222
  ui: config.app.ui ? parsePort((config.app.ui as RemoteConfig).development) : 3002,
@@ -205,10 +233,14 @@ export function getConfigPath(): string {
205
233
 
206
234
  export function getHostRemoteUrl(): string | undefined {
207
235
  const config = loadConfig();
236
+ if (!config) return undefined;
208
237
  return config.app.host.production || undefined;
209
238
  }
210
239
 
211
240
  export function getGatewayUrl(env: "development" | "production" = "development"): string {
212
241
  const config = loadConfig();
242
+ if (!config) {
243
+ throw new Error("No bos.config.json found");
244
+ }
213
245
  return config.gateway[env];
214
246
  }
package/src/plugin.ts CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  getPortsFromConfig,
15
15
  getRemotes,
16
16
  loadConfig,
17
+ setConfig,
17
18
  type SourceMode
18
19
  } from "./config";
19
20
  import { bosContract } from "./contract";
@@ -37,7 +38,7 @@ import { run } from "./utils/run";
37
38
  import { colors, icons } from "./utils/theme";
38
39
 
39
40
  interface BosDeps {
40
- bosConfig: BosConfigType;
41
+ bosConfig: BosConfigType | null;
41
42
  configDir: string;
42
43
  nearPrivateKey?: string;
43
44
  }
@@ -164,7 +165,7 @@ export default createPlugin({
164
165
  bosConfig,
165
166
  configDir,
166
167
  nearPrivateKey: config.secrets.nearPrivateKey
167
- };
168
+ } as BosDeps;
168
169
  }),
169
170
 
170
171
  shutdown: () => Effect.void,
@@ -233,6 +234,7 @@ export default createPlugin({
233
234
  }
234
235
  if (typeof current === "string") {
235
236
  remoteConfig = JSON.parse(current) as BosConfigType;
237
+ setConfig(remoteConfig);
236
238
  }
237
239
  }
238
240
  } catch (error) {
@@ -253,6 +255,15 @@ export default createPlugin({
253
255
  }
254
256
 
255
257
  const config = remoteConfig || deps.bosConfig;
258
+
259
+ if (!config) {
260
+ console.error("No configuration available. Provide --account and --domain, or run from a BOS project directory.");
261
+ return {
262
+ status: "error" as const,
263
+ url: "",
264
+ };
265
+ }
266
+
256
267
  const port = input.port ?? 3000;
257
268
 
258
269
  const env: Record<string, string> = {
@@ -361,6 +372,15 @@ export default createPlugin({
361
372
  publish: builder.publish.handler(async ({ input: publishInput }) => {
362
373
  const { bosConfig, nearPrivateKey } = deps;
363
374
 
375
+ if (!bosConfig) {
376
+ return {
377
+ status: "error" as const,
378
+ txHash: "",
379
+ registryUrl: "",
380
+ error: "No bos.config.json found. Run from a BOS project directory.",
381
+ };
382
+ }
383
+
364
384
  const gatewayDomain = getGatewayDomain(bosConfig);
365
385
  const socialPath = `${bosConfig.account}/bos/gateways/${gatewayDomain}/bos.config.json`;
366
386
 
@@ -425,7 +445,7 @@ export default createPlugin({
425
445
  gateway: "near-everything/every-plugin/demo/gateway",
426
446
  };
427
447
 
428
- const template = input.template || deps.bosConfig.create?.[input.type] || DEFAULT_TEMPLATES[input.type];
448
+ const template = input.template || deps.bosConfig?.create?.[input.type] || DEFAULT_TEMPLATES[input.type];
429
449
  const dest = input.type === "project" ? input.name! : input.type;
430
450
 
431
451
  try {
@@ -434,6 +454,10 @@ export default createPlugin({
434
454
  if (input.type === "project" && input.name) {
435
455
  const newConfig = {
436
456
  account: `${input.name}.near`,
457
+ gateway: {
458
+ development: "http://localhost:8787",
459
+ production: `https://gateway.${input.name}.example.com`,
460
+ },
437
461
  create: DEFAULT_TEMPLATES,
438
462
  app: {
439
463
  host: {
@@ -493,6 +517,11 @@ export default createPlugin({
493
517
 
494
518
  status: builder.status.handler(async ({ input }) => {
495
519
  const config = deps.bosConfig;
520
+
521
+ if (!config) {
522
+ return { endpoints: [] };
523
+ }
524
+
496
525
  const host = getHost();
497
526
  const remotes = getRemotes();
498
527
  const env = input.env;
@@ -582,6 +611,14 @@ export default createPlugin({
582
611
  register: builder.register.handler(async ({ input }) => {
583
612
  const { bosConfig } = deps;
584
613
 
614
+ if (!bosConfig) {
615
+ return {
616
+ status: "error" as const,
617
+ account: input.name,
618
+ error: "No bos.config.json found. Run from a BOS project directory.",
619
+ };
620
+ }
621
+
585
622
  const registerEffect = Effect.gen(function* () {
586
623
  yield* ensureNearCli;
587
624
 
@@ -625,6 +662,14 @@ export default createPlugin({
625
662
  secretsSync: builder.secretsSync.handler(async ({ input }) => {
626
663
  const { bosConfig } = deps;
627
664
 
665
+ if (!bosConfig) {
666
+ return {
667
+ status: "error" as const,
668
+ count: 0,
669
+ error: "No bos.config.json found. Run from a BOS project directory.",
670
+ };
671
+ }
672
+
628
673
  const syncEffect = Effect.gen(function* () {
629
674
  const novaConfig = yield* getNovaConfig;
630
675
  const nova = createNovaClient(novaConfig);
@@ -659,6 +704,13 @@ export default createPlugin({
659
704
  secretsSet: builder.secretsSet.handler(async ({ input }) => {
660
705
  const { bosConfig } = deps;
661
706
 
707
+ if (!bosConfig) {
708
+ return {
709
+ status: "error" as const,
710
+ error: "No bos.config.json found. Run from a BOS project directory.",
711
+ };
712
+ }
713
+
662
714
  const setEffect = Effect.gen(function* () {
663
715
  const novaConfig = yield* getNovaConfig;
664
716
  const nova = createNovaClient(novaConfig);
@@ -685,6 +737,14 @@ export default createPlugin({
685
737
  secretsList: builder.secretsList.handler(async () => {
686
738
  const { bosConfig } = deps;
687
739
 
740
+ if (!bosConfig) {
741
+ return {
742
+ status: "error" as const,
743
+ keys: [],
744
+ error: "No bos.config.json found. Run from a BOS project directory.",
745
+ };
746
+ }
747
+
688
748
  const listEffect = Effect.gen(function* () {
689
749
  const novaConfig = yield* getNovaConfig;
690
750
  const nova = createNovaClient(novaConfig);
@@ -722,6 +782,13 @@ export default createPlugin({
722
782
  secretsDelete: builder.secretsDelete.handler(async ({ input }) => {
723
783
  const { bosConfig } = deps;
724
784
 
785
+ if (!bosConfig) {
786
+ return {
787
+ status: "error" as const,
788
+ error: "No bos.config.json found. Run from a BOS project directory.",
789
+ };
790
+ }
791
+
725
792
  const deleteEffect = Effect.gen(function* () {
726
793
  const novaConfig = yield* getNovaConfig;
727
794
  const nova = createNovaClient(novaConfig);
@@ -853,6 +920,15 @@ export default createPlugin({
853
920
 
854
921
  gatewayDeploy: builder.gatewayDeploy.handler(async ({ input }) => {
855
922
  const { configDir, bosConfig } = deps;
923
+
924
+ if (!bosConfig) {
925
+ return {
926
+ status: "error" as const,
927
+ url: "",
928
+ error: "No bos.config.json found. Run from a BOS project directory.",
929
+ };
930
+ }
931
+
856
932
  const gatewayDir = `${configDir}/gateway`;
857
933
 
858
934
  const deployEffect = Effect.gen(function* () {
@@ -896,6 +972,14 @@ export default createPlugin({
896
972
 
897
973
  gatewaySync: builder.gatewaySync.handler(async () => {
898
974
  const { configDir, bosConfig } = deps;
975
+
976
+ if (!bosConfig) {
977
+ return {
978
+ status: "error" as const,
979
+ error: "No bos.config.json found. Run from a BOS project directory.",
980
+ };
981
+ }
982
+
899
983
  const wranglerPath = `${configDir}/gateway/wrangler.toml`;
900
984
 
901
985
  try {
@@ -0,0 +1,11 @@
1
+ const URL_REGEX = /https?:\/\/[^\s<>"{}|\\^`[\]]+/g;
2
+
3
+ const OSC_START = "\x1b]8;;";
4
+ const OSC_END = "\x07";
5
+ const OSC_RESET = "\x1b]8;;\x07";
6
+
7
+ export const linkify = (text: string): string => {
8
+ return text.replace(URL_REGEX, (url) => {
9
+ return `${OSC_START}${url}${OSC_END}${url}${OSC_RESET}`;
10
+ });
11
+ };