vibeview 0.12.3 → 0.13.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/dist/{chunk-OHRTLUBD.js → chunk-J7V3GYOI.js} +42 -3
- package/dist/{chunk-2OI7HL6X.js → chunk-YFXMXO5T.js} +1 -1
- package/dist/cli.js +254 -17
- package/dist/{server-25CXVTUV.js → server-JA5A5PY6.js} +3 -3
- package/dist/{upload-app-G5SEYBTU.js → upload-app-ZAWCZFQE.js} +1 -1
- package/package.json +2 -1
|
@@ -56,6 +56,10 @@ function extractDetail(text) {
|
|
|
56
56
|
}
|
|
57
57
|
return void 0;
|
|
58
58
|
}
|
|
59
|
+
async function parseJsonBody(resp) {
|
|
60
|
+
if (resp.status === 204 || resp.status === 205) return void 0;
|
|
61
|
+
return resp.json();
|
|
62
|
+
}
|
|
59
63
|
var ApiClient = class {
|
|
60
64
|
constructor(baseUrl, token) {
|
|
61
65
|
this.baseUrl = baseUrl;
|
|
@@ -96,7 +100,7 @@ var ApiClient = class {
|
|
|
96
100
|
if (resp.status === 404) throw new NotFoundError(`Not found: ${extractDetail(text) ?? path}`);
|
|
97
101
|
throw new Error(extractDetail(text) ?? `API error ${resp.status}: ${text}`);
|
|
98
102
|
}
|
|
99
|
-
return resp
|
|
103
|
+
return parseJsonBody(resp);
|
|
100
104
|
}
|
|
101
105
|
async listSuites(opts) {
|
|
102
106
|
const params = new URLSearchParams();
|
|
@@ -168,7 +172,7 @@ var ApiClient = class {
|
|
|
168
172
|
if (resp.status === 404) throw new NotFoundError(`Not found: ${extractDetail(text) ?? path}`);
|
|
169
173
|
throw new Error(extractDetail(text) ?? `API error ${resp.status}: ${text}`);
|
|
170
174
|
}
|
|
171
|
-
return resp
|
|
175
|
+
return parseJsonBody(resp);
|
|
172
176
|
}
|
|
173
177
|
/** FormData request without /ci prefix — for file uploads */
|
|
174
178
|
async requestFormData(method, path, formData) {
|
|
@@ -188,7 +192,7 @@ var ApiClient = class {
|
|
|
188
192
|
if (resp.status === 404) throw new NotFoundError(`Not found: ${extractDetail(text) ?? path}`);
|
|
189
193
|
throw new Error(extractDetail(text) ?? `API error ${resp.status}: ${text}`);
|
|
190
194
|
}
|
|
191
|
-
return resp
|
|
195
|
+
return parseJsonBody(resp);
|
|
192
196
|
}
|
|
193
197
|
async listApps() {
|
|
194
198
|
return this.requestRaw("GET", "/apps/");
|
|
@@ -335,6 +339,41 @@ var ApiClient = class {
|
|
|
335
339
|
}
|
|
336
340
|
return { filename, path };
|
|
337
341
|
}
|
|
342
|
+
/** GET /api/v1/build-env/[?app=]. Requires VIEWER role. Never returns a
|
|
343
|
+
* value — see BuildEnvVar. */
|
|
344
|
+
async listBuildEnvVars(app) {
|
|
345
|
+
const qs = app ? `?app=${encodeURIComponent(app)}` : "";
|
|
346
|
+
return this.requestRaw("GET", `/build-env/${qs}`);
|
|
347
|
+
}
|
|
348
|
+
/** POST /api/v1/build-env/ (JSON body) for a plain/sensitive/secret value.
|
|
349
|
+
* Requires ADMIN role. Upserts by (org, app, name). */
|
|
350
|
+
async setBuildEnvVar(body) {
|
|
351
|
+
return this.requestRaw("POST", "/build-env/", body);
|
|
352
|
+
}
|
|
353
|
+
/** POST /api/v1/build-env/ (multipart) for a file variable. `content` is
|
|
354
|
+
* sent as the raw bytes read from disk — the server does its own
|
|
355
|
+
* base64-encoding internally, so encoding here too would double-encode
|
|
356
|
+
* and corrupt the file. Requires ADMIN role. */
|
|
357
|
+
async setBuildEnvFileVar(body) {
|
|
358
|
+
const formData = new FormData();
|
|
359
|
+
formData.append("name", body.name);
|
|
360
|
+
formData.append("visibility", body.visibility);
|
|
361
|
+
if (body.app) formData.append("app", body.app);
|
|
362
|
+
const blob = new Blob([new Uint8Array(body.content)]);
|
|
363
|
+
formData.append("file", blob, body.filename);
|
|
364
|
+
return this.requestFormData("POST", "/build-env/", formData);
|
|
365
|
+
}
|
|
366
|
+
/** DELETE /api/v1/build-env/{id}. Requires ADMIN role. */
|
|
367
|
+
async deleteBuildEnvVar(id) {
|
|
368
|
+
await this.requestRaw("DELETE", `/build-env/${id}`);
|
|
369
|
+
}
|
|
370
|
+
/** GET /api/v1/build-env/pull[?app=]. Requires DEVELOPER role. The only
|
|
371
|
+
* endpoint that returns values — already excludes secret/file variables
|
|
372
|
+
* server-side. */
|
|
373
|
+
async pullBuildEnvVars(app) {
|
|
374
|
+
const qs = app ? `?app=${encodeURIComponent(app)}` : "";
|
|
375
|
+
return this.requestRaw("GET", `/build-env/pull${qs}`);
|
|
376
|
+
}
|
|
338
377
|
};
|
|
339
378
|
function parseContentDispositionFilename(header) {
|
|
340
379
|
if (!header) return void 0;
|
package/dist/cli.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
readDevState,
|
|
14
14
|
readMetroState,
|
|
15
15
|
runAgentVerb
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-YFXMXO5T.js";
|
|
17
17
|
import {
|
|
18
18
|
ApiClient,
|
|
19
19
|
EXIT_INFRA_ERROR,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
resolveAuth,
|
|
26
26
|
saveConfig,
|
|
27
27
|
uploadApp
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-J7V3GYOI.js";
|
|
29
29
|
|
|
30
30
|
// src/cli.ts
|
|
31
31
|
import { createRequire } from "module";
|
|
@@ -1353,8 +1353,233 @@ function printError(opts, err) {
|
|
|
1353
1353
|
console.error(chalk11.red(`error: ${message}`));
|
|
1354
1354
|
}
|
|
1355
1355
|
|
|
1356
|
-
// src/
|
|
1356
|
+
// src/commands/env.ts
|
|
1357
|
+
import { chmod, open as open2, readFile as readFileAsync } from "fs/promises";
|
|
1358
|
+
import { basename } from "path";
|
|
1357
1359
|
import chalk12 from "chalk";
|
|
1360
|
+
import { password } from "@inquirer/prompts";
|
|
1361
|
+
|
|
1362
|
+
// src/lib/gitignore.ts
|
|
1363
|
+
import { execFile } from "child_process";
|
|
1364
|
+
function isGitIgnored(path, cwd = process.cwd()) {
|
|
1365
|
+
return new Promise((resolvePromise) => {
|
|
1366
|
+
execFile("git", ["check-ignore", "-q", path], { cwd }, (error) => {
|
|
1367
|
+
resolvePromise(!error);
|
|
1368
|
+
});
|
|
1369
|
+
});
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
// src/commands/env.ts
|
|
1373
|
+
function clientFor(opts) {
|
|
1374
|
+
const { token, baseUrl } = resolveAuth(opts);
|
|
1375
|
+
return new ApiClient(baseUrl, token);
|
|
1376
|
+
}
|
|
1377
|
+
async function writeSecureFile(path, content) {
|
|
1378
|
+
const fh = await open2(path, "w", 384);
|
|
1379
|
+
try {
|
|
1380
|
+
await fh.writeFile(content, "utf-8");
|
|
1381
|
+
} finally {
|
|
1382
|
+
await fh.close();
|
|
1383
|
+
}
|
|
1384
|
+
await chmod(path, 384);
|
|
1385
|
+
}
|
|
1386
|
+
function defaultDeps2(client) {
|
|
1387
|
+
return {
|
|
1388
|
+
listVars: (app) => client.listBuildEnvVars(app),
|
|
1389
|
+
setVar: (body) => client.setBuildEnvVar(body),
|
|
1390
|
+
setFileVar: (body) => client.setBuildEnvFileVar(body),
|
|
1391
|
+
rmVar: async ({ name, app }) => {
|
|
1392
|
+
const { variables } = await client.listBuildEnvVars(app);
|
|
1393
|
+
const wantApp = app ?? null;
|
|
1394
|
+
const match = variables.find((v) => v.name === name && (v.app_public_id ?? null) === wantApp);
|
|
1395
|
+
if (!match) {
|
|
1396
|
+
const otherScope = variables.find((v) => v.name === name);
|
|
1397
|
+
if (otherScope) {
|
|
1398
|
+
throw new Error(
|
|
1399
|
+
`No variable named "${name}" in ${app ? `app "${app}"` : "the org-wide scope"}. It exists ${otherScope.app_public_id ? `for app "${otherScope.app_public_id}"` : "org-wide"} \u2014 re-run ${otherScope.app_public_id ? `with --app ${otherScope.app_public_id}` : "without --app"} to remove that one.`
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
throw new Error(`No variable named "${name}" found${app ? ` for app "${app}"` : ""}.`);
|
|
1403
|
+
}
|
|
1404
|
+
await client.deleteBuildEnvVar(match.id);
|
|
1405
|
+
},
|
|
1406
|
+
pullVars: (app) => client.pullBuildEnvVars(app),
|
|
1407
|
+
// `hidden` is always true in practice (env set only prompts for secret
|
|
1408
|
+
// values), but the parameter exists so a future caller/test can't
|
|
1409
|
+
// accidentally get echoed input by omitting it.
|
|
1410
|
+
prompt: ({ message }) => password({ message }),
|
|
1411
|
+
isTTY: Boolean(process.stdin.isTTY),
|
|
1412
|
+
isIgnored: (path) => isGitIgnored(path),
|
|
1413
|
+
writeFile: writeSecureFile,
|
|
1414
|
+
readFile: (path) => readFileAsync(path),
|
|
1415
|
+
warn: (message) => console.error(chalk12.yellow(`warning: ${message}`))
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
async function envList(opts, deps) {
|
|
1419
|
+
const d = deps ?? defaultDeps2(clientFor(opts));
|
|
1420
|
+
try {
|
|
1421
|
+
const { variables } = await d.listVars(opts.app);
|
|
1422
|
+
if (opts.json) {
|
|
1423
|
+
console.log(JSON.stringify(variables, null, 2));
|
|
1424
|
+
} else {
|
|
1425
|
+
process.stdout.write(formatEnvVarsHuman(variables));
|
|
1426
|
+
}
|
|
1427
|
+
process.exitCode = 0;
|
|
1428
|
+
} catch (err) {
|
|
1429
|
+
printError2(opts, err);
|
|
1430
|
+
process.exitCode = 1;
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
function formatEnvVarsHuman(variables) {
|
|
1434
|
+
if (variables.length === 0) {
|
|
1435
|
+
return "No build variables found.\n";
|
|
1436
|
+
}
|
|
1437
|
+
const HEADERS3 = ["NAME", "VISIBILITY", "SCOPE", "HINT"];
|
|
1438
|
+
const rows = variables.map((v) => [
|
|
1439
|
+
v.name,
|
|
1440
|
+
v.is_file ? `${v.visibility} (file)` : v.visibility,
|
|
1441
|
+
v.app_public_id ?? "org-wide",
|
|
1442
|
+
v.value_hint ?? ""
|
|
1443
|
+
]);
|
|
1444
|
+
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
1445
|
+
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
1446
|
+
const join2 = (cells) => cells.join(" ").trimEnd();
|
|
1447
|
+
const lines = [chalk12.bold(join2(padCells(HEADERS3)))];
|
|
1448
|
+
for (const row of rows) lines.push(join2(padCells(row)));
|
|
1449
|
+
return lines.join("\n") + "\n";
|
|
1450
|
+
}
|
|
1451
|
+
async function envSet(name, value, opts, deps) {
|
|
1452
|
+
const d = deps ?? defaultDeps2(clientFor(opts));
|
|
1453
|
+
let resolvedValue = value;
|
|
1454
|
+
if (resolvedValue === void 0 && !opts.file) {
|
|
1455
|
+
if (opts.json || !d.isTTY) {
|
|
1456
|
+
throw new Error(
|
|
1457
|
+
"A value is required: pass it as the VALUE argument, or run interactively to be prompted."
|
|
1458
|
+
);
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
try {
|
|
1462
|
+
const visibility = resolveVisibility(opts);
|
|
1463
|
+
if (opts.file) {
|
|
1464
|
+
const content = await d.readFile(opts.file);
|
|
1465
|
+
const filename = basename(opts.file);
|
|
1466
|
+
const result2 = await d.setFileVar({ name, content, filename, visibility, app: opts.app });
|
|
1467
|
+
printSetResult(opts, result2);
|
|
1468
|
+
process.exitCode = 0;
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
if (resolvedValue === void 0) {
|
|
1472
|
+
resolvedValue = await d.prompt({ message: `Value for ${name}`, hidden: true });
|
|
1473
|
+
}
|
|
1474
|
+
const result = await d.setVar({ name, value: resolvedValue, visibility, app: opts.app });
|
|
1475
|
+
printSetResult(opts, result);
|
|
1476
|
+
process.exitCode = 0;
|
|
1477
|
+
} catch (err) {
|
|
1478
|
+
printError2(opts, err);
|
|
1479
|
+
process.exitCode = 1;
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
function resolveVisibility(opts) {
|
|
1483
|
+
const chosen = [
|
|
1484
|
+
opts.secret && "secret",
|
|
1485
|
+
opts.sensitive && "sensitive",
|
|
1486
|
+
opts.plain && "plain"
|
|
1487
|
+
].filter((v) => Boolean(v));
|
|
1488
|
+
if (chosen.length > 1) {
|
|
1489
|
+
throw new Error("Pass at most one of --secret, --sensitive, --plain.");
|
|
1490
|
+
}
|
|
1491
|
+
return chosen[0] ?? "secret";
|
|
1492
|
+
}
|
|
1493
|
+
function printSetResult(opts, result) {
|
|
1494
|
+
if (opts.json) {
|
|
1495
|
+
console.log(JSON.stringify(result));
|
|
1496
|
+
} else {
|
|
1497
|
+
console.log(chalk12.green(`\u2713 set ${result.name} (${result.visibility})`));
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
async function envRm(name, opts, deps) {
|
|
1501
|
+
const d = deps ?? defaultDeps2(clientFor(opts));
|
|
1502
|
+
try {
|
|
1503
|
+
await d.rmVar({ name, app: opts.app });
|
|
1504
|
+
if (opts.json) {
|
|
1505
|
+
console.log(JSON.stringify({ event: "deleted", name }));
|
|
1506
|
+
} else {
|
|
1507
|
+
console.log(chalk12.green(`\u2713 removed ${name}`));
|
|
1508
|
+
}
|
|
1509
|
+
process.exitCode = 0;
|
|
1510
|
+
} catch (err) {
|
|
1511
|
+
printError2(opts, err);
|
|
1512
|
+
process.exitCode = 1;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
async function envPull(opts, deps) {
|
|
1516
|
+
const d = deps ?? defaultDeps2(clientFor(opts));
|
|
1517
|
+
const outPath = opts.out ?? ".env";
|
|
1518
|
+
try {
|
|
1519
|
+
const { variables } = await d.pullVars(opts.app);
|
|
1520
|
+
const ignored = await d.isIgnored(outPath);
|
|
1521
|
+
if (!ignored) {
|
|
1522
|
+
d.warn(
|
|
1523
|
+
`${outPath} is not covered by .gitignore \u2014 writing credentials into a tracked file risks committing them. Add it to .gitignore.`
|
|
1524
|
+
);
|
|
1525
|
+
}
|
|
1526
|
+
for (const v of variables) {
|
|
1527
|
+
if (isLossyEnvValue(v.value)) {
|
|
1528
|
+
const why = v.value.includes("\r") ? "contains a carriage return, which the .env format normalizes away" : "contains a single quote, a double quote and a backtick, which no .env quoting form can carry together";
|
|
1529
|
+
d.warn(
|
|
1530
|
+
`${v.name} ${why} \u2014 the written value differs from the stored one. Read this variable from the API instead of ${outPath}.`
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
const content = formatDotEnv(variables);
|
|
1535
|
+
await d.writeFile(outPath, content);
|
|
1536
|
+
if (opts.json) {
|
|
1537
|
+
console.log(JSON.stringify({ event: "pulled", path: outPath, count: variables.length }));
|
|
1538
|
+
} else {
|
|
1539
|
+
console.log(chalk12.green(`\u2713 wrote ${variables.length} variable(s) to ${outPath}`));
|
|
1540
|
+
}
|
|
1541
|
+
process.exitCode = 0;
|
|
1542
|
+
} catch (err) {
|
|
1543
|
+
printError2(opts, err);
|
|
1544
|
+
process.exitCode = 1;
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
function isLossyEnvValue(value) {
|
|
1548
|
+
if (value.includes("\r")) return true;
|
|
1549
|
+
return value.includes("'") && value.includes('"') && value.includes("`");
|
|
1550
|
+
}
|
|
1551
|
+
function formatDotEnv(variables) {
|
|
1552
|
+
const lines = [
|
|
1553
|
+
"# Generated by `vibeview env pull` \u2014 do not commit.",
|
|
1554
|
+
"# Values are quoted/escaped only where the .env format requires it."
|
|
1555
|
+
];
|
|
1556
|
+
for (const v of variables) {
|
|
1557
|
+
lines.push(`${v.name}=${escapeEnvValue(v.value)}`);
|
|
1558
|
+
}
|
|
1559
|
+
return lines.join("\n") + "\n";
|
|
1560
|
+
}
|
|
1561
|
+
function escapeEnvValue(value) {
|
|
1562
|
+
if (value === "") return "''";
|
|
1563
|
+
if (!value.includes("'")) {
|
|
1564
|
+
if (!/[\s"'#\\=`]/.test(value)) return value;
|
|
1565
|
+
return `'${value}'`;
|
|
1566
|
+
}
|
|
1567
|
+
if (!value.includes('"') && !value.includes("\\")) {
|
|
1568
|
+
return `"${value}"`;
|
|
1569
|
+
}
|
|
1570
|
+
if (!value.includes("`")) {
|
|
1571
|
+
return `\`${value}\``;
|
|
1572
|
+
}
|
|
1573
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
1574
|
+
}
|
|
1575
|
+
function printError2(opts, err) {
|
|
1576
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1577
|
+
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
1578
|
+
console.error(chalk12.red(`error: ${message}`));
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
// src/lib/formatters/devices.ts
|
|
1582
|
+
import chalk13 from "chalk";
|
|
1358
1583
|
var HEADERS2 = ["DEVICE_ID", "NAME", "TYPE", "CATEGORY", "OS", "STATUS"];
|
|
1359
1584
|
function formatDevicesHuman(devices) {
|
|
1360
1585
|
if (devices.length === 0) {
|
|
@@ -1370,9 +1595,9 @@ function formatDevicesHuman(devices) {
|
|
|
1370
1595
|
]);
|
|
1371
1596
|
const widths = HEADERS2.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
1372
1597
|
const fmt = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ").trimEnd();
|
|
1373
|
-
const lines = [
|
|
1598
|
+
const lines = [chalk13.bold(fmt(HEADERS2))];
|
|
1374
1599
|
for (const row of rows.sort(rowSort)) {
|
|
1375
|
-
const statusColored = row[5] === "available" ?
|
|
1600
|
+
const statusColored = row[5] === "available" ? chalk13.green(row[5]) : row[5] === "busy" ? chalk13.yellow(row[5]) : chalk13.dim(row[5]);
|
|
1376
1601
|
lines.push(fmt([row[0], row[1], row[2], row[3], row[4], statusColored]));
|
|
1377
1602
|
}
|
|
1378
1603
|
return lines.join("\n") + "\n";
|
|
@@ -1462,7 +1687,7 @@ import { dirname, join } from "path";
|
|
|
1462
1687
|
import { fileURLToPath } from "url";
|
|
1463
1688
|
import { spawnSync } from "child_process";
|
|
1464
1689
|
import { confirm } from "@inquirer/prompts";
|
|
1465
|
-
import
|
|
1690
|
+
import chalk14 from "chalk";
|
|
1466
1691
|
function resolveShippedSkillPath(fromDir) {
|
|
1467
1692
|
for (const rel of ["..", join("..", "..")]) {
|
|
1468
1693
|
const candidate = join(fromDir, rel, "skills", "vibeview-agent", "SKILL.md");
|
|
@@ -1490,23 +1715,23 @@ function registerMcp() {
|
|
|
1490
1715
|
encoding: "utf8"
|
|
1491
1716
|
});
|
|
1492
1717
|
if (res.status === 0) {
|
|
1493
|
-
console.log(
|
|
1718
|
+
console.log(chalk14.green("\u2713 MCP server registered with Claude Code (claude mcp add vibeview)"));
|
|
1494
1719
|
return;
|
|
1495
1720
|
}
|
|
1496
1721
|
const out = `${res.stderr ?? ""}${res.stdout ?? ""}`;
|
|
1497
1722
|
if (out.includes("already exists")) {
|
|
1498
|
-
console.log(
|
|
1723
|
+
console.log(chalk14.dim("MCP server already registered with Claude Code \u2014 nothing to do"));
|
|
1499
1724
|
return;
|
|
1500
1725
|
}
|
|
1501
|
-
console.error(
|
|
1502
|
-
console.error(
|
|
1726
|
+
console.error(chalk14.yellow(`\u26A0 could not register the MCP server: ${out.trim() || res.error}`));
|
|
1727
|
+
console.error(chalk14.yellow(" register it manually: claude mcp add vibeview -- vibeview mcp"));
|
|
1503
1728
|
}
|
|
1504
1729
|
async function agentSetup(opts) {
|
|
1505
1730
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
1506
1731
|
const shipped = resolveShippedSkillPath(moduleDir);
|
|
1507
1732
|
if (!shipped) {
|
|
1508
1733
|
console.error(
|
|
1509
|
-
|
|
1734
|
+
chalk14.red(
|
|
1510
1735
|
"error: could not find the packaged skill (skills/vibeview-agent/SKILL.md) \u2014 is this a complete install of the vibeview package?"
|
|
1511
1736
|
)
|
|
1512
1737
|
);
|
|
@@ -1515,9 +1740,9 @@ async function agentSetup(opts) {
|
|
|
1515
1740
|
}
|
|
1516
1741
|
const targetDir = skillInstallDir(opts.project ?? false, process.cwd(), homedir());
|
|
1517
1742
|
const result = installSkill(shipped, targetDir);
|
|
1518
|
-
console.log(
|
|
1743
|
+
console.log(chalk14.green(`\u2713 vibeview-agent skill ${result}: ${join(targetDir, "SKILL.md")}`));
|
|
1519
1744
|
console.log(
|
|
1520
|
-
|
|
1745
|
+
chalk14.dim(
|
|
1521
1746
|
opts.project ? " Claude Code discovers it automatically for anyone working in this project." : " Claude Code discovers it automatically in every project on this machine."
|
|
1522
1747
|
)
|
|
1523
1748
|
);
|
|
@@ -1525,7 +1750,7 @@ async function agentSetup(opts) {
|
|
|
1525
1750
|
if (!claudeCliAvailable()) {
|
|
1526
1751
|
if (opts.mcp === true) {
|
|
1527
1752
|
console.error(
|
|
1528
|
-
|
|
1753
|
+
chalk14.yellow(
|
|
1529
1754
|
"\u26A0 claude CLI not found \u2014 install Claude Code, then run: claude mcp add vibeview -- vibeview mcp"
|
|
1530
1755
|
)
|
|
1531
1756
|
);
|
|
@@ -1539,7 +1764,7 @@ async function agentSetup(opts) {
|
|
|
1539
1764
|
}
|
|
1540
1765
|
console.log(
|
|
1541
1766
|
`
|
|
1542
|
-
Suggested line for your project's ${
|
|
1767
|
+
Suggested line for your project's ${chalk14.bold("CLAUDE.md")} (or ${chalk14.bold(
|
|
1543
1768
|
"AGENTS.md"
|
|
1544
1769
|
)} for other agents):
|
|
1545
1770
|
`
|
|
@@ -1547,7 +1772,7 @@ Suggested line for your project's ${chalk13.bold("CLAUDE.md")} (or ${chalk13.bol
|
|
|
1547
1772
|
console.log(` ${CLAUDE_MD_SUGGESTION}
|
|
1548
1773
|
`);
|
|
1549
1774
|
console.log(
|
|
1550
|
-
|
|
1775
|
+
chalk14.dim(
|
|
1551
1776
|
"Other agents (Cursor, Copilot, Codex, ...): point them at the skill file above, or\nadd the MCP server per client \u2014 see https://vibeview.io/docs/agent-control"
|
|
1552
1777
|
)
|
|
1553
1778
|
);
|
|
@@ -1712,12 +1937,24 @@ builds.command("list").description("List cloud builds in your organization").opt
|
|
|
1712
1937
|
builds.command("logs <id>").description("Print a cloud build's log output").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print NDJSON log/outcome events instead of raw text", false).option("--follow", "Keep polling until the build reaches a terminal status", false).action((id, opts) => buildsLogs(id, opts));
|
|
1713
1938
|
builds.command("download <id>").description("Download a succeeded cloud build's artifact").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print the saved filename/path as one JSON line", false).option("--out <dir>", "Destination directory (default: current directory)").action((id, opts) => buildsDownload(id, opts));
|
|
1714
1939
|
builds.command("cancel <id>").description("Cancel a queued or running cloud build").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print the resulting build summary as one JSON line", false).action((id, opts) => buildsCancel(id, opts));
|
|
1940
|
+
var env = program.command("env").description("Manage cloud build environment variables and secrets");
|
|
1941
|
+
env.command("list").description("List build variables and secrets (never prints a value)").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "Scope to a single app (default: org-wide variables only)").option("--json", "Print the raw variable list as a JSON array", false).action((opts) => envList(opts));
|
|
1942
|
+
env.command("set <name> [value]").description(
|
|
1943
|
+
"Set a build variable or secret. Defaults to --secret (never retrievable via the API) unless --plain or --sensitive is passed explicitly."
|
|
1944
|
+
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "Scope to a single app (default: org-wide)").option("--secret", "Store as secret (default): never returned by the API, including `env pull`").option(
|
|
1945
|
+
"--sensitive",
|
|
1946
|
+
"Store as sensitive: value_hint only in `env list`, full value via `env pull`"
|
|
1947
|
+
).option("--plain", "Store as plain: value returned by `env pull`, unmasked").option("--file <path>", "Upload a file as the value (e.g. a keystore) instead of a text value").option("--json", "Print the resulting variable as one JSON line", false).action((name, value, opts) => envSet(name, value, opts));
|
|
1948
|
+
env.command("rm <name>").description("Remove a build variable or secret").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "Scope to a single app (default: org-wide)").option("--json", "Print the result as one JSON line", false).action((name, opts) => envRm(name, opts));
|
|
1949
|
+
env.command("pull").description(
|
|
1950
|
+
"Write plain/sensitive variable values to a .env file (secrets and files are never included)"
|
|
1951
|
+
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "Scope to a single app (default: org-wide variables only)").option("--out <path>", "Output file path (default: .env)").option("--json", "Print the result as one JSON line", false).action((opts) => envPull(opts));
|
|
1715
1952
|
program.command("dev-status").description("Show the status of a `vibeview dev --detach` session in this project").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print as one JSON line", false).action((opts) => devStatus(opts));
|
|
1716
1953
|
program.command("dev-stop").description("Stop a `vibeview dev --detach` session in this project").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print as one JSON line", false).action((opts) => devStop(opts));
|
|
1717
1954
|
program.command("mcp").description(
|
|
1718
1955
|
"Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop as tools"
|
|
1719
1956
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").action(async (opts) => {
|
|
1720
|
-
const { runMcpServer } = await import("./server-
|
|
1957
|
+
const { runMcpServer } = await import("./server-JA5A5PY6.js");
|
|
1721
1958
|
await runMcpServer(opts);
|
|
1722
1959
|
});
|
|
1723
1960
|
program.command("agent-setup").description(
|
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
readDevState,
|
|
12
12
|
renderEnvelope,
|
|
13
13
|
startDevSession
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-YFXMXO5T.js";
|
|
15
15
|
import {
|
|
16
16
|
ApiClient,
|
|
17
17
|
resolveAuth
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-J7V3GYOI.js";
|
|
19
19
|
|
|
20
20
|
// src/mcp/server.ts
|
|
21
21
|
import { writeFileSync } from "fs";
|
|
@@ -239,7 +239,7 @@ async function handleUploadApp(args, deps) {
|
|
|
239
239
|
}
|
|
240
240
|
const name = typeof args.name === "string" && args.name ? args.name : void 0;
|
|
241
241
|
const upload = deps.uploadApp ?? (async (f, n) => {
|
|
242
|
-
const { uploadAppBuild } = await import("./upload-app-
|
|
242
|
+
const { uploadAppBuild } = await import("./upload-app-ZAWCZFQE.js");
|
|
243
243
|
const { result: result2 } = await uploadAppBuild(f, { ...deps.auth, name: n });
|
|
244
244
|
return result2;
|
|
245
245
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibeview",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "VibeView CLI -- run mobile tests from CI/CD pipelines",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/node": "^20.0.0",
|
|
40
40
|
"@types/ws": "^8.5.0",
|
|
41
|
+
"dotenv": "^16.4.5",
|
|
41
42
|
"tsup": "^8.0.0",
|
|
42
43
|
"typescript": "^5.0.0",
|
|
43
44
|
"vitest": "^3.0.0"
|