@hypersocial/decks-cli 0.1.8 → 0.1.9
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/package.json +1 -1
- package/src/commands.mjs +30 -3
- package/src/ops.mjs +32 -0
- package/src/runtime.mjs +2 -1
package/package.json
CHANGED
package/src/commands.mjs
CHANGED
|
@@ -227,6 +227,8 @@ commands:
|
|
|
227
227
|
skill install [--agent codex,claude-code] [--all-agents]
|
|
228
228
|
skills install [--agent codex,claude-code] [--all-agents]
|
|
229
229
|
activity [--deck <slug>] [--limit <n>] [--json]
|
|
230
|
+
gen-image "<prompt>" [--transparent] [--image <url>] [--size <s>] [--quality <q>] [--count <n>] [--slug <deck>] [--download <file>]
|
|
231
|
+
image-job <jobId> [--json]
|
|
230
232
|
|
|
231
233
|
global:
|
|
232
234
|
--json
|
|
@@ -244,7 +246,7 @@ async function genImage(pos, opts) {
|
|
|
244
246
|
'usage: gen-image "<prompt>" [--image <url>] [--transparent] [--size <s>] [--quality <q>] [--count <n>] [--format png|jpeg|webp] [--slug <deck>] [--name <name>] [--download <file>]',
|
|
245
247
|
);
|
|
246
248
|
}
|
|
247
|
-
|
|
249
|
+
let out = await ops.generateImage(opts, {
|
|
248
250
|
prompt,
|
|
249
251
|
image: opts.image,
|
|
250
252
|
transparent: opts.transparent,
|
|
@@ -255,6 +257,16 @@ async function genImage(pos, opts) {
|
|
|
255
257
|
slug: opts.slug,
|
|
256
258
|
name: opts.name,
|
|
257
259
|
});
|
|
260
|
+
// Slow requests (high/auto quality, transparent, count>1) come back as a job id;
|
|
261
|
+
// poll it so gen-image stays one blocking command. Progress prints to stderr so
|
|
262
|
+
// it doesn't pollute the URL output (or --json).
|
|
263
|
+
if (out.jobId) {
|
|
264
|
+
process.stderr.write(`queued ${out.count} image(s) — job ${out.jobId}\n`);
|
|
265
|
+
out = await ops.pollImageJob(opts, out.jobId, {
|
|
266
|
+
onProgress: (job) => process.stderr.write(` ${job.progress} (${job.status})…\r`),
|
|
267
|
+
});
|
|
268
|
+
process.stderr.write("\n");
|
|
269
|
+
}
|
|
258
270
|
const images = out.images || [];
|
|
259
271
|
if (opts.download && images.length) {
|
|
260
272
|
for (let i = 0; i < images.length; i++) {
|
|
@@ -267,9 +279,23 @@ async function genImage(pos, opts) {
|
|
|
267
279
|
if (opts.json) {
|
|
268
280
|
console.log(JSON.stringify(out, null, 2));
|
|
269
281
|
} else {
|
|
270
|
-
for (const im of images) console.log(im.url);
|
|
271
|
-
if (opts.download) console.log(
|
|
282
|
+
for (const im of images) if (im.url) console.log(im.url);
|
|
283
|
+
if (opts.download) console.log(`\nSaved ${images.length} file(s) to ${opts.download}`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async function imageJob(pos, opts) {
|
|
288
|
+
const [jobId] = pos;
|
|
289
|
+
if (!jobId) throw new Error("usage: image-job <jobId>");
|
|
290
|
+
const job = await ops.checkImageJob(opts, jobId);
|
|
291
|
+
if (opts.json) {
|
|
292
|
+
console.log(JSON.stringify(job, null, 2));
|
|
293
|
+
return;
|
|
272
294
|
}
|
|
295
|
+
console.log(`status: ${job.status}`);
|
|
296
|
+
console.log(`progress: ${job.progress}`);
|
|
297
|
+
if (job.error) console.log(`error: ${job.error}`);
|
|
298
|
+
for (const im of job.results || []) if (im.url) console.log(im.url);
|
|
273
299
|
}
|
|
274
300
|
|
|
275
301
|
export async function runCli(argv = process.argv.slice(2)) {
|
|
@@ -307,6 +333,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
307
333
|
"skills install": () => installHyperdecksSkill(opts, rest),
|
|
308
334
|
activity: () => activity(pos.slice(1), opts),
|
|
309
335
|
"gen-image": () => genImage(pos.slice(1), opts),
|
|
336
|
+
"image-job": () => imageJob(pos.slice(1), opts),
|
|
310
337
|
};
|
|
311
338
|
|
|
312
339
|
const handler = handlers[`${group} ${cmd}`] || handlers[group];
|
package/src/ops.mjs
CHANGED
|
@@ -51,9 +51,41 @@ export async function generateImage(opts, fields = {}) {
|
|
|
51
51
|
if (fields.quality) args.quality = String(fields.quality);
|
|
52
52
|
if (fields.count != null && fields.count !== "") args.count = Number(fields.count);
|
|
53
53
|
if (fields.format) args.format = String(fields.format);
|
|
54
|
+
// Returns { images } for fast requests, or { jobId, status, count } for slow ones
|
|
55
|
+
// (high/auto quality, transparent, count>1). The MCP tool returns this as-is so it
|
|
56
|
+
// never blocks; the CLI polls with pollImageJob to stay one-command (see commands).
|
|
54
57
|
return (await client(opts)).action(api.imagegen.generateImage, args);
|
|
55
58
|
}
|
|
56
59
|
|
|
60
|
+
// Read one async image job's status/results. Shared by the MCP check_image_job tool
|
|
61
|
+
// and the CLI poller.
|
|
62
|
+
export async function checkImageJob(opts, jobId) {
|
|
63
|
+
if (!jobId) throw new Error("jobId is required");
|
|
64
|
+
return (await client(opts)).query(api.imagegen.checkImageJob, { jobId: String(jobId) });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Poll an async image job until it finishes. Returns { images, slug } on success
|
|
68
|
+
// (results are the same shape as the inline { images }); throws on job error or
|
|
69
|
+
// timeout. `onProgress(job)` is called each tick. Used by the CLI so gen-image stays
|
|
70
|
+
// a single blocking command even when it routes to a background job.
|
|
71
|
+
export async function pollImageJob(opts, jobId, { intervalMs = 3000, timeoutMs = 900_000, onProgress } = {}) {
|
|
72
|
+
const c = await client(opts);
|
|
73
|
+
const start = Date.now();
|
|
74
|
+
for (;;) {
|
|
75
|
+
const job = await c.query(api.imagegen.checkImageJob, { jobId: String(jobId) });
|
|
76
|
+
if (job.status === "done") return { images: job.results || [], slug: job.slug };
|
|
77
|
+
if (job.status === "error") {
|
|
78
|
+
// A batch isn't atomic: earlier images may have been generated + saved before
|
|
79
|
+
// the failure. Surface that so the user knows assets/spend exist despite "error".
|
|
80
|
+
const ok = (job.results || []).filter((r) => r.url).length;
|
|
81
|
+
throw new Error(`${job.error || "image job failed"}${ok ? ` (${ok} image(s) completed before the failure)` : ""}`);
|
|
82
|
+
}
|
|
83
|
+
if (Date.now() - start > timeoutMs) throw new Error(`image job ${jobId} timed out after ${Math.round(timeoutMs / 1000)}s`);
|
|
84
|
+
if (onProgress) onProgress(job);
|
|
85
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
57
89
|
export async function listShares(opts, slug) {
|
|
58
90
|
if (!slug) throw new Error("slug is required");
|
|
59
91
|
const rows = await (await client(opts)).query(api.shares.listForDeck, { deckSlug: slug });
|
package/src/runtime.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import { fileURLToPath } from "node:url";
|
|
|
8
8
|
import { ConvexHttpClient } from "convex/browser";
|
|
9
9
|
import { makeFunctionReference } from "convex/server";
|
|
10
10
|
|
|
11
|
-
export const VERSION = "0.1.
|
|
11
|
+
export const VERSION = "0.1.9";
|
|
12
12
|
const UPLOAD_TIMEOUT_MS = 125_000;
|
|
13
13
|
|
|
14
14
|
export const PROD = {
|
|
@@ -47,6 +47,7 @@ export const api = {
|
|
|
47
47
|
},
|
|
48
48
|
imagegen: {
|
|
49
49
|
generateImage: makeFunctionReference("imagegen:generateImage"),
|
|
50
|
+
checkImageJob: makeFunctionReference("imagegen:checkImageJob"),
|
|
50
51
|
},
|
|
51
52
|
};
|
|
52
53
|
|