@ai-agent-tools/picgen 0.1.0-alpha.2 → 0.1.0-alpha.4
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/README.md +14 -0
- package/dist/cli.js +168 -21
- package/docs/release-alpha.md +19 -1
- package/package.json +1 -1
- package/skills/picgen/SKILL.md +2 -2
package/README.md
CHANGED
|
@@ -91,6 +91,14 @@ OPENAI_API_KEY=...
|
|
|
91
91
|
GEMINI_API_KEY=...
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
+
For non-technical users, `picgen setup` can save API keys for you in:
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
~/.picgen/.env
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
PicGen loads this managed env file automatically. Shell environment variables take priority, and a project `.env` can override the managed file for local testing.
|
|
101
|
+
|
|
94
102
|
You can start from the included example:
|
|
95
103
|
|
|
96
104
|
```bash
|
|
@@ -101,6 +109,12 @@ Provider `base_url` values should be host-only. Do not include `/v1` or `/v1beta
|
|
|
101
109
|
|
|
102
110
|
Providers may optionally set `test_model` in `~/.picgen/config.yaml` when health checks should use a lightweight model instead of the first generation model.
|
|
103
111
|
|
|
112
|
+
Generation requests use adaptive provider timeouts: fast draft requests allow 120s, balanced requests allow 180s, and high quality or large requests allow 300s. If a third-party channel is slower, override it with `PICGEN_PROVIDER_TIMEOUT_MS`:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
PICGEN_PROVIDER_TIMEOUT_MS=450000 picgen create --yes --preset poster "一张产品发布会主视觉"
|
|
116
|
+
```
|
|
117
|
+
|
|
104
118
|
Providers expose capabilities such as `text-to-image` and `reference-image`. Old configs that omit capabilities are upgraded in memory from the provider protocol: Gemini supports both text and reference-image generation, while OpenAI-compatible `/v1/images/generations` supports text-to-image only.
|
|
105
119
|
|
|
106
120
|
Generated image data and provider-only fields such as base64 image payloads and Gemini thought signatures are redacted from metadata. PicGen keeps the generated assets as local image files and keeps stdout compact for agent workflows.
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import "dotenv/config";
|
|
5
4
|
import { Command } from "commander";
|
|
6
5
|
|
|
7
6
|
// src/commands/create.ts
|
|
@@ -459,6 +458,53 @@ async function ensureConfig() {
|
|
|
459
458
|
// src/providers/gemini.ts
|
|
460
459
|
import { readFile as readFile2 } from "fs/promises";
|
|
461
460
|
|
|
461
|
+
// src/providers/timeout.ts
|
|
462
|
+
var FAST_PROVIDER_TIMEOUT_MS = 12e4;
|
|
463
|
+
var DEFAULT_PROVIDER_TIMEOUT_MS = 18e4;
|
|
464
|
+
var SLOW_PROVIDER_TIMEOUT_MS = 3e5;
|
|
465
|
+
function resolveProviderTimeoutMs(plan) {
|
|
466
|
+
const override = parseTimeoutOverride(process.env.PICGEN_PROVIDER_TIMEOUT_MS);
|
|
467
|
+
if (override !== void 0) return override;
|
|
468
|
+
if (plan.presetName === "fast-draft" || plan.modeName === "fast" || plan.preset.quality === "low") {
|
|
469
|
+
return FAST_PROVIDER_TIMEOUT_MS;
|
|
470
|
+
}
|
|
471
|
+
if (plan.modeName === "premium" || plan.preset.size === "large" || plan.preset.quality === "high") {
|
|
472
|
+
return SLOW_PROVIDER_TIMEOUT_MS;
|
|
473
|
+
}
|
|
474
|
+
return DEFAULT_PROVIDER_TIMEOUT_MS;
|
|
475
|
+
}
|
|
476
|
+
async function fetchWithProviderTimeout(input3, init, timeoutMs) {
|
|
477
|
+
const controller = new AbortController();
|
|
478
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
479
|
+
try {
|
|
480
|
+
return await fetch(input3, {
|
|
481
|
+
...init,
|
|
482
|
+
signal: controller.signal
|
|
483
|
+
});
|
|
484
|
+
} catch (error) {
|
|
485
|
+
if (isAbortError(error)) {
|
|
486
|
+
throw new Error(formatProviderTimeoutError(timeoutMs));
|
|
487
|
+
}
|
|
488
|
+
throw error;
|
|
489
|
+
} finally {
|
|
490
|
+
clearTimeout(timeout);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
function formatProviderTimeoutError(timeoutMs) {
|
|
494
|
+
return `Provider request timed out after ${Math.ceil(
|
|
495
|
+
timeoutMs / 1e3
|
|
496
|
+
)}s. The provider may still be processing or temporarily unavailable. Try again, use a faster preset, or increase PICGEN_PROVIDER_TIMEOUT_MS.`;
|
|
497
|
+
}
|
|
498
|
+
function parseTimeoutOverride(value) {
|
|
499
|
+
if (!value) return void 0;
|
|
500
|
+
const timeoutMs = Number(value);
|
|
501
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs <= 0) return void 0;
|
|
502
|
+
return timeoutMs;
|
|
503
|
+
}
|
|
504
|
+
function isAbortError(error) {
|
|
505
|
+
return error instanceof Error && error.name === "AbortError";
|
|
506
|
+
}
|
|
507
|
+
|
|
462
508
|
// src/providers/urls.ts
|
|
463
509
|
function normalizeProviderBaseUrl(baseUrl) {
|
|
464
510
|
const url = new URL(baseUrl);
|
|
@@ -495,15 +541,20 @@ var GeminiAdapter = class {
|
|
|
495
541
|
const providerImages = [];
|
|
496
542
|
const requestCount = Math.max(1, plan.preset.n);
|
|
497
543
|
const referenceParts = await readReferenceImageParts(plan);
|
|
544
|
+
const timeoutMs = resolveProviderTimeoutMs(plan);
|
|
498
545
|
for (let index = 0; index < requestCount; index += 1) {
|
|
499
|
-
const response = await
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
"
|
|
503
|
-
|
|
546
|
+
const response = await fetchWithProviderTimeout(
|
|
547
|
+
buildGeminiGenerateContentUrl(plan.provider.base_url, plan.model),
|
|
548
|
+
{
|
|
549
|
+
method: "POST",
|
|
550
|
+
headers: {
|
|
551
|
+
"x-goog-api-key": apiKey,
|
|
552
|
+
"Content-Type": "application/json"
|
|
553
|
+
},
|
|
554
|
+
body: JSON.stringify(buildGeminiGenerateContentRequest(plan, referenceParts))
|
|
504
555
|
},
|
|
505
|
-
|
|
506
|
-
|
|
556
|
+
timeoutMs
|
|
557
|
+
);
|
|
507
558
|
const raw = await readJsonResponse(response);
|
|
508
559
|
if (!response.ok) {
|
|
509
560
|
throw new Error(formatGeminiError(response.status, response.statusText, raw));
|
|
@@ -624,14 +675,18 @@ var OpenAIImagesAdapter = class {
|
|
|
624
675
|
if (!apiKey) {
|
|
625
676
|
throw new Error(`Missing API key environment variable: ${plan.provider.api_key_env}`);
|
|
626
677
|
}
|
|
627
|
-
const response = await
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
678
|
+
const response = await fetchWithProviderTimeout(
|
|
679
|
+
buildOpenAIImagesUrl(plan.provider.base_url),
|
|
680
|
+
{
|
|
681
|
+
method: "POST",
|
|
682
|
+
headers: {
|
|
683
|
+
Authorization: `Bearer ${apiKey}`,
|
|
684
|
+
"Content-Type": "application/json"
|
|
685
|
+
},
|
|
686
|
+
body: JSON.stringify(buildOpenAIImagesRequest(plan))
|
|
632
687
|
},
|
|
633
|
-
|
|
634
|
-
|
|
688
|
+
resolveProviderTimeoutMs(plan)
|
|
689
|
+
);
|
|
635
690
|
const raw = await readJsonResponse2(response);
|
|
636
691
|
if (!response.ok) {
|
|
637
692
|
throw new Error(formatOpenAIImagesError(response.status, response.statusText, raw));
|
|
@@ -999,7 +1054,7 @@ import { homedir as homedir2 } from "os";
|
|
|
999
1054
|
|
|
1000
1055
|
// src/version.ts
|
|
1001
1056
|
var PACKAGE_NAME = "@ai-agent-tools/picgen";
|
|
1002
|
-
var VERSION = "0.1.0-alpha.
|
|
1057
|
+
var VERSION = "0.1.0-alpha.4";
|
|
1003
1058
|
|
|
1004
1059
|
// src/commands/update.ts
|
|
1005
1060
|
var UPDATE_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -1469,7 +1524,7 @@ function formatQuickstart() {
|
|
|
1469
1524
|
" npm install -g @ai-agent-tools/picgen",
|
|
1470
1525
|
"",
|
|
1471
1526
|
"Configure:",
|
|
1472
|
-
" picgen setup",
|
|
1527
|
+
" picgen setup # can save provider API keys for you",
|
|
1473
1528
|
" picgen doctor --json",
|
|
1474
1529
|
"",
|
|
1475
1530
|
"Preview before spending quota:",
|
|
@@ -1482,10 +1537,11 @@ function formatQuickstart() {
|
|
|
1482
1537
|
' picgen create --dry-run --reference ./reference.png "\u57FA\u4E8E\u53C2\u8003\u56FE\u751F\u6210\u4E00\u5F20\u54C1\u724C\u6D77\u62A5"',
|
|
1483
1538
|
"",
|
|
1484
1539
|
"Agent prompt:",
|
|
1485
|
-
" \u8BF7\u5B89\u88C5\u5E76\u4F53\u9A8C @ai-agent-tools/picgen\uFF1A\u5168\u5C40\u5B89\u88C5 npm install -g @ai-agent-tools/picgen\uFF0C\u8FD0\u884C picgen setup \u914D\u7F6E\uFF0C\u7136\u540E\u5148 dry-run \u9884\u89C8\uFF0C\u518D\u786E\u8BA4\u751F\u6210\u4E00\u5F20\u6D4B\u8BD5\u56FE\u3002\u5982\u679C\u6211\u8981\u7528\u53C2\u8003\u56FE\uFF0C\u8BF7\u4F7F\u7528 --reference <\u56FE\u7247\u8DEF\u5F84>\u3002",
|
|
1540
|
+
" \u8BF7\u5B89\u88C5\u5E76\u4F53\u9A8C @ai-agent-tools/picgen\uFF1A\u5168\u5C40\u5B89\u88C5 npm install -g @ai-agent-tools/picgen\uFF0C\u8FD0\u884C picgen setup \u5F15\u5BFC\u6211\u914D\u7F6E provider \u548C API key\uFF0C\u7136\u540E\u5148 dry-run \u9884\u89C8\uFF0C\u518D\u786E\u8BA4\u751F\u6210\u4E00\u5F20\u6D4B\u8BD5\u56FE\u3002\u5982\u679C\u6211\u8981\u7528\u53C2\u8003\u56FE\uFF0C\u8BF7\u4F7F\u7528 --reference <\u56FE\u7247\u8DEF\u5F84>\u3002",
|
|
1486
1541
|
"",
|
|
1487
1542
|
"Notes:",
|
|
1488
1543
|
" - Provider host URLs should not include /v1 or /v1beta.",
|
|
1544
|
+
" - picgen setup can store API keys in ~/.picgen/.env.",
|
|
1489
1545
|
" - Agent workflows should dry-run before real generation.",
|
|
1490
1546
|
" - Generated images are saved locally; do not paste base64 into chat.",
|
|
1491
1547
|
" - First-user rollout checklist: docs/release-alpha.md"
|
|
@@ -1493,7 +1549,62 @@ function formatQuickstart() {
|
|
|
1493
1549
|
}
|
|
1494
1550
|
|
|
1495
1551
|
// src/commands/setup.ts
|
|
1496
|
-
import { confirm as confirm2, input as input2, select as select2 } from "@inquirer/prompts";
|
|
1552
|
+
import { confirm as confirm2, input as input2, password, select as select2 } from "@inquirer/prompts";
|
|
1553
|
+
|
|
1554
|
+
// src/config/env.ts
|
|
1555
|
+
import { chmod, mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
1556
|
+
import { existsSync } from "fs";
|
|
1557
|
+
import { dirname as dirname3, join as join5, resolve as resolve2 } from "path";
|
|
1558
|
+
import { homedir as homedir3 } from "os";
|
|
1559
|
+
import { parse } from "dotenv";
|
|
1560
|
+
function getManagedEnvPath() {
|
|
1561
|
+
return process.env.PICGEN_ENV_PATH ?? join5(homedir3(), ".picgen", ".env");
|
|
1562
|
+
}
|
|
1563
|
+
async function loadPicgenEnv() {
|
|
1564
|
+
const shellEnv = new Set(Object.keys(process.env));
|
|
1565
|
+
await loadEnvFile(getManagedEnvPath(), shellEnv, false);
|
|
1566
|
+
await loadEnvFile(resolve2(process.cwd(), ".env"), shellEnv, true);
|
|
1567
|
+
}
|
|
1568
|
+
async function saveManagedEnvVar(name, value) {
|
|
1569
|
+
const path = getManagedEnvPath();
|
|
1570
|
+
const current = await readManagedEnvFile(path);
|
|
1571
|
+
const next = {
|
|
1572
|
+
...current,
|
|
1573
|
+
[name]: value
|
|
1574
|
+
};
|
|
1575
|
+
await mkdir4(dirname3(path), { recursive: true });
|
|
1576
|
+
await writeFile4(path, stringifyEnv(next), "utf8");
|
|
1577
|
+
await chmod(path, 384);
|
|
1578
|
+
process.env[name] = value;
|
|
1579
|
+
return path;
|
|
1580
|
+
}
|
|
1581
|
+
async function loadEnvFile(path, shellEnv, overrideManagedValues) {
|
|
1582
|
+
if (!existsSync(path)) return;
|
|
1583
|
+
const parsed = parse(await readFile4(path, "utf8"));
|
|
1584
|
+
for (const [name, value] of Object.entries(parsed)) {
|
|
1585
|
+
if (shellEnv.has(name)) continue;
|
|
1586
|
+
if (!overrideManagedValues && process.env[name] !== void 0) continue;
|
|
1587
|
+
process.env[name] = value;
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
async function readManagedEnvFile(path) {
|
|
1591
|
+
try {
|
|
1592
|
+
return parse(await readFile4(path, "utf8"));
|
|
1593
|
+
} catch (error) {
|
|
1594
|
+
if (error.code === "ENOENT") return {};
|
|
1595
|
+
throw error;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
function stringifyEnv(values) {
|
|
1599
|
+
return `${Object.entries(values).map(([name, value]) => `${name}=${quoteEnvValue(value)}`).join("\n")}
|
|
1600
|
+
`;
|
|
1601
|
+
}
|
|
1602
|
+
function quoteEnvValue(value) {
|
|
1603
|
+
if (/^[A-Za-z0-9_./:@+-]+$/.test(value)) return value;
|
|
1604
|
+
return JSON.stringify(value);
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
// src/commands/setup.ts
|
|
1497
1608
|
async function runSetup() {
|
|
1498
1609
|
await ensureConfig();
|
|
1499
1610
|
console.log(`PicGen config: ${getConfigPath()}`);
|
|
@@ -1508,6 +1619,7 @@ async function runSetup() {
|
|
|
1508
1619
|
{ name: "Quick add a common provider/channel", value: "quick-add" },
|
|
1509
1620
|
{ name: "Choose default provider/channel", value: "provider" },
|
|
1510
1621
|
{ name: "Choose generation preference", value: "mode" },
|
|
1622
|
+
{ name: "Configure API key", value: "key" },
|
|
1511
1623
|
{ name: "Test a provider", value: "test" },
|
|
1512
1624
|
{ name: "Advanced: add a custom provider/channel", value: "add" },
|
|
1513
1625
|
{ name: "Finish setup", value: "done" }
|
|
@@ -1519,6 +1631,8 @@ async function runSetup() {
|
|
|
1519
1631
|
await chooseDefaultProvider();
|
|
1520
1632
|
} else if (action === "mode") {
|
|
1521
1633
|
await chooseDefaultMode();
|
|
1634
|
+
} else if (action === "key") {
|
|
1635
|
+
await chooseProviderKeyToConfigure();
|
|
1522
1636
|
} else if (action === "test") {
|
|
1523
1637
|
await chooseProviderToTest();
|
|
1524
1638
|
} else if (action === "add") {
|
|
@@ -1537,7 +1651,7 @@ async function printSetupSummary() {
|
|
|
1537
1651
|
for (const [providerName, provider2] of Object.entries(config.providers)) {
|
|
1538
1652
|
const preference = providerName === config.routing.default_provider ? "default" : config.routing.fallback_providers.includes(providerName) ? "fallback" : "manual";
|
|
1539
1653
|
console.log(
|
|
1540
|
-
`- ${providerName}: ${provider2.enabled ? "enabled" : "disabled"}, ${preference}, ${providerLabel(provider2)}, capabilities=${provider2.capabilities.join(",")}`
|
|
1654
|
+
`- ${providerName}: ${provider2.enabled ? "enabled" : "disabled"}, ${preference}, ${providerLabel(provider2)}, key=${provider2.api_key_env}${process.env[provider2.api_key_env] ? " set" : " missing"}, capabilities=${provider2.capabilities.join(",")}`
|
|
1541
1655
|
);
|
|
1542
1656
|
}
|
|
1543
1657
|
}
|
|
@@ -1585,6 +1699,18 @@ async function chooseProviderToTest() {
|
|
|
1585
1699
|
if (result.model) console.log(`Model: ${result.model}`);
|
|
1586
1700
|
if (result.http_status) console.log(`HTTP status: ${result.http_status}`);
|
|
1587
1701
|
}
|
|
1702
|
+
async function chooseProviderKeyToConfigure() {
|
|
1703
|
+
const config = await loadConfig();
|
|
1704
|
+
const name = await select2({
|
|
1705
|
+
message: "Choose the provider key to configure",
|
|
1706
|
+
default: config.routing.default_provider,
|
|
1707
|
+
choices: Object.entries(config.providers).map(([providerName, provider2]) => ({
|
|
1708
|
+
name: `${providerName} (${provider2.api_key_env}${process.env[provider2.api_key_env] ? ", currently set" : ", missing"})`,
|
|
1709
|
+
value: providerName
|
|
1710
|
+
}))
|
|
1711
|
+
});
|
|
1712
|
+
await configureProviderApiKey(config.providers[name]);
|
|
1713
|
+
}
|
|
1588
1714
|
async function quickAddProvider() {
|
|
1589
1715
|
const config = await loadConfig();
|
|
1590
1716
|
const template = await select2({
|
|
@@ -1644,7 +1770,27 @@ async function quickAddProvider() {
|
|
|
1644
1770
|
}
|
|
1645
1771
|
await saveConfig(config);
|
|
1646
1772
|
console.log(`Added provider: ${name}`);
|
|
1647
|
-
|
|
1773
|
+
await configureProviderApiKey(provider2);
|
|
1774
|
+
}
|
|
1775
|
+
async function configureProviderApiKey(provider2) {
|
|
1776
|
+
if (process.env[provider2.api_key_env]) {
|
|
1777
|
+
const replace = await confirm2({
|
|
1778
|
+
message: `${provider2.api_key_env} is already available. Replace the saved PicGen key?`,
|
|
1779
|
+
default: false
|
|
1780
|
+
});
|
|
1781
|
+
if (!replace) return;
|
|
1782
|
+
}
|
|
1783
|
+
const value = await password({
|
|
1784
|
+
message: `Paste API key for ${provider2.api_key_env} (leave empty to skip)`,
|
|
1785
|
+
mask: "*"
|
|
1786
|
+
});
|
|
1787
|
+
if (!value.trim()) {
|
|
1788
|
+
console.log(`Skipped API key. You can configure it later with picgen setup.`);
|
|
1789
|
+
return;
|
|
1790
|
+
}
|
|
1791
|
+
const path = await saveManagedEnvVar(provider2.api_key_env, value.trim());
|
|
1792
|
+
console.log(`Saved ${provider2.api_key_env} to ${path}`);
|
|
1793
|
+
console.log(`PicGen loads this file automatically. Advanced users can override it with shell env vars or a project .env.`);
|
|
1648
1794
|
}
|
|
1649
1795
|
function quickProviderDefaults(template) {
|
|
1650
1796
|
switch (template) {
|
|
@@ -1709,6 +1855,7 @@ function modeLabel(modeName) {
|
|
|
1709
1855
|
}
|
|
1710
1856
|
|
|
1711
1857
|
// src/cli.ts
|
|
1858
|
+
await loadPicgenEnv();
|
|
1712
1859
|
var program = new Command();
|
|
1713
1860
|
program.name("picgen").description("Lightweight image generation connector for AI agents.").version(VERSION);
|
|
1714
1861
|
program.command("setup").description("Run the interactive PicGen setup wizard.").action(runSetup);
|
package/docs/release-alpha.md
CHANGED
|
@@ -40,7 +40,17 @@ https://generativelanguage.googleapis.com
|
|
|
40
40
|
|
|
41
41
|
Do not include `/v1` or `/v1beta`.
|
|
42
42
|
|
|
43
|
-
4.
|
|
43
|
+
4. Configure API keys:
|
|
44
|
+
|
|
45
|
+
For non-technical users, prefer `picgen setup`. It can save provider API keys in PicGen's managed env file:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
~/.picgen/.env
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
PicGen loads this file automatically.
|
|
52
|
+
|
|
53
|
+
Advanced users can still use shell environment variables or a local project `.env`:
|
|
44
54
|
|
|
45
55
|
```bash
|
|
46
56
|
cp .env.example .env
|
|
@@ -143,6 +153,14 @@ picgen provider test <provider-name> --json
|
|
|
143
153
|
|
|
144
154
|
Check `base_url`, API key, model name, and provider availability.
|
|
145
155
|
|
|
156
|
+
`Provider request timed out`
|
|
157
|
+
|
|
158
|
+
High quality, large, or slow third-party image channels can take longer. Try again, use a faster preset, or raise the request timeout:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
PICGEN_PROVIDER_TIMEOUT_MS=450000 picgen create --yes --preset poster "<prompt>"
|
|
162
|
+
```
|
|
163
|
+
|
|
146
164
|
`No enabled provider can satisfy...`
|
|
147
165
|
|
|
148
166
|
Run `picgen provider list`, enable a provider, add a fallback provider, or adjust the selected mode/model.
|
package/package.json
CHANGED
package/skills/picgen/SKILL.md
CHANGED
|
@@ -20,7 +20,7 @@ Never silently spend user quota. Do not send full conversation context to provid
|
|
|
20
20
|
## Workflow
|
|
21
21
|
|
|
22
22
|
1. Run `picgen doctor --json` to check configuration.
|
|
23
|
-
2. If no usable provider is configured, guide the user to run `picgen setup`.
|
|
23
|
+
2. If no usable provider is configured, guide the user to run `picgen setup`. The setup wizard can configure providers and save API keys for non-technical users.
|
|
24
24
|
3. Choose a preset from the user's intent, such as `poster`, `product-shot`, or `social-cover`.
|
|
25
25
|
4. Run `picgen create --dry-run --preset <preset> "<prompt>"`.
|
|
26
26
|
5. Present the dry-run as a user-facing generation preview. Do not expose `dry-run` as a technical term unless useful.
|
|
@@ -75,7 +75,7 @@ PicGen redacts generated image payloads and Gemini thought signatures from metad
|
|
|
75
75
|
|
|
76
76
|
If `doctor` reports no usable provider, ask the user to run `picgen setup`.
|
|
77
77
|
|
|
78
|
-
If an API key is missing,
|
|
78
|
+
If an API key is missing, guide the user to run `picgen setup` and choose `Configure API key`. Name the required environment variable only when useful for debugging.
|
|
79
79
|
|
|
80
80
|
If a provider is disabled, suggest enabling it or using a one-off provider override.
|
|
81
81
|
|