@ai-agent-tools/picgen 0.1.0-alpha.3 → 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 +8 -0
- package/dist/cli.js +98 -7
- package/docs/release-alpha.md +11 -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
|
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
|
|
@@ -1055,7 +1054,7 @@ import { homedir as homedir2 } from "os";
|
|
|
1055
1054
|
|
|
1056
1055
|
// src/version.ts
|
|
1057
1056
|
var PACKAGE_NAME = "@ai-agent-tools/picgen";
|
|
1058
|
-
var VERSION = "0.1.0-alpha.
|
|
1057
|
+
var VERSION = "0.1.0-alpha.4";
|
|
1059
1058
|
|
|
1060
1059
|
// src/commands/update.ts
|
|
1061
1060
|
var UPDATE_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -1525,7 +1524,7 @@ function formatQuickstart() {
|
|
|
1525
1524
|
" npm install -g @ai-agent-tools/picgen",
|
|
1526
1525
|
"",
|
|
1527
1526
|
"Configure:",
|
|
1528
|
-
" picgen setup",
|
|
1527
|
+
" picgen setup # can save provider API keys for you",
|
|
1529
1528
|
" picgen doctor --json",
|
|
1530
1529
|
"",
|
|
1531
1530
|
"Preview before spending quota:",
|
|
@@ -1538,10 +1537,11 @@ function formatQuickstart() {
|
|
|
1538
1537
|
' picgen create --dry-run --reference ./reference.png "\u57FA\u4E8E\u53C2\u8003\u56FE\u751F\u6210\u4E00\u5F20\u54C1\u724C\u6D77\u62A5"',
|
|
1539
1538
|
"",
|
|
1540
1539
|
"Agent prompt:",
|
|
1541
|
-
" \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",
|
|
1542
1541
|
"",
|
|
1543
1542
|
"Notes:",
|
|
1544
1543
|
" - Provider host URLs should not include /v1 or /v1beta.",
|
|
1544
|
+
" - picgen setup can store API keys in ~/.picgen/.env.",
|
|
1545
1545
|
" - Agent workflows should dry-run before real generation.",
|
|
1546
1546
|
" - Generated images are saved locally; do not paste base64 into chat.",
|
|
1547
1547
|
" - First-user rollout checklist: docs/release-alpha.md"
|
|
@@ -1549,7 +1549,62 @@ function formatQuickstart() {
|
|
|
1549
1549
|
}
|
|
1550
1550
|
|
|
1551
1551
|
// src/commands/setup.ts
|
|
1552
|
-
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
|
|
1553
1608
|
async function runSetup() {
|
|
1554
1609
|
await ensureConfig();
|
|
1555
1610
|
console.log(`PicGen config: ${getConfigPath()}`);
|
|
@@ -1564,6 +1619,7 @@ async function runSetup() {
|
|
|
1564
1619
|
{ name: "Quick add a common provider/channel", value: "quick-add" },
|
|
1565
1620
|
{ name: "Choose default provider/channel", value: "provider" },
|
|
1566
1621
|
{ name: "Choose generation preference", value: "mode" },
|
|
1622
|
+
{ name: "Configure API key", value: "key" },
|
|
1567
1623
|
{ name: "Test a provider", value: "test" },
|
|
1568
1624
|
{ name: "Advanced: add a custom provider/channel", value: "add" },
|
|
1569
1625
|
{ name: "Finish setup", value: "done" }
|
|
@@ -1575,6 +1631,8 @@ async function runSetup() {
|
|
|
1575
1631
|
await chooseDefaultProvider();
|
|
1576
1632
|
} else if (action === "mode") {
|
|
1577
1633
|
await chooseDefaultMode();
|
|
1634
|
+
} else if (action === "key") {
|
|
1635
|
+
await chooseProviderKeyToConfigure();
|
|
1578
1636
|
} else if (action === "test") {
|
|
1579
1637
|
await chooseProviderToTest();
|
|
1580
1638
|
} else if (action === "add") {
|
|
@@ -1593,7 +1651,7 @@ async function printSetupSummary() {
|
|
|
1593
1651
|
for (const [providerName, provider2] of Object.entries(config.providers)) {
|
|
1594
1652
|
const preference = providerName === config.routing.default_provider ? "default" : config.routing.fallback_providers.includes(providerName) ? "fallback" : "manual";
|
|
1595
1653
|
console.log(
|
|
1596
|
-
`- ${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(",")}`
|
|
1597
1655
|
);
|
|
1598
1656
|
}
|
|
1599
1657
|
}
|
|
@@ -1641,6 +1699,18 @@ async function chooseProviderToTest() {
|
|
|
1641
1699
|
if (result.model) console.log(`Model: ${result.model}`);
|
|
1642
1700
|
if (result.http_status) console.log(`HTTP status: ${result.http_status}`);
|
|
1643
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
|
+
}
|
|
1644
1714
|
async function quickAddProvider() {
|
|
1645
1715
|
const config = await loadConfig();
|
|
1646
1716
|
const template = await select2({
|
|
@@ -1700,7 +1770,27 @@ async function quickAddProvider() {
|
|
|
1700
1770
|
}
|
|
1701
1771
|
await saveConfig(config);
|
|
1702
1772
|
console.log(`Added provider: ${name}`);
|
|
1703
|
-
|
|
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.`);
|
|
1704
1794
|
}
|
|
1705
1795
|
function quickProviderDefaults(template) {
|
|
1706
1796
|
switch (template) {
|
|
@@ -1765,6 +1855,7 @@ function modeLabel(modeName) {
|
|
|
1765
1855
|
}
|
|
1766
1856
|
|
|
1767
1857
|
// src/cli.ts
|
|
1858
|
+
await loadPicgenEnv();
|
|
1768
1859
|
var program = new Command();
|
|
1769
1860
|
program.name("picgen").description("Lightweight image generation connector for AI agents.").version(VERSION);
|
|
1770
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
|
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
|
|