@gurulu/cli 0.4.2 → 0.4.4

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.
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Sprint E SE-B — `gurulu releases list`.
3
+ *
4
+ * Mirror of MCP `gurulu.releases.list`. Returns recent releases with
5
+ * crash-free session %, adoption, and regression deltas.
6
+ */
7
+ export interface ReleasesArgs {
8
+ action?: string;
9
+ site?: string;
10
+ range?: string;
11
+ environment?: string;
12
+ limit?: number;
13
+ format?: string;
14
+ json?: boolean;
15
+ profile?: string;
16
+ }
17
+ export declare function releasesCommand(args: ReleasesArgs): Promise<void>;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ /**
3
+ * Sprint E SE-B — `gurulu releases list`.
4
+ *
5
+ * Mirror of MCP `gurulu.releases.list`. Returns recent releases with
6
+ * crash-free session %, adoption, and regression deltas.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.releasesCommand = releasesCommand;
10
+ const api_client_1 = require("../api-client");
11
+ const ui_1 = require("../utils/ui");
12
+ async function releasesCommand(args) {
13
+ const action = args.action || 'list';
14
+ switch (action) {
15
+ case 'list':
16
+ return listCmd(args);
17
+ default:
18
+ (0, ui_1.error)(`Unknown releases action: ${action}`);
19
+ (0, ui_1.info)('Usage: gurulu releases list --site=<id> [--limit=20] [--range=7d|30d]');
20
+ process.exit(1);
21
+ }
22
+ }
23
+ async function listCmd(args) {
24
+ if (!args.site) {
25
+ (0, ui_1.error)('Usage: gurulu releases list --site=<id> [--limit=20]');
26
+ process.exit(1);
27
+ }
28
+ const usp = new URLSearchParams();
29
+ usp.set('site', args.site);
30
+ if (args.range)
31
+ usp.set('range', args.range);
32
+ if (args.environment)
33
+ usp.set('environment', args.environment);
34
+ if (args.limit)
35
+ usp.set('limit', String(args.limit));
36
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/releases?${usp.toString()}`, {
37
+ profile: args.profile,
38
+ });
39
+ if (args.format === 'table') {
40
+ const rows = body.releases || [];
41
+ process.stdout.write(['VERSION', 'SESSIONS', 'CRASH_FREE_%', 'USERS', 'ERRORS'].join('\t') + '\n');
42
+ for (const r of rows) {
43
+ process.stdout.write([
44
+ r.version ?? '-',
45
+ r.totalSessions ?? 0,
46
+ r.crashFreeSessionRate?.toFixed?.(2) ?? r.crashFreeSessionRate ?? '-',
47
+ r.totalUsers ?? 0,
48
+ r.errorCount ?? 0,
49
+ ].join('\t') + '\n');
50
+ }
51
+ return;
52
+ }
53
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
54
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Sprint E SE-B — `gurulu replay list|get`.
3
+ *
4
+ * Mirror of MCP `gurulu.replay.list` / `.get`. Hits new `/api/cli/replay/*`
5
+ * proxy endpoints with CLI-auth.
6
+ */
7
+ export interface ReplayArgs {
8
+ action?: string;
9
+ site?: string;
10
+ sessionId?: string;
11
+ range?: string;
12
+ hasError?: boolean;
13
+ limit?: number;
14
+ format?: string;
15
+ json?: boolean;
16
+ profile?: string;
17
+ }
18
+ export declare function replayCommand(args: ReplayArgs): Promise<void>;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /**
3
+ * Sprint E SE-B — `gurulu replay list|get`.
4
+ *
5
+ * Mirror of MCP `gurulu.replay.list` / `.get`. Hits new `/api/cli/replay/*`
6
+ * proxy endpoints with CLI-auth.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.replayCommand = replayCommand;
10
+ const api_client_1 = require("../api-client");
11
+ const ui_1 = require("../utils/ui");
12
+ async function replayCommand(args) {
13
+ const action = args.action || 'list';
14
+ switch (action) {
15
+ case 'list':
16
+ return listCmd(args);
17
+ case 'get':
18
+ return getCmd(args);
19
+ default:
20
+ (0, ui_1.error)(`Unknown replay action: ${action}`);
21
+ (0, ui_1.info)('Usage: gurulu replay [list|get] --site=<id> [--session-id=<id>]');
22
+ process.exit(1);
23
+ }
24
+ }
25
+ async function listCmd(args) {
26
+ if (!args.site) {
27
+ (0, ui_1.error)('Usage: gurulu replay list --site=<id> [--range=7d|30d] [--limit=20]');
28
+ process.exit(1);
29
+ }
30
+ const usp = new URLSearchParams();
31
+ usp.set('site', args.site);
32
+ if (args.range)
33
+ usp.set('range', args.range);
34
+ if (args.hasError !== undefined)
35
+ usp.set('hasError', String(args.hasError));
36
+ if (args.limit)
37
+ usp.set('limit', String(args.limit));
38
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/replay?${usp.toString()}`, {
39
+ profile: args.profile,
40
+ });
41
+ if (args.format === 'table') {
42
+ const rows = body.sessions || [];
43
+ process.stdout.write(['SESSION', 'STARTED', 'DURATION', 'ERRORS', 'SEGMENTS'].join('\t') + '\n');
44
+ for (const s of rows) {
45
+ process.stdout.write([
46
+ s.sessionId ?? s.id ?? '-',
47
+ String(s.startedAt ?? '-'),
48
+ String(s.duration ?? 0),
49
+ s.hasError ? 'yes' : 'no',
50
+ String(s.segments ?? 0),
51
+ ].join('\t') + '\n');
52
+ }
53
+ return;
54
+ }
55
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
56
+ }
57
+ async function getCmd(args) {
58
+ if (!args.site || !args.sessionId) {
59
+ (0, ui_1.error)('Usage: gurulu replay get --site=<id> --session-id=<id>');
60
+ process.exit(1);
61
+ }
62
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/replay/${encodeURIComponent(args.sessionId)}?site=${encodeURIComponent(args.site)}`, { profile: args.profile });
63
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
64
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * CLI@0.4.4 — `gurulu secrets` — list & rotate keys in the tenant credential vault.
3
+ *
4
+ * Subcommands:
5
+ * gurulu secrets list (key names only — never values)
6
+ * gurulu secrets rotate --key API_TOKEN (issues new value, keeps old in 24h grace window)
7
+ *
8
+ * TODO(Sprint K): backend endpoints below are not yet live. The CLI command
9
+ * surface is implemented now so customers can wire automation.
10
+ * - GET /api/cli/secrets/list -> { secrets: [{ key, lastRotatedAt, rotationStatus }] }
11
+ * - POST /api/cli/secrets/rotate { key } -> { key, newRevealedOnce: string, graceUntil: ISO }
12
+ */
13
+ export interface SecretsArgs {
14
+ action?: string;
15
+ key?: string;
16
+ json?: boolean;
17
+ profile?: string;
18
+ }
19
+ export declare function secretsCommand(args: SecretsArgs): Promise<void>;
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ /**
3
+ * CLI@0.4.4 — `gurulu secrets` — list & rotate keys in the tenant credential vault.
4
+ *
5
+ * Subcommands:
6
+ * gurulu secrets list (key names only — never values)
7
+ * gurulu secrets rotate --key API_TOKEN (issues new value, keeps old in 24h grace window)
8
+ *
9
+ * TODO(Sprint K): backend endpoints below are not yet live. The CLI command
10
+ * surface is implemented now so customers can wire automation.
11
+ * - GET /api/cli/secrets/list -> { secrets: [{ key, lastRotatedAt, rotationStatus }] }
12
+ * - POST /api/cli/secrets/rotate { key } -> { key, newRevealedOnce: string, graceUntil: ISO }
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.secretsCommand = secretsCommand;
16
+ const config_1 = require("../config");
17
+ const api_client_1 = require("../api-client");
18
+ const ui_1 = require("../utils/ui");
19
+ const redact_1 = require("../utils/redact");
20
+ async function secretsCommand(args) {
21
+ // Defensive — never echo args wholesale in any future debug path.
22
+ if (process.env.GURULU_DEBUG === '1') {
23
+ console.log(`[debug] secrets args: ${JSON.stringify((0, redact_1.redactSensitiveArgs)({ ...args }))}`);
24
+ }
25
+ switch (args.action) {
26
+ case 'list':
27
+ return secretsList(args);
28
+ case 'rotate':
29
+ return secretsRotate(args);
30
+ default:
31
+ (0, ui_1.error)(`Unknown action: ${args.action}. Use: list, rotate`);
32
+ process.exit(1);
33
+ }
34
+ }
35
+ async function loadProfileOrExit(args) {
36
+ try {
37
+ return await (0, config_1.loadActiveProfile)({ profile: args.profile });
38
+ }
39
+ catch {
40
+ (0, ui_1.error)('Not authenticated. Run "gurulu login" first.');
41
+ process.exit(1);
42
+ }
43
+ }
44
+ function emitFallbackNotice(json) {
45
+ if (json)
46
+ return;
47
+ (0, ui_1.info)((0, ui_1.dim)('Note: backend endpoint not yet live (Sprint K candidate). Command-shape is stable.'));
48
+ }
49
+ // ── list ───────────────────────────────────────────────────────────────────
50
+ async function secretsList(args) {
51
+ const profile = await loadProfileOrExit(args);
52
+ try {
53
+ const res = await (0, api_client_1.cliApi)('/api/cli/secrets/list', { preloadedProfile: profile });
54
+ const data = await res.json().catch(() => ({}));
55
+ if (res.status === 404) {
56
+ emitFallbackNotice(args.json);
57
+ if (args.json)
58
+ console.log(JSON.stringify({ ok: false, pending: true, secrets: [] }));
59
+ else
60
+ (0, ui_1.info)('Pending: would list tenant secrets (key names only).');
61
+ return;
62
+ }
63
+ if (!res.ok) {
64
+ const msg = data.message || data.error || `HTTP ${res.status}`;
65
+ if (args.json)
66
+ console.log(JSON.stringify({ ok: false, error: msg }));
67
+ else
68
+ (0, ui_1.error)(msg);
69
+ process.exit(1);
70
+ }
71
+ if (args.json) {
72
+ console.log(JSON.stringify(data, null, 2));
73
+ return;
74
+ }
75
+ const secrets = data.secrets || [];
76
+ if (secrets.length === 0) {
77
+ (0, ui_1.info)('No secrets in this tenant vault.');
78
+ return;
79
+ }
80
+ console.log((0, ui_1.bold)('Secrets (values are never exposed):'));
81
+ for (const s of secrets) {
82
+ const rotated = s.lastRotatedAt ? (0, ui_1.dim)(` rotated ${s.lastRotatedAt}`) : '';
83
+ const status = s.rotationStatus ? (0, ui_1.dim)(` [${s.rotationStatus}]`) : '';
84
+ (0, ui_1.step)(`${s.key}${rotated}${status}`);
85
+ }
86
+ }
87
+ catch (err) {
88
+ if (args.json)
89
+ console.log(JSON.stringify({ ok: false, error: err.message }));
90
+ else
91
+ (0, ui_1.error)(`Failed: ${err.message}`);
92
+ process.exit(1);
93
+ }
94
+ }
95
+ // ── rotate ─────────────────────────────────────────────────────────────────
96
+ async function secretsRotate(args) {
97
+ if (!args.key) {
98
+ (0, ui_1.error)('Usage: gurulu secrets rotate --key <KEY_NAME>');
99
+ process.exit(1);
100
+ }
101
+ const profile = await loadProfileOrExit(args);
102
+ try {
103
+ const res = await (0, api_client_1.cliApi)('/api/cli/secrets/rotate', {
104
+ method: 'POST',
105
+ body: JSON.stringify({ key: args.key }),
106
+ preloadedProfile: profile,
107
+ });
108
+ const data = await res.json().catch(() => ({}));
109
+ if (res.status === 404) {
110
+ emitFallbackNotice(args.json);
111
+ if (args.json)
112
+ console.log(JSON.stringify({ ok: false, pending: true, key: args.key }));
113
+ else
114
+ (0, ui_1.info)(`Pending: would rotate ${args.key} (24h grace window).`);
115
+ return;
116
+ }
117
+ if (!res.ok) {
118
+ const msg = data.message || data.error || `HTTP ${res.status}`;
119
+ if (args.json)
120
+ console.log(JSON.stringify({ ok: false, error: msg }));
121
+ else
122
+ (0, ui_1.error)(msg);
123
+ process.exit(1);
124
+ }
125
+ if (args.json) {
126
+ console.log(JSON.stringify(data, null, 2));
127
+ return;
128
+ }
129
+ (0, ui_1.success)(`Rotated ${args.key}`);
130
+ if (data.newRevealedOnce) {
131
+ (0, ui_1.warn)('New value (shown ONCE — store it now, you cannot retrieve it again):');
132
+ console.log(` ${(0, ui_1.bold)(data.newRevealedOnce)}`);
133
+ }
134
+ if (data.graceUntil) {
135
+ (0, ui_1.info)(`Old value remains valid until: ${data.graceUntil}`);
136
+ }
137
+ }
138
+ catch (err) {
139
+ if (args.json)
140
+ console.log(JSON.stringify({ ok: false, error: err.message }));
141
+ else
142
+ (0, ui_1.error)(`Failed: ${err.message}`);
143
+ process.exit(1);
144
+ }
145
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Sprint E SE-B — `gurulu skad postbacks`.
3
+ *
4
+ * Mirror of MCP `gurulu.skad.list_postbacks`. Backend already exists at
5
+ * `/api/cli/skad` (Sprint C C9).
6
+ */
7
+ export interface SkadArgs {
8
+ action?: string;
9
+ site?: string;
10
+ from?: string;
11
+ to?: string;
12
+ goal?: string;
13
+ limit?: number;
14
+ format?: string;
15
+ json?: boolean;
16
+ profile?: string;
17
+ }
18
+ export declare function skadCommand(args: SkadArgs): Promise<void>;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ /**
3
+ * Sprint E SE-B — `gurulu skad postbacks`.
4
+ *
5
+ * Mirror of MCP `gurulu.skad.list_postbacks`. Backend already exists at
6
+ * `/api/cli/skad` (Sprint C C9).
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.skadCommand = skadCommand;
10
+ const api_client_1 = require("../api-client");
11
+ const ui_1 = require("../utils/ui");
12
+ async function skadCommand(args) {
13
+ const action = args.action || '';
14
+ switch (action) {
15
+ case 'postbacks':
16
+ return postbacksCmd(args);
17
+ default:
18
+ (0, ui_1.error)(`Unknown skad action: ${action}`);
19
+ (0, ui_1.info)('Usage: gurulu skad postbacks --site=<id> [--from=...] [--goal=$purchase] [--limit=50]');
20
+ process.exit(1);
21
+ }
22
+ }
23
+ async function postbacksCmd(args) {
24
+ const usp = new URLSearchParams();
25
+ if (args.site)
26
+ usp.set('site', args.site);
27
+ if (args.from)
28
+ usp.set('from', args.from);
29
+ if (args.to)
30
+ usp.set('to', args.to);
31
+ if (args.goal)
32
+ usp.set('goal', args.goal);
33
+ if (args.limit)
34
+ usp.set('limit', String(args.limit));
35
+ const path = `/api/cli/skad${usp.toString() ? `?${usp.toString()}` : ''}`;
36
+ const body = await (0, api_client_1.cliApiJson)(path, { profile: args.profile });
37
+ if (args.format === 'table') {
38
+ const rows = body.postbacks || [];
39
+ process.stdout.write(['RECEIVED', 'NETWORK', 'GOAL', 'CV', 'WIN', 'VERIFIED'].join('\t') + '\n');
40
+ for (const p of rows) {
41
+ process.stdout.write([
42
+ String(p.received_at ?? '-'),
43
+ p.ad_network_id ?? '-',
44
+ p.resolved_goal ?? '-',
45
+ String(p.conversion_value ?? '-'),
46
+ p.did_win ? 'yes' : 'no',
47
+ p.signature_verified ? 'yes' : 'no',
48
+ ].join('\t') + '\n');
49
+ }
50
+ return;
51
+ }
52
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
53
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * FA-1 P1-4 — `gurulu upgrade` command.
3
+ *
4
+ * Bumps installed @gurulu/* packages to the latest version published on npm.
5
+ * Reads current versions from `npm ls --depth=0 --json` so it works regardless
6
+ * of which package manager is in use, then dispatches to the same `pm`
7
+ * detection install.ts uses (npm/pnpm/yarn/bun) for the actual upgrade.
8
+ *
9
+ * Usage:
10
+ * gurulu upgrade # default: bump @gurulu/web
11
+ * gurulu upgrade --package=@gurulu/cli
12
+ * gurulu upgrade --all # bump @gurulu/cli + @gurulu/node + @gurulu/web
13
+ * gurulu upgrade --dry-run # show plan without executing
14
+ */
15
+ export interface UpgradeArgs {
16
+ package?: string;
17
+ all?: boolean;
18
+ dryRun?: boolean;
19
+ path?: string;
20
+ }
21
+ export declare function upgradeCommand(args: UpgradeArgs): Promise<void>;
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ /**
3
+ * FA-1 P1-4 — `gurulu upgrade` command.
4
+ *
5
+ * Bumps installed @gurulu/* packages to the latest version published on npm.
6
+ * Reads current versions from `npm ls --depth=0 --json` so it works regardless
7
+ * of which package manager is in use, then dispatches to the same `pm`
8
+ * detection install.ts uses (npm/pnpm/yarn/bun) for the actual upgrade.
9
+ *
10
+ * Usage:
11
+ * gurulu upgrade # default: bump @gurulu/web
12
+ * gurulu upgrade --package=@gurulu/cli
13
+ * gurulu upgrade --all # bump @gurulu/cli + @gurulu/node + @gurulu/web
14
+ * gurulu upgrade --dry-run # show plan without executing
15
+ */
16
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ var desc = Object.getOwnPropertyDescriptor(m, k);
19
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
20
+ desc = { enumerable: true, get: function() { return m[k]; } };
21
+ }
22
+ Object.defineProperty(o, k2, desc);
23
+ }) : (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ o[k2] = m[k];
26
+ }));
27
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
28
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
29
+ }) : function(o, v) {
30
+ o["default"] = v;
31
+ });
32
+ var __importStar = (this && this.__importStar) || (function () {
33
+ var ownKeys = function(o) {
34
+ ownKeys = Object.getOwnPropertyNames || function (o) {
35
+ var ar = [];
36
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
37
+ return ar;
38
+ };
39
+ return ownKeys(o);
40
+ };
41
+ return function (mod) {
42
+ if (mod && mod.__esModule) return mod;
43
+ var result = {};
44
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
45
+ __setModuleDefault(result, mod);
46
+ return result;
47
+ };
48
+ })();
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.upgradeCommand = upgradeCommand;
51
+ const path = __importStar(require("path"));
52
+ const child_process_1 = require("child_process");
53
+ const ui_1 = require("../utils/ui");
54
+ const install_1 = require("./install");
55
+ const DEFAULT_PACKAGES = ['@gurulu/cli', '@gurulu/node', '@gurulu/web'];
56
+ function runCmd(cmd, args, opts = {}) {
57
+ return new Promise((resolve) => {
58
+ const child = (0, child_process_1.spawn)(cmd, args, {
59
+ cwd: opts.cwd,
60
+ stdio: ['ignore', 'pipe', 'pipe'],
61
+ shell: process.platform === 'win32',
62
+ });
63
+ let stdout = '';
64
+ let stderr = '';
65
+ child.stdout.on('data', (c) => (stdout += c.toString()));
66
+ child.stderr.on('data', (c) => (stderr += c.toString()));
67
+ child.on('close', (code) => resolve({ code: code ?? 0, stdout, stderr }));
68
+ child.on('error', (err) => resolve({ code: 1, stdout, stderr: String(err) }));
69
+ });
70
+ }
71
+ async function getCurrentVersion(repoRoot, pkg) {
72
+ // `npm ls --depth=0 --json` works in any project regardless of which PM
73
+ // wrote the lockfile (npm reads node_modules directly). When the package
74
+ // is missing from node_modules the JSON is still valid; we just see no
75
+ // entry under `dependencies`.
76
+ const res = await runCmd('npm', ['ls', pkg, '--depth=0', '--json'], { cwd: repoRoot });
77
+ if (!res.stdout)
78
+ return null;
79
+ try {
80
+ const parsed = JSON.parse(res.stdout);
81
+ const dep = parsed.dependencies && parsed.dependencies[pkg];
82
+ if (dep && typeof dep.version === 'string')
83
+ return dep.version;
84
+ return null;
85
+ }
86
+ catch {
87
+ return null;
88
+ }
89
+ }
90
+ async function getLatestVersion(pkg) {
91
+ const res = await runCmd('npm', ['view', pkg, 'version']);
92
+ if (res.code !== 0)
93
+ return null;
94
+ const v = res.stdout.trim();
95
+ return v || null;
96
+ }
97
+ function pmUpgradeArgs(pm, pkg) {
98
+ if (pm === 'pnpm')
99
+ return ['update', '--latest', pkg];
100
+ if (pm === 'yarn')
101
+ return ['upgrade', `${pkg}@latest`];
102
+ if (pm === 'bun')
103
+ return ['update', pkg];
104
+ return ['install', `${pkg}@latest`];
105
+ }
106
+ function compareSemver(a, b) {
107
+ // Returns -1 if a < b, 0 if equal, 1 if a > b. Strips any leading `v` and
108
+ // pre-release suffixes ("1.2.3-rc.1" → ["1","2","3"]).
109
+ const norm = (s) => s
110
+ .replace(/^v/, '')
111
+ .split('-')[0]
112
+ .split('.')
113
+ .map((n) => parseInt(n, 10) || 0);
114
+ const aa = norm(a);
115
+ const bb = norm(b);
116
+ for (let i = 0; i < Math.max(aa.length, bb.length); i++) {
117
+ const x = aa[i] || 0;
118
+ const y = bb[i] || 0;
119
+ if (x < y)
120
+ return -1;
121
+ if (x > y)
122
+ return 1;
123
+ }
124
+ return 0;
125
+ }
126
+ async function upgradeCommand(args) {
127
+ const repoRoot = path.resolve(args.path || process.cwd());
128
+ const targets = args.all
129
+ ? DEFAULT_PACKAGES
130
+ : [args.package || '@gurulu/web'];
131
+ (0, ui_1.info)(`${(0, ui_1.bold)('gurulu upgrade')} — checking ${targets.length} package(s) in ${(0, ui_1.cyan)(repoRoot)}`);
132
+ const pm = (0, install_1.detectPackageManager)(repoRoot);
133
+ (0, ui_1.step)(`Package manager: ${(0, ui_1.bold)(pm)}`);
134
+ const plans = [];
135
+ for (const pkg of targets) {
136
+ const [current, latest] = await Promise.all([
137
+ getCurrentVersion(repoRoot, pkg),
138
+ getLatestVersion(pkg),
139
+ ]);
140
+ plans.push({ pkg, current, latest });
141
+ }
142
+ const toBump = [];
143
+ for (const p of plans) {
144
+ if (!p.latest) {
145
+ (0, ui_1.warn)(`${p.pkg}: could not resolve latest version (npm view failed).`);
146
+ continue;
147
+ }
148
+ if (!p.current) {
149
+ (0, ui_1.info)(`${p.pkg}: not installed — skipping (run \`gurulu install\` first).`);
150
+ continue;
151
+ }
152
+ const cmp = compareSemver(p.current, p.latest);
153
+ if (cmp >= 0) {
154
+ (0, ui_1.success)(`${p.pkg}: up-to-date (${p.current})`);
155
+ continue;
156
+ }
157
+ (0, ui_1.info)(`${p.pkg}: ${(0, ui_1.dim)(p.current)} → ${(0, ui_1.cyan)(p.latest)}`);
158
+ toBump.push(p);
159
+ }
160
+ if (toBump.length === 0) {
161
+ (0, ui_1.success)('All packages up-to-date.');
162
+ return;
163
+ }
164
+ if (args.dryRun) {
165
+ (0, ui_1.info)(`Dry-run: would upgrade ${toBump.length} package(s).`);
166
+ return;
167
+ }
168
+ let failures = 0;
169
+ for (const p of toBump) {
170
+ (0, ui_1.step)(`Upgrading ${p.pkg} via ${pm}...`);
171
+ const res = await runCmd(pm, pmUpgradeArgs(pm, p.pkg), { cwd: repoRoot });
172
+ if (res.code !== 0) {
173
+ failures++;
174
+ (0, ui_1.error)(`${pm} upgrade failed for ${p.pkg} (exit ${res.code}): ${res.stderr.trim()}`);
175
+ }
176
+ else {
177
+ (0, ui_1.success)(`${p.pkg} upgraded.`);
178
+ }
179
+ }
180
+ if (failures > 0) {
181
+ process.exit(1);
182
+ }
183
+ }
@@ -1,4 +1,4 @@
1
- export type Framework = 'nextjs-app' | 'nextjs-pages' | 'react-vite' | 'react-cra' | 'vue3' | 'nuxt3' | 'svelte' | 'sveltekit' | 'astro' | 'express' | 'nestjs' | 'html' | 'react-native' | 'ios-swift' | 'android-kotlin' | 'flutter' | 'unknown';
1
+ export type Framework = 'nextjs-app' | 'nextjs-pages' | 'react-vite' | 'react-cra' | 'vue3' | 'nuxt3' | 'svelte' | 'sveltekit' | 'astro' | 'express' | 'fastify' | 'hono' | 'nestjs' | 'html' | 'react-native' | 'ios-swift' | 'android-kotlin' | 'flutter' | 'unknown';
2
2
  export declare function detectFramework(projectDir: string): Framework;
3
3
  export declare function getSetupSnippet(framework: Framework, siteId: string, token: string): {
4
4
  file: string;