@vrdmr/fnx-test 0.2.0 → 0.3.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.
package/README.md CHANGED
@@ -11,7 +11,7 @@ npm install -g @vrdmr/fnx-test
11
11
  Or run without installing:
12
12
 
13
13
  ```bash
14
- npx @vrdmr/fnx-test start --sku flex --scriptroot ./my-app
14
+ npx @vrdmr/fnx-test start --sku flex --app-path ./my-app
15
15
  ```
16
16
 
17
17
  ## Quick Start
@@ -21,10 +21,10 @@ npx @vrdmr/fnx-test start --sku flex --scriptroot ./my-app
21
21
  fnx start --sku list
22
22
 
23
23
  # Run your function app with the Flex Consumption host
24
- fnx start --sku flex --scriptroot ./my-function-app
24
+ fnx start --sku flex --app-path ./my-function-app
25
25
 
26
26
  # Run with Windows Consumption host
27
- fnx start --sku windows-consumption --scriptroot ./my-function-app
27
+ fnx start --sku windows-consumption --app-path ./my-function-app
28
28
  ```
29
29
 
30
30
  ## What It Does
@@ -53,12 +53,24 @@ fnx start --sku flex
53
53
  ## Commands
54
54
 
55
55
  ```bash
56
- fnx start --sku <sku> --scriptroot <path> # Run function app
56
+ fnx start --sku <sku> --app-path <path> # Run function app
57
57
  fnx start --sku list # List available SKUs
58
58
  fnx warmup [--sku <sku>] [--all] # Pre-download host + bundle
59
+ fnx sync [host|extensions] [--sku <sku>] # Reconcile cache with latest catalog / rollback
59
60
  fnx templates-mcp # Start MCP server for AI agents
