@novastorm-ai/cli 0.0.4 → 0.0.6

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/dist/bin/nova.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  run
4
- } from "../chunk-CLQXFM4X.js";
4
+ } from "../chunk-C6GQ45IR.js";
5
5
  import "../chunk-4AQQAQBM.js";
6
6
  import "../chunk-QKD6A4EK.js";
7
7
  import "../chunk-KKTDQOQX.js";
@@ -105,16 +105,42 @@ var TeamDetector = class {
105
105
  }
106
106
  };
107
107
  var FREE_DEV_LIMIT = 3;
108
- var KEY_PATTERN = /^NOVA-([A-Z2-7]+)-([a-f0-9]{4})$/;
108
+ var NOVA_KEY_PATTERN = /^NOVA-([A-Z0-9]+)-([a-f0-9]{4})$/;
109
+ var NA_KEY_PATTERN = /^NA-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}$/;
110
+ var VALIDATE_ENDPOINT = "https://cli-api.novastorm.ai/v1/license/validate";
111
+ var TIMEOUT_MS = 5e3;
109
112
  function computeChecksum(base32Body) {
110
113
  return createHash("sha256").update(base32Body).digest("hex").slice(0, 4);
111
114
  }
112
- function validateKey(key) {
113
- const match = KEY_PATTERN.exec(key);
115
+ function validateKeyFormat(key) {
116
+ if (NA_KEY_PATTERN.test(key)) return true;
117
+ const match = NOVA_KEY_PATTERN.exec(key);
114
118
  if (!match) return false;
115
119
  const [, body, checksum] = match;
116
120
  return computeChecksum(body) === checksum;
117
121
  }
122
+ async function validateKeyOnline(key, devCount) {
123
+ try {
124
+ const controller = new AbortController();
125
+ const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
126
+ try {
127
+ const response = await fetch(VALIDATE_ENDPOINT, {
128
+ method: "POST",
129
+ headers: { "Content-Type": "application/json" },
130
+ body: JSON.stringify({ licenseKey: key, gitAuthors90d: devCount }),
131
+ signal: controller.signal
132
+ });
133
+ if (response.ok) {
134
+ return await response.json();
135
+ }
136
+ return null;
137
+ } finally {
138
+ clearTimeout(timeout);
139
+ }
140
+ } catch {
141
+ return null;
142
+ }
143
+ }
118
144
  var LicenseChecker = class {
119
145
  teamDetector = new TeamDetector();
120
146
  async check(projectPath, _config) {
@@ -129,27 +155,46 @@ var LicenseChecker = class {
129
155
  valid: false,
130
156
  tier: "company",
131
157
  devCount,
132
- message: "Company license required: this project has more than 3 contributors. Set NOVA_LICENSE_KEY to continue."
158
+ message: "Company license required: this project has more than 3 contributors. Set NOVA_LICENSE_KEY or run: nova license activate <key>"
133
159
  };
134
160
  }
135
- if (!validateKey(key)) {
161
+ if (!validateKeyFormat(key)) {
136
162
  return {
137
163
  valid: false,
138
164
  tier: "company",
139
165
  devCount,
140
- message: "Invalid license key format. Expected NOVA-{BASE32}-{CHECKSUM}."
166
+ message: "Invalid license key format. Get a valid key at https://cli.novastorm.ai/#pricing"
141
167
  };
142
168
  }
169
+ const onlineResult = await validateKeyOnline(key, devCount);
170
+ if (onlineResult) {
171
+ if (!onlineResult.valid) {
172
+ return {
173
+ valid: false,
174
+ tier: "company",
175
+ devCount,
176
+ message: "License key is invalid or expired. Visit https://cli.novastorm.ai/#pricing"
177
+ };
178
+ }
179
+ if (onlineResult.maxDevs && devCount > onlineResult.maxDevs) {
180
+ return {
181
+ valid: false,
182
+ tier: "company",
183
+ devCount,
184
+ message: `Your license covers ${onlineResult.maxDevs} developers but your team has ${devCount}. Upgrade at https://cli.novastorm.ai/#pricing`
185
+ };
186
+ }
187
+ }
143
188
  return { valid: true, tier: "company", devCount };
144
189
  }
145
190
  };
146
- var TELEMETRY_ENDPOINT = "https://api.nova-architect.dev/v1/telemetry";
147
- var TIMEOUT_MS = 3e3;
191
+ var TELEMETRY_ENDPOINT = "https://cli-api.novastorm.ai/v1/telemetry";
192
+ var TIMEOUT_MS2 = 3e3;
148
193
  var Telemetry = class {
149
194
  async send(payload) {
150
195
  try {
151
196
  const controller = new AbortController();
152
- const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
197
+ const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS2);
153
198
  try {
154
199
  const response = await fetch(TELEMETRY_ENDPOINT, {
155
200
  method: "POST",
@@ -176,16 +221,16 @@ var NudgeRenderer = class {
176
221
  case 0:
177
222
  return null;
178
223
  case 1:
179
- return "Nova Architect is free for teams of 3 or fewer. Learn more: https://nova-architect.dev/pricing";
224
+ return "Nova Architect is free for teams of 3 or fewer. Learn more: https://cli.novastorm.ai/#pricing";
180
225
  case 2:
181
- return `Your team has ${context.devCount} developers. A license is recommended. Visit https://nova-architect.dev/pricing`;
226
+ return `Your team has ${context.devCount} developers. A license is recommended. Visit https://cli.novastorm.ai/#pricing`;
182
227
  case 3:
183
228
  return [
184
229
  "+-------------------------------------------------+",
185
230
  "| License Required |",
186
231
  `| Your team of ${String(context.devCount).padEnd(3)} developers needs a |`,
187
232
  "| commercial license. |",
188
- "| -> https://nova-architect.dev/pricing |",
233
+ "| -> https://cli.novastorm.ai/#pricing |",
189
234
  "+-------------------------------------------------+"
190
235
  ].join("\n");
191
236
  default:
@@ -882,13 +927,13 @@ async function startCommand() {
882
927
  if (config.telemetry.enabled && process.env["NOVA_TELEMETRY"] !== "false") {
883
928
  const { createHash: createHash2 } = await import("crypto");
884
929
  const os = await import("os");
885
- const { execFile: execFile2 } = await import("child_process");
930
+ const { execFile: execFile3 } = await import("child_process");
886
931
  const mac = Object.values(os.networkInterfaces()).flat().find((i) => !i?.internal && i?.mac !== "00:00:00:00:00:00")?.mac ?? "";
887
932
  const machineId = createHash2("sha256").update(os.hostname() + os.userInfo().username + mac).digest("hex");
888
933
  let projectHash;
889
934
  try {
890
935
  const remoteUrl = await new Promise((resolve4, reject) => {
891
- execFile2("git", ["remote", "get-url", "origin"], { cwd }, (err, stdout3) => {
936
+ execFile3("git", ["remote", "get-url", "origin"], { cwd }, (err, stdout3) => {
892
937
  if (err) reject(err);
893
938
  else resolve4(stdout3.trim());
894
939
  });
@@ -898,7 +943,7 @@ async function startCommand() {
898
943
  projectHash = createHash2("sha256").update(cwd).digest("hex");
899
944
  }
900
945
  const telemetry = new Telemetry();
901
- const cliPkg = await import("./package-BDGFABJG.js").catch(
946
+ const cliPkg = await import("./package-6R6RECMI.js").catch(
902
947
  () => ({ default: { version: "0.0.1" } })
903
948
  );
904
949
  telemetry.send({
@@ -1573,9 +1618,9 @@ async function watchCommand() {
1573
1618
 
1574
1619
  // src/commands/license.ts
1575
1620
  import chalk7 from "chalk";
1576
- var KEY_PATTERN2 = /^NOVA-([A-Z2-7]+)-([a-f0-9]{4})$/;
1577
- var VALIDATE_ENDPOINT = "https://api.nova-architect.dev/v1/license/validate";
1578
- var TIMEOUT_MS2 = 5e3;
1621
+ var KEY_PATTERN = /^NOVA-([A-Z2-7]+)-([a-f0-9]{4})$/;
1622
+ var VALIDATE_ENDPOINT2 = "https://cli-api.novastorm.ai/v1/license/validate";
1623
+ var TIMEOUT_MS3 = 5e3;
1579
1624
  async function licenseCommand(subcommand, key) {
1580
1625
  const cwd = process.cwd();
1581
1626
  const configReader = new ConfigReader();
@@ -1621,7 +1666,7 @@ async function showStatus(cwd, config) {
1621
1666
  console.log("");
1622
1667
  }
1623
1668
  async function activateKey(cwd, configReader, key) {
1624
- if (!KEY_PATTERN2.test(key)) {
1669
+ if (!KEY_PATTERN.test(key)) {
1625
1670
  console.error(chalk7.red("Invalid key format. Expected: NOVA-{BASE32}-{CHECKSUM}"));
1626
1671
  process.exit(1);
1627
1672
  }
@@ -1629,9 +1674,9 @@ async function activateKey(cwd, configReader, key) {
1629
1674
  let serverValid = true;
1630
1675
  try {
1631
1676
  const controller = new AbortController();
1632
- const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS2);
1677
+ const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS3);
1633
1678
  try {
1634
- const response = await fetch(VALIDATE_ENDPOINT, {
1679
+ const response = await fetch(VALIDATE_ENDPOINT2, {
1635
1680
  method: "POST",
1636
1681
  headers: { "Content-Type": "application/json" },
1637
1682
  body: JSON.stringify({ key }),
@@ -1968,6 +2013,88 @@ async function bibleCommand(subcommand) {
1968
2013
  }
1969
2014
  }
1970
2015
 
2016
+ // src/commands/update.ts
2017
+ import { execFile as execFile2 } from "child_process";
2018
+ import chalk10 from "chalk";
2019
+ import ora3 from "ora";
2020
+ var PKG_NAME = "@novastorm-ai/cli";
2021
+ async function getLatestVersion() {
2022
+ try {
2023
+ const controller = new AbortController();
2024
+ const timeout = setTimeout(() => controller.abort(), 5e3);
2025
+ try {
2026
+ const res = await fetch(`https://registry.npmjs.org/${PKG_NAME}/latest`, {
2027
+ signal: controller.signal
2028
+ });
2029
+ if (res.ok) {
2030
+ const data = await res.json();
2031
+ return data.version;
2032
+ }
2033
+ return null;
2034
+ } finally {
2035
+ clearTimeout(timeout);
2036
+ }
2037
+ } catch {
2038
+ return null;
2039
+ }
2040
+ }
2041
+ function runNpmInstall() {
2042
+ return new Promise((resolve4) => {
2043
+ execFile2("npm", ["install", "-g", `${PKG_NAME}@latest`], { timeout: 6e4 }, (error, stdout3, stderr) => {
2044
+ if (error) {
2045
+ resolve4({ ok: false, output: stderr || error.message });
2046
+ } else {
2047
+ resolve4({ ok: true, output: stdout3 });
2048
+ }
2049
+ });
2050
+ });
2051
+ }
2052
+ async function updateCommand() {
2053
+ const spinner = ora3("Checking for updates...").start();
2054
+ const latest = await getLatestVersion();
2055
+ if (!latest) {
2056
+ spinner.fail("Could not reach npm registry. Check your internet connection.");
2057
+ return;
2058
+ }
2059
+ const { readFileSync: readFileSync2 } = await import("fs");
2060
+ const { dirname: dirname2, resolve: resolve4 } = await import("path");
2061
+ const { fileURLToPath: fileURLToPath2 } = await import("url");
2062
+ const __dirname2 = dirname2(fileURLToPath2(import.meta.url));
2063
+ let currentVersion = "0.0.0";
2064
+ try {
2065
+ const pkg2 = JSON.parse(readFileSync2(resolve4(__dirname2, "..", "package.json"), "utf-8"));
2066
+ currentVersion = pkg2.version;
2067
+ } catch {
2068
+ }
2069
+ if (currentVersion === latest) {
2070
+ spinner.succeed(`Already on the latest version ${chalk10.green(latest)}`);
2071
+ return;
2072
+ }
2073
+ spinner.text = `Updating ${chalk10.gray(currentVersion)} \u2192 ${chalk10.green(latest)}...`;
2074
+ const result = await runNpmInstall();
2075
+ if (result.ok) {
2076
+ spinner.succeed(`Updated to ${chalk10.green(latest)}`);
2077
+ } else {
2078
+ spinner.fail("Update failed. Try manually:");
2079
+ console.log(chalk10.cyan(` npm install -g ${PKG_NAME}@latest`));
2080
+ if (result.output) {
2081
+ console.log(chalk10.gray(result.output.trim()));
2082
+ }
2083
+ }
2084
+ }
2085
+ async function checkForUpdates(currentVersion) {
2086
+ try {
2087
+ const latest = await getLatestVersion();
2088
+ if (latest && latest !== currentVersion) {
2089
+ console.log(
2090
+ chalk10.yellow(` Update available: ${chalk10.gray(currentVersion)} \u2192 ${chalk10.green(latest)}`) + chalk10.gray(` Run ${chalk10.cyan("nova update")} to install
2091
+ `)
2092
+ );
2093
+ }
2094
+ } catch {
2095
+ }
2096
+ }
2097
+
1971
2098
  // src/index.ts
1972
2099
  var __dirname = dirname(fileURLToPath(import.meta.url));
1973
2100
  var pkg = JSON.parse(
@@ -2028,6 +2155,9 @@ function createCli() {
2028
2155
  program.command("bible [subcommand]").description("Read the Ambient Development manifesto: nova bible [--read]").action(async (subcommand) => {
2029
2156
  await bibleCommand(subcommand);
2030
2157
  });
2158
+ program.command("update").description("Update Novastorm CLI to the latest version").action(async () => {
2159
+ await updateCommand();
2160
+ });
2031
2161
  return program;
2032
2162
  }
2033
2163
  var BANNER = `\x1B[96m
@@ -2040,12 +2170,15 @@ var BANNER = `\x1B[96m
2040
2170
  \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
2041
2171
  \u2551 A M B I E N T D E V E L O P M E N T T O O L \u2551
2042
2172
  \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
2043
- \x1B[0m`;
2173
+ \x1B[0m\x1B[90m https://cli.novastorm.ai\x1B[0m
2174
+ `;
2044
2175
  async function run(argv = process.argv) {
2045
2176
  const args = argv.slice(2);
2046
2177
  const suppressBanner = args.includes("--version") || args.includes("-V") || args.includes("--help") || args.includes("-h");
2047
2178
  if (!suppressBanner) {
2048
2179
  console.log(BANNER);
2180
+ checkForUpdates(pkg.version).catch(() => {
2181
+ });
2049
2182
  }
2050
2183
  const program = createCli();
2051
2184
  await program.parseAsync(argv);
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  createCli,
5
5
  promptAndScaffold,
6
6
  run
7
- } from "./chunk-CLQXFM4X.js";
7
+ } from "./chunk-C6GQ45IR.js";
8
8
  import "./chunk-4AQQAQBM.js";
9
9
  import {
10
10
  ConfigReader,
@@ -4,7 +4,7 @@ import "./chunk-3RG5ZIWI.js";
4
4
  var package_default = {
5
5
  name: "@novastorm-ai/cli",
6
6
  publishConfig: { access: "public" },
7
- version: "0.0.4",
7
+ version: "0.0.6",
8
8
  license: "SEE LICENSE IN LICENSE.md",
9
9
  type: "module",
10
10
  main: "dist/index.js",
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.0.4",
6
+ "version": "0.0.6",
7
7
  "license": "SEE LICENSE IN LICENSE.md",
8
8
  "type": "module",
9
9
  "main": "dist/index.js",