@mondaydotcomorg/z2h-cli 0.13.1 → 0.14.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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=doctor.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/doctor.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ export interface DeleteOptions {
2
+ yes?: boolean;
3
+ json?: boolean;
4
+ }
5
+ export declare function deleteCommand(opts?: DeleteOptions): Promise<void>;
6
+ //# sourceMappingURL=delete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../src/commands/delete.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAkBD,wBAAsB,aAAa,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC3E"}
@@ -0,0 +1,53 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ const node_readline = require('node:readline');
4
+ const util_logger = require('../util/logger.js');
5
+ const util_appName = require('../util/app-name.js');
6
+ const constants = require('../constants.js');
7
+ const util_auth_authStore = require('../util/auth/auth-store.js');
8
+
9
+ // Type-the-app-name confirmation. The caller must retype the de-scoped app
10
+ // name (what they see in the URL) to guard against accidental deletes.
11
+ async function confirmByName(appSlug) {
12
+ const rl = node_readline.createInterface({ input: process.stdin, output: process.stderr });
13
+ const answer = await new Promise(resolve => {
14
+ rl.question(`This permanently deletes "${appSlug}" and takes it offline. This cannot be undone.\n` +
15
+ `Type the app name to confirm: `, resolve);
16
+ });
17
+ rl.close();
18
+ return answer.trim() === appSlug;
19
+ }
20
+ // Should be run from the consumer app directory — uses process.cwd() to locate it.
21
+ async function deleteCommand(opts = {}) {
22
+ if (opts.json) {
23
+ util_logger.setOutputMode('json');
24
+ }
25
+ const cwdName = util_appName.getCwdAppName();
26
+ if (!cwdName) {
27
+ throw new Error('Run this from inside a consumer app directory (the one with your package.json).');
28
+ }
29
+ const appSlug = util_appName.cleanAppName(cwdName); // user-facing name (matches the bigbrain.me URL)
30
+ const appName = util_appName.mfAppName(cwdName); // canonical mf- bundle name the backend keys on
31
+ // Confirmation. --json is always non-interactive, so it requires --yes.
32
+ if (!opts.yes) {
33
+ if (opts.json || !process.stdin.isTTY) {
34
+ throw new Error('Refusing to delete without confirmation — pass --yes to delete non-interactively.');
35
+ }
36
+ if (!(await confirmByName(appSlug))) {
37
+ throw new Error(`Name did not match "${appSlug}" — aborted, nothing was deleted.`);
38
+ }
39
+ }
40
+ const { token } = await util_auth_authStore.readZthAuth();
41
+ util_logger.info(`deleting "${appName}"`);
42
+ const res = await fetch(`${constants.brokerBaseUrl()}/z2h-apps/${encodeURIComponent(appName)}`, {
43
+ method: 'DELETE',
44
+ headers: { Authorization: `Bearer ${token}`, ...constants.routingHeaders() },
45
+ });
46
+ if (!res.ok) {
47
+ const text = await res.text();
48
+ throw new Error(`Delete failed (${res.status}): ${text.slice(0, 1024)}`);
49
+ }
50
+ util_logger.output(`[z2h-cli] "${appName}" deleted ✅ — it is now offline.`, { ok: true, appName, deleted: true });
51
+ }
52
+
53
+ exports.deleteCommand = deleteCommand;
@@ -0,0 +1,5 @@
1
+ export interface DoctorOptions {
2
+ json?: boolean;
3
+ }
4
+ export declare function doctorCommand(opts?: DoctorOptions): Promise<void>;
5
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAiSD,wBAAsB,aAAa,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyE3E"}
@@ -0,0 +1,334 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ const node_module = require('node:module');
4
+ const promises = require('node:fs/promises');
5
+ const os = require('node:os');
6
+ const path = require('node:path');
7
+ const execa = require('execa');
8
+ const semver = require('semver');
9
+ const fs = require('fs-extra');
10
+ const pc = require('picocolors');
11
+ const util_logger = require('../util/logger.js');
12
+ const util_workspace = require('../util/workspace.js');
13
+ const util_auth_authStore = require('../util/auth/auth-store.js');
14
+ const constants = require('../constants.js');
15
+
16
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
17
+ const _interopDefault = e => e && e.__esModule ? e : { default: e };
18
+
19
+ const os__default = /*#__PURE__*/_interopDefault(os);
20
+ const path__default = /*#__PURE__*/_interopDefault(path);
21
+ const semver__default = /*#__PURE__*/_interopDefault(semver);
22
+ const pc__default = /*#__PURE__*/_interopDefault(pc);
23
+
24
+ const HOME = os__default.default.homedir();
25
+ const Z2H_REPO_ROOT = path__default.default.join(HOME, 'Development', 'bigbrain-z2h');
26
+ const BOOTSTRAP_CMD = `bash ${path__default.default.join(Z2H_REPO_ROOT, 'z2h/setup/bootstrap.sh')}`;
27
+ const UPDATE_CMD = `bash ${path__default.default.join(Z2H_REPO_ROOT, 'z2h/setup/update.sh')}`;
28
+ const CLAUDE_PLUGINS_JSON = path__default.default.join(HOME, '.claude', 'plugins', 'installed_plugins.json');
29
+ const SSL_CERT_PATH = path__default.default.join(HOME, 'Library', 'Application Support', 'devcert', 'domains', 'webpack.llama.fan', 'certificate.crt');
30
+ // ── helpers ───────────────────────────────────────────────────────────────────
31
+ async function checkTool(label, opts) {
32
+ const { cmd, args, failStatus = 'fail', getVersion, fix } = opts;
33
+ let stdout;
34
+ try {
35
+ ({ stdout } = await execa.execa(cmd, args));
36
+ stdout = stdout.trim();
37
+ }
38
+ catch {
39
+ return { label, status: failStatus, detail: 'not found', fix };
40
+ }
41
+ let version;
42
+ try {
43
+ version = getVersion ? await getVersion(stdout) : stdout;
44
+ }
45
+ catch {
46
+ /* best-effort */
47
+ }
48
+ return { label, status: 'ok', detail: version };
49
+ }
50
+ // ── checks ───────────────────────────────────────────────────────────────────
51
+ function checkNvm() {
52
+ const nvmScript = path__default.default.join(HOME, '.nvm', 'nvm.sh');
53
+ return checkTool('nvm', {
54
+ cmd: 'bash',
55
+ args: ['-c', `source "${nvmScript}" && nvm --version`],
56
+ failStatus: 'warn',
57
+ fix: BOOTSTRAP_CMD,
58
+ });
59
+ }
60
+ async function checkNode() {
61
+ try {
62
+ const { stdout } = await execa.execa('node', ['--version']);
63
+ const current = stdout.trim();
64
+ let requiredNodeVersion;
65
+ try {
66
+ const nvmrc = (await promises.readFile(path__default.default.join(Z2H_REPO_ROOT, '.nvmrc'), 'utf8')).trim();
67
+ requiredNodeVersion = semver__default.default.coerce(nvmrc)?.version;
68
+ }
69
+ catch {
70
+ // repo not cloned — skip version comparison
71
+ }
72
+ if (!requiredNodeVersion) {
73
+ return { label: 'node', status: 'ok', detail: current };
74
+ }
75
+ const matches = semver__default.default.satisfies(current, `~${requiredNodeVersion}`);
76
+ return {
77
+ label: 'node',
78
+ status: matches ? 'ok' : 'fail',
79
+ detail: matches ? current : `${current} (expected ${requiredNodeVersion})`,
80
+ fix: matches ? undefined : BOOTSTRAP_CMD,
81
+ };
82
+ }
83
+ catch {
84
+ return { label: 'node', status: 'fail', detail: 'not found', fix: BOOTSTRAP_CMD };
85
+ }
86
+ }
87
+ function checkYarn() {
88
+ return checkTool('yarn', { cmd: 'yarn', args: ['--version'], fix: BOOTSTRAP_CMD });
89
+ }
90
+ function checkAws() {
91
+ return checkTool('aws', { cmd: 'aws', args: ['--version'], getVersion: s => s.split(/\s/)[0], fix: BOOTSTRAP_CMD });
92
+ }
93
+ function checkGitRemoteS3() {
94
+ return checkTool('git-remote-s3', {
95
+ cmd: 'which',
96
+ args: ['git-remote-s3'],
97
+ getVersion: async () => {
98
+ const { stdout: pipxOut } = await execa.execa('pipx', ['runpip', 'git-remote-s3', 'show', 'git-remote-s3']);
99
+ return pipxOut.match(/^Version:\s+(.+)/m)?.[1]?.trim();
100
+ },
101
+ fix: BOOTSTRAP_CMD,
102
+ });
103
+ }
104
+ async function checkZ2hCli() {
105
+ const { version } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('commands/doctor.js', document.baseURI).href)))('../../package.json');
106
+ let latestVersion;
107
+ try {
108
+ const token = util_auth_authStore.tryReadAuthTokenSync();
109
+ if (token) {
110
+ const res = await fetch(`${constants.brokerNpmRegistryUrl()}/@mondaydotcomorg%2Fz2h-cli`, {
111
+ headers: { Authorization: `Bearer ${token}`, ...constants.routingHeaders() },
112
+ signal: AbortSignal.timeout(5000),
113
+ });
114
+ if (res.ok) {
115
+ const data = (await res.json());
116
+ latestVersion = data['dist-tags']?.latest;
117
+ }
118
+ }
119
+ }
120
+ catch {
121
+ // version check is best-effort — no network / token not ready
122
+ }
123
+ if (latestVersion && latestVersion !== version) {
124
+ return { label: 'z2h-cli', status: 'warn', detail: `${version} (latest: ${latestVersion})`, fix: UPDATE_CMD };
125
+ }
126
+ return { label: 'z2h-cli', status: 'ok', detail: latestVersion ? `${version} (up to date)` : version };
127
+ }
128
+ async function checkAuthToken() {
129
+ try {
130
+ const parsed = (await fs.readJson(util_auth_authStore.Z2H_AUTH_FILE));
131
+ if (!parsed.token) {
132
+ return {
133
+ label: 'auth token',
134
+ status: 'fail',
135
+ detail: `${util_auth_authStore.Z2H_AUTH_FILE} missing token field`,
136
+ fix: 'z2h-cli generate-z2h-token',
137
+ };
138
+ }
139
+ return { label: 'auth token', status: 'ok', detail: util_auth_authStore.Z2H_AUTH_FILE };
140
+ }
141
+ catch (err) {
142
+ let detail;
143
+ if (err.code === 'ENOENT') {
144
+ detail = `${util_auth_authStore.Z2H_AUTH_FILE} not found`;
145
+ }
146
+ else if (err instanceof SyntaxError) {
147
+ detail = `${util_auth_authStore.Z2H_AUTH_FILE} could not be parsed`;
148
+ }
149
+ else {
150
+ detail = `${util_auth_authStore.Z2H_AUTH_FILE} is unreadable`;
151
+ }
152
+ return { label: 'auth token', status: 'fail', detail, fix: 'z2h-cli generate-z2h-token' };
153
+ }
154
+ }
155
+ async function checkNpmTokenInProfile() {
156
+ const profiles = [
157
+ path__default.default.join(HOME, 'dotfiles', '.bash_profile'),
158
+ path__default.default.join(HOME, '.zshrc'),
159
+ path__default.default.join(HOME, '.bash_profile'),
160
+ ];
161
+ for (const profile of profiles) {
162
+ if (!(await fs.pathExists(profile))) {
163
+ continue;
164
+ }
165
+ try {
166
+ const content = await promises.readFile(profile, 'utf8');
167
+ if (content.includes('export Z2H_NPM_TOKEN')) {
168
+ return { label: 'npm token in profile', status: 'ok', detail: path__default.default.basename(profile) };
169
+ }
170
+ }
171
+ catch {
172
+ // skip unreadable profile
173
+ }
174
+ }
175
+ return {
176
+ label: 'npm token in profile',
177
+ status: 'fail',
178
+ detail: 'export Z2H_NPM_TOKEN not found in any shell profile',
179
+ fix: BOOTSTRAP_CMD,
180
+ };
181
+ }
182
+ async function checkWorkspace() {
183
+ const home = util_workspace.getWorkspaceHome();
184
+ const ok = await util_workspace.isZ2hWorkspace(home);
185
+ return {
186
+ label: 'workspace',
187
+ status: ok ? 'ok' : 'fail',
188
+ detail: ok ? home : `${home} not initialized`,
189
+ fix: ok ? undefined : 'z2h-cli create-workspace',
190
+ };
191
+ }
192
+ async function checkReactVersion() {
193
+ const REACT_VERSION_LABEL = 'react version';
194
+ const REQUIRED_REACT_VERSION = '18.x';
195
+ const reactVersionFail = detail => ({
196
+ label: REACT_VERSION_LABEL,
197
+ status: 'fail',
198
+ detail,
199
+ fix: BOOTSTRAP_CMD,
200
+ });
201
+ const home = util_workspace.getWorkspaceHome();
202
+ let pkg;
203
+ try {
204
+ pkg = (await fs.readJson(path__default.default.join(home, 'package.json')));
205
+ }
206
+ catch {
207
+ return reactVersionFail('could not read workspace package.json');
208
+ }
209
+ const reactVersion = pkg.dependencies?.react;
210
+ if (!reactVersion) {
211
+ return reactVersionFail('react not in workspace dependencies');
212
+ }
213
+ const coerced = semver__default.default.coerce(reactVersion)?.version;
214
+ if (!coerced || !semver__default.default.satisfies(coerced, REQUIRED_REACT_VERSION)) {
215
+ return reactVersionFail(`invalid react version: ${reactVersion} (expected ${REQUIRED_REACT_VERSION})`);
216
+ }
217
+ return {
218
+ label: REACT_VERSION_LABEL,
219
+ status: 'ok',
220
+ detail: reactVersion,
221
+ };
222
+ }
223
+ async function checkSslCert() {
224
+ const ok = await fs.pathExists(SSL_CERT_PATH);
225
+ return {
226
+ label: 'SSL cert',
227
+ status: ok ? 'ok' : 'warn',
228
+ detail: ok ? 'webpack.llama.fan' : 'not found — auto-installs on first z2h-cli dev',
229
+ fix: ok ? undefined : 'z2h-cli dev',
230
+ };
231
+ }
232
+ async function checkPlugin(pluginKey, label, isMandatory, pluginsJson) {
233
+ const missingStatus = isMandatory ? 'fail' : 'warn';
234
+ if (!pluginsJson) {
235
+ return { label, status: missingStatus, detail: `${CLAUDE_PLUGINS_JSON} not found`, fix: BOOTSTRAP_CMD };
236
+ }
237
+ const entries = pluginsJson.plugins?.[pluginKey];
238
+ if (!entries?.length) {
239
+ return { label, status: missingStatus, detail: `plugin ${pluginKey} not registered`, fix: BOOTSTRAP_CMD };
240
+ }
241
+ const installPath = entries[0].installPath;
242
+ if (!installPath) {
243
+ return { label, status: missingStatus, detail: 'installPath missing', fix: BOOTSTRAP_CMD };
244
+ }
245
+ const exists = await fs.pathExists(installPath);
246
+ return {
247
+ label,
248
+ status: exists ? 'ok' : missingStatus,
249
+ detail: exists ? installPath : `${installPath} not found`,
250
+ fix: exists ? undefined : BOOTSTRAP_CMD,
251
+ };
252
+ }
253
+ // ── rendering ─────────────────────────────────────────────────────────────────
254
+ const STATUS_ICON = {
255
+ ok: pc__default.default.green('✅'),
256
+ warn: pc__default.default.yellow('⚠️ '),
257
+ fail: pc__default.default.red('❌'),
258
+ };
259
+ function renderCheck(c) {
260
+ const icon = STATUS_ICON[c.status];
261
+ const label = c.label.padEnd(22);
262
+ const detail = c.detail ? pc__default.default.dim(c.detail) : '';
263
+ process.stdout.write(` ${icon} ${label}${detail}\n`);
264
+ }
265
+ // ── command ───────────────────────────────────────────────────────────────────
266
+ async function doctorCommand(opts = {}) {
267
+ if (opts.json) {
268
+ util_logger.setOutputMode('json');
269
+ }
270
+ let pluginsJson = null;
271
+ try {
272
+ if (await fs.pathExists(CLAUDE_PLUGINS_JSON)) {
273
+ pluginsJson = (await fs.readJson(CLAUDE_PLUGINS_JSON));
274
+ }
275
+ }
276
+ catch {
277
+ // treat as missing
278
+ }
279
+ const systemChecks = await Promise.all([checkNvm(), checkNode(), checkYarn(), checkAws(), checkGitRemoteS3()]);
280
+ const cliChecks = await Promise.all([checkZ2hCli(), checkAuthToken(), checkNpmTokenInProfile()]);
281
+ const workspaceChecks = await Promise.all([checkWorkspace(), checkReactVersion(), checkSslCert()]);
282
+ const pluginChecks = await Promise.all([
283
+ checkPlugin('z2h@bigbrain-z2h', 'z2h plugin', true, pluginsJson),
284
+ checkPlugin('cf-external-claude-plugin@client-foundations-ai-tools', 'cf-external plugin', false, pluginsJson),
285
+ checkPlugin('pr-guardrails@agentic-builders-hub', 'pr-guardrails plugin', false, pluginsJson),
286
+ ]);
287
+ const sections = [
288
+ { title: 'System tools', checks: systemChecks },
289
+ { title: 'CLI & auth', checks: cliChecks },
290
+ { title: 'Workspace', checks: workspaceChecks },
291
+ { title: 'Plugins', checks: pluginChecks },
292
+ ];
293
+ const allChecks = sections.flatMap(s => s.checks);
294
+ const issues = allChecks.filter(c => c.status !== 'ok');
295
+ if (opts.json) {
296
+ util_logger.output('', {
297
+ ok: issues.filter(c => c.status === 'fail').length === 0,
298
+ checks: allChecks,
299
+ });
300
+ return;
301
+ }
302
+ for (const section of sections) {
303
+ process.stdout.write(`\n${pc__default.default.bold(section.title)}\n`);
304
+ for (const check of section.checks) {
305
+ renderCheck(check);
306
+ }
307
+ }
308
+ process.stdout.write('\n');
309
+ if (issues.length === 0) {
310
+ process.stdout.write(pc__default.default.bold(pc__default.default.green('No issues detected 🚀\n')));
311
+ }
312
+ else {
313
+ const fails = issues.filter(c => c.status === 'fail').length;
314
+ const warns = issues.filter(c => c.status === 'warn').length;
315
+ const parts = [
316
+ fails && `${fails} failure${fails > 1 ? 's' : ''}`,
317
+ warns && `${warns} warning${warns > 1 ? 's' : ''}`,
318
+ ].filter(Boolean);
319
+ process.stdout.write(pc__default.default.yellow(`${parts.join(', ')} found\n`));
320
+ // Deduplicated fixes in the order they were first encountered
321
+ const fixes = [...new Set(issues.map(c => c.fix).filter(Boolean))];
322
+ if (fixes.length > 0) {
323
+ process.stdout.write('\n');
324
+ process.stdout.write(pc__default.default.bold(pc__default.default.red('In order to fix these issues, run the following commands:')));
325
+ process.stdout.write('\n');
326
+ for (const fix of fixes) {
327
+ process.stdout.write(` ${pc__default.default.cyan(fix)}\n`);
328
+ }
329
+ }
330
+ process.stdout.write('\n');
331
+ }
332
+ }
333
+
334
+ exports.doctorCommand = doctorCommand;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=doctor.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.test.d.ts","sourceRoot":"","sources":["../../../../src/commands/__tests__/doctor.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ export interface DeleteOptions {
2
+ yes?: boolean;
3
+ json?: boolean;
4
+ }
5
+ export declare function deleteCommand(opts?: DeleteOptions): Promise<void>;
6
+ //# sourceMappingURL=delete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../src/commands/delete.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAkBD,wBAAsB,aAAa,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC3E"}
@@ -0,0 +1,51 @@
1
+ import { createInterface } from 'node:readline';
2
+ import { setOutputMode, info, output } from '../util/logger.mjs';
3
+ import { getCwdAppName, cleanAppName, mfAppName } from '../util/app-name.mjs';
4
+ import { brokerBaseUrl, routingHeaders } from '../constants.mjs';
5
+ import { readZthAuth } from '../util/auth/auth-store.mjs';
6
+
7
+ // Type-the-app-name confirmation. The caller must retype the de-scoped app
8
+ // name (what they see in the URL) to guard against accidental deletes.
9
+ async function confirmByName(appSlug) {
10
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
11
+ const answer = await new Promise(resolve => {
12
+ rl.question(`This permanently deletes "${appSlug}" and takes it offline. This cannot be undone.\n` +
13
+ `Type the app name to confirm: `, resolve);
14
+ });
15
+ rl.close();
16
+ return answer.trim() === appSlug;
17
+ }
18
+ // Should be run from the consumer app directory — uses process.cwd() to locate it.
19
+ async function deleteCommand(opts = {}) {
20
+ if (opts.json) {
21
+ setOutputMode('json');
22
+ }
23
+ const cwdName = getCwdAppName();
24
+ if (!cwdName) {
25
+ throw new Error('Run this from inside a consumer app directory (the one with your package.json).');
26
+ }
27
+ const appSlug = cleanAppName(cwdName); // user-facing name (matches the bigbrain.me URL)
28
+ const appName = mfAppName(cwdName); // canonical mf- bundle name the backend keys on
29
+ // Confirmation. --json is always non-interactive, so it requires --yes.
30
+ if (!opts.yes) {
31
+ if (opts.json || !process.stdin.isTTY) {
32
+ throw new Error('Refusing to delete without confirmation — pass --yes to delete non-interactively.');
33
+ }
34
+ if (!(await confirmByName(appSlug))) {
35
+ throw new Error(`Name did not match "${appSlug}" — aborted, nothing was deleted.`);
36
+ }
37
+ }
38
+ const { token } = await readZthAuth();
39
+ info(`deleting "${appName}"`);
40
+ const res = await fetch(`${brokerBaseUrl()}/z2h-apps/${encodeURIComponent(appName)}`, {
41
+ method: 'DELETE',
42
+ headers: { Authorization: `Bearer ${token}`, ...routingHeaders() },
43
+ });
44
+ if (!res.ok) {
45
+ const text = await res.text();
46
+ throw new Error(`Delete failed (${res.status}): ${text.slice(0, 1024)}`);
47
+ }
48
+ output(`[z2h-cli] "${appName}" deleted ✅ — it is now offline.`, { ok: true, appName, deleted: true });
49
+ }
50
+
51
+ export { deleteCommand };
@@ -0,0 +1,5 @@
1
+ export interface DoctorOptions {
2
+ json?: boolean;
3
+ }
4
+ export declare function doctorCommand(opts?: DoctorOptions): Promise<void>;
5
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/commands/doctor.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAiSD,wBAAsB,aAAa,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyE3E"}