61
+ fnx pack --app-path <path> # Package function app as deployment zip
60
62
  ```
61
63
 
64
+
65
+ ## Upgrades, Rollbacks, and Cache Retention
66
+
67
+ - On `fnx start`, `fnx warmup`, and `fnx sync`, fnx attempts to refresh the SKU catalog from CDN first.
68
+ - If catalog host version is newer than your local cache, fnx highlights that and recommends `fnx sync` (or `fnx sync host`).
69
+ - If catalog host version is lower than a locally cached version (service rollback), fnx warns and recommends syncing back to the supported host.
70
+ - `fnx sync` defaults to keeping only the latest 2 host and bundle versions to avoid cache bloat (`--keep <n>` to override).
71
+ - fnx also checks npm for newer fnx CLI releases and prints an upgrade tip when available.
72
+ - Version comparisons use numeric dot-segment precedence (e.g. `4.1047.100 > 4.1046.999`) with support for `v` prefixes and pre-release suffixes.
73
+
62
74
  ## MCP Server (for AI Agents)
63
75
 
64
76
  fnx includes an MCP server that exposes Azure Functions templates to AI coding assistants:
@@ -3,6 +3,7 @@ import { createConnection } from 'node:net';
3
3
  import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
4
4
  import { join } from 'node:path';
5
5
  import { homedir } from 'node:os';
6
+ import { info, url as urlColor, warning, error as errorColor } from './colors.js';
6
7
 
7
8
  const BLOB_PORT = 10000;
8
9
  const QUEUE_PORT = 10001;
@@ -75,7 +76,7 @@ function findAzurite() {
75
76
  * Install azurite into ~/.fnx/tools/azurite/ if not already present.
76
77
  */
77
78
  function installAzurite() {
78
- console.log('[fnx] Installing Azurite to ~/.fnx/tools/azurite/ (first-time only)...');
79
+ console.log(info('[fnx] Installing Azurite to ~/.fnx/tools/azurite/ (first-time only)...'));
79
80
  mkdirSync(AZURITE_INSTALL_DIR, { recursive: true });
80
81
 
81
82
  // Initialize a minimal package.json if missing so npm install works
@@ -91,14 +92,14 @@ function installAzurite() {
91
92
  timeout: 120_000,
92
93
  });
93
94
  } catch (err) {
94
- console.error('[fnx] Failed to install Azurite. Install manually: npm install -g azurite');
95
- console.error(` ${err.message}`);
95
+ console.error(errorColor('[fnx] Failed to install Azurite. Install manually: npm install -g azurite'));
96
+ console.error(warning(` ${err.message}`));
96
97
  return null;
97
98
  }
98
99
 
99
100
  const installed = join(AZURITE_INSTALL_DIR, 'node_modules', '.bin', 'azurite');
100
101
  if (existsSync(installed)) {
101
- console.log('[fnx] Azurite installed successfully.');
102
+ console.log(info('[fnx] Azurite installed successfully.'));
102
103
  return installed;
103
104
  }
104
105
  return null;
@@ -127,23 +128,23 @@ export async function ensureAzurite(mergedValues, opts = {}) {
127
128
  }
128
129
 
129
130
  const storageVal = mergedValues?.AzureWebJobsStorage || '(empty)';
130
- console.log(`[fnx] Detected AzureWebJobsStorage=${storageVal}`);
131
+ console.log(info(`[fnx] Detected AzureWebJobsStorage=${storageVal}`));
131
132
 
132
133
  // Check if Azurite is already running
133
134
  if (await isAzuriteRunning()) {
134
- console.log('[fnx] Using existing Azurite instance on default ports.');
135
+ console.log(info('[fnx] Using existing Azurite instance on default ports.'));
135
136
  return null;
136
137
  }
137
138
 
138
139
  // Find or install azurite
139
140
  const azuriteBin = findOrInstallAzurite();
140
141
  if (!azuriteBin) {
141
- console.error('[fnx] ⚠️ Azurite not available. Storage triggers may fail.');
142
- console.error(' Install with: npm install -g azurite');
142
+ console.error(warning('[fnx] ⚠️ Azurite not available. Storage triggers may fail.'));
143
+ console.error(warning(' Install with: npm install -g azurite'));
143
144
  return null;
144
145
  }
145
146
 
146
- console.log('[fnx] Starting Azurite storage emulator...');
147
+ console.log(info('[fnx] Starting Azurite storage emulator...'));
147
148
 
148
149
  const azuriteArgs = [
149
150
  '--blobHost', '127.0.0.1', '--blobPort', String(BLOB_PORT),
@@ -161,13 +162,13 @@ export async function ensureAzurite(mergedValues, opts = {}) {
161
162
  });
162
163
 
163
164
  azuriteProcess.on('error', (err) => {
164
- console.error(`[fnx] Azurite failed to start: ${err.message}`);
165
+ console.error(errorColor(`[fnx] Azurite failed to start: ${err.message}`));
165
166
  azuriteProcess = null;
166
167
  });
167
168
 
168
169
  azuriteProcess.on('exit', (code) => {
169
170
  if (code && code !== 0) {
170
- console.error(`[fnx] Azurite exited unexpectedly with code ${code}.`);
171
+ console.error(errorColor(`[fnx] Azurite exited unexpectedly with code ${code}.`));
171
172
  }
172
173
  azuriteProcess = null;
173
174
  });
@@ -175,13 +176,13 @@ export async function ensureAzurite(mergedValues, opts = {}) {
175
176
  // Wait for Azurite to be ready
176
177
  const ready = await waitForTcp(BLOB_PORT, { timeoutMs: 15000 });
177
178
  if (!ready) {
178
- console.error('[fnx] ⚠️ Azurite did not become ready in time. Storage triggers may fail.');
179
+ console.error(warning('[fnx] ⚠️ Azurite did not become ready in time. Storage triggers may fail.'));
179
180
  return azuriteProcess;
180
181
  }
181
182
 
182
- console.log(`[fnx] Azurite Blob → http://127.0.0.1:${BLOB_PORT}`);
183
- console.log(`[fnx] Azurite Queue → http://127.0.0.1:${QUEUE_PORT}`);
184
- console.log(`[fnx] Azurite Table → http://127.0.0.1:${TABLE_PORT}`);
183
+ console.log(info(`[fnx] Azurite Blob → ${urlColor(`http://127.0.0.1:${BLOB_PORT}`)}`));
184
+ console.log(info(`[fnx] Azurite Queue → ${urlColor(`http://127.0.0.1:${QUEUE_PORT}`)}`));
185
+ console.log(info(`[fnx] Azurite Table → ${urlColor(`http://127.0.0.1:${TABLE_PORT}`)}`));
185
186
 
186
187
  return azuriteProcess;
187
188
  }