@zalify/cli 0.2.3 → 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/dist/cli.js +139 -33
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -11308,11 +11308,35 @@ import { homedir } from "node:os";
|
|
|
11308
11308
|
import { join } from "node:path";
|
|
11309
11309
|
var dir = join(homedir(), ".zalify");
|
|
11310
11310
|
var file = join(dir, "config.json");
|
|
11311
|
+
function migrate(raw) {
|
|
11312
|
+
if (!raw || typeof raw !== "object")
|
|
11313
|
+
return null;
|
|
11314
|
+
const obj = raw;
|
|
11315
|
+
if (obj.version === 2 && obj.workspaces && obj.appUrl) {
|
|
11316
|
+
return obj;
|
|
11317
|
+
}
|
|
11318
|
+
if (typeof obj.key === "string" && typeof obj.workspaceId === "string") {
|
|
11319
|
+
return {
|
|
11320
|
+
version: 2,
|
|
11321
|
+
appUrl: obj.appUrl ?? "https://app.zalify.com",
|
|
11322
|
+
activeWorkspaceId: obj.workspaceId,
|
|
11323
|
+
workspaces: {
|
|
11324
|
+
[obj.workspaceId]: {
|
|
11325
|
+
key: obj.key,
|
|
11326
|
+
name: obj.workspaceName ?? "",
|
|
11327
|
+
slug: obj.workspaceSlug ?? "",
|
|
11328
|
+
createdAt: obj.createdAt ?? new Date().toISOString()
|
|
11329
|
+
}
|
|
11330
|
+
}
|
|
11331
|
+
};
|
|
11332
|
+
}
|
|
11333
|
+
return null;
|
|
11334
|
+
}
|
|
11311
11335
|
function readConfig() {
|
|
11312
11336
|
if (!existsSync(file))
|
|
11313
11337
|
return null;
|
|
11314
11338
|
try {
|
|
11315
|
-
return JSON.parse(readFileSync(file, "utf8"));
|
|
11339
|
+
return migrate(JSON.parse(readFileSync(file, "utf8")));
|
|
11316
11340
|
} catch {
|
|
11317
11341
|
return null;
|
|
11318
11342
|
}
|
|
@@ -11325,12 +11349,13 @@ function writeConfig(config) {
|
|
|
11325
11349
|
function deleteConfig() {
|
|
11326
11350
|
rmSync(file, { force: true });
|
|
11327
11351
|
}
|
|
11328
|
-
function
|
|
11352
|
+
function requireActive() {
|
|
11329
11353
|
const config = readConfig();
|
|
11330
|
-
if (!config
|
|
11354
|
+
if (!config || Object.keys(config.workspaces).length === 0) {
|
|
11331
11355
|
throw new Error("Not logged in. Run `zalify login` first.");
|
|
11332
11356
|
}
|
|
11333
|
-
|
|
11357
|
+
const workspaceId = config.activeWorkspaceId && config.workspaces[config.activeWorkspaceId] ? config.activeWorkspaceId : Object.keys(config.workspaces)[0];
|
|
11358
|
+
return { appUrl: config.appUrl, workspaceId, entry: config.workspaces[workspaceId], config };
|
|
11334
11359
|
}
|
|
11335
11360
|
|
|
11336
11361
|
// src/auth.ts
|
|
@@ -11346,10 +11371,14 @@ function openBrowser(url) {
|
|
|
11346
11371
|
detached: true
|
|
11347
11372
|
}).unref();
|
|
11348
11373
|
}
|
|
11374
|
+
function decodePayload(encoded) {
|
|
11375
|
+
const b64 = encoded.replace(/-/g, "+").replace(/_/g, "/");
|
|
11376
|
+
return JSON.parse(Buffer.from(b64, "base64").toString("utf8"));
|
|
11377
|
+
}
|
|
11349
11378
|
async function login(options) {
|
|
11350
11379
|
const base = (options.appUrl || readConfig()?.appUrl || DEFAULT_APP_URL).replace(/\/$/, "");
|
|
11351
11380
|
const state = randomBytes(32).toString("hex");
|
|
11352
|
-
const
|
|
11381
|
+
const payload = await new Promise((resolvePromise, reject) => {
|
|
11353
11382
|
const timer = setTimeout(() => {
|
|
11354
11383
|
server.close();
|
|
11355
11384
|
server.closeAllConnections();
|
|
@@ -11387,22 +11416,38 @@ async function login(options) {
|
|
|
11387
11416
|
});
|
|
11388
11417
|
return;
|
|
11389
11418
|
}
|
|
11390
|
-
const
|
|
11391
|
-
|
|
11392
|
-
|
|
11419
|
+
const encoded = url.searchParams.get("payload");
|
|
11420
|
+
const legacyKey = url.searchParams.get("key");
|
|
11421
|
+
let parsed = null;
|
|
11422
|
+
if (encoded) {
|
|
11423
|
+
try {
|
|
11424
|
+
parsed = decodePayload(encoded);
|
|
11425
|
+
} catch {
|
|
11426
|
+
parsed = null;
|
|
11427
|
+
}
|
|
11428
|
+
} else if (legacyKey) {
|
|
11429
|
+
parsed = {
|
|
11430
|
+
keys: [
|
|
11431
|
+
{
|
|
11432
|
+
workspaceId: url.searchParams.get("workspace_id") ?? "",
|
|
11433
|
+
workspaceName: url.searchParams.get("workspace_name") ?? "",
|
|
11434
|
+
workspaceSlug: url.searchParams.get("workspace_slug") ?? "",
|
|
11435
|
+
key: legacyKey
|
|
11436
|
+
}
|
|
11437
|
+
]
|
|
11438
|
+
};
|
|
11439
|
+
}
|
|
11440
|
+
if (!parsed || parsed.keys.length === 0) {
|
|
11441
|
+
done("Login failed: no keys in callback.", () => {
|
|
11393
11442
|
teardown();
|
|
11394
|
-
reject(new Error("Callback did not include
|
|
11443
|
+
reject(new Error("Callback did not include any keys."));
|
|
11395
11444
|
});
|
|
11396
11445
|
return;
|
|
11397
11446
|
}
|
|
11398
|
-
|
|
11447
|
+
const result = parsed;
|
|
11448
|
+
done(`Logged in to ${result.keys.length} workspace${result.keys.length === 1 ? "" : "s"}!`, () => {
|
|
11399
11449
|
teardown();
|
|
11400
|
-
resolvePromise(
|
|
11401
|
-
key,
|
|
11402
|
-
workspaceId: url.searchParams.get("workspace_id") ?? "",
|
|
11403
|
-
workspaceName: url.searchParams.get("workspace_name") ?? "",
|
|
11404
|
-
workspaceSlug: url.searchParams.get("workspace_slug") ?? ""
|
|
11405
|
-
});
|
|
11450
|
+
resolvePromise(result);
|
|
11406
11451
|
});
|
|
11407
11452
|
});
|
|
11408
11453
|
server.listen(0, "127.0.0.1", () => {
|
|
@@ -11416,13 +11461,40 @@ async function login(options) {
|
|
|
11416
11461
|
});
|
|
11417
11462
|
server.on("error", reject);
|
|
11418
11463
|
});
|
|
11419
|
-
|
|
11420
|
-
|
|
11464
|
+
const existing = readConfig();
|
|
11465
|
+
const config = {
|
|
11466
|
+
version: 2,
|
|
11467
|
+
appUrl: base,
|
|
11468
|
+
activeWorkspaceId: existing?.activeWorkspaceId ?? null,
|
|
11469
|
+
workspaces: { ...existing?.appUrl === base ? existing?.workspaces : {} }
|
|
11470
|
+
};
|
|
11471
|
+
const now = new Date().toISOString();
|
|
11472
|
+
for (const k of payload.keys) {
|
|
11473
|
+
config.workspaces[k.workspaceId] = {
|
|
11474
|
+
key: k.key,
|
|
11475
|
+
name: k.workspaceName,
|
|
11476
|
+
slug: k.workspaceSlug,
|
|
11477
|
+
createdAt: now
|
|
11478
|
+
};
|
|
11479
|
+
}
|
|
11480
|
+
if (!config.activeWorkspaceId || !config.workspaces[config.activeWorkspaceId]) {
|
|
11481
|
+
config.activeWorkspaceId = payload.keys[0].workspaceId;
|
|
11482
|
+
}
|
|
11483
|
+
writeConfig(config);
|
|
11484
|
+
console.log(`✓ Authorized ${payload.keys.length} workspace(s):`);
|
|
11485
|
+
for (const k of payload.keys) {
|
|
11486
|
+
const active = k.workspaceId === config.activeWorkspaceId ? " (active)" : "";
|
|
11487
|
+
console.log(` - ${k.workspaceName} (${k.workspaceSlug})${active}`);
|
|
11488
|
+
}
|
|
11489
|
+
for (const f of payload.failed ?? []) {
|
|
11490
|
+
console.log(` ✗ ${f.workspaceName}: ${f.error}`);
|
|
11491
|
+
}
|
|
11492
|
+
console.log("Switch with `zalify workspace set <slug>`.");
|
|
11421
11493
|
}
|
|
11422
11494
|
function logout() {
|
|
11423
11495
|
deleteConfig();
|
|
11424
11496
|
console.log("✓ Logged out (local credentials deleted).");
|
|
11425
|
-
console.log(" To revoke
|
|
11497
|
+
console.log(" To revoke keys: each workspace's Settings → Developer on app.zalify.com.");
|
|
11426
11498
|
}
|
|
11427
11499
|
function ago(iso) {
|
|
11428
11500
|
if (!iso)
|
|
@@ -11439,12 +11511,12 @@ function ago(iso) {
|
|
|
11439
11511
|
return `${Math.round(hours / 24)}d ago`;
|
|
11440
11512
|
}
|
|
11441
11513
|
async function whoami() {
|
|
11442
|
-
const config =
|
|
11514
|
+
const { appUrl, workspaceId, entry, config } = requireActive();
|
|
11443
11515
|
let live = null;
|
|
11444
11516
|
let liveError = null;
|
|
11445
11517
|
try {
|
|
11446
|
-
const res = await fetch(`${
|
|
11447
|
-
headers: { Authorization: `Bearer ${
|
|
11518
|
+
const res = await fetch(`${appUrl}/api/cli/whoami`, {
|
|
11519
|
+
headers: { Authorization: `Bearer ${entry.key}` }
|
|
11448
11520
|
});
|
|
11449
11521
|
if (res.ok) {
|
|
11450
11522
|
live = await res.json();
|
|
@@ -11457,17 +11529,19 @@ async function whoami() {
|
|
|
11457
11529
|
liveError = "could not reach the server (offline?)";
|
|
11458
11530
|
}
|
|
11459
11531
|
const ws = live?.workspace;
|
|
11460
|
-
|
|
11532
|
+
const total = Object.keys(config.workspaces).length;
|
|
11533
|
+
console.log(`workspace ${ws?.name ?? entry.name ?? "(unknown)"} (${ws?.slug ?? entry.slug ?? "?"}) [${ws?.id ?? workspaceId}]` + (total > 1 ? ` — 1 of ${total}, see \`zalify workspace list\`` : ""));
|
|
11461
11534
|
if (live) {
|
|
11462
11535
|
console.log(`plan ${live.plan.name} (${live.plan.type})`);
|
|
11463
11536
|
if (live.key.createdByName) {
|
|
11464
11537
|
console.log(`user ${live.key.createdByName}`);
|
|
11465
11538
|
}
|
|
11466
|
-
console.log(`key ${live.key.name ??
|
|
11539
|
+
console.log(`key ${live.key.name ?? entry.key.slice(0, 16) + "…"} — created ${live.key.createdAt ? new Date(live.key.createdAt).toISOString().slice(0, 10) : "?"}, last used ${ago(live.key.lastRequest)}`);
|
|
11467
11540
|
} else {
|
|
11468
|
-
console.log(`key ${
|
|
11541
|
+
console.log(`key ${entry.key.slice(0, 16)}…`);
|
|
11469
11542
|
}
|
|
11470
|
-
|
|
11543
|
+
const slug = ws?.slug ?? entry.slug;
|
|
11544
|
+
console.log(`app ${slug ? `${appUrl}/store/${slug}` : appUrl}`);
|
|
11471
11545
|
console.log(live ? "status ✓ key verified" : `status ! not verified — ${liveError}`);
|
|
11472
11546
|
}
|
|
11473
11547
|
|
|
@@ -11512,24 +11586,25 @@ async function assetsPull(storeDir) {
|
|
|
11512
11586
|
console.log(`Done: ${pulled} downloaded, ${Object.keys(index).length - pulled} already local.`);
|
|
11513
11587
|
}
|
|
11514
11588
|
async function assetsPush(storeDir) {
|
|
11515
|
-
const
|
|
11589
|
+
const { appUrl, workspaceId, entry } = requireActive();
|
|
11516
11590
|
const imagesDir = imagesDirFor(storeDir);
|
|
11517
11591
|
const indexPath = join2(imagesDir, "assets.json");
|
|
11518
11592
|
const index = readIndex(imagesDir);
|
|
11519
11593
|
async function api(path9, body) {
|
|
11520
|
-
const res = await fetch(`${
|
|
11594
|
+
const res = await fetch(`${appUrl}${path9}`, {
|
|
11521
11595
|
method: "POST",
|
|
11522
11596
|
headers: {
|
|
11523
11597
|
"Content-Type": "application/json",
|
|
11524
|
-
Authorization: `Bearer ${
|
|
11598
|
+
Authorization: `Bearer ${entry.key}`
|
|
11525
11599
|
},
|
|
11526
11600
|
body: JSON.stringify(body)
|
|
11527
11601
|
});
|
|
11528
11602
|
const json = await res.json().catch(() => ({}));
|
|
11529
11603
|
if (!res.ok) {
|
|
11530
11604
|
if (json.code === "UPGRADE_REQUIRED") {
|
|
11605
|
+
const billingUrl = entry.slug ? `${appUrl}/store/${entry.slug}/settings/billing` : `${appUrl} (Settings → Billing)`;
|
|
11531
11606
|
throw new Error(`${json.error ?? "This feature requires a paid plan."}
|
|
11532
|
-
` + ` Upgrade this workspace
|
|
11607
|
+
` + ` Upgrade this workspace: ${billingUrl}`);
|
|
11533
11608
|
}
|
|
11534
11609
|
throw new Error(`${path9} ${res.status}: ${JSON.stringify(json)}`);
|
|
11535
11610
|
}
|
|
@@ -11543,11 +11618,11 @@ async function assetsPush(storeDir) {
|
|
|
11543
11618
|
console.log("Everything already pushed.");
|
|
11544
11619
|
return;
|
|
11545
11620
|
}
|
|
11546
|
-
console.log(`Pushing ${files.length} file(s) to "${
|
|
11621
|
+
console.log(`Pushing ${files.length} file(s) to "${entry.name || workspaceId}"`);
|
|
11547
11622
|
for (let i = 0;i < files.length; i += BATCH) {
|
|
11548
11623
|
const batch = files.slice(i, i + BATCH);
|
|
11549
11624
|
const { uploads } = await api("/api/assets/presign", {
|
|
11550
|
-
workspaceId
|
|
11625
|
+
workspaceId,
|
|
11551
11626
|
folderId: process.env.ZALIFY_FOLDER_ID ?? null,
|
|
11552
11627
|
files: batch.map((f) => ({
|
|
11553
11628
|
id: f.name,
|
|
@@ -11587,7 +11662,7 @@ async function assetsPush(storeDir) {
|
|
|
11587
11662
|
}
|
|
11588
11663
|
if (toFinalize.length) {
|
|
11589
11664
|
const { assets, pending } = await api("/api/assets/finalize", {
|
|
11590
|
-
workspaceId
|
|
11665
|
+
workspaceId,
|
|
11591
11666
|
items: toFinalize.map(({ file: file2, up }) => ({
|
|
11592
11667
|
assetId: up.assetId,
|
|
11593
11668
|
checksum: file2.checksum,
|
|
@@ -11646,6 +11721,34 @@ Try manually: ${cmd} ${args.join(" ")}`);
|
|
|
11646
11721
|
✓ Done${updated ? ` (version: ${updated})` : ""}`);
|
|
11647
11722
|
}
|
|
11648
11723
|
|
|
11724
|
+
// src/workspace.ts
|
|
11725
|
+
function workspaceList() {
|
|
11726
|
+
const config = readConfig();
|
|
11727
|
+
const entries = Object.entries(config?.workspaces ?? {});
|
|
11728
|
+
if (!config || entries.length === 0) {
|
|
11729
|
+
throw new Error("Not logged in. Run `zalify login` first.");
|
|
11730
|
+
}
|
|
11731
|
+
for (const [id, w] of entries) {
|
|
11732
|
+
const marker = id === config.activeWorkspaceId ? "* " : " ";
|
|
11733
|
+
console.log(`${marker}${w.slug.padEnd(36)} ${w.name} [${id}]`);
|
|
11734
|
+
}
|
|
11735
|
+
}
|
|
11736
|
+
function workspaceSet(slugOrId) {
|
|
11737
|
+
const config = readConfig();
|
|
11738
|
+
if (!config || Object.keys(config.workspaces).length === 0) {
|
|
11739
|
+
throw new Error("Not logged in. Run `zalify login` first.");
|
|
11740
|
+
}
|
|
11741
|
+
const match = Object.entries(config.workspaces).find(([id, w]) => id === slugOrId || w.slug === slugOrId || w.name === slugOrId);
|
|
11742
|
+
if (!match) {
|
|
11743
|
+
const known = Object.values(config.workspaces).map((w) => w.slug).join(", ");
|
|
11744
|
+
throw new Error(`No authorized workspace matches "${slugOrId}". Known: ${known}.
|
|
11745
|
+
` + "Run `zalify login` to authorize more workspaces.");
|
|
11746
|
+
}
|
|
11747
|
+
config.activeWorkspaceId = match[0];
|
|
11748
|
+
writeConfig(config);
|
|
11749
|
+
console.log(`✓ Active workspace: ${match[1].name} (${match[1].slug})`);
|
|
11750
|
+
}
|
|
11751
|
+
|
|
11649
11752
|
// src/cli.ts
|
|
11650
11753
|
var __dirname4 = dirname(fileURLToPath3(import.meta.url));
|
|
11651
11754
|
var require2 = createRequire2(import.meta.url);
|
|
@@ -11686,6 +11789,9 @@ program2.command("logout").description("Delete the stored CLI credentials").acti
|
|
|
11686
11789
|
program2.command("whoami").description("Verify the stored key and show workspace, plan, and key details").action(async () => {
|
|
11687
11790
|
await whoami();
|
|
11688
11791
|
});
|
|
11792
|
+
var workspace = program2.command("workspace").description("List authorized workspaces or switch the active one");
|
|
11793
|
+
workspace.command("list", { isDefault: true }).description("List authorized workspaces (* = active)").action(() => workspaceList());
|
|
11794
|
+
workspace.command("set <slug-or-id>").description("Switch the active workspace (local, no re-login)").action((slugOrId) => workspaceSet(slugOrId));
|
|
11689
11795
|
var assets = program2.command("assets").description("Sync images with the Zalify asset library");
|
|
11690
11796
|
assets.command("push <store-dir>").description("Upload <store-dir>/images/*.png (sha256 dedup, writes assets.json)").action(async (storeDir) => {
|
|
11691
11797
|
await assetsPush(storeDir);
|