@astablebridge/core 0.1.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/auth/credentials.d.ts +19 -0
- package/dist/auth/credentials.d.ts.map +1 -0
- package/dist/auth/credentials.js +154 -0
- package/dist/auth/credentials.js.map +1 -0
- package/dist/cdn/index.d.ts +3 -0
- package/dist/cdn/index.d.ts.map +1 -0
- package/dist/cdn/index.js +47 -0
- package/dist/cdn/index.js.map +1 -0
- package/dist/client/stability.d.ts +17 -0
- package/dist/client/stability.d.ts.map +1 -0
- package/dist/client/stability.js +83 -0
- package/dist/client/stability.js.map +1 -0
- package/dist/config/project.d.ts +16 -0
- package/dist/config/project.d.ts.map +1 -0
- package/dist/config/project.js +126 -0
- package/dist/config/project.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/insert/index.d.ts +10 -0
- package/dist/insert/index.d.ts.map +1 -0
- package/dist/insert/index.js +92 -0
- package/dist/insert/index.js.map +1 -0
- package/dist/onboard/server.d.ts +20 -0
- package/dist/onboard/server.d.ts.map +1 -0
- package/dist/onboard/server.js +278 -0
- package/dist/onboard/server.js.map +1 -0
- package/dist/placement/assets.d.ts +12 -0
- package/dist/placement/assets.d.ts.map +1 -0
- package/dist/placement/assets.js +65 -0
- package/dist/placement/assets.js.map +1 -0
- package/dist/types.d.ts +85 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { basename } from "node:path";
|
|
4
|
+
import { placeInProject } from "../placement/assets.js";
|
|
5
|
+
export function detectFramework(filePath) {
|
|
6
|
+
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
7
|
+
const base = basename(filePath).toLowerCase();
|
|
8
|
+
if (ext === "css" || ext === "scss")
|
|
9
|
+
return "css";
|
|
10
|
+
if (ext === "md" || ext === "mdx")
|
|
11
|
+
return "markdown";
|
|
12
|
+
if (ext === "html" || ext === "htm")
|
|
13
|
+
return "html";
|
|
14
|
+
if (ext === "tsx" || ext === "jsx") {
|
|
15
|
+
if (base.includes("page.") || filePath.includes("/app/")) {
|
|
16
|
+
return "nextjs";
|
|
17
|
+
}
|
|
18
|
+
return "react";
|
|
19
|
+
}
|
|
20
|
+
return "html";
|
|
21
|
+
}
|
|
22
|
+
export function buildSnippet(framework, imagePath, alt = "Generated image") {
|
|
23
|
+
switch (framework) {
|
|
24
|
+
case "nextjs":
|
|
25
|
+
return `import Image from "next/image";\n\n<Image src="${imagePath}" alt="${alt}" width={1200} height={630} />`;
|
|
26
|
+
case "react":
|
|
27
|
+
return `<img src="${imagePath}" alt="${alt}" />`;
|
|
28
|
+
case "markdown":
|
|
29
|
+
return ``;
|
|
30
|
+
case "css":
|
|
31
|
+
return `background-image: url("${imagePath}");`;
|
|
32
|
+
case "html":
|
|
33
|
+
default:
|
|
34
|
+
return `<img src="${imagePath}" alt="${alt}" />`;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export async function applySnippet(filePath, snippet, options) {
|
|
38
|
+
if (!existsSync(filePath)) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
let content = await readFile(filePath, "utf8");
|
|
42
|
+
if (options.placeholder && content.includes(options.placeholder)) {
|
|
43
|
+
content = content.replace(options.placeholder, snippet);
|
|
44
|
+
await writeFile(filePath, content, "utf8");
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (options.framework === "css") {
|
|
48
|
+
const cssBlock = `\n.hero {\n ${snippet}\n background-size: cover;\n background-position: center;\n}\n`;
|
|
49
|
+
content += cssBlock;
|
|
50
|
+
await writeFile(filePath, content, "utf8");
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
if (options.framework === "markdown") {
|
|
54
|
+
content += `\n\n${snippet}\n`;
|
|
55
|
+
await writeFile(filePath, content, "utf8");
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
const imgPattern = /<img[^>]*src=["'][^"']*["'][^>]*\/?>/;
|
|
59
|
+
if (imgPattern.test(content)) {
|
|
60
|
+
content = content.replace(imgPattern, snippet.split("\n").pop() ?? snippet);
|
|
61
|
+
await writeFile(filePath, content, "utf8");
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
export async function insertImage(cwd, apiKey, options) {
|
|
67
|
+
const framework = options.framework ?? detectFramework(options.file);
|
|
68
|
+
const generateResult = await placeInProject(cwd, apiKey, {
|
|
69
|
+
prompt: options.prompt,
|
|
70
|
+
model: options.model,
|
|
71
|
+
outputFormat: options.outputFormat,
|
|
72
|
+
aspectRatio: options.aspectRatio,
|
|
73
|
+
name: options.name,
|
|
74
|
+
});
|
|
75
|
+
const imagePath = generateResult.cdnUrl ?? generateResult.publicPath;
|
|
76
|
+
const alt = options.alt ?? options.name ?? "Generated image";
|
|
77
|
+
const snippet = buildSnippet(framework, imagePath, alt);
|
|
78
|
+
let applied = false;
|
|
79
|
+
if (options.apply) {
|
|
80
|
+
applied = await applySnippet(options.file, snippet, {
|
|
81
|
+
placeholder: options.placeholder,
|
|
82
|
+
framework,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
...generateResult,
|
|
87
|
+
snippet,
|
|
88
|
+
applied,
|
|
89
|
+
framework,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/insert/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Preferences } from "../types.js";
|
|
2
|
+
export interface OnboardCompletion {
|
|
3
|
+
success: boolean;
|
|
4
|
+
keyHint?: string;
|
|
5
|
+
balance?: number;
|
|
6
|
+
preferences?: Preferences;
|
|
7
|
+
}
|
|
8
|
+
export interface OnboardServerOptions {
|
|
9
|
+
port?: number;
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
onSuccess?: (keyHint: string, balance: number, preferences: Preferences) => void;
|
|
12
|
+
}
|
|
13
|
+
export interface OnboardServerResult {
|
|
14
|
+
port: number;
|
|
15
|
+
url: string;
|
|
16
|
+
close: () => Promise<void>;
|
|
17
|
+
waitForCompletion: () => Promise<OnboardCompletion>;
|
|
18
|
+
}
|
|
19
|
+
export declare function startOnboardServer(options?: OnboardServerOptions): Promise<OnboardServerResult>;
|
|
20
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/onboard/server.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAgB,WAAW,EAAE,MAAM,aAAa,CAAC;AAgL7D,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,KAAK,IAAI,CAAC;CAClF;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,iBAAiB,EAAE,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACrD;AAoCD,wBAAsB,kBAAkB,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA0FzG"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { setApiKey, validateKey, getKeyHint, setPreferences, DEFAULT_PREFERENCES } from "../auth/credentials.js";
|
|
3
|
+
const ONBOARD_HTML = `<!DOCTYPE html>
|
|
4
|
+
<html lang="en">
|
|
5
|
+
<head>
|
|
6
|
+
<meta charset="UTF-8">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
8
|
+
<title>StableBridge — API Key Setup</title>
|
|
9
|
+
<style>
|
|
10
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
11
|
+
body {
|
|
12
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
13
|
+
background: #0a0a0a;
|
|
14
|
+
color: #fafafa;
|
|
15
|
+
min-height: 100vh;
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
justify-content: center;
|
|
19
|
+
padding: 24px;
|
|
20
|
+
}
|
|
21
|
+
.card {
|
|
22
|
+
background: #171717;
|
|
23
|
+
border: 1px solid #262626;
|
|
24
|
+
border-radius: 12px;
|
|
25
|
+
padding: 32px;
|
|
26
|
+
max-width: 440px;
|
|
27
|
+
width: 100%;
|
|
28
|
+
}
|
|
29
|
+
h1 { font-size: 1.25rem; margin-bottom: 8px; }
|
|
30
|
+
p { color: #a3a3a3; font-size: 0.875rem; line-height: 1.5; margin-bottom: 20px; }
|
|
31
|
+
label { display: block; font-size: 0.75rem; color: #a3a3a3; margin-bottom: 6px; }
|
|
32
|
+
input[type="password"], input[type="text"], input[type="number"], select {
|
|
33
|
+
width: 100%;
|
|
34
|
+
padding: 10px 12px;
|
|
35
|
+
background: #0a0a0a;
|
|
36
|
+
border: 1px solid #404040;
|
|
37
|
+
border-radius: 8px;
|
|
38
|
+
color: #fafafa;
|
|
39
|
+
font-size: 0.875rem;
|
|
40
|
+
margin-bottom: 16px;
|
|
41
|
+
}
|
|
42
|
+
input:focus, select:focus { outline: none; border-color: #6366f1; }
|
|
43
|
+
.row { display: flex; gap: 12px; }
|
|
44
|
+
.row > div { flex: 1; }
|
|
45
|
+
.checkbox-row { display: flex; align-items: center; gap: 8px; margin-bottom: 16px; }
|
|
46
|
+
.checkbox-row input { width: auto; margin: 0; }
|
|
47
|
+
.checkbox-row label { margin: 0; }
|
|
48
|
+
.section-title { font-size: 0.8125rem; color: #fafafa; margin: 8px 0 12px; font-weight: 500; }
|
|
49
|
+
button {
|
|
50
|
+
width: 100%;
|
|
51
|
+
padding: 10px;
|
|
52
|
+
background: #6366f1;
|
|
53
|
+
color: white;
|
|
54
|
+
border: none;
|
|
55
|
+
border-radius: 8px;
|
|
56
|
+
font-size: 0.875rem;
|
|
57
|
+
font-weight: 500;
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
}
|
|
60
|
+
button:hover { background: #4f46e5; }
|
|
61
|
+
button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
62
|
+
.error { color: #f87171; font-size: 0.8125rem; margin-bottom: 12px; display: none; }
|
|
63
|
+
.success { color: #4ade80; font-size: 0.875rem; display: none; }
|
|
64
|
+
.hint { font-size: 0.75rem; color: #737373; margin-top: 16px; }
|
|
65
|
+
a { color: #818cf8; }
|
|
66
|
+
</style>
|
|
67
|
+
</head>
|
|
68
|
+
<body>
|
|
69
|
+
<div class="card">
|
|
70
|
+
<h1>StableBridge Setup</h1>
|
|
71
|
+
<p>Paste your Stability AI API key below. It is saved locally on your machine and never sent to any agent or chat.</p>
|
|
72
|
+
<div id="error" class="error"></div>
|
|
73
|
+
<div id="success" class="success"></div>
|
|
74
|
+
<form id="form">
|
|
75
|
+
<label for="apiKey">Stability API Key</label>
|
|
76
|
+
<input type="password" id="apiKey" name="apiKey" placeholder="sk-..." autocomplete="off" required />
|
|
77
|
+
|
|
78
|
+
<div class="section-title">Generation defaults</div>
|
|
79
|
+
<div class="row">
|
|
80
|
+
<div>
|
|
81
|
+
<label for="defaultModel">Model</label>
|
|
82
|
+
<select id="defaultModel" name="defaultModel">
|
|
83
|
+
<option value="core">core</option>
|
|
84
|
+
<option value="ultra">ultra</option>
|
|
85
|
+
</select>
|
|
86
|
+
</div>
|
|
87
|
+
<div>
|
|
88
|
+
<label for="defaultFormat">Format</label>
|
|
89
|
+
<select id="defaultFormat" name="defaultFormat">
|
|
90
|
+
<option value="webp">webp</option>
|
|
91
|
+
<option value="png">png</option>
|
|
92
|
+
<option value="jpeg">jpeg</option>
|
|
93
|
+
</select>
|
|
94
|
+
</div>
|
|
95
|
+
<div>
|
|
96
|
+
<label for="defaultAspectRatio">Aspect ratio</label>
|
|
97
|
+
<select id="defaultAspectRatio" name="defaultAspectRatio">
|
|
98
|
+
<option value="1:1">1:1</option>
|
|
99
|
+
<option value="16:9">16:9</option>
|
|
100
|
+
<option value="9:16">9:16</option>
|
|
101
|
+
<option value="3:2">3:2</option>
|
|
102
|
+
<option value="2:3">2:3</option>
|
|
103
|
+
<option value="21:9">21:9</option>
|
|
104
|
+
</select>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<div class="checkbox-row">
|
|
109
|
+
<input type="checkbox" id="cdnEnabled" name="cdnEnabled" />
|
|
110
|
+
<label for="cdnEnabled">Enable CDN upload (Vercel Blob)</label>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<div class="section-title">Autonomy & budget</div>
|
|
114
|
+
<label for="autonomyMode">Autonomy mode</label>
|
|
115
|
+
<select id="autonomyMode" name="autonomyMode">
|
|
116
|
+
<option value="plan-then-execute">plan-then-execute — propose a batch plan, then run it</option>
|
|
117
|
+
<option value="bounded">bounded — run freely, stop at the budget floor</option>
|
|
118
|
+
<option value="full-auto">full-auto — run with no confirmation up to the floor</option>
|
|
119
|
+
</select>
|
|
120
|
+
|
|
121
|
+
<label for="creditBudget">Credit budget floor (minimum credits to keep)</label>
|
|
122
|
+
<input type="number" id="creditBudget" name="creditBudget" min="0" step="1" value="0" />
|
|
123
|
+
|
|
124
|
+
<button type="submit" id="submitBtn">Save & Validate</button>
|
|
125
|
+
</form>
|
|
126
|
+
<p class="hint">Don't have a key? <a href="https://platform.stability.ai/account/keys" target="_blank" rel="noopener">Create one at Stability AI</a></p>
|
|
127
|
+
</div>
|
|
128
|
+
<script>
|
|
129
|
+
const form = document.getElementById('form');
|
|
130
|
+
const errorEl = document.getElementById('error');
|
|
131
|
+
const successEl = document.getElementById('success');
|
|
132
|
+
const submitBtn = document.getElementById('submitBtn');
|
|
133
|
+
|
|
134
|
+
form.addEventListener('submit', async (e) => {
|
|
135
|
+
e.preventDefault();
|
|
136
|
+
errorEl.style.display = 'none';
|
|
137
|
+
successEl.style.display = 'none';
|
|
138
|
+
submitBtn.disabled = true;
|
|
139
|
+
submitBtn.textContent = 'Validating...';
|
|
140
|
+
|
|
141
|
+
const apiKey = document.getElementById('apiKey').value.trim();
|
|
142
|
+
const preferences = {
|
|
143
|
+
defaultModel: document.getElementById('defaultModel').value,
|
|
144
|
+
defaultFormat: document.getElementById('defaultFormat').value,
|
|
145
|
+
defaultAspectRatio: document.getElementById('defaultAspectRatio').value,
|
|
146
|
+
cdnEnabled: document.getElementById('cdnEnabled').checked,
|
|
147
|
+
autonomyMode: document.getElementById('autonomyMode').value,
|
|
148
|
+
creditBudgetFloor: Number(document.getElementById('creditBudget').value) || 0,
|
|
149
|
+
};
|
|
150
|
+
try {
|
|
151
|
+
const res = await fetch('/save', {
|
|
152
|
+
method: 'POST',
|
|
153
|
+
headers: { 'Content-Type': 'application/json' },
|
|
154
|
+
body: JSON.stringify({ apiKey, preferences }),
|
|
155
|
+
});
|
|
156
|
+
const data = await res.json();
|
|
157
|
+
if (!res.ok) {
|
|
158
|
+
errorEl.textContent = data.error || 'Failed to save key';
|
|
159
|
+
errorEl.style.display = 'block';
|
|
160
|
+
} else {
|
|
161
|
+
successEl.textContent = 'Success! Key saved (' + data.keyHint + '). Balance: ' + data.balance + ' credits. You can close this tab.';
|
|
162
|
+
successEl.style.display = 'block';
|
|
163
|
+
form.style.display = 'none';
|
|
164
|
+
setTimeout(() => window.close(), 3000);
|
|
165
|
+
}
|
|
166
|
+
} catch (err) {
|
|
167
|
+
errorEl.textContent = 'Network error. Please try again.';
|
|
168
|
+
errorEl.style.display = 'block';
|
|
169
|
+
}
|
|
170
|
+
submitBtn.disabled = false;
|
|
171
|
+
submitBtn.textContent = 'Save & Validate';
|
|
172
|
+
});
|
|
173
|
+
</script>
|
|
174
|
+
</body>
|
|
175
|
+
</html>`;
|
|
176
|
+
function parsePreferences(input) {
|
|
177
|
+
const raw = (input ?? {});
|
|
178
|
+
const model = raw.defaultModel === "ultra" ? "ultra" : "core";
|
|
179
|
+
const format = raw.defaultFormat === "png" || raw.defaultFormat === "jpeg"
|
|
180
|
+
? raw.defaultFormat
|
|
181
|
+
: "webp";
|
|
182
|
+
const autonomy = raw.autonomyMode === "full-auto" || raw.autonomyMode === "bounded"
|
|
183
|
+
? raw.autonomyMode
|
|
184
|
+
: "plan-then-execute";
|
|
185
|
+
const floor = Number(raw.creditBudgetFloor);
|
|
186
|
+
return {
|
|
187
|
+
defaultModel: model,
|
|
188
|
+
defaultFormat: format,
|
|
189
|
+
defaultAspectRatio: typeof raw.defaultAspectRatio === "string" && raw.defaultAspectRatio.trim()
|
|
190
|
+
? raw.defaultAspectRatio.trim()
|
|
191
|
+
: DEFAULT_PREFERENCES.defaultAspectRatio,
|
|
192
|
+
cdnEnabled: raw.cdnEnabled === true,
|
|
193
|
+
creditBudgetFloor: Number.isFinite(floor) && floor >= 0 ? floor : 0,
|
|
194
|
+
autonomyMode: autonomy,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function readBody(req) {
|
|
198
|
+
return new Promise((resolve, reject) => {
|
|
199
|
+
const chunks = [];
|
|
200
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
201
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
|
202
|
+
req.on("error", reject);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
export async function startOnboardServer(options = {}) {
|
|
206
|
+
const timeoutMs = options.timeoutMs ?? 10 * 60 * 1000;
|
|
207
|
+
let completed = false;
|
|
208
|
+
let completionResolve;
|
|
209
|
+
const completionPromise = new Promise((resolve) => {
|
|
210
|
+
completionResolve = resolve;
|
|
211
|
+
});
|
|
212
|
+
let timeoutId;
|
|
213
|
+
const server = createServer(async (req, res) => {
|
|
214
|
+
const url = req.url ?? "/";
|
|
215
|
+
if (req.method === "GET" && (url === "/" || url === "/index.html")) {
|
|
216
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
217
|
+
res.end(ONBOARD_HTML);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (req.method === "POST" && url === "/save") {
|
|
221
|
+
try {
|
|
222
|
+
const body = await readBody(req);
|
|
223
|
+
const { apiKey, preferences } = JSON.parse(body);
|
|
224
|
+
if (!apiKey?.trim()) {
|
|
225
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
226
|
+
res.end(JSON.stringify({ error: "API key is required" }));
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const validation = await validateKey(apiKey.trim());
|
|
230
|
+
if (!validation.valid) {
|
|
231
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
232
|
+
res.end(JSON.stringify({ error: validation.error ?? "Invalid API key" }));
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
await setApiKey(apiKey.trim());
|
|
236
|
+
const savedPreferences = await setPreferences(parsePreferences(preferences));
|
|
237
|
+
const keyHint = getKeyHint(apiKey.trim());
|
|
238
|
+
const balance = validation.balance ?? 0;
|
|
239
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
240
|
+
res.end(JSON.stringify({ success: true, keyHint, balance, preferences: savedPreferences }));
|
|
241
|
+
completed = true;
|
|
242
|
+
clearTimeout(timeoutId);
|
|
243
|
+
options.onSuccess?.(keyHint, balance, savedPreferences);
|
|
244
|
+
completionResolve({ success: true, keyHint, balance, preferences: savedPreferences });
|
|
245
|
+
setTimeout(() => server.close(), 500);
|
|
246
|
+
}
|
|
247
|
+
catch (err) {
|
|
248
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
249
|
+
res.end(JSON.stringify({ error: "Internal server error" }));
|
|
250
|
+
}
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
res.writeHead(404);
|
|
254
|
+
res.end("Not found");
|
|
255
|
+
});
|
|
256
|
+
await new Promise((resolve, reject) => {
|
|
257
|
+
server.listen(options.port ?? 0, "127.0.0.1", () => resolve());
|
|
258
|
+
server.on("error", reject);
|
|
259
|
+
});
|
|
260
|
+
const address = server.address();
|
|
261
|
+
const port = typeof address === "object" && address ? address.port : (options.port ?? 3847);
|
|
262
|
+
timeoutId = setTimeout(() => {
|
|
263
|
+
if (!completed) {
|
|
264
|
+
completionResolve({ success: false });
|
|
265
|
+
server.close();
|
|
266
|
+
}
|
|
267
|
+
}, timeoutMs);
|
|
268
|
+
return {
|
|
269
|
+
port,
|
|
270
|
+
url: `http://127.0.0.1:${port}`,
|
|
271
|
+
close: () => new Promise((resolve) => {
|
|
272
|
+
clearTimeout(timeoutId);
|
|
273
|
+
server.close(() => resolve());
|
|
274
|
+
}),
|
|
275
|
+
waitForCompletion: () => completionPromise,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/onboard/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { GenerateOptions, GenerateResult } from "../types.js";
|
|
2
|
+
export declare const ESTIMATED_COST: Record<"core" | "ultra", number>;
|
|
3
|
+
export declare class BudgetExceededError extends Error {
|
|
4
|
+
readonly balance: number;
|
|
5
|
+
readonly estimatedCost: number;
|
|
6
|
+
readonly creditBudgetFloor: number;
|
|
7
|
+
readonly model: "core" | "ultra";
|
|
8
|
+
readonly budgetExceeded = true;
|
|
9
|
+
constructor(balance: number, estimatedCost: number, creditBudgetFloor: number, model: "core" | "ultra");
|
|
10
|
+
}
|
|
11
|
+
export declare function placeInProject(cwd: string, apiKey: string, options: GenerateOptions): Promise<GenerateResult>;
|
|
12
|
+
//# sourceMappingURL=assets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/placement/assets.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKnE,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,GAAG,OAAO,EAAE,MAAM,CAG3D,CAAC;AAEF,qBAAa,mBAAoB,SAAQ,KAAK;IAG1C,QAAQ,CAAC,OAAO,EAAE,MAAM;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM;IAC9B,QAAQ,CAAC,iBAAiB,EAAE,MAAM;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IALlC,QAAQ,CAAC,cAAc,QAAQ;gBAEpB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,iBAAiB,EAAE,MAAM,EACzB,KAAK,EAAE,MAAM,GAAG,OAAO;CAQnC;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,cAAc,CAAC,CAyCzB"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { buildFilename, loadProjectConfig, toPublicPath } from "../config/project.js";
|
|
4
|
+
import { generateImageWithModel } from "../client/stability.js";
|
|
5
|
+
import { getBalance } from "../auth/credentials.js";
|
|
6
|
+
import { uploadToCdn } from "../cdn/index.js";
|
|
7
|
+
// Approximate Stability AI list prices (credits per image). Used only as a
|
|
8
|
+
// pre-flight estimate to enforce the local credit-budget floor; the real spend
|
|
9
|
+
// is whatever the API deducts on success.
|
|
10
|
+
export const ESTIMATED_COST = {
|
|
11
|
+
core: 3,
|
|
12
|
+
ultra: 8,
|
|
13
|
+
};
|
|
14
|
+
export class BudgetExceededError extends Error {
|
|
15
|
+
balance;
|
|
16
|
+
estimatedCost;
|
|
17
|
+
creditBudgetFloor;
|
|
18
|
+
model;
|
|
19
|
+
budgetExceeded = true;
|
|
20
|
+
constructor(balance, estimatedCost, creditBudgetFloor, model) {
|
|
21
|
+
super(`Credit budget floor reached: balance ${balance} - estimated cost ${estimatedCost} ` +
|
|
22
|
+
`would fall below floor ${creditBudgetFloor} (model: ${model}).`);
|
|
23
|
+
this.balance = balance;
|
|
24
|
+
this.estimatedCost = estimatedCost;
|
|
25
|
+
this.creditBudgetFloor = creditBudgetFloor;
|
|
26
|
+
this.model = model;
|
|
27
|
+
this.name = "BudgetExceededError";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export async function placeInProject(cwd, apiKey, options) {
|
|
31
|
+
const config = await loadProjectConfig(cwd);
|
|
32
|
+
const model = options.model ?? config.defaultModel;
|
|
33
|
+
const format = options.outputFormat ?? config.defaultFormat;
|
|
34
|
+
const aspectRatio = options.aspectRatio ?? config.defaultAspectRatio;
|
|
35
|
+
const estimatedCost = ESTIMATED_COST[model] ?? ESTIMATED_COST.core;
|
|
36
|
+
const balance = await getBalance(apiKey);
|
|
37
|
+
if (balance - estimatedCost < config.creditBudgetFloor) {
|
|
38
|
+
throw new BudgetExceededError(balance, estimatedCost, config.creditBudgetFloor, model);
|
|
39
|
+
}
|
|
40
|
+
const image = await generateImageWithModel(apiKey, {
|
|
41
|
+
...options,
|
|
42
|
+
model,
|
|
43
|
+
outputFormat: format,
|
|
44
|
+
aspectRatio,
|
|
45
|
+
});
|
|
46
|
+
const filename = buildFilename(options.prompt, format, options.name);
|
|
47
|
+
const assetsDir = join(cwd, config.assetsDir);
|
|
48
|
+
await mkdir(assetsDir, { recursive: true });
|
|
49
|
+
const localPath = join(assetsDir, filename);
|
|
50
|
+
await writeFile(localPath, image.buffer);
|
|
51
|
+
const publicPath = toPublicPath(config.assetsDir, filename);
|
|
52
|
+
let cdnUrl;
|
|
53
|
+
if (config.cdn?.enabled) {
|
|
54
|
+
cdnUrl = await uploadToCdn(image.buffer, filename, config, image.contentType);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
localPath: join(config.assetsDir, filename),
|
|
58
|
+
publicPath,
|
|
59
|
+
cdnUrl,
|
|
60
|
+
filename,
|
|
61
|
+
model: image.model,
|
|
62
|
+
format,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=assets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/placement/assets.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export declare const CONFIG_DIR: string;
|
|
2
|
+
export declare const CREDENTIALS_FILE: string;
|
|
3
|
+
export declare const PREFERENCES_FILE: string;
|
|
4
|
+
export declare const KEYCHAIN_SERVICE = "stable-bridge";
|
|
5
|
+
export declare const KEYCHAIN_ACCOUNT = "api-key";
|
|
6
|
+
export declare const STABILITY_API_BASE = "https://api.stability.ai";
|
|
7
|
+
export interface Credentials {
|
|
8
|
+
apiKey: string;
|
|
9
|
+
createdAt: string;
|
|
10
|
+
}
|
|
11
|
+
export type AutonomyMode = "full-auto" | "bounded" | "plan-then-execute";
|
|
12
|
+
export interface Preferences {
|
|
13
|
+
defaultModel: "core" | "ultra";
|
|
14
|
+
defaultFormat: "webp" | "png" | "jpeg";
|
|
15
|
+
defaultAspectRatio: string;
|
|
16
|
+
cdnEnabled: boolean;
|
|
17
|
+
creditBudgetFloor: number;
|
|
18
|
+
autonomyMode: AutonomyMode;
|
|
19
|
+
}
|
|
20
|
+
export interface ProjectConfig {
|
|
21
|
+
assetsDir: string;
|
|
22
|
+
cdn?: {
|
|
23
|
+
provider: "vercel-blob" | "s3";
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
bucket?: string;
|
|
26
|
+
region?: string;
|
|
27
|
+
prefix?: string;
|
|
28
|
+
};
|
|
29
|
+
defaultModel: "core" | "ultra";
|
|
30
|
+
defaultFormat: "webp" | "png" | "jpeg";
|
|
31
|
+
defaultAspectRatio: string;
|
|
32
|
+
creditBudgetFloor: number;
|
|
33
|
+
autonomyMode: AutonomyMode;
|
|
34
|
+
}
|
|
35
|
+
export interface GenerateOptions {
|
|
36
|
+
prompt: string;
|
|
37
|
+
model?: "core" | "ultra";
|
|
38
|
+
outputFormat?: "webp" | "png" | "jpeg";
|
|
39
|
+
aspectRatio?: string;
|
|
40
|
+
name?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface GenerateResult {
|
|
43
|
+
localPath: string;
|
|
44
|
+
publicPath: string;
|
|
45
|
+
cdnUrl?: string;
|
|
46
|
+
filename: string;
|
|
47
|
+
model: string;
|
|
48
|
+
format: string;
|
|
49
|
+
}
|
|
50
|
+
export interface InsertOptions {
|
|
51
|
+
file: string;
|
|
52
|
+
prompt: string;
|
|
53
|
+
framework?: "nextjs" | "react" | "html" | "markdown" | "css";
|
|
54
|
+
alt?: string;
|
|
55
|
+
apply?: boolean;
|
|
56
|
+
placeholder?: string;
|
|
57
|
+
model?: "core" | "ultra";
|
|
58
|
+
outputFormat?: "webp" | "png" | "jpeg";
|
|
59
|
+
aspectRatio?: string;
|
|
60
|
+
name?: string;
|
|
61
|
+
}
|
|
62
|
+
export interface InsertResult extends GenerateResult {
|
|
63
|
+
snippet: string;
|
|
64
|
+
applied: boolean;
|
|
65
|
+
framework: string;
|
|
66
|
+
}
|
|
67
|
+
export interface StatusResult {
|
|
68
|
+
configured: boolean;
|
|
69
|
+
keyHint?: string;
|
|
70
|
+
balance?: number;
|
|
71
|
+
source?: "env" | "keychain" | "file";
|
|
72
|
+
error?: string;
|
|
73
|
+
autonomyMode?: AutonomyMode;
|
|
74
|
+
creditBudgetFloor?: number;
|
|
75
|
+
defaultModel?: "core" | "ultra";
|
|
76
|
+
defaultFormat?: "webp" | "png" | "jpeg";
|
|
77
|
+
defaultAspectRatio?: string;
|
|
78
|
+
cdnEnabled?: boolean;
|
|
79
|
+
}
|
|
80
|
+
export interface InitResult {
|
|
81
|
+
configPath: string;
|
|
82
|
+
assetsDir: string;
|
|
83
|
+
created: boolean;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,UAAU,QAA8C,CAAC;AACtE,eAAO,MAAM,gBAAgB,QAAuC,CAAC;AACrE,eAAO,MAAM,gBAAgB,QAAuC,CAAC;AACrE,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAChD,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAE1C,eAAO,MAAM,kBAAkB,6BAA6B,CAAC;AAE7D,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,SAAS,GAAG,mBAAmB,CAAC;AAEzE,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,aAAa,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;IACvC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE;QACJ,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;QAC/B,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,aAAa,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;IACvC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,KAAK,CAAC;IAC7D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAa,SAAQ,cAAc;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,KAAK,GAAG,UAAU,GAAG,MAAM,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;IACxC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export const CONFIG_DIR = join(homedir(), ".config", "stable-bridge");
|
|
4
|
+
export const CREDENTIALS_FILE = join(CONFIG_DIR, "credentials.json");
|
|
5
|
+
export const PREFERENCES_FILE = join(CONFIG_DIR, "preferences.json");
|
|
6
|
+
export const KEYCHAIN_SERVICE = "stable-bridge";
|
|
7
|
+
export const KEYCHAIN_ACCOUNT = "api-key";
|
|
8
|
+
export const STABILITY_API_BASE = "https://api.stability.ai";
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;AACtE,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC;AACrE,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC;AACrE,MAAM,CAAC,MAAM,gBAAgB,GAAG,eAAe,CAAC;AAChD,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAC;AAE1C,MAAM,CAAC,MAAM,kBAAkB,GAAG,0BAA0B,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@astablebridge/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "StableBridge core library — Stability AI client, auth, CDN, and insertion helpers",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/mastercoder26/StableBridge.git",
|
|
20
|
+
"directory": "packages/core"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/mastercoder26/StableBridge#readme",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/mastercoder26/StableBridge/issues"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc",
|
|
31
|
+
"prepublishOnly": "npm run build",
|
|
32
|
+
"test": "node --import tsx --test src/**/*.test.ts"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@aws-sdk/client-s3": "^3.750.0",
|
|
36
|
+
"@vercel/blob": "^0.27.3"
|
|
37
|
+
},
|
|
38
|
+
"optionalDependencies": {
|
|
39
|
+
"keytar": "^7.9.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^22.13.10",
|
|
43
|
+
"tsx": "^4.19.3",
|
|
44
|
+
"typescript": "^5.8.2"
|
|
45
|
+
},
|
|
46
|
+
"license": "MIT"
|
|
47
|
+
}
|