@pasko70/pibo 1.4.3 → 1.4.5

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.
Files changed (23) hide show
  1. package/dist/apps/chat-ui/assets/{dist-BAXNalar.js → dist-85Cc5Hut.js} +1 -1
  2. package/dist/apps/chat-ui/assets/{dist-DnACFKyO.js → dist-B8jspmzT.js} +1 -1
  3. package/dist/apps/chat-ui/assets/{dist-CiDSXgtg.js → dist-BGqK-7Ep.js} +1 -1
  4. package/dist/apps/chat-ui/assets/{dist-C3PnEkhb.js → dist-BtF63vik.js} +1 -1
  5. package/dist/apps/chat-ui/assets/{dist-vKlxFkTa.js → dist-CQXtvBTs.js} +1 -1
  6. package/dist/apps/chat-ui/assets/{dist-ZB1-ui2y.js → dist-D1Laodgo.js} +1 -1
  7. package/dist/apps/chat-ui/assets/{dist-CYPL-B2Z.js → dist-DE8W5WKg.js} +1 -1
  8. package/dist/apps/chat-ui/assets/{dist-BwUvs6Ph.js → dist-DcME5mJj.js} +1 -1
  9. package/dist/apps/chat-ui/assets/{dist-BAGS_xkV.js → dist-eG89IJAV.js} +1 -1
  10. package/dist/apps/chat-ui/assets/{dist-C0zsJ8II.js → dist-ecTM1pdv.js} +1 -1
  11. package/dist/apps/chat-ui/assets/{dist-B2BEpL7n.js → dist-oLAGkW6G.js} +1 -1
  12. package/dist/apps/chat-ui/assets/{index-0x7tuTNX.js → index-D4uifikB.js} +17 -17
  13. package/dist/apps/chat-ui/index.html +1 -1
  14. package/dist/apps/chat-vscode-web/assets/{index-C3GTPyDo.js → index-CRUSv6iR.js} +3 -3
  15. package/dist/apps/chat-vscode-web/index.html +1 -1
  16. package/dist/apps/vscode-artifacts/latest.vsix +0 -0
  17. package/dist/apps/vscode-artifacts/{pibo-vscode-ext-1.4.2.vsix → pibo-vscode-ext-1.4.5.vsix} +0 -0
  18. package/dist/plugins/codex-compat.js +2 -0
  19. package/dist/providers/glm.js +1 -1
  20. package/dist/session-ui/terminalRows.js +55 -8
  21. package/dist/tools/codex-image-generation.js +272 -0
  22. package/package.json +1 -1
  23. package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.4.3.vsix +0 -0
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta name="theme-color" content="#101d22" />
7
7
  <title>Pibo</title>
8
- <script type="module" crossorigin src="/apps/chat-vscode/assets/index-C3GTPyDo.js"></script>
8
+ <script type="module" crossorigin src="/apps/chat-vscode/assets/index-CRUSv6iR.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/apps/chat-vscode/assets/index-B5QK07zO.css">
10
10
  </head>
11
11
  <body>
@@ -1,5 +1,6 @@
1
1
  import { dirname, resolve } from "node:path";
2
2
  import { fileURLToPath } from "node:url";
3
+ import { createCodexImageGenerationToolProfile } from "../tools/codex-image-generation.js";
3
4
  import { definePiboPlugin } from "./registry.js";
4
5
  const CODEX_COMPAT_REGISTERED_TOOL_NAMES = [
5
6
  "apply_patch",
@@ -22,6 +23,7 @@ export const piboCodexCompatPlugin = definePiboPlugin({
22
23
  description: toolDescriptions[name],
23
24
  });
24
25
  }
