@framers/agentos-ext-image-generation 1.1.0 → 2.0.1
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 +88 -0
- package/dist/ImageGenerationService.d.ts +36 -4
- package/dist/ImageGenerationService.d.ts.map +1 -1
- package/dist/ImageGenerationService.js +73 -35
- package/dist/ImageGenerationService.js.map +1 -1
- package/dist/imageFiles.d.ts +130 -0
- package/dist/imageFiles.d.ts.map +1 -0
- package/dist/imageFiles.js +393 -0
- package/dist/imageFiles.js.map +1 -0
- package/dist/index.d.ts +36 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/tools/generateImage.d.ts +14 -4
- package/dist/tools/generateImage.d.ts.map +1 -1
- package/dist/tools/generateImage.js +57 -11
- package/dist/tools/generateImage.js.map +1 -1
- package/manifest.json +2 -2
- package/package.json +3 -3
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Where the image tools keep image data, and how a saved image
|
|
5
|
+
* comes back as a source.
|
|
6
|
+
*
|
|
7
|
+
* A provider that answers with image data (OpenAI's GPT Image models always
|
|
8
|
+
* do) used to have that data returned to the model as a `data:` URL: megabytes
|
|
9
|
+
* of base64 in the next request. The tools save the bytes to a file and return
|
|
10
|
+
* its `file:` URL, or hand them to the host's own saver. A saved file's URL is
|
|
11
|
+
* accepted as a source by the image-editing tools and by `vision-pipeline`.
|
|
12
|
+
* The image-generation, image-editing and vision-pipeline packs each carry
|
|
13
|
+
* this file, the same in all three, so they agree on the directory.
|
|
14
|
+
*
|
|
15
|
+
* The directory is private to the service's user, and each caller has a
|
|
16
|
+
* subdirectory of its own, which is read by the directory's real path and
|
|
17
|
+
* never through a link. That is the trust boundary: only this user can put a
|
|
18
|
+
* file there, so a file there with the saver's name pattern is one the
|
|
19
|
+
* service wrote, for that caller. It holds only while no other user can
|
|
20
|
+
* change a directory above the images directory: one who could rename an
|
|
21
|
+
* entry there could swap the tree between a check and the write or open
|
|
22
|
+
* that follows it, and Node has no openat to pin a checked directory. So
|
|
23
|
+
* each directory above it must belong to this user or root and be writable
|
|
24
|
+
* by no one else, unless it is sticky, as /tmp is. A Kubernetes emptyDir on
|
|
25
|
+
* disk is 0777 with no sticky bit and is refused as a parent; SKILL.md says
|
|
26
|
+
* which volumes pass.
|
|
27
|
+
*/
|
|
28
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
29
|
+
if (k2 === undefined) k2 = k;
|
|
30
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
31
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
32
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
33
|
+
}
|
|
34
|
+
Object.defineProperty(o, k2, desc);
|
|
35
|
+
}) : (function(o, m, k, k2) {
|
|
36
|
+
if (k2 === undefined) k2 = k;
|
|
37
|
+
o[k2] = m[k];
|
|
38
|
+
}));
|
|
39
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
40
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
41
|
+
}) : function(o, v) {
|
|
42
|
+
o["default"] = v;
|
|
43
|
+
});
|
|
44
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
45
|
+
var ownKeys = function(o) {
|
|
46
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
47
|
+
var ar = [];
|
|
48
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
49
|
+
return ar;
|
|
50
|
+
};
|
|
51
|
+
return ownKeys(o);
|
|
52
|
+
};
|
|
53
|
+
return function (mod) {
|
|
54
|
+
if (mod && mod.__esModule) return mod;
|
|
55
|
+
var result = {};
|
|
56
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
57
|
+
__setModuleDefault(result, mod);
|
|
58
|
+
return result;
|
|
59
|
+
};
|
|
60
|
+
})();
|
|
61
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
62
|
+
exports.MAX_SOURCE_BYTES = void 0;
|
|
63
|
+
exports.imageDirectory = imageDirectory;
|
|
64
|
+
exports.scopeOf = scopeOf;
|
|
65
|
+
exports.imageKind = imageKind;
|
|
66
|
+
exports.saveImageFile = saveImageFile;
|
|
67
|
+
exports.readSavedImage = readSavedImage;
|
|
68
|
+
exports.checkImageStore = checkImageStore;
|
|
69
|
+
exports.imageStore = imageStore;
|
|
70
|
+
exports.storeImage = storeImage;
|
|
71
|
+
const node_crypto_1 = require("node:crypto");
|
|
72
|
+
const node_fs_1 = require("node:fs");
|
|
73
|
+
const fs = __importStar(require("node:fs/promises"));
|
|
74
|
+
const os = __importStar(require("node:os"));
|
|
75
|
+
const path = __importStar(require("node:path"));
|
|
76
|
+
const node_url_1 = require("node:url");
|
|
77
|
+
/** The most bytes of a saved image the tools read back as a source: 50 MiB. */
|
|
78
|
+
exports.MAX_SOURCE_BYTES = 50 * 1024 * 1024;
|
|
79
|
+
/** The name of a file the saver wrote. */
|
|
80
|
+
const SAVED_NAME = /^agentos-image-[0-9a-f]{32}\.(png|jpg|webp)$/;
|
|
81
|
+
/** The eight bytes every PNG starts with. */
|
|
82
|
+
const PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
83
|
+
/**
|
|
84
|
+
* The images directory: the pack option `imageDir`, else the environment
|
|
85
|
+
* variable `AGENTOS_IMAGE_DIR`, else a folder in the user's temp directory,
|
|
86
|
+
* which the operating system clears (the packs delete nothing). On POSIX the
|
|
87
|
+
* default carries the user id, since `/tmp` is shared. On Windows it is the
|
|
88
|
+
* user's own temp folder whatever `TEMP` says: `os.tmpdir()` reads `TEMP`,
|
|
89
|
+
* which can name a folder every user shares.
|
|
90
|
+
*/
|
|
91
|
+
function imageDirectory(option) {
|
|
92
|
+
const named = [option, process.env.AGENTOS_IMAGE_DIR]
|
|
93
|
+
.map((value) => (typeof value === 'string' ? value.trim() : ''))
|
|
94
|
+
.find(Boolean);
|
|
95
|
+
if (named)
|
|
96
|
+
return path.resolve(named);
|
|
97
|
+
if (typeof process.getuid === 'function')
|
|
98
|
+
return path.join(os.tmpdir(), `agentos-images-${process.getuid()}`);
|
|
99
|
+
return path.join(os.homedir(), 'AppData', 'Local', 'Temp', 'agentos-images');
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The name of a caller's subdirectory: `u-` and 32 hex characters of the
|
|
103
|
+
* SHA-256 of its user id, or `shared` for a call that carries none. A host
|
|
104
|
+
* with several users gives each call its user (`context.userContext.userId`),
|
|
105
|
+
* so one user's saved image is no source for another.
|
|
106
|
+
*/
|
|
107
|
+
function scopeOf(context) {
|
|
108
|
+
const userId = context?.userContext?.userId;
|
|
109
|
+
return typeof userId === 'string' && userId !== ''
|
|
110
|
+
? `u-${(0, node_crypto_1.createHash)('sha256').update(userId).digest('hex').slice(0, 32)}`
|
|
111
|
+
: 'shared';
|
|
112
|
+
}
|
|
113
|
+
/** The kind of image `bytes` are, by their signature: PNG, JPEG or WebP. */
|
|
114
|
+
function imageKind(bytes) {
|
|
115
|
+
if (bytes.length >= 8 && bytes.subarray(0, 8).equals(PNG_SIGNATURE))
|
|
116
|
+
return { ext: 'png', mimeType: 'image/png' };
|
|
117
|
+
if (bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff) {
|
|
118
|
+
return { ext: 'jpg', mimeType: 'image/jpeg' };
|
|
119
|
+
}
|
|
120
|
+
if (bytes.length >= 12 && bytes.toString('latin1', 0, 4) === 'RIFF' && bytes.toString('latin1', 8, 12) === 'WEBP') {
|
|
121
|
+
return { ext: 'webp', mimeType: 'image/webp' };
|
|
122
|
+
}
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Whether a directory's `stat` (taken with `bigint: true`) shows it private
|
|
127
|
+
* to this user: a directory this user owns that neither its group nor others
|
|
128
|
+
* can write. Where the platform has no user ids (Windows) only the kind is
|
|
129
|
+
* checked: the default there is inside the user's own profile.
|
|
130
|
+
*/
|
|
131
|
+
function isPrivateDirectory(stat) {
|
|
132
|
+
if (!stat.isDirectory())
|
|
133
|
+
return false;
|
|
134
|
+
if (typeof process.getuid !== 'function')
|
|
135
|
+
return true;
|
|
136
|
+
return stat.uid === BigInt(process.getuid()) && (stat.mode & BigInt(0o022)) === BigInt(0);
|
|
137
|
+
}
|
|
138
|
+
/** The error for a directory that is not private to this user. */
|
|
139
|
+
function notPrivate(dir) {
|
|
140
|
+
return new Error(`${dir} is not a directory that this user owns and no one else can write`);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Why another user could change a directory above `dir`, a real path, or
|
|
144
|
+
* `undefined` when none can: each directory above it must belong to this
|
|
145
|
+
* user or root and be writable by no one else, unless it is sticky, where
|
|
146
|
+
* only an entry's owner can rename it. Where the platform has no user ids
|
|
147
|
+
* (Windows) nothing is checked, as in {@link isPrivateDirectory}.
|
|
148
|
+
*/
|
|
149
|
+
async function changeableParent(dir) {
|
|
150
|
+
if (typeof process.getuid !== 'function')
|
|
151
|
+
return undefined;
|
|
152
|
+
const uid = BigInt(process.getuid());
|
|
153
|
+
for (let parent = path.dirname(dir);; parent = path.dirname(parent)) {
|
|
154
|
+
const stat = await fs.lstat(parent, { bigint: true });
|
|
155
|
+
const mode = (stat.mode & BigInt(0o7777)).toString(8).padStart(4, '0');
|
|
156
|
+
if (!stat.isDirectory())
|
|
157
|
+
return `${parent}, above it, is not a directory`;
|
|
158
|
+
if (stat.uid !== uid && stat.uid !== BigInt(0))
|
|
159
|
+
return `${parent}, above it, belongs to uid ${stat.uid} (mode ${mode})`;
|
|
160
|
+
if ((stat.mode & BigInt(0o022)) !== BigInt(0) && (stat.mode & BigInt(0o1000)) === BigInt(0)) {
|
|
161
|
+
return `${parent}, above it, can be written by others and is not sticky (owner uid ${stat.uid}, mode ${mode})`;
|
|
162
|
+
}
|
|
163
|
+
if (path.dirname(parent) === parent)
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* The caller's subdirectory of `dir`, by `dir`'s real path, with both made
|
|
169
|
+
* when missing, for this user alone.
|
|
170
|
+
*
|
|
171
|
+
* @throws When either cannot be made, or is not a directory this user owns
|
|
172
|
+
* that no one else can write; the caller's directory must not be a link.
|
|
173
|
+
*/
|
|
174
|
+
async function callerDirectory(dir, scope) {
|
|
175
|
+
await fs.mkdir(dir, { recursive: true, mode: 0o700 });
|
|
176
|
+
// Written by the real path, so a link swapped in for it later moves nothing.
|
|
177
|
+
const root = await fs.realpath(dir);
|
|
178
|
+
if (!isPrivateDirectory(await fs.stat(root, { bigint: true })))
|
|
179
|
+
throw notPrivate(root);
|
|
180
|
+
const parent = await changeableParent(root);
|
|
181
|
+
if (parent)
|
|
182
|
+
throw new Error(parent);
|
|
183
|
+
const scopeDir = path.join(root, scope);
|
|
184
|
+
await fs.mkdir(scopeDir, { recursive: true, mode: 0o700 });
|
|
185
|
+
// lstat: a caller's directory that is a link to another caller's is refused.
|
|
186
|
+
if (!isPrivateDirectory(await fs.lstat(scopeDir, { bigint: true })))
|
|
187
|
+
throw notPrivate(scopeDir);
|
|
188
|
+
// A directory this user owns can still refuse a new file: its mode (0500),
|
|
189
|
+
// or a file system mounted read-only.
|
|
190
|
+
await fs.access(scopeDir, node_fs_1.constants.W_OK | node_fs_1.constants.X_OK);
|
|
191
|
+
return scopeDir;
|
|
192
|
+
}
|
|
193
|
+
/** The error of a save that cannot use `dir`: it names the directory and the option that sets it. */
|
|
194
|
+
function cannotSave(dir, error) {
|
|
195
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
196
|
+
return new Error(`The image could not be saved under ${dir}: ${reason}. Set the pack's imageDir option, or AGENTOS_IMAGE_DIR, to a directory that the service's user owns and no one else can write, under directories that no other user can change (a sticky one such as /tmp is fine).`);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Saves image bytes as a new file in the caller's subdirectory of `dir` and
|
|
200
|
+
* returns its `file:` URL. The extension comes from the bytes' signature, and
|
|
201
|
+
* the name is random, so a file is never overwritten.
|
|
202
|
+
*
|
|
203
|
+
* @throws When the bytes are not a PNG, JPEG or WebP image, or when the
|
|
204
|
+
* directory cannot be made, is not private to this user, or cannot be
|
|
205
|
+
* written. The message names the directory and the option that sets it.
|
|
206
|
+
*/
|
|
207
|
+
async function saveImageFile(bytes, dir, scope) {
|
|
208
|
+
const kind = imageKind(bytes);
|
|
209
|
+
if (!kind)
|
|
210
|
+
throw new Error('The provider returned data that is not a PNG, JPEG or WebP image.');
|
|
211
|
+
try {
|
|
212
|
+
const scopeDir = await callerDirectory(dir, scope);
|
|
213
|
+
const file = path.join(scopeDir, `agentos-image-${(0, node_crypto_1.randomBytes)(16).toString('hex')}.${kind.ext}`);
|
|
214
|
+
await fs.writeFile(file, bytes, { flag: 'wx', mode: 0o600 });
|
|
215
|
+
return (0, node_url_1.pathToFileURL)(file).href;
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
throw cannotSave(dir, error);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* The bytes of the saved image that `source` names, or `undefined` when
|
|
223
|
+
* `source` is not the `file:` URL of an image the saver wrote for this
|
|
224
|
+
* caller. Every reason gives `undefined`, so the caller's one refusal does
|
|
225
|
+
* not tell which files exist.
|
|
226
|
+
*
|
|
227
|
+
* The file must carry the saver's name pattern and sit directly in the
|
|
228
|
+
* caller's subdirectory of the images directory's real path: the same
|
|
229
|
+
* directory by device and inode, itself a directory and not a link, private
|
|
230
|
+
* to this user, as the images directory is, with no directory above the
|
|
231
|
+
* images directory that another user can change. It must be a regular file with
|
|
232
|
+
* one link and at most {@link MAX_SOURCE_BYTES}, and start with a PNG, JPEG
|
|
233
|
+
* or WebP signature. It is opened once, without following a link, and read
|
|
234
|
+
* from that handle.
|
|
235
|
+
*/
|
|
236
|
+
async function readSavedImage(source, dir, scope) {
|
|
237
|
+
let handle;
|
|
238
|
+
try {
|
|
239
|
+
const url = new URL(source);
|
|
240
|
+
if (url.protocol !== 'file:' || url.hostname !== '')
|
|
241
|
+
return undefined;
|
|
242
|
+
const named = (0, node_url_1.fileURLToPath)(url);
|
|
243
|
+
if (!SAVED_NAME.test(path.basename(named)))
|
|
244
|
+
return undefined;
|
|
245
|
+
// fs.promises.realpath asks the operating system. (fs.realpathSync and
|
|
246
|
+
// fs.realpath resolve ".." in the text first, as path.resolve does.)
|
|
247
|
+
const real = await fs.realpath(named);
|
|
248
|
+
if (!SAVED_NAME.test(path.basename(real)))
|
|
249
|
+
return undefined;
|
|
250
|
+
// The images directory as it is now, by its real path, so a directory
|
|
251
|
+
// swapped in for it is read as what it is; and the caller's directory in
|
|
252
|
+
// it by lstat, so a link to another caller's directory is refused.
|
|
253
|
+
const root = await fs.realpath(dir);
|
|
254
|
+
if (await changeableParent(root))
|
|
255
|
+
return undefined;
|
|
256
|
+
// The directories the file is opened through, every one above its own,
|
|
257
|
+
// must be ones no other user can change, as the images directory's are.
|
|
258
|
+
// Checked on the file's path itself, so a path spelled in another case on
|
|
259
|
+
// a file system that ignores case, which realpath can leave as given,
|
|
260
|
+
// reaches the same directories and is read.
|
|
261
|
+
if (await changeableParent(path.dirname(real)))
|
|
262
|
+
return undefined;
|
|
263
|
+
const scopePath = path.join(root, scope);
|
|
264
|
+
const [rootDir, parent, scopeDir] = await Promise.all([
|
|
265
|
+
fs.stat(root, { bigint: true }),
|
|
266
|
+
fs.stat(path.dirname(real), { bigint: true }),
|
|
267
|
+
fs.lstat(scopePath, { bigint: true }),
|
|
268
|
+
]);
|
|
269
|
+
if (!isPrivateDirectory(rootDir) || !isPrivateDirectory(scopeDir))
|
|
270
|
+
return undefined;
|
|
271
|
+
if (parent.dev !== scopeDir.dev || parent.ino !== scopeDir.ino)
|
|
272
|
+
return undefined;
|
|
273
|
+
// A FIFO would hold an open until a writer came; only a regular file is opened.
|
|
274
|
+
if (!(await fs.lstat(real)).isFile())
|
|
275
|
+
return undefined;
|
|
276
|
+
handle = await fs.open(real, node_fs_1.constants.O_RDONLY | (node_fs_1.constants.O_NOFOLLOW ?? 0) | (node_fs_1.constants.O_NONBLOCK ?? 0));
|
|
277
|
+
const stat = await handle.stat({ bigint: true });
|
|
278
|
+
if (!stat.isFile() || stat.nlink !== BigInt(1) || stat.size > BigInt(exports.MAX_SOURCE_BYTES))
|
|
279
|
+
return undefined;
|
|
280
|
+
// Checked again now that the file is open: the path still reaches the
|
|
281
|
+
// file opened, in the caller's directory.
|
|
282
|
+
const [linked, parentNow] = await Promise.all([
|
|
283
|
+
fs.lstat(real, { bigint: true }),
|
|
284
|
+
fs.stat(path.dirname(real), { bigint: true }),
|
|
285
|
+
]);
|
|
286
|
+
if (linked.dev !== stat.dev || linked.ino !== stat.ino)
|
|
287
|
+
return undefined;
|
|
288
|
+
if (parentNow.dev !== scopeDir.dev || parentNow.ino !== scopeDir.ino)
|
|
289
|
+
return undefined;
|
|
290
|
+
// One byte more than the size at open: a file that has grown is refused.
|
|
291
|
+
const size = Number(stat.size);
|
|
292
|
+
const buffer = Buffer.alloc(size + 1);
|
|
293
|
+
let length = 0;
|
|
294
|
+
while (length < buffer.length) {
|
|
295
|
+
const { bytesRead } = await handle.read(buffer, length, buffer.length - length, null);
|
|
296
|
+
if (bytesRead === 0)
|
|
297
|
+
break;
|
|
298
|
+
length += bytesRead;
|
|
299
|
+
}
|
|
300
|
+
if (length > size)
|
|
301
|
+
return undefined;
|
|
302
|
+
const bytes = buffer.subarray(0, length);
|
|
303
|
+
return imageKind(bytes) ? bytes : undefined;
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
308
|
+
finally {
|
|
309
|
+
await handle?.close().catch(() => { });
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Checks, before a provider is called, that the image data it may return can
|
|
314
|
+
* be saved for this caller: with no host `saveImage`, the images directory
|
|
315
|
+
* and the caller's directory in it are made and checked as a save makes and
|
|
316
|
+
* checks them. A provider bills for the image it makes, so a directory the
|
|
317
|
+
* saver would refuse is refused first.
|
|
318
|
+
*
|
|
319
|
+
* @throws The error a save would give, naming the directory and the option.
|
|
320
|
+
*/
|
|
321
|
+
async function checkImageStore(store, context) {
|
|
322
|
+
if (store.saveImage)
|
|
323
|
+
return;
|
|
324
|
+
try {
|
|
325
|
+
await callerDirectory(store.dir, scopeOf(context));
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
throw cannotSave(store.dir, error);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
/** The store for a pack's options: its `imageDir` and its `saveImage`, when that is a function. */
|
|
332
|
+
function imageStore(options) {
|
|
333
|
+
return {
|
|
334
|
+
dir: imageDirectory(options?.imageDir),
|
|
335
|
+
saveImage: typeof options?.saveImage === 'function' ? options.saveImage : undefined,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
/** The longest reference a host's saver may return. */
|
|
339
|
+
const MAX_REFERENCE_LENGTH = 2048;
|
|
340
|
+
/** The bytes of a base64 `data:` URL, the form AgentOS's providers give image data in; `undefined` for anything else. */
|
|
341
|
+
function dataUrlBytes(value) {
|
|
342
|
+
if (typeof value !== 'string' || !/^data:/i.test(value))
|
|
343
|
+
return undefined;
|
|
344
|
+
const comma = value.indexOf(',');
|
|
345
|
+
if (comma === -1 || !/; *base64 *$/i.test(value.slice(0, comma)))
|
|
346
|
+
return undefined;
|
|
347
|
+
return Buffer.from(value.slice(comma + 1), 'base64');
|
|
348
|
+
}
|
|
349
|
+
/** Whether a saver's result is an http(s) URL short enough to hand to the model. */
|
|
350
|
+
function isHttpReference(value) {
|
|
351
|
+
if (typeof value !== 'string' || value.length === 0 || value.length > MAX_REFERENCE_LENGTH)
|
|
352
|
+
return false;
|
|
353
|
+
try {
|
|
354
|
+
const { protocol } = new URL(value);
|
|
355
|
+
return protocol === 'http:' || protocol === 'https:';
|
|
356
|
+
}
|
|
357
|
+
catch {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* An image a provider returned, as the reference the caller gets: the
|
|
363
|
+
* provider's http(s) URL as it is, or, for image data, the URL under which
|
|
364
|
+
* it was saved. Image data is never returned itself: as a `data:` URL it
|
|
365
|
+
* would go into the model's next request, megabytes of base64. `undefined`
|
|
366
|
+
* when the provider returned neither a URL nor data.
|
|
367
|
+
*
|
|
368
|
+
* @throws When the data is not a PNG, JPEG or WebP image, when it cannot be
|
|
369
|
+
* saved, or when the host's saver throws or returns anything but an
|
|
370
|
+
* http(s) URL of at most 2,048 characters.
|
|
371
|
+
*/
|
|
372
|
+
async function storeImage(image, tool, store, context) {
|
|
373
|
+
if (!image)
|
|
374
|
+
return undefined;
|
|
375
|
+
if (typeof image.url === 'string' && /^https?:\/\//i.test(image.url))
|
|
376
|
+
return image.url;
|
|
377
|
+
const bytes = typeof image.base64 === 'string' && image.base64 !== ''
|
|
378
|
+
? Buffer.from(image.base64, 'base64')
|
|
379
|
+
: (dataUrlBytes(image.dataUrl) ?? dataUrlBytes(image.url));
|
|
380
|
+
if (!bytes || bytes.length === 0)
|
|
381
|
+
return undefined;
|
|
382
|
+
if (!store.saveImage)
|
|
383
|
+
return saveImageFile(bytes, store.dir, scopeOf(context));
|
|
384
|
+
const kind = imageKind(bytes);
|
|
385
|
+
if (!kind)
|
|
386
|
+
throw new Error('The provider returned data that is not a PNG, JPEG or WebP image.');
|
|
387
|
+
const reference = await store.saveImage({ bytes, mimeType: kind.mimeType, tool, context });
|
|
388
|
+
if (!isHttpReference(reference)) {
|
|
389
|
+
throw new Error("The host's saveImage must return an http(s) URL of at most 2,048 characters.");
|
|
390
|
+
}
|
|
391
|
+
return reference;
|
|
392
|
+
}
|
|
393
|
+
//# sourceMappingURL=imageFiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imageFiles.js","sourceRoot":"","sources":["../src/imageFiles.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Image Generation Extension Pack: the `generate_image` tool on
|
|
3
|
+
* AgentOS's `generateImage`, with OpenAI, OpenRouter, Stability AI and
|
|
4
|
+
* Replicate.
|
|
5
|
+
*
|
|
6
|
+
* Image data a provider returns is saved, never returned: to a file in the
|
|
7
|
+
* images directory (`imageDir`, `AGENTOS_IMAGE_DIR`, or a folder in the
|
|
8
|
+
* user's temp directory), or by the host's `saveImage`.
|
|
9
|
+
*/
|
|
1
10
|
import type { ExtensionPackContext, ExtensionPack } from '@framers/agentos';
|
|
11
|
+
import { type ImageQuality } from './ImageGenerationService.js';
|
|
12
|
+
import type { SaveImage } from './imageFiles.js';
|
|
2
13
|
export interface ImageGenerationOptions {
|
|
3
14
|
openaiApiKey?: string;
|
|
4
15
|
openrouterApiKey?: string;
|
|
@@ -7,11 +18,35 @@ export interface ImageGenerationOptions {
|
|
|
7
18
|
defaultProvider?: 'openai' | 'openrouter' | 'stability' | 'replicate';
|
|
8
19
|
defaultModel?: string;
|
|
9
20
|
defaultSize?: string;
|
|
10
|
-
defaultQuality?:
|
|
21
|
+
defaultQuality?: ImageQuality;
|
|
22
|
+
/**
|
|
23
|
+
* Where image data a provider returns is saved, each caller in a
|
|
24
|
+
* subdirectory of its own (else `AGENTOS_IMAGE_DIR`, else a folder in the
|
|
25
|
+
* user's temp directory). The directory must belong to the service's user,
|
|
26
|
+
* with no one else able to write it; it is made and checked before a
|
|
27
|
+
* provider is called, so a directory the tools refuse costs no image.
|
|
28
|
+
* Every directory above it must belong to that user or to root and be
|
|
29
|
+
* writable by no one else unless it is sticky, as `/tmp` is, so a disk-backed
|
|
30
|
+
* Kubernetes `emptyDir` (0777, not sticky) is refused as a place for it: make
|
|
31
|
+
* the mount sticky (`chmod 1777` in an init container), give it to the
|
|
32
|
+
* service's user, or pass `saveImage`. The image-editing and
|
|
33
|
+
* vision-pipeline packs take the same option: give all three the same
|
|
34
|
+
* directory, so an image this pack saved is a source for them.
|
|
35
|
+
*/
|
|
36
|
+
imageDir?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Stores image data in place of the default saver and returns the http(s)
|
|
39
|
+
* URL under which the host's clients load it. A `file:` URL names a file
|
|
40
|
+
* on the machine that ran the tool, so a host whose clients are elsewhere
|
|
41
|
+
* sets this.
|
|
42
|
+
*/
|
|
43
|
+
saveImage?: SaveImage;
|
|
11
44
|
priority?: number;
|
|
12
45
|
}
|
|
13
46
|
export declare function createExtensionPack(context: ExtensionPackContext): ExtensionPack;
|
|
14
47
|
export { ImageGenerationService } from './ImageGenerationService.js';
|
|
48
|
+
export type { ImageGenerationConfig, GenerateImageOptions, GeneratedImage, ImageQuality } from './ImageGenerationService.js';
|
|
49
|
+
export type { ImageStore, SaveImage } from './imageFiles.js';
|
|
15
50
|
export { GenerateImageTool } from './tools/generateImage.js';
|
|
16
51
|
export default createExtensionPack;
|
|
17
52
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EACpB,aAAa,EAEd,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AACH,OAAO,KAAK,EACV,oBAAoB,EACpB,aAAa,EAEd,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAA0B,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAC;AACxF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAOjD,MAAM,WAAW,sBAAsB;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,QAAQ,GAAG,YAAY,GAAG,WAAW,GAAG,WAAW,CAAC;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,YAAY,CAAC;IAC9B;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAwEhF;AAED,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC7H,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,eAAe,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,9 @@ exports.GenerateImageTool = exports.ImageGenerationService = void 0;
|
|
|
4
4
|
exports.createExtensionPack = createExtensionPack;
|
|
5
5
|
const ImageGenerationService_js_1 = require("./ImageGenerationService.js");
|
|
6
6
|
const generateImage_js_1 = require("./tools/generateImage.js");
|
|
7
|
+
// This pack compiles to CommonJS, where `require` reads the package's own
|
|
8
|
+
// version: `import.meta` does not exist there.
|
|
9
|
+
const { version } = require('../package.json');
|
|
7
10
|
function createExtensionPack(context) {
|
|
8
11
|
const options = (context.options ?? {});
|
|
9
12
|
const openaiApiKey = options.openaiApiKey ||
|
|
@@ -27,11 +30,13 @@ function createExtensionPack(context) {
|
|
|
27
30
|
defaultModel: options.defaultModel,
|
|
28
31
|
defaultSize: options.defaultSize,
|
|
29
32
|
defaultQuality: options.defaultQuality,
|
|
33
|
+
imageDir: options.imageDir,
|
|
34
|
+
saveImage: options.saveImage,
|
|
30
35
|
});
|
|
31
36
|
const tool = new generateImage_js_1.GenerateImageTool(service);
|
|
32
37
|
return {
|
|
33
38
|
name: '@framers/agentos-ext-image-generation',
|
|
34
|
-
version
|
|
39
|
+
version,
|
|
35
40
|
descriptors: [
|
|
36
41
|
{
|
|
37
42
|
id: tool.name,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,16 +6,18 @@ import type { ITool, JSONSchemaObject, ToolExecutionContext, ToolExecutionResult
|
|
|
6
6
|
import type { ImageGenerationService } from '../ImageGenerationService.js';
|
|
7
7
|
export interface GenerateImageInput {
|
|
8
8
|
prompt: string;
|
|
9
|
-
size?: '1024x1024' | '1792x1024' | '1024x1792';
|
|
9
|
+
size?: '1024x1024' | '1536x1024' | '1024x1536' | '1792x1024' | '1024x1792';
|
|
10
10
|
aspectRatio?: string;
|
|
11
|
-
quality?: 'standard' | 'hd';
|
|
11
|
+
quality?: 'low' | 'medium' | 'high' | 'auto' | 'standard' | 'hd';
|
|
12
12
|
style?: 'vivid' | 'natural';
|
|
13
13
|
provider?: 'openai' | 'openrouter' | 'stability' | 'replicate';
|
|
14
|
+
/** The provider's own model id, without a provider prefix. */
|
|
14
15
|
model?: string;
|
|
15
16
|
seed?: number;
|
|
16
17
|
negativePrompt?: string;
|
|
17
18
|
}
|
|
18
19
|
export interface GenerateImageOutput {
|
|
20
|
+
/** The image as a URL: the provider's, or the file: URL of the copy that was saved of the image data it returned. */
|
|
19
21
|
url: string;
|
|
20
22
|
revisedPrompt?: string;
|
|
21
23
|
provider: string;
|
|
@@ -26,12 +28,20 @@ export declare class GenerateImageTool implements ITool<GenerateImageInput, Gene
|
|
|
26
28
|
readonly id = "tool.generate_image";
|
|
27
29
|
readonly name = "generate_image";
|
|
28
30
|
readonly displayName = "Generate Image";
|
|
29
|
-
readonly description
|
|
31
|
+
readonly description: string;
|
|
30
32
|
readonly category = "media";
|
|
31
33
|
readonly hasSideEffects = false;
|
|
32
34
|
readonly inputSchema: JSONSchemaObject;
|
|
33
35
|
private service;
|
|
34
36
|
constructor(service: ImageGenerationService);
|
|
35
|
-
execute(args: GenerateImageInput,
|
|
37
|
+
execute(args: GenerateImageInput, context?: ToolExecutionContext): Promise<ToolExecutionResult<GenerateImageOutput>>;
|
|
38
|
+
/**
|
|
39
|
+
* The service call for a tool call's arguments, or the error for an
|
|
40
|
+
* argument the schema does not allow. Only the schema's own fields are
|
|
41
|
+
* taken: the model writes the arguments, and anything else it adds (`n`,
|
|
42
|
+
* `providerOptions`, whose `extraBody` goes into the provider's request)
|
|
43
|
+
* is not passed on.
|
|
44
|
+
*/
|
|
45
|
+
private optionsFrom;
|
|
36
46
|
}
|
|
37
47
|
//# sourceMappingURL=generateImage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateImage.d.ts","sourceRoot":"","sources":["../../src/tools/generateImage.ts"],"names":[],"mappings":"AACA;;;GAGG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC3G,OAAO,KAAK,EAAE,sBAAsB,EAAwB,MAAM,8BAA8B,CAAC;AAEjG,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"generateImage.d.ts","sourceRoot":"","sources":["../../src/tools/generateImage.ts"],"names":[],"mappings":"AACA;;;GAGG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC3G,OAAO,KAAK,EAAE,sBAAsB,EAAwB,MAAM,8BAA8B,CAAC;AAEjG,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;IACjE,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,QAAQ,CAAC,EAAE,QAAQ,GAAG,YAAY,GAAG,WAAW,GAAG,WAAW,CAAC;IAC/D,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,qHAAqH;IACrH,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AASD,qBAAa,iBAAkB,YAAW,KAAK,CAAC,kBAAkB,EAAE,mBAAmB,CAAC;IACtF,QAAQ,CAAC,EAAE,yBAAyB;IACpC,QAAQ,CAAC,IAAI,oBAAoB;IACjC,QAAQ,CAAC,WAAW,oBAAoB;IACxC,QAAQ,CAAC,WAAW,SAEgH;IACpI,QAAQ,CAAC,QAAQ,WAAW;IAC5B,QAAQ,CAAC,cAAc,SAAS;IAEhC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CA6CpC;IAEF,OAAO,CAAC,OAAO,CAAyB;gBAE5B,OAAO,EAAE,sBAAsB;IAIrC,OAAO,CACX,IAAI,EAAE,kBAAkB,EACxB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,mBAAmB,CAAC,mBAAmB,CAAC,CAAC;IAiCpD;;;;;;OAMG;IACH,OAAO,CAAC,WAAW;CAsBpB"}
|