@zalify/cli 0.3.0 → 0.4.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 +154 -52
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -11312,9 +11312,10 @@ function migrate(raw) {
|
|
|
11312
11312
|
if (!raw || typeof raw !== "object")
|
|
11313
11313
|
return null;
|
|
11314
11314
|
const obj = raw;
|
|
11315
|
-
if (obj.version ===
|
|
11315
|
+
if (obj.version === 3 && obj.userKey && obj.appUrl)
|
|
11316
|
+
return obj;
|
|
11317
|
+
if (obj.version === 2 && obj.workspaces && obj.appUrl)
|
|
11316
11318
|
return obj;
|
|
11317
|
-
}
|
|
11318
11319
|
if (typeof obj.key === "string" && typeof obj.workspaceId === "string") {
|
|
11319
11320
|
return {
|
|
11320
11321
|
version: 2,
|
|
@@ -11351,11 +11352,36 @@ function deleteConfig() {
|
|
|
11351
11352
|
}
|
|
11352
11353
|
function requireActive() {
|
|
11353
11354
|
const config = readConfig();
|
|
11354
|
-
if (!config
|
|
11355
|
+
if (!config) {
|
|
11355
11356
|
throw new Error("Not logged in. Run `zalify login` first.");
|
|
11356
11357
|
}
|
|
11357
|
-
|
|
11358
|
-
|
|
11358
|
+
if (config.version === 3) {
|
|
11359
|
+
if (!config.active) {
|
|
11360
|
+
throw new Error("No active workspace. Run `zalify workspace list` then `zalify workspace set <slug>`.");
|
|
11361
|
+
}
|
|
11362
|
+
return {
|
|
11363
|
+
appUrl: config.appUrl,
|
|
11364
|
+
key: config.userKey,
|
|
11365
|
+
workspaceId: config.active.id,
|
|
11366
|
+
workspaceName: config.active.name,
|
|
11367
|
+
workspaceSlug: config.active.slug,
|
|
11368
|
+
config
|
|
11369
|
+
};
|
|
11370
|
+
}
|
|
11371
|
+
const ids = Object.keys(config.workspaces);
|
|
11372
|
+
if (ids.length === 0) {
|
|
11373
|
+
throw new Error("Not logged in. Run `zalify login` first.");
|
|
11374
|
+
}
|
|
11375
|
+
const workspaceId = config.activeWorkspaceId && config.workspaces[config.activeWorkspaceId] ? config.activeWorkspaceId : ids[0];
|
|
11376
|
+
const entry = config.workspaces[workspaceId];
|
|
11377
|
+
return {
|
|
11378
|
+
appUrl: config.appUrl,
|
|
11379
|
+
key: entry.key,
|
|
11380
|
+
workspaceId,
|
|
11381
|
+
workspaceName: entry.name,
|
|
11382
|
+
workspaceSlug: entry.slug,
|
|
11383
|
+
config
|
|
11384
|
+
};
|
|
11359
11385
|
}
|
|
11360
11386
|
|
|
11361
11387
|
// src/auth.ts
|
|
@@ -11375,6 +11401,18 @@ function decodePayload(encoded) {
|
|
|
11375
11401
|
const b64 = encoded.replace(/-/g, "+").replace(/_/g, "/");
|
|
11376
11402
|
return JSON.parse(Buffer.from(b64, "base64").toString("utf8"));
|
|
11377
11403
|
}
|
|
11404
|
+
async function fetchWorkspaces(appUrl, userKey) {
|
|
11405
|
+
try {
|
|
11406
|
+
const res = await fetch(`${appUrl}/api/cli/workspaces`, {
|
|
11407
|
+
headers: { Authorization: `Bearer ${userKey}` }
|
|
11408
|
+
});
|
|
11409
|
+
if (!res.ok)
|
|
11410
|
+
return [];
|
|
11411
|
+
return (await res.json()).workspaces;
|
|
11412
|
+
} catch {
|
|
11413
|
+
return [];
|
|
11414
|
+
}
|
|
11415
|
+
}
|
|
11378
11416
|
async function login(options) {
|
|
11379
11417
|
const base = (options.appUrl || readConfig()?.appUrl || DEFAULT_APP_URL).replace(/\/$/, "");
|
|
11380
11418
|
const state = randomBytes(32).toString("hex");
|
|
@@ -11437,15 +11475,15 @@ async function login(options) {
|
|
|
11437
11475
|
]
|
|
11438
11476
|
};
|
|
11439
11477
|
}
|
|
11440
|
-
if (!parsed || parsed.keys
|
|
11441
|
-
done("Login failed: no
|
|
11478
|
+
if (!parsed || !parsed.userKey && !parsed.keys?.length) {
|
|
11479
|
+
done("Login failed: no credentials in callback.", () => {
|
|
11442
11480
|
teardown();
|
|
11443
|
-
reject(new Error("Callback did not include
|
|
11481
|
+
reject(new Error("Callback did not include credentials."));
|
|
11444
11482
|
});
|
|
11445
11483
|
return;
|
|
11446
11484
|
}
|
|
11447
11485
|
const result = parsed;
|
|
11448
|
-
done(
|
|
11486
|
+
done("Logged in!", () => {
|
|
11449
11487
|
teardown();
|
|
11450
11488
|
resolvePromise(result);
|
|
11451
11489
|
});
|
|
@@ -11461,15 +11499,40 @@ async function login(options) {
|
|
|
11461
11499
|
});
|
|
11462
11500
|
server.on("error", reject);
|
|
11463
11501
|
});
|
|
11502
|
+
if (payload.userKey && payload.user) {
|
|
11503
|
+
const previous = readConfig();
|
|
11504
|
+
const previousActive = previous?.version === 3 ? previous.active : previous?.version === 2 && previous.activeWorkspaceId ? {
|
|
11505
|
+
id: previous.activeWorkspaceId,
|
|
11506
|
+
name: previous.workspaces[previous.activeWorkspaceId]?.name ?? "",
|
|
11507
|
+
slug: previous.workspaces[previous.activeWorkspaceId]?.slug ?? ""
|
|
11508
|
+
} : null;
|
|
11509
|
+
const workspaces = await fetchWorkspaces(base, payload.userKey);
|
|
11510
|
+
const active = previousActive && workspaces.find((w) => w.id === previousActive.id) || workspaces[0] || previousActive || null;
|
|
11511
|
+
writeConfig({
|
|
11512
|
+
version: 3,
|
|
11513
|
+
appUrl: base,
|
|
11514
|
+
userKey: payload.userKey,
|
|
11515
|
+
user: payload.user,
|
|
11516
|
+
active,
|
|
11517
|
+
workspacesCache: workspaces
|
|
11518
|
+
});
|
|
11519
|
+
console.log(`✓ Logged in as ${payload.user.name ?? payload.user.email ?? payload.user.id} — key covers ${workspaces.length || "your"} workspace(s), expires in 90 days.`);
|
|
11520
|
+
if (active)
|
|
11521
|
+
console.log(` Active workspace: ${active.name} (${active.slug})`);
|
|
11522
|
+
console.log(" Switch with `zalify workspace set <slug>`.");
|
|
11523
|
+
return;
|
|
11524
|
+
}
|
|
11464
11525
|
const existing = readConfig();
|
|
11465
11526
|
const config = {
|
|
11466
11527
|
version: 2,
|
|
11467
11528
|
appUrl: base,
|
|
11468
|
-
activeWorkspaceId: existing?.activeWorkspaceId
|
|
11469
|
-
workspaces: {
|
|
11529
|
+
activeWorkspaceId: existing?.version === 2 ? existing.activeWorkspaceId : null,
|
|
11530
|
+
workspaces: {
|
|
11531
|
+
...existing?.version === 2 && existing.appUrl === base ? existing.workspaces : {}
|
|
11532
|
+
}
|
|
11470
11533
|
};
|
|
11471
11534
|
const now = new Date().toISOString();
|
|
11472
|
-
for (const k of payload.keys) {
|
|
11535
|
+
for (const k of payload.keys ?? []) {
|
|
11473
11536
|
config.workspaces[k.workspaceId] = {
|
|
11474
11537
|
key: k.key,
|
|
11475
11538
|
name: k.workspaceName,
|
|
@@ -11478,23 +11541,15 @@ async function login(options) {
|
|
|
11478
11541
|
};
|
|
11479
11542
|
}
|
|
11480
11543
|
if (!config.activeWorkspaceId || !config.workspaces[config.activeWorkspaceId]) {
|
|
11481
|
-
config.activeWorkspaceId = payload.keys[0]
|
|
11544
|
+
config.activeWorkspaceId = payload.keys?.[0]?.workspaceId ?? null;
|
|
11482
11545
|
}
|
|
11483
11546
|
writeConfig(config);
|
|
11484
|
-
console.log(`✓ Authorized ${payload.keys
|
|
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>`.");
|
|
11547
|
+
console.log(`✓ Authorized ${payload.keys?.length ?? 0} workspace(s) (legacy keys).`);
|
|
11493
11548
|
}
|
|
11494
11549
|
function logout() {
|
|
11495
11550
|
deleteConfig();
|
|
11496
11551
|
console.log("✓ Logged out (local credentials deleted).");
|
|
11497
|
-
console.log("
|
|
11552
|
+
console.log(" CLI keys expire after 90 days; revoke sooner via app.zalify.com if needed.");
|
|
11498
11553
|
}
|
|
11499
11554
|
function ago(iso) {
|
|
11500
11555
|
if (!iso)
|
|
@@ -11511,17 +11566,15 @@ function ago(iso) {
|
|
|
11511
11566
|
return `${Math.round(hours / 24)}d ago`;
|
|
11512
11567
|
}
|
|
11513
11568
|
async function whoami() {
|
|
11514
|
-
const
|
|
11569
|
+
const auth = requireActive();
|
|
11515
11570
|
let live = null;
|
|
11516
11571
|
let liveError = null;
|
|
11517
11572
|
try {
|
|
11518
|
-
const res = await fetch(`${appUrl}/api/cli/whoami`, {
|
|
11519
|
-
headers: { Authorization: `Bearer ${entry.key}` }
|
|
11520
|
-
});
|
|
11573
|
+
const res = await fetch(`${auth.appUrl}/api/cli/whoami?workspaceId=${encodeURIComponent(auth.workspaceId)}`, { headers: { Authorization: `Bearer ${auth.key}` } });
|
|
11521
11574
|
if (res.ok) {
|
|
11522
11575
|
live = await res.json();
|
|
11523
11576
|
} else if (res.status === 401) {
|
|
11524
|
-
liveError = "key is invalid or
|
|
11577
|
+
liveError = "key is invalid, expired, or revoked — run `zalify login`";
|
|
11525
11578
|
} else {
|
|
11526
11579
|
liveError = `server returned ${res.status}`;
|
|
11527
11580
|
}
|
|
@@ -11529,19 +11582,21 @@ async function whoami() {
|
|
|
11529
11582
|
liveError = "could not reach the server (offline?)";
|
|
11530
11583
|
}
|
|
11531
11584
|
const ws = live?.workspace;
|
|
11532
|
-
const
|
|
11533
|
-
console.log(`workspace ${ws?.name ??
|
|
11585
|
+
const keyKind = auth.key.startsWith("zalify_cli_") ? "user key" : "workspace key";
|
|
11586
|
+
console.log(`workspace ${ws?.name ?? auth.workspaceName ?? "(unknown)"} (${ws?.slug ?? auth.workspaceSlug ?? "?"}) [${ws?.id ?? auth.workspaceId}]`);
|
|
11587
|
+
if (auth.config.version === 3) {
|
|
11588
|
+
console.log(`user ${auth.config.user.name ?? auth.config.user.email ?? auth.config.user.id}`);
|
|
11589
|
+
} else if (live?.key.createdByName) {
|
|
11590
|
+
console.log(`user ${live.key.createdByName}`);
|
|
11591
|
+
}
|
|
11534
11592
|
if (live) {
|
|
11535
11593
|
console.log(`plan ${live.plan.name} (${live.plan.type})`);
|
|
11536
|
-
|
|
11537
|
-
console.log(`user ${live.key.createdByName}`);
|
|
11538
|
-
}
|
|
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)}`);
|
|
11594
|
+
console.log(`key ${live.key.name ?? auth.key.slice(0, 16) + "…"} (${keyKind}) — created ${live.key.createdAt ? new Date(live.key.createdAt).toISOString().slice(0, 10) : "?"}, last used ${ago(live.key.lastRequest)}`);
|
|
11540
11595
|
} else {
|
|
11541
|
-
console.log(`key ${
|
|
11596
|
+
console.log(`key ${auth.key.slice(0, 16)}… (${keyKind})`);
|
|
11542
11597
|
}
|
|
11543
|
-
const slug = ws?.slug ??
|
|
11544
|
-
console.log(`app ${slug ? `${appUrl}/store/${slug}` : appUrl}`);
|
|
11598
|
+
const slug = ws?.slug ?? auth.workspaceSlug;
|
|
11599
|
+
console.log(`app ${slug ? `${auth.appUrl}/store/${slug}` : auth.appUrl}`);
|
|
11545
11600
|
console.log(live ? "status ✓ key verified" : `status ! not verified — ${liveError}`);
|
|
11546
11601
|
}
|
|
11547
11602
|
|
|
@@ -11586,7 +11641,7 @@ async function assetsPull(storeDir) {
|
|
|
11586
11641
|
console.log(`Done: ${pulled} downloaded, ${Object.keys(index).length - pulled} already local.`);
|
|
11587
11642
|
}
|
|
11588
11643
|
async function assetsPush(storeDir) {
|
|
11589
|
-
const { appUrl, workspaceId,
|
|
11644
|
+
const { appUrl, workspaceId, key, workspaceName, workspaceSlug } = requireActive();
|
|
11590
11645
|
const imagesDir = imagesDirFor(storeDir);
|
|
11591
11646
|
const indexPath = join2(imagesDir, "assets.json");
|
|
11592
11647
|
const index = readIndex(imagesDir);
|
|
@@ -11595,14 +11650,14 @@ async function assetsPush(storeDir) {
|
|
|
11595
11650
|
method: "POST",
|
|
11596
11651
|
headers: {
|
|
11597
11652
|
"Content-Type": "application/json",
|
|
11598
|
-
Authorization: `Bearer ${
|
|
11653
|
+
Authorization: `Bearer ${key}`
|
|
11599
11654
|
},
|
|
11600
11655
|
body: JSON.stringify(body)
|
|
11601
11656
|
});
|
|
11602
11657
|
const json = await res.json().catch(() => ({}));
|
|
11603
11658
|
if (!res.ok) {
|
|
11604
11659
|
if (json.code === "UPGRADE_REQUIRED") {
|
|
11605
|
-
const billingUrl =
|
|
11660
|
+
const billingUrl = workspaceSlug ? `${appUrl}/store/${workspaceSlug}/settings/billing` : `${appUrl} (Settings → Billing)`;
|
|
11606
11661
|
throw new Error(`${json.error ?? "This feature requires a paid plan."}
|
|
11607
11662
|
` + ` Upgrade this workspace: ${billingUrl}`);
|
|
11608
11663
|
}
|
|
@@ -11618,7 +11673,7 @@ async function assetsPush(storeDir) {
|
|
|
11618
11673
|
console.log("Everything already pushed.");
|
|
11619
11674
|
return;
|
|
11620
11675
|
}
|
|
11621
|
-
console.log(`Pushing ${files.length} file(s) to "${
|
|
11676
|
+
console.log(`Pushing ${files.length} file(s) to "${workspaceName || workspaceId}"`);
|
|
11622
11677
|
for (let i = 0;i < files.length; i += BATCH) {
|
|
11623
11678
|
const batch = files.slice(i, i + BATCH);
|
|
11624
11679
|
const { uploads } = await api("/api/assets/presign", {
|
|
@@ -11722,31 +11777,74 @@ Try manually: ${cmd} ${args.join(" ")}`);
|
|
|
11722
11777
|
}
|
|
11723
11778
|
|
|
11724
11779
|
// src/workspace.ts
|
|
11725
|
-
function
|
|
11780
|
+
async function fetchWorkspaces2(appUrl, userKey) {
|
|
11781
|
+
try {
|
|
11782
|
+
const res = await fetch(`${appUrl}/api/cli/workspaces`, {
|
|
11783
|
+
headers: { Authorization: `Bearer ${userKey}` }
|
|
11784
|
+
});
|
|
11785
|
+
if (!res.ok)
|
|
11786
|
+
return null;
|
|
11787
|
+
const json = await res.json();
|
|
11788
|
+
return json.workspaces;
|
|
11789
|
+
} catch {
|
|
11790
|
+
return null;
|
|
11791
|
+
}
|
|
11792
|
+
}
|
|
11793
|
+
async function workspaceList() {
|
|
11726
11794
|
const config = readConfig();
|
|
11727
|
-
|
|
11728
|
-
if (!config || entries.length === 0) {
|
|
11795
|
+
if (!config)
|
|
11729
11796
|
throw new Error("Not logged in. Run `zalify login` first.");
|
|
11797
|
+
if (config.version === 3) {
|
|
11798
|
+
const live = await fetchWorkspaces2(config.appUrl, config.userKey);
|
|
11799
|
+
const list = live ?? config.workspacesCache;
|
|
11800
|
+
if (live) {
|
|
11801
|
+
config.workspacesCache = live;
|
|
11802
|
+
writeConfig(config);
|
|
11803
|
+
} else {
|
|
11804
|
+
console.log(`! could not fetch live list — showing cached workspaces
|
|
11805
|
+
`);
|
|
11806
|
+
}
|
|
11807
|
+
for (const w of list) {
|
|
11808
|
+
const marker = w.id === config.active?.id ? "* " : " ";
|
|
11809
|
+
console.log(`${marker}${w.slug.padEnd(36)} ${w.name} [${w.id}]`);
|
|
11810
|
+
}
|
|
11811
|
+
return;
|
|
11730
11812
|
}
|
|
11813
|
+
const entries = Object.entries(config.workspaces);
|
|
11814
|
+
if (entries.length === 0)
|
|
11815
|
+
throw new Error("Not logged in. Run `zalify login` first.");
|
|
11731
11816
|
for (const [id, w] of entries) {
|
|
11732
11817
|
const marker = id === config.activeWorkspaceId ? "* " : " ";
|
|
11733
11818
|
console.log(`${marker}${w.slug.padEnd(36)} ${w.name} [${id}]`);
|
|
11734
11819
|
}
|
|
11735
11820
|
}
|
|
11736
|
-
function workspaceSet(slugOrId) {
|
|
11821
|
+
async function workspaceSet(slugOrId) {
|
|
11737
11822
|
const config = readConfig();
|
|
11738
|
-
if (!config
|
|
11823
|
+
if (!config)
|
|
11739
11824
|
throw new Error("Not logged in. Run `zalify login` first.");
|
|
11825
|
+
if (config.version === 3) {
|
|
11826
|
+
const live = await fetchWorkspaces2(config.appUrl, config.userKey);
|
|
11827
|
+
const list = live ?? config.workspacesCache;
|
|
11828
|
+
if (live)
|
|
11829
|
+
config.workspacesCache = live;
|
|
11830
|
+
const match = list.find((w) => w.id === slugOrId || w.slug === slugOrId || w.name === slugOrId);
|
|
11831
|
+
if (!match) {
|
|
11832
|
+
throw new Error(`No workspace matches "${slugOrId}". Known: ${list.map((w) => w.slug).join(", ")}.`);
|
|
11833
|
+
}
|
|
11834
|
+
config.active = match;
|
|
11835
|
+
writeConfig(config);
|
|
11836
|
+
console.log(`✓ Active workspace: ${match.name} (${match.slug})`);
|
|
11837
|
+
return;
|
|
11740
11838
|
}
|
|
11741
|
-
const
|
|
11742
|
-
if (!
|
|
11839
|
+
const found = Object.entries(config.workspaces).find(([id, w]) => id === slugOrId || w.slug === slugOrId || w.name === slugOrId);
|
|
11840
|
+
if (!found) {
|
|
11743
11841
|
const known = Object.values(config.workspaces).map((w) => w.slug).join(", ");
|
|
11744
11842
|
throw new Error(`No authorized workspace matches "${slugOrId}". Known: ${known}.
|
|
11745
11843
|
` + "Run `zalify login` to authorize more workspaces.");
|
|
11746
11844
|
}
|
|
11747
|
-
config.activeWorkspaceId =
|
|
11845
|
+
config.activeWorkspaceId = found[0];
|
|
11748
11846
|
writeConfig(config);
|
|
11749
|
-
console.log(`✓ Active workspace: ${
|
|
11847
|
+
console.log(`✓ Active workspace: ${found[1].name} (${found[1].slug})`);
|
|
11750
11848
|
}
|
|
11751
11849
|
|
|
11752
11850
|
// src/cli.ts
|
|
@@ -11790,8 +11888,12 @@ program2.command("whoami").description("Verify the stored key and show workspace
|
|
|
11790
11888
|
await whoami();
|
|
11791
11889
|
});
|
|
11792
11890
|
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(() =>
|
|
11794
|
-
|
|
11891
|
+
workspace.command("list", { isDefault: true }).description("List authorized workspaces (* = active)").action(async () => {
|
|
11892
|
+
await workspaceList();
|
|
11893
|
+
});
|
|
11894
|
+
workspace.command("set <slug-or-id>").description("Switch the active workspace (local, no re-login)").action(async (slugOrId) => {
|
|
11895
|
+
await workspaceSet(slugOrId);
|
|
11896
|
+
});
|
|
11795
11897
|
var assets = program2.command("assets").description("Sync images with the Zalify asset library");
|
|
11796
11898
|
assets.command("push <store-dir>").description("Upload <store-dir>/images/*.png (sha256 dedup, writes assets.json)").action(async (storeDir) => {
|
|
11797
11899
|
await assetsPush(storeDir);
|