26
+ api.registerTool(createCodexImageGenerationToolProfile());
25
27
  api.registerContextFile({
26
28
  key: CODEX_BASE_PROMPT_CONTEXT_FILE_KEY,
27
29
  label: "Codex Base Prompt",
@@ -7,7 +7,7 @@ export const GLM_5_2_MODEL = {
7
7
  id: "GLM-5.2",
8
8
  name: "GLM 5.2",
9
9
  reasoning: false,
10
- contextWindow: 200000,
10
+ contextWindow: 1_000_000,
11
11
  maxTokens: 8192,
12
12
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
13
13
  input: ["text"],
@@ -128,7 +128,7 @@ function createToolRowCandidate(node, turnId) {
128
128
  const image = classifyImageTool(node);
129
129
  if (image) {
130
130
  const row = createImageToolRow(node, image);
131
- return { row, turnId, image: image.detail };
131
+ return { row, turnId, image: image.groupable ? image.detail : undefined };
132
132
  }
133
133
  const preview = previewLines(node.error ?? node.output, COMPACT_TERMINAL_OUTPUT_PREVIEW_LINES, node.error ? "red" : "dim");
134
134
  const row = {
@@ -159,7 +159,7 @@ function createToolRowCandidate(node, turnId) {
159
159
  }
160
160
  function createImageToolRow(node, image) {
161
161
  const status = mapStatus(node.status);
162
- const detailLabel = image.path ? `Path: ${image.path}` : image.query ? `Query: ${image.query}` : image.mimeType ? `Type: ${image.mimeType}` : "Image content returned";
162
+ const detailLabel = image.path ? `Path: ${image.path}` : image.artifactId ? `Artifact: ${image.artifactId}` : image.query ? `Query: ${image.query}` : image.mimeType ? `Type: ${image.mimeType}` : "Image content returned";
163
163
  return {
164
164
  id: node.id,
165
165
  kind: "tool.image",
@@ -168,7 +168,7 @@ function createImageToolRow(node, image) {
168
168
  lines: [
169
169
  {
170
170
  prefix: "bullet",
171
- tokens: [token(imageToolVerb(node.status, image.count), toneForStatus(node.status), "semibold")],
171
+ tokens: [token(image.verb, toneForStatus(node.status), "semibold")],
172
172
  },
173
173
  {
174
174
  prefix: "detail",
@@ -186,7 +186,7 @@ function createToolResultRowCandidate(node, turnId) {
186
186
  const image = classifyImageTool(node);
187
187
  if (image) {
188
188
  const row = createImageToolRow(node, image);
189
- return { row, turnId, image: image.detail };
189
+ return { row, turnId, image: image.groupable ? image.detail : undefined };
190
190
  }
191
191
  return { row: createToolResultRow(node), turnId };
192
192
  }
@@ -640,32 +640,48 @@ function detailItemsForGroup(candidates, kind) {
640
640
  function classifyImageTool(node) {
641
641
  const normalized = (node.title ?? "").trim().toLowerCase();
642
642
  const args = isRecord(node.input) ? node.input : undefined;
643
- const path = previewPath(args) ?? previewPath(isRecord(node.output) ? recordField(node.output, "details") : undefined) ?? previewPath(isRecord(node.output) ? node.output : undefined);
643
+ const output = isRecord(node.output) ? node.output : undefined;
644
+ const details = recordField(output, "details");
645
+ const codexOperation = normalized === "codex_image_generation" ? codexImageOperation(details) ?? codexImageOperationFromInput(args) : undefined;
646
+ const path = codexOperation
647
+ ? stringValue(details?.savedPath) ?? previewPath(details)
648
+ : previewPath(args) ?? previewPath(details) ?? previewPath(output);
649
+ const artifactId = codexOperation ? stringValue(details?.artifactId) : undefined;
644
650
  const query = previewQuery(args);
645
651
  const images = collectImagePayloads(node.output);
646
652
  const isImageTool = matchesTool(normalized, ["view_image", "image", "screenshot"]);
647
- if (!images.length && !isImageTool)
653
+ if (!images.length && !isImageTool && !codexOperation)
648
654
  return undefined;
649
655
  const mimeType = images.map((image) => image.mimeType).find((value) => Boolean(value));
650
656
  const count = Math.max(1, images.length);
651
657
  const summary = {
652
658
  type: images.length > 1 ? "images" : images.length === 1 ? "image" : "image_reference",
659
+ toolName: codexOperation ? node.title : undefined,
660
+ operation: codexOperation,
653
661
  path,
662
+ savedPath: codexOperation ? path : undefined,
663
+ artifactId,
664
+ model: codexOperation ? stringValue(details?.model) : undefined,
665
+ referencedImageCount: codexOperation ? numberValue(details?.referencedImageCount) : undefined,
654
666
  query,
655
667
  mimeType,
656
668
  count: count > 1 ? count : undefined,
657
669
  detail: images.length ? "Image data hidden in terminal view." : "Image path only; binary data is hidden in terminal view.",
658
670
  };
659
- const labelTarget = path ?? query ?? mimeType ?? "image";
671
+ const labelTarget = path ?? artifactId ?? query ?? mimeType ?? "image";
672
+ const verb = codexOperation ? codexImageVerb(node.status, codexOperation) : imageToolVerb(node.status, count);
660
673
  return {
661
674
  count,
662
675
  path,
676
+ artifactId,
663
677
  query,
664
678
  mimeType,
679
+ verb,
680
+ groupable: !codexOperation,
665
681
  summary,
666
682
  detail: {
667
683
  id: node.id,
668
- label: `${node.status === "error" ? "Image failed" : "Viewed image"} ${labelTarget}`,
684
+ label: `${verb} ${labelTarget}`,
669
685
  status: mapStatus(node.status),
670
686
  input: sanitizeImagePayload(node.input),
671
687
  output: summary,
@@ -725,6 +741,34 @@ function imageToolVerb(status, count) {
725
741
  return count > 1 ? "Image reads failed" : "Image read failed";
726
742
  return count > 1 ? "Viewed images" : "Viewed image";
727
743
  }
744
+ function codexImageOperation(details) {
745
+ const operation = stringValue(details?.operation);
746
+ return operation === "generate" || operation === "edit" ? operation : undefined;
747
+ }
748
+ function codexImageOperationFromInput(args) {
749
+ if (!args)
750
+ return undefined;
751
+ const referencedPaths = args.referenced_image_paths;
752
+ if (Array.isArray(referencedPaths) && referencedPaths.length > 0)
753
+ return "edit";
754
+ if (numberValue(args.num_last_images_to_include) !== undefined)
755
+ return "edit";
756
+ return "generate";
757
+ }
758
+ function codexImageVerb(status, operation) {
759
+ if (operation === "edit") {
760
+ if (status === "running")
761
+ return "Editing image";
762
+ if (status === "error")
763
+ return "Image edit failed";
764
+ return "Edited image";
765
+ }
766
+ if (status === "running")
767
+ return "Generating image";
768
+ if (status === "error")
769
+ return "Image generation failed";
770
+ return "Generated image";
771
+ }
728
772
  function recordField(record, key) {
729
773
  const value = record?.[key];
730
774
  return isRecord(value) ? value : undefined;
@@ -789,6 +833,9 @@ function previewQuery(value) {
789
833
  }
790
834
  return undefined;
791
835
  }
836
+ function numberValue(value) {
837
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
838
+ }
792
839
  function previewLines(value, maxVisibleLines, tone = "dim", _maxLineLength = 160) {
793
840
  const empty = { lines: [], visibleLineCount: 0, omittedLineCount: 0, totalLineCount: 0, maxVisibleLineCount: maxVisibleLines };
794
841
  const text = previewText(value);
@@ -0,0 +1,272 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { platform, release, arch } from "node:os";
3
+ import { dirname, extname, isAbsolute, join, resolve } from "node:path";
4
+ import { AuthStorage } from "@mariozechner/pi-coding-agent";
5
+ import { Type } from "@mariozechner/pi-ai";
6
+ import { defineTool } from "@mariozechner/pi-coding-agent";
7
+ import { getPiboHome } from "../core/pibo-home.js";
8
+ const OPENAI_CODEX_PROVIDER = "openai-codex";
9
+ const DEFAULT_CODEX_BACKEND_BASE_URL = "https://chatgpt.com/backend-api";
10
+ const CODEX_IMAGE_MODEL = "gpt-image-2";
11
+ const MAX_EDIT_IMAGES = 5;
12
+ const OPENAI_JWT_CLAIM_PATH = "https://api.openai.com/auth";
13
+ function trimTrailingSlashes(value) {
14
+ return value.replace(/\/+$/, "");
15
+ }
16
+ export function resolveCodexImageUrl(operation, baseUrl) {
17
+ const raw = baseUrl && baseUrl.trim().length > 0 ? baseUrl : DEFAULT_CODEX_BACKEND_BASE_URL;
18
+ let normalized = trimTrailingSlashes(raw.trim());
19
+ if (normalized.endsWith("/codex/responses"))
20
+ normalized = normalized.slice(0, -"/responses".length);
21
+ if (!normalized.endsWith("/codex"))
22
+ normalized = `${normalized}/codex`;
23
+ return `${normalized}/images/${operation}`;
24
+ }
25
+ function decodeJwtPayload(token) {
26
+ try {
27
+ const payload = token.split(".")[1];
28
+ if (!payload)
29
+ return undefined;
30
+ const normalized = payload.replace(/-/g, "+").replace(/_/g, "/");
31
+ const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=");
32
+ const parsed = JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
33
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : undefined;
34
+ }
35
+ catch {
36
+ return undefined;
37
+ }
38
+ }
39
+ function getOpenAiAccountId(accessToken, storedAccountId) {
40
+ if (typeof storedAccountId === "string" && storedAccountId.trim().length > 0)
41
+ return storedAccountId;
42
+ const payload = decodeJwtPayload(accessToken);
43
+ const auth = payload?.[OPENAI_JWT_CLAIM_PATH];
44
+ if (!auth || typeof auth !== "object" || Array.isArray(auth))
45
+ return undefined;
46
+ const accountId = auth.chatgpt_account_id;
47
+ return typeof accountId === "string" && accountId.trim().length > 0 ? accountId : undefined;
48
+ }
49
+ async function getCodexImageAuth() {
50
+ const authStorage = AuthStorage.create();
51
+ const credential = authStorage.get(OPENAI_CODEX_PROVIDER);
52
+ if (credential?.type !== "oauth") {
53
+ throw new Error("codex_image_generation requires ChatGPT/Codex OAuth login for provider openai-codex. Use the existing OpenAI Codex login flow; API keys are not supported for this tool.");
54
+ }
55
+ const accessToken = await authStorage.getApiKey(OPENAI_CODEX_PROVIDER, { includeFallback: false });
56
+ if (!accessToken) {
57
+ throw new Error("codex_image_generation could not load a ChatGPT/Codex OAuth access token for provider openai-codex. Please log in again with the OpenAI Codex login flow.");
58
+ }
59
+ const accountId = getOpenAiAccountId(accessToken, credential.accountId);
60
+ if (!accountId) {
61
+ throw new Error("codex_image_generation could not resolve the ChatGPT account id from the openai-codex OAuth credential. Please log in again with the OpenAI Codex login flow.");
62
+ }
63
+ return { accessToken, accountId };
64
+ }
65
+ function mimeTypeForPath(path) {
66
+ switch (extname(path).toLowerCase()) {
67
+ case ".jpg":
68
+ case ".jpeg":
69
+ return "image/jpeg";
70
+ case ".webp":
71
+ return "image/webp";
72
+ case ".gif":
73
+ return "image/gif";
74
+ default:
75
+ return "image/png";
76
+ }
77
+ }
78
+ function resolveCwd(baseCwd, path) {
79
+ return isAbsolute(path) ? path : resolve(baseCwd, path);
80
+ }
81
+ function isImageContent(value) {
82
+ return Boolean(value)
83
+ && typeof value === "object"
84
+ && value.type === "image"
85
+ && typeof value.data === "string"
86
+ && typeof value.mimeType === "string";
87
+ }
88
+ function imageContentToDataUrl(image) {
89
+ if (image.data.startsWith("data:"))
90
+ return image.data;
91
+ return `data:${image.mimeType};base64,${image.data}`;
92
+ }
93
+ async function imageFileToDataUrl(cwd, path) {
94
+ const resolved = resolveCwd(cwd, path);
95
+ const data = await readFile(resolved);
96
+ return `data:${mimeTypeForPath(resolved)};base64,${data.toString("base64")}`;
97
+ }
98
+ function contentFromSessionEntry(entry) {
99
+ if (!entry || typeof entry !== "object")
100
+ return undefined;
101
+ const record = entry;
102
+ if (record.type === "message") {
103
+ const message = record.message;
104
+ return message && typeof message === "object" ? message.content : undefined;
105
+ }
106
+ if (record.type === "custom_message")
107
+ return record.content;
108
+ return undefined;
109
+ }
110
+ function recentImagesFromSessionEntries(entries, count) {
111
+ const images = [];
112
+ for (const entry of [...entries].reverse()) {
113
+ const content = contentFromSessionEntry(entry);
114
+ if (!Array.isArray(content))
115
+ continue;
116
+ for (const item of [...content].reverse()) {
117
+ if (!isImageContent(item))
118
+ continue;
119
+ images.push(imageContentToDataUrl(item));
120
+ if (images.length === count)
121
+ break;
122
+ }
123
+ if (images.length === count)
124
+ break;
125
+ }
126
+ images.reverse();
127
+ return images.map((image_url) => ({ image_url }));
128
+ }
129
+ function validateImageArgs(params) {
130
+ if (params.prompt.trim().length === 0)
131
+ throw new Error("`prompt` must not be empty.");
132
+ const pathCount = params.referenced_image_paths?.length ?? 0;
133
+ if (pathCount > MAX_EDIT_IMAGES)
134
+ throw new Error(`\`referenced_image_paths\` must contain at most ${MAX_EDIT_IMAGES} paths.`);
135
+ if (pathCount > 0 && params.num_last_images_to_include !== undefined) {
136
+ throw new Error("Provide only one of `referenced_image_paths` or `num_last_images_to_include`.");
137
+ }
138
+ if (params.num_last_images_to_include !== undefined) {
139
+ if (!Number.isInteger(params.num_last_images_to_include) || params.num_last_images_to_include < 1 || params.num_last_images_to_include > MAX_EDIT_IMAGES) {
140
+ throw new Error(`\`num_last_images_to_include\` must be an integer between 1 and ${MAX_EDIT_IMAGES}.`);
141
+ }
142
+ }
143
+ }
144
+ function sanitizePathPart(value) {
145
+ return value.replace(/[^a-zA-Z0-9._-]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 120) || "unknown";
146
+ }
147
+ function artifactPath(sessionId, toolCallId) {
148
+ const safeSessionId = sanitizePathPart(sessionId?.trim() || "local");
149
+ const safeToolCallId = sanitizePathPart(toolCallId || `image_${Date.now()}`);
150
+ const artifactId = `${safeSessionId}/${safeToolCallId}.png`;
151
+ return {
152
+ artifactId,
153
+ savedPath: join(getPiboHome(), "generated_images", safeSessionId, `${safeToolCallId}.png`),
154
+ };
155
+ }
156
+ async function saveGeneratedImage(sessionId, toolCallId, b64Json) {
157
+ const target = artifactPath(sessionId, toolCallId);
158
+ await mkdir(dirname(target.savedPath), { recursive: true });
159
+ await writeFile(target.savedPath, Buffer.from(b64Json.trim(), "base64"));
160
+ return target;
161
+ }
162
+ function truncateErrorBody(text) {
163
+ const compact = text.replace(/\s+/g, " ").trim();
164
+ return compact.length > 1000 ? `${compact.slice(0, 1000)}…` : compact;
165
+ }
166
+ async function postCodexImageRequest(operation, body, auth, signal, baseUrl) {
167
+ const url = resolveCodexImageUrl(operation, baseUrl);
168
+ const response = await fetch(url, {
169
+ method: "POST",
170
+ signal,
171
+ headers: {
172
+ Authorization: `Bearer ${auth.accessToken}`,
173
+ "chatgpt-account-id": auth.accountId,
174
+ originator: "pi",
175
+ "User-Agent": `pibo (${platform()} ${release()}; ${arch()})`,
176
+ Accept: "application/json",
177
+ "Content-Type": "application/json",
178
+ },
179
+ body: JSON.stringify(body),
180
+ });
181
+ if (!response.ok) {
182
+ const text = await response.text().catch(() => "");
183
+ throw new Error(`Codex image ${operation} request failed: ${response.status}${text ? ` ${truncateErrorBody(text)}` : ""}`);
184
+ }
185
+ return await response.json();
186
+ }
187
+ function firstImageB64(response) {
188
+ const b64 = response.data?.[0]?.b64_json;
189
+ if (!b64 || typeof b64 !== "string")
190
+ throw new Error("Codex image generation returned no image data.");
191
+ return b64;
192
+ }
193
+ function createRequest(prompt, images) {
194
+ return {
195
+ ...(images && images.length > 0 ? { images } : {}),
196
+ prompt,
197
+ background: "auto",
198
+ model: CODEX_IMAGE_MODEL,
199
+ quality: "auto",
200
+ size: "auto",
201
+ };
202
+ }
203
+ async function collectEditImages(params, cwd, sessionEntries) {
204
+ const paths = params.referenced_image_paths ?? [];
205
+ if (paths.length > 0) {
206
+ return await Promise.all(paths.map(async (path) => ({ image_url: await imageFileToDataUrl(cwd, path) })));
207
+ }
208
+ if (params.num_last_images_to_include !== undefined) {
209
+ const images = recentImagesFromSessionEntries(sessionEntries, params.num_last_images_to_include);
210
+ if (images.length !== params.num_last_images_to_include) {
211
+ throw new Error(`Requested the last ${params.num_last_images_to_include} conversation images, but only ${images.length} were available.`);
212
+ }
213
+ return images;
214
+ }
215
+ return [];
216
+ }
217
+ export function createCodexImageGenerationToolDefinition(context = {}, options = {}) {
218
+ return defineTool({
219
+ name: "codex_image_generation",
220
+ label: "Codex Image Generation",
221
+ description: "Generates or edits images through the ChatGPT/Codex backend API using openai-codex OAuth entitlement. Does not use the public OpenAI Images API.",
222
+ promptSnippet: "Use codex_image_generation to create an image from a prompt, or to edit referenced/recent images with the Codex/ChatGPT image backend.",
223
+ promptGuidelines: [
224
+ "Use codex_image_generation for image generation and image edits when the user asks for pictures, visual variants, or edits to existing images.",
225
+ "For edits, pass either referenced_image_paths for local image files or num_last_images_to_include for recent conversation images, but not both.",
226
+ ],
227
+ executionMode: "sequential",
228
+ parameters: Type.Object({
229
+ prompt: Type.String({ description: "Image generation/editing prompt. Be specific about the desired final image." }),
230
+ referenced_image_paths: Type.Optional(Type.Array(Type.String({ description: "Local filesystem image path to edit." }), { description: `Optional local image paths to edit. At most ${MAX_EDIT_IMAGES}.` })),
231
+ num_last_images_to_include: Type.Optional(Type.Number({ description: `Use the last N conversation images as edit references. Must be between 1 and ${MAX_EDIT_IMAGES}.` })),
232
+ }),
233
+ async execute(toolCallId, params, signal, _onUpdate, ctx) {
234
+ validateImageArgs(params);
235
+ const images = await collectEditImages(params, ctx.cwd, ctx.sessionManager.getBranch());
236
+ const operation = images.length > 0 ? "edit" : "generate";
237
+ const endpoint = operation === "edit" ? "edits" : "generations";
238
+ const auth = await getCodexImageAuth();
239
+ const response = await postCodexImageRequest(endpoint, createRequest(params.prompt, images), auth, signal, options.baseUrl);
240
+ const result = firstImageB64(response);
241
+ const saved = await saveGeneratedImage(context.piboSessionId, toolCallId, result);
242
+ const details = {
243
+ provider: OPENAI_CODEX_PROVIDER,
244
+ api: "codex-chatgpt-images",
245
+ operation,
246
+ model: CODEX_IMAGE_MODEL,
247
+ savedPath: saved.savedPath,
248
+ artifactId: saved.artifactId,
249
+ referencedImageCount: images.length,
250
+ endpoint,
251
+ created: response.created,
252
+ background: response.background,
253
+ quality: response.quality,
254
+ size: response.size,
255
+ };
256
+ return {
257
+ content: [
258
+ { type: "image", data: result, mimeType: "image/png" },
259
+ { type: "text", text: `Generated image saved to ${saved.savedPath}` },
260
+ ],
261
+ details,
262
+ };
263
+ },
264
+ });
265
+ }
266
+ export function createCodexImageGenerationToolProfile() {
267
+ return {
268
+ name: "codex_image_generation",
269
+ description: "Generate and edit images through the ChatGPT/Codex backend API using openai-codex OAuth.",
270
+ createDefinition: createCodexImageGenerationToolDefinition,
271
+ };
272
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pasko70/pibo",
3
- "version": "1.4.3",
3
+ "version": "1.4.5",
4
4
  "type": "module",
5
5
  "imports": {
6
6
  "vscode": "./src/apps/chat-vscode/extension/src/vscode-shim.js"