octwin-cli 0.1.6 → 0.1.7

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 (2) hide show
  1. package/dist/index.js +81 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@
7
7
  * tenant. Standalone: no platform checkout, no build step for the developer —
8
8
  * `npx octwin-cli <cmd>` (published as `octwin-cli`, command `octwin`).
9
9
  *
10
+ * octwin --version | -v # print the CLI version (+ any upgrade notice)
10
11
  * octwin init <dir> [--id my-pack] [--description "..."] [--display-name "..."]
11
12
  * octwin validate [--dir .] [--remote] # --remote → the platform's FULL schema check, all errors at once
12
13
  * octwin login --url <platformUrl> --token oct_…
@@ -37,6 +38,17 @@ import { validatePackBundle } from './lib/validate.js';
37
38
  // level under the package root), so `../templates/starter` resolves for the
38
39
  // built CLI and `tsx` dev alike.
39
40
  const TEMPLATE_DIR = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'templates', 'starter');
41
+ /** This CLI's version — read from its own package.json (one level up from dist/,
42
+ * always shipped in the npm tarball). Falls back to '0.0.0' if unreadable. */
43
+ const VERSION = (() => {
44
+ try {
45
+ const pkg = JSON.parse(readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf8'));
46
+ return typeof pkg?.version === 'string' ? pkg.version : '0.0.0';
47
+ }
48
+ catch {
49
+ return '0.0.0';
50
+ }
51
+ })();
40
52
  function parseFlags(argv) {
41
53
  const f = { _: [] };
42
54
  for (let i = 0; i < argv.length; i++) {
@@ -119,6 +131,67 @@ function writeCreds(map) {
119
131
  mkdirSync(join(homedir(), '.octwin'), { recursive: true });
120
132
  writeFileSync(credsPath(), JSON.stringify(map, null, 2), 'utf8');
121
133
  }
134
+ // ── update check (daily, fail-silent, TTY-only) ──────────────────────────────
135
+ function updateCachePath() { return join(homedir(), '.octwin', 'update-check.json'); }
136
+ /** True when semver `a` is strictly greater than `b` (simple x.y.z compare). */
137
+ function isNewer(a, b) {
138
+ const pa = a.split('.').map(n => parseInt(n, 10));
139
+ const pb = b.split('.').map(n => parseInt(n, 10));
140
+ for (let i = 0; i < 3; i++) {
141
+ const x = pa[i] ?? 0, y = pb[i] ?? 0;
142
+ if (Number.isNaN(x) || Number.isNaN(y))
143
+ return false;
144
+ if (x !== y)
145
+ return x > y;
146
+ }
147
+ return false;
148
+ }
149
+ /** The latest published `octwin-cli` version, cached for a day. Fail-silent:
150
+ * returns null on any error / offline — a version check must never break a command. */
151
+ async function latestPublishedVersion() {
152
+ const DAY = 24 * 60 * 60 * 1000;
153
+ try {
154
+ const cache = JSON.parse(readFileSync(updateCachePath(), 'utf8'));
155
+ if (cache.latest && typeof cache.checkedAt === 'number' && Date.now() - cache.checkedAt < DAY)
156
+ return cache.latest;
157
+ }
158
+ catch { /* no / stale cache → fetch below */ }
159
+ try {
160
+ const ctrl = new AbortController();
161
+ const timer = setTimeout(() => ctrl.abort(), 1500);
162
+ const res = await fetch('https://registry.npmjs.org/octwin-cli/latest', { signal: ctrl.signal });
163
+ clearTimeout(timer);
164
+ if (!res.ok)
165
+ return null;
166
+ const latest = (await res.json()).version;
167
+ if (typeof latest !== 'string')
168
+ return null;
169
+ try {
170
+ mkdirSync(join(homedir(), '.octwin'), { recursive: true });
171
+ writeFileSync(updateCachePath(), JSON.stringify({ checkedAt: Date.now(), latest }), 'utf8');
172
+ }
173
+ catch { /* cache is best-effort */ }
174
+ return latest;
175
+ }
176
+ catch {
177
+ return null;
178
+ }
179
+ }
180
+ /** Print a one-line upgrade notice (to stderr) when a newer octwin-cli is published.
181
+ * Skipped when not attached to a TTY (CI / piped) so it never adds noise to scripts.
182
+ * Never throws. */
183
+ async function notifyIfOutdated() {
184
+ if (!process.stdout.isTTY)
185
+ return;
186
+ try {
187
+ const latest = await latestPublishedVersion();
188
+ if (latest && isNewer(latest, VERSION)) {
189
+ console.error(`\n⬆ octwin-cli ${latest} is available (you have ${VERSION}).`);
190
+ console.error(' Upgrade: npm i -g octwin-cli@latest (or just use npx octwin-cli@latest)');
191
+ }
192
+ }
193
+ catch { /* a version check must never break the CLI */ }
194
+ }
122
195
  // ── commands ────────────────────────────────────────────────────────────────
123
196
  function cmdInit(flags) {
124
197
  const target = flags._[0] ?? die('usage: octwin init <dir> [--id my-pack]');
@@ -643,8 +716,9 @@ async function cmdChat(flags) {
643
716
  }
644
717
  }
645
718
  function help() {
646
- console.log(`octwin — Octwin external-pack developer CLI (by CEQUENS)
719
+ console.log(`octwin ${VERSION} — Octwin external-pack developer CLI (by CEQUENS)
647
720
 
721
+ octwin --version # print the CLI version (+ any upgrade notice)
648
722
  octwin init <dir> [--id my-pack] [--description "..."] [--display-name "..."]
649
723
  octwin validate [--dir .] [--remote] # --remote runs the platform's FULL schema check (all errors at once)
650
724
  octwin login --url <platformUrl> --token oct_… # a deploy token from the console
@@ -698,6 +772,11 @@ async function main() {
698
772
  case 'test':
699
773
  await cmdValidate({ ...flags, remote: true });
700
774
  break; // A6: `test` = the full remote validate, not a validate-clone
775
+ case '-v':
776
+ case '--version':
777
+ case 'version':
778
+ console.log(`octwin-cli ${VERSION}`);
779
+ break;
701
780
  case undefined:
702
781
  case 'help':
703
782
  case '--help':
@@ -706,5 +785,6 @@ async function main() {
706
785
  break;
707
786
  default: die(`unknown command '${command}' — run \`octwin help\``);
708
787
  }
788
+ await notifyIfOutdated(); // trailing, fail-silent, TTY-only "newer version available" notice
709
789
  }
710
790
  main().catch((err) => die(err?.message ?? String(err)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "octwin-cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Octwin external-pack developer CLI (by CEQUENS) — scaffold, validate, deploy, and check pure-YAML packs on your tenant.",
5
5
  "type": "module",
6
6
  "bin": {