@agimon-ai/po-artifact 0.0.0-alpha.2 → 0.0.0-alpha.3
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 +1 -1
- package/dist/{artifactFiles-B8w_HQXv.mjs → artifactFiles-8yvD8PX3.mjs} +2 -2
- package/dist/artifactFiles-8yvD8PX3.mjs.map +1 -0
- package/dist/{artifactFiles-Bo5Mhwq0.cjs → artifactFiles-Cr2X2lhY.cjs} +2 -2
- package/dist/artifactFiles-Cr2X2lhY.cjs.map +1 -0
- package/dist/artifactFiles.cjs +1 -1
- package/dist/artifactFiles.mjs +1 -1
- package/dist/extensions/server.cjs +1 -1
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.d.mts +1 -1
- package/dist/extensions/server.mjs +1 -1
- package/dist/extensions/server.mjs.map +1 -1
- package/dist/extensions/web.mjs +27 -27
- package/dist/extensions/web.mjs.map +1 -1
- package/package.json +16 -16
- package/dist/artifactFiles-B8w_HQXv.mjs.map +0 -1
- package/dist/artifactFiles-Bo5Mhwq0.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Always-loaded session artifact storage and web/native views.
|
|
4
4
|
|
|
5
5
|
Each session owns a durable folder under the configured Po agent directory:
|
|
6
|
-
`
|
|
6
|
+
`po-artifacts/<session-id>/`. The agent's system prompt names that folder for
|
|
7
7
|
user-designated artifacts and important deliverable documents. Ordinary source
|
|
8
8
|
edits stay in the project.
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@ import fs from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { piAgentDirectory } from "@agimon-ai/po-core/piSettings";
|
|
5
5
|
//#region src/constants/artifacts.ts
|
|
6
|
-
const ARTIFACT_DIRECTORY = "
|
|
6
|
+
const ARTIFACT_DIRECTORY = "po-artifacts";
|
|
7
7
|
const MAX_PREVIEW_BYTES = 1048576;
|
|
8
8
|
const MAX_CONTENT_BYTES = 26214400;
|
|
9
9
|
const ARTIFACT_REFRESH_MS = 2e3;
|
|
@@ -194,4 +194,4 @@ async function listArtifacts(root) {
|
|
|
194
194
|
//#endregion
|
|
195
195
|
export { ArtifactAccessError as a, MAX_CONTENT_BYTES as c, readArtifactBytes as i, MAX_PREVIEW_BYTES as l, listArtifacts as n, artifactRoot as o, openArtifact as r, ARTIFACT_REFRESH_MS as s, ensureArtifactRoot as t };
|
|
196
196
|
|
|
197
|
-
//# sourceMappingURL=artifactFiles-
|
|
197
|
+
//# sourceMappingURL=artifactFiles-8yvD8PX3.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifactFiles-8yvD8PX3.mjs","names":[],"sources":["../src/constants/artifacts.ts","../src/services/artifactFiles/artifactPaths.ts","../src/services/artifactFiles/index.ts"],"sourcesContent":["export const ARTIFACT_DIRECTORY = 'po-artifacts';\nexport const MAX_PREVIEW_BYTES = 1024 * 1024;\nexport const MAX_CONTENT_BYTES = 25 * 1024 * 1024;\nexport const ARTIFACT_REFRESH_MS = 2000;\n\nexport const ARTIFACT_CONTENT_TYPES: Readonly<Record<string, string>> = {\n '.avif': 'image/avif',\n '.bmp': 'image/bmp',\n '.gif': 'image/gif',\n '.jpeg': 'image/jpeg',\n '.jpg': 'image/jpeg',\n '.png': 'image/png',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.ico': 'image/vnd.microsoft.icon',\n '.mp3': 'audio/mpeg',\n '.wav': 'audio/wav',\n '.m4a': 'audio/mp4',\n '.ogg': 'audio/ogg',\n '.m4v': 'video/mp4',\n '.mov': 'video/quicktime',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.pdf': 'application/pdf',\n '.md': 'text/markdown; charset=utf-8',\n '.markdown': 'text/markdown; charset=utf-8',\n '.txt': 'text/plain; charset=utf-8',\n '.html': 'text/html; charset=utf-8',\n '.htm': 'text/html; charset=utf-8',\n};\n","import path from 'node:path';\n\nimport { piAgentDirectory } from '@agimon-ai/po-core/piSettings';\n\nimport { ARTIFACT_CONTENT_TYPES, ARTIFACT_DIRECTORY } from '../../constants/artifacts';\n\nexport class ArtifactAccessError extends Error {\n constructor(\n message: string,\n readonly status: 400 | 403 | 404 | 413,\n ) {\n super(message);\n }\n}\n\nexport function artifactRoot(sessionId: string, environment: Readonly<Record<string, string | undefined>>): string {\n if (!/^[a-zA-Z0-9_-]+$/u.test(sessionId)) throw new ArtifactAccessError('Invalid artifact session identity.', 400);\n return path.join(piAgentDirectory(environment, environment.HOME), ARTIFACT_DIRECTORY, sessionId);\n}\n\nexport function artifactMimeType(relativePath: string): string {\n return ARTIFACT_CONTENT_TYPES[path.extname(relativePath).toLowerCase()] ?? 'application/octet-stream';\n}\n","import { constants } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport type { ArtifactItem, ArtifactSnapshot } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactMimeType } from './artifactPaths';\n\nexport { ArtifactAccessError, artifactMimeType, artifactRoot } from './artifactPaths';\n\nfunction inside(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);\n}\n\nfunction codeOf(error: unknown): string | undefined {\n return error instanceof Error && 'code' in error ? String(error.code) : undefined;\n}\n\n/** The owned directories must not redirect artifact access through a symlink. */\nasync function checkedRoot(root: string): Promise<string> {\n const base = path.dirname(root);\n for (const directory of [base, root]) {\n const stat = await fs.lstat(directory);\n if (!stat.isDirectory() || stat.isSymbolicLink())\n throw new ArtifactAccessError('The artifact folder is unavailable.', 403);\n }\n const realBase = await fs.realpath(base);\n const realRoot = await fs.realpath(root);\n if (!inside(realBase, realRoot))\n throw new ArtifactAccessError('The artifact folder is outside its storage directory.', 403);\n return realRoot;\n}\n\nexport async function ensureArtifactRoot(root: string): Promise<void> {\n await fs.mkdir(path.dirname(root), { recursive: true, mode: 0o700 });\n await fs.mkdir(root, { recursive: true, mode: 0o700 });\n await checkedRoot(root);\n}\n\n/** Open without following mutable ancestors. Node exposes no portable openat API. */\nasync function openContained(root: string, segments: string[]): Promise<fs.FileHandle> {\n if (process.platform === 'darwin') {\n // Darwin O_NOFOLLOW_ANY checks every component atomically. Do not combine it with O_NOFOLLOW.\n return fs.open(path.join(root, ...segments), constants.O_RDONLY | constants.O_NONBLOCK | 0x20000000);\n }\n if (process.platform !== 'linux') throw new ArtifactAccessError('Secure artifact reads require macOS or Linux.', 403);\n const flags = constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW;\n let directory = await fs.open(path.dirname(root), flags);\n try {\n for (const segment of [path.basename(root), ...segments.slice(0, -1)]) {\n const child = await fs.open(`/proc/self/fd/${directory.fd}/${segment}`, flags);\n await directory.close();\n directory = child;\n }\n return await fs.open(\n `/proc/self/fd/${directory.fd}/${segments.at(-1)!}`,\n constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK,\n );\n } finally {\n await directory.close();\n }\n}\n\n/** One guarded open shared by metadata, text and byte reads. The caller closes the handle. */\nexport async function openArtifact(root: string, requested: string | undefined) {\n if (\n !requested ||\n requested.includes('\\0') ||\n requested.includes('\\\\') ||\n path.isAbsolute(requested) ||\n requested.split('/').some((part) => part === '..' || part === '.' || part === '')\n )\n throw new ArtifactAccessError('Invalid artifact path.', 400);\n try {\n const realRoot = await checkedRoot(root);\n const candidate = path.resolve(realRoot, requested);\n if (!inside(realRoot, candidate))\n throw new ArtifactAccessError('The artifact is outside this session folder.', 403);\n let current = realRoot;\n const segments = requested.split('/');\n for (let index = 0; index < segments.length; index += 1) {\n current = path.join(current, segments[index]!);\n const stat = await fs.lstat(current);\n if (stat.isSymbolicLink()) throw new ArtifactAccessError('Artifact symlinks cannot be read.', 403);\n if (index < segments.length - 1 ? !stat.isDirectory() : !stat.isFile())\n throw new ArtifactAccessError('The artifact is not a regular file.', 404);\n }\n const real = await fs.realpath(candidate);\n if (!inside(realRoot, real)) throw new ArtifactAccessError('The artifact is outside this session folder.', 403);\n const expected = await fs.lstat(real);\n const handle = await openContained(realRoot, segments);\n try {\n const stat = await handle.stat();\n if (!stat.isFile() || stat.dev !== expected.dev || stat.ino !== expected.ino)\n throw new ArtifactAccessError('The artifact changed while it was opened. Try again.', 403);\n return { handle, stat, path: requested, mimeType: artifactMimeType(requested) };\n } catch (error) {\n await handle.close();\n throw error;\n }\n } catch (error) {\n if (error instanceof ArtifactAccessError) throw error;\n if (codeOf(error) === 'ENOENT' || codeOf(error) === 'ENOTDIR')\n throw new ArtifactAccessError('The artifact no longer exists.', 404);\n if (codeOf(error) === 'EACCES' || codeOf(error) === 'EPERM' || codeOf(error) === 'ELOOP')\n throw new ArtifactAccessError('The artifact cannot be read.', 403);\n throw error;\n }\n}\n\n/** Bound allocation and read length even when the file grows after its metadata was read. */\nexport async function readArtifactBytes(handle: fs.FileHandle, size: number, offset = 0): Promise<Uint8Array> {\n const bytes = new Uint8Array(size);\n let read = 0;\n while (read < size) {\n const result = await handle.read(bytes, read, size - read, offset + read);\n if (result.bytesRead === 0) break;\n read += result.bytesRead;\n }\n return bytes.subarray(0, read);\n}\n\nexport async function listArtifacts(root: string): Promise<ArtifactSnapshot> {\n const items: ArtifactItem[] = [];\n try {\n const realRoot = await checkedRoot(root);\n const visit = async (directory: string): Promise<void> => {\n for (const entry of await fs.readdir(directory, { withFileTypes: true })) {\n const location = path.join(directory, entry.name);\n if (entry.isDirectory()) {\n // Recheck each directory rather than trusting a readdir entry across a rename.\n const stat = await fs.lstat(location);\n if (!stat.isDirectory() || stat.isSymbolicLink()) continue;\n const real = await fs.realpath(location);\n if (inside(realRoot, real)) await visit(real);\n } else if (entry.isFile()) {\n const relativePath = path.relative(realRoot, location).split(path.sep).join('/');\n try {\n const file = await openArtifact(root, relativePath);\n try {\n items.push({ path: relativePath, name: entry.name, size: file.stat.size, modifiedAt: file.stat.mtimeMs });\n } finally {\n await file.handle.close();\n }\n } catch (error) {\n if (error instanceof ArtifactAccessError && (error.status === 404 || error.status === 403)) continue;\n throw error;\n }\n }\n }\n };\n await visit(realRoot);\n items.sort(\n (left, right) =>\n right.modifiedAt - left.modifiedAt || (left.path < right.path ? -1 : left.path > right.path ? 1 : 0),\n );\n return { items };\n } catch (error) {\n if (codeOf(error) === 'ENOENT') return { items: [] };\n return { items: [], error: error instanceof Error ? error.message : 'The artifact folder could not be read.' };\n }\n}\n"],"mappings":";;;;;AAAA,MAAa,qBAAqB;AAClC,MAAa,oBAAoB;AACjC,MAAa,oBAAoB;AACjC,MAAa,sBAAsB;AAEnC,MAAa,yBAA2D;CACtE,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;CACT,QAAQ;AACV;;;ACvBA,IAAa,sBAAb,cAAyC,MAAM;CAGlC;CAFX,YACE,SACA,QACA;EACA,MAAM,OAAO;EAFJ,KAAA,SAAA;CAGX;AACF;AAEA,SAAgB,aAAa,WAAmB,aAAmE;CACjH,IAAI,CAAC,oBAAoB,KAAK,SAAS,GAAG,MAAM,IAAI,oBAAoB,sCAAsC,GAAG;CACjH,OAAO,KAAK,KAAK,iBAAiB,aAAa,YAAY,IAAI,GAAG,oBAAoB,SAAS;AACjG;AAEA,SAAgB,iBAAiB,cAA8B;CAC7D,OAAO,uBAAuB,KAAK,QAAQ,YAAY,CAAC,CAAC,YAAY,MAAM;AAC7E;;;ACbA,SAAS,OAAO,MAAc,WAA4B;CACxD,MAAM,WAAW,KAAK,SAAS,MAAM,SAAS;CAC9C,OAAO,aAAa,QAAQ,CAAC,SAAS,WAAW,KAAK,KAAK,KAAK,KAAK,CAAC,KAAK,WAAW,QAAQ;AAChG;AAEA,SAAS,OAAO,OAAoC;CAClD,OAAO,iBAAiB,SAAS,UAAU,QAAQ,OAAO,MAAM,IAAI,IAAI,KAAA;AAC1E;;AAGA,eAAe,YAAY,MAA+B;CACxD,MAAM,OAAO,KAAK,QAAQ,IAAI;CAC9B,KAAK,MAAM,aAAa,CAAC,MAAM,IAAI,GAAG;EACpC,MAAM,OAAO,MAAM,GAAG,MAAM,SAAS;EACrC,IAAI,CAAC,KAAK,YAAY,KAAK,KAAK,eAAe,GAC7C,MAAM,IAAI,oBAAoB,uCAAuC,GAAG;CAC5E;CACA,MAAM,WAAW,MAAM,GAAG,SAAS,IAAI;CACvC,MAAM,WAAW,MAAM,GAAG,SAAS,IAAI;CACvC,IAAI,CAAC,OAAO,UAAU,QAAQ,GAC5B,MAAM,IAAI,oBAAoB,yDAAyD,GAAG;CAC5F,OAAO;AACT;AAEA,eAAsB,mBAAmB,MAA6B;CACpE,MAAM,GAAG,MAAM,KAAK,QAAQ,IAAI,GAAG;EAAE,WAAW;EAAM,MAAM;CAAM,CAAC;CACnE,MAAM,GAAG,MAAM,MAAM;EAAE,WAAW;EAAM,MAAM;CAAM,CAAC;CACrD,MAAM,YAAY,IAAI;AACxB;;AAGA,eAAe,cAAc,MAAc,UAA4C;CACrF,IAAI,QAAQ,aAAa,UAEvB,OAAO,GAAG,KAAK,KAAK,KAAK,MAAM,GAAG,QAAQ,GAAG,UAAU,WAAW,UAAU,aAAa,SAAU;CAErG,IAAI,QAAQ,aAAa,SAAS,MAAM,IAAI,oBAAoB,iDAAiD,GAAG;CACpH,MAAM,QAAQ,UAAU,WAAW,UAAU,cAAc,UAAU;CACrE,IAAI,YAAY,MAAM,GAAG,KAAK,KAAK,QAAQ,IAAI,GAAG,KAAK;CACvD,IAAI;EACF,KAAK,MAAM,WAAW,CAAC,KAAK,SAAS,IAAI,GAAG,GAAG,SAAS,MAAM,GAAG,EAAE,CAAC,GAAG;GACrE,MAAM,QAAQ,MAAM,GAAG,KAAK,iBAAiB,UAAU,GAAG,GAAG,WAAW,KAAK;GAC7E,MAAM,UAAU,MAAM;GACtB,YAAY;EACd;EACA,OAAO,MAAM,GAAG,KACd,iBAAiB,UAAU,GAAG,GAAG,SAAS,GAAG,EAAE,KAC/C,UAAU,WAAW,UAAU,aAAa,UAAU,UACxD;CACF,UAAU;EACR,MAAM,UAAU,MAAM;CACxB;AACF;;AAGA,eAAsB,aAAa,MAAc,WAA+B;CAC9E,IACE,CAAC,aACD,UAAU,SAAS,IAAI,KACvB,UAAU,SAAS,IAAI,KACvB,KAAK,WAAW,SAAS,KACzB,UAAU,MAAM,GAAG,CAAC,CAAC,MAAM,SAAS,SAAS,QAAQ,SAAS,OAAO,SAAS,EAAE,GAEhF,MAAM,IAAI,oBAAoB,0BAA0B,GAAG;CAC7D,IAAI;EACF,MAAM,WAAW,MAAM,YAAY,IAAI;EACvC,MAAM,YAAY,KAAK,QAAQ,UAAU,SAAS;EAClD,IAAI,CAAC,OAAO,UAAU,SAAS,GAC7B,MAAM,IAAI,oBAAoB,gDAAgD,GAAG;EACnF,IAAI,UAAU;EACd,MAAM,WAAW,UAAU,MAAM,GAAG;EACpC,KAAK,IAAI,QAAQ,GAAG,QAAQ,SAAS,QAAQ,SAAS,GAAG;GACvD,UAAU,KAAK,KAAK,SAAS,SAAS,MAAO;GAC7C,MAAM,OAAO,MAAM,GAAG,MAAM,OAAO;GACnC,IAAI,KAAK,eAAe,GAAG,MAAM,IAAI,oBAAoB,qCAAqC,GAAG;GACjG,IAAI,QAAQ,SAAS,SAAS,IAAI,CAAC,KAAK,YAAY,IAAI,CAAC,KAAK,OAAO,GACnE,MAAM,IAAI,oBAAoB,uCAAuC,GAAG;EAC5E;EACA,MAAM,OAAO,MAAM,GAAG,SAAS,SAAS;EACxC,IAAI,CAAC,OAAO,UAAU,IAAI,GAAG,MAAM,IAAI,oBAAoB,gDAAgD,GAAG;EAC9G,MAAM,WAAW,MAAM,GAAG,MAAM,IAAI;EACpC,MAAM,SAAS,MAAM,cAAc,UAAU,QAAQ;EACrD,IAAI;GACF,MAAM,OAAO,MAAM,OAAO,KAAK;GAC/B,IAAI,CAAC,KAAK,OAAO,KAAK,KAAK,QAAQ,SAAS,OAAO,KAAK,QAAQ,SAAS,KACvE,MAAM,IAAI,oBAAoB,wDAAwD,GAAG;GAC3F,OAAO;IAAE;IAAQ;IAAM,MAAM;IAAW,UAAU,iBAAiB,SAAS;GAAE;EAChF,SAAS,OAAO;GACd,MAAM,OAAO,MAAM;GACnB,MAAM;EACR;CACF,SAAS,OAAO;EACd,IAAI,iBAAiB,qBAAqB,MAAM;EAChD,IAAI,OAAO,KAAK,MAAM,YAAY,OAAO,KAAK,MAAM,WAClD,MAAM,IAAI,oBAAoB,kCAAkC,GAAG;EACrE,IAAI,OAAO,KAAK,MAAM,YAAY,OAAO,KAAK,MAAM,WAAW,OAAO,KAAK,MAAM,SAC/E,MAAM,IAAI,oBAAoB,gCAAgC,GAAG;EACnE,MAAM;CACR;AACF;;AAGA,eAAsB,kBAAkB,QAAuB,MAAc,SAAS,GAAwB;CAC5G,MAAM,QAAQ,IAAI,WAAW,IAAI;CACjC,IAAI,OAAO;CACX,OAAO,OAAO,MAAM;EAClB,MAAM,SAAS,MAAM,OAAO,KAAK,OAAO,MAAM,OAAO,MAAM,SAAS,IAAI;EACxE,IAAI,OAAO,cAAc,GAAG;EAC5B,QAAQ,OAAO;CACjB;CACA,OAAO,MAAM,SAAS,GAAG,IAAI;AAC/B;AAEA,eAAsB,cAAc,MAAyC;CAC3E,MAAM,QAAwB,CAAC;CAC/B,IAAI;EACF,MAAM,WAAW,MAAM,YAAY,IAAI;EACvC,MAAM,QAAQ,OAAO,cAAqC;GACxD,KAAK,MAAM,SAAS,MAAM,GAAG,QAAQ,WAAW,EAAE,eAAe,KAAK,CAAC,GAAG;IACxE,MAAM,WAAW,KAAK,KAAK,WAAW,MAAM,IAAI;IAChD,IAAI,MAAM,YAAY,GAAG;KAEvB,MAAM,OAAO,MAAM,GAAG,MAAM,QAAQ;KACpC,IAAI,CAAC,KAAK,YAAY,KAAK,KAAK,eAAe,GAAG;KAClD,MAAM,OAAO,MAAM,GAAG,SAAS,QAAQ;KACvC,IAAI,OAAO,UAAU,IAAI,GAAG,MAAM,MAAM,IAAI;IAC9C,OAAO,IAAI,MAAM,OAAO,GAAG;KACzB,MAAM,eAAe,KAAK,SAAS,UAAU,QAAQ,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG;KAC/E,IAAI;MACF,MAAM,OAAO,MAAM,aAAa,MAAM,YAAY;MAClD,IAAI;OACF,MAAM,KAAK;QAAE,MAAM;QAAc,MAAM,MAAM;QAAM,MAAM,KAAK,KAAK;QAAM,YAAY,KAAK,KAAK;OAAQ,CAAC;MAC1G,UAAU;OACR,MAAM,KAAK,OAAO,MAAM;MAC1B;KACF,SAAS,OAAO;MACd,IAAI,iBAAiB,wBAAwB,MAAM,WAAW,OAAO,MAAM,WAAW,MAAM;MAC5F,MAAM;KACR;IACF;GACF;EACF;EACA,MAAM,MAAM,QAAQ;EACpB,MAAM,MACH,MAAM,UACL,MAAM,aAAa,KAAK,eAAe,KAAK,OAAO,MAAM,OAAO,KAAK,KAAK,OAAO,MAAM,OAAO,IAAI,EACtG;EACA,OAAO,EAAE,MAAM;CACjB,SAAS,OAAO;EACd,IAAI,OAAO,KAAK,MAAM,UAAU,OAAO,EAAE,OAAO,CAAC,EAAE;EACnD,OAAO;GAAE,OAAO,CAAC;GAAG,OAAO,iBAAiB,QAAQ,MAAM,UAAU;EAAyC;CAC/G;AACF"}
|
|
@@ -27,7 +27,7 @@ let node_path = require("node:path");
|
|
|
27
27
|
node_path = __toESM(node_path, 1);
|
|
28
28
|
let _agimon_ai_po_core_piSettings = require("@agimon-ai/po-core/piSettings");
|
|
29
29
|
//#region src/constants/artifacts.ts
|
|
30
|
-
const ARTIFACT_DIRECTORY = "
|
|
30
|
+
const ARTIFACT_DIRECTORY = "po-artifacts";
|
|
31
31
|
const MAX_PREVIEW_BYTES = 1048576;
|
|
32
32
|
const MAX_CONTENT_BYTES = 26214400;
|
|
33
33
|
const ARTIFACT_REFRESH_MS = 2e3;
|
|
@@ -277,4 +277,4 @@ Object.defineProperty(exports, "readArtifactBytes", {
|
|
|
277
277
|
}
|
|
278
278
|
});
|
|
279
279
|
|
|
280
|
-
//# sourceMappingURL=artifactFiles-
|
|
280
|
+
//# sourceMappingURL=artifactFiles-Cr2X2lhY.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifactFiles-Cr2X2lhY.cjs","names":["path","piAgentDirectory","path","fs","constants"],"sources":["../src/constants/artifacts.ts","../src/services/artifactFiles/artifactPaths.ts","../src/services/artifactFiles/index.ts"],"sourcesContent":["export const ARTIFACT_DIRECTORY = 'po-artifacts';\nexport const MAX_PREVIEW_BYTES = 1024 * 1024;\nexport const MAX_CONTENT_BYTES = 25 * 1024 * 1024;\nexport const ARTIFACT_REFRESH_MS = 2000;\n\nexport const ARTIFACT_CONTENT_TYPES: Readonly<Record<string, string>> = {\n '.avif': 'image/avif',\n '.bmp': 'image/bmp',\n '.gif': 'image/gif',\n '.jpeg': 'image/jpeg',\n '.jpg': 'image/jpeg',\n '.png': 'image/png',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.ico': 'image/vnd.microsoft.icon',\n '.mp3': 'audio/mpeg',\n '.wav': 'audio/wav',\n '.m4a': 'audio/mp4',\n '.ogg': 'audio/ogg',\n '.m4v': 'video/mp4',\n '.mov': 'video/quicktime',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.pdf': 'application/pdf',\n '.md': 'text/markdown; charset=utf-8',\n '.markdown': 'text/markdown; charset=utf-8',\n '.txt': 'text/plain; charset=utf-8',\n '.html': 'text/html; charset=utf-8',\n '.htm': 'text/html; charset=utf-8',\n};\n","import path from 'node:path';\n\nimport { piAgentDirectory } from '@agimon-ai/po-core/piSettings';\n\nimport { ARTIFACT_CONTENT_TYPES, ARTIFACT_DIRECTORY } from '../../constants/artifacts';\n\nexport class ArtifactAccessError extends Error {\n constructor(\n message: string,\n readonly status: 400 | 403 | 404 | 413,\n ) {\n super(message);\n }\n}\n\nexport function artifactRoot(sessionId: string, environment: Readonly<Record<string, string | undefined>>): string {\n if (!/^[a-zA-Z0-9_-]+$/u.test(sessionId)) throw new ArtifactAccessError('Invalid artifact session identity.', 400);\n return path.join(piAgentDirectory(environment, environment.HOME), ARTIFACT_DIRECTORY, sessionId);\n}\n\nexport function artifactMimeType(relativePath: string): string {\n return ARTIFACT_CONTENT_TYPES[path.extname(relativePath).toLowerCase()] ?? 'application/octet-stream';\n}\n","import { constants } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport type { ArtifactItem, ArtifactSnapshot } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactMimeType } from './artifactPaths';\n\nexport { ArtifactAccessError, artifactMimeType, artifactRoot } from './artifactPaths';\n\nfunction inside(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);\n}\n\nfunction codeOf(error: unknown): string | undefined {\n return error instanceof Error && 'code' in error ? String(error.code) : undefined;\n}\n\n/** The owned directories must not redirect artifact access through a symlink. */\nasync function checkedRoot(root: string): Promise<string> {\n const base = path.dirname(root);\n for (const directory of [base, root]) {\n const stat = await fs.lstat(directory);\n if (!stat.isDirectory() || stat.isSymbolicLink())\n throw new ArtifactAccessError('The artifact folder is unavailable.', 403);\n }\n const realBase = await fs.realpath(base);\n const realRoot = await fs.realpath(root);\n if (!inside(realBase, realRoot))\n throw new ArtifactAccessError('The artifact folder is outside its storage directory.', 403);\n return realRoot;\n}\n\nexport async function ensureArtifactRoot(root: string): Promise<void> {\n await fs.mkdir(path.dirname(root), { recursive: true, mode: 0o700 });\n await fs.mkdir(root, { recursive: true, mode: 0o700 });\n await checkedRoot(root);\n}\n\n/** Open without following mutable ancestors. Node exposes no portable openat API. */\nasync function openContained(root: string, segments: string[]): Promise<fs.FileHandle> {\n if (process.platform === 'darwin') {\n // Darwin O_NOFOLLOW_ANY checks every component atomically. Do not combine it with O_NOFOLLOW.\n return fs.open(path.join(root, ...segments), constants.O_RDONLY | constants.O_NONBLOCK | 0x20000000);\n }\n if (process.platform !== 'linux') throw new ArtifactAccessError('Secure artifact reads require macOS or Linux.', 403);\n const flags = constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW;\n let directory = await fs.open(path.dirname(root), flags);\n try {\n for (const segment of [path.basename(root), ...segments.slice(0, -1)]) {\n const child = await fs.open(`/proc/self/fd/${directory.fd}/${segment}`, flags);\n await directory.close();\n directory = child;\n }\n return await fs.open(\n `/proc/self/fd/${directory.fd}/${segments.at(-1)!}`,\n constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK,\n );\n } finally {\n await directory.close();\n }\n}\n\n/** One guarded open shared by metadata, text and byte reads. The caller closes the handle. */\nexport async function openArtifact(root: string, requested: string | undefined) {\n if (\n !requested ||\n requested.includes('\\0') ||\n requested.includes('\\\\') ||\n path.isAbsolute(requested) ||\n requested.split('/').some((part) => part === '..' || part === '.' || part === '')\n )\n throw new ArtifactAccessError('Invalid artifact path.', 400);\n try {\n const realRoot = await checkedRoot(root);\n const candidate = path.resolve(realRoot, requested);\n if (!inside(realRoot, candidate))\n throw new ArtifactAccessError('The artifact is outside this session folder.', 403);\n let current = realRoot;\n const segments = requested.split('/');\n for (let index = 0; index < segments.length; index += 1) {\n current = path.join(current, segments[index]!);\n const stat = await fs.lstat(current);\n if (stat.isSymbolicLink()) throw new ArtifactAccessError('Artifact symlinks cannot be read.', 403);\n if (index < segments.length - 1 ? !stat.isDirectory() : !stat.isFile())\n throw new ArtifactAccessError('The artifact is not a regular file.', 404);\n }\n const real = await fs.realpath(candidate);\n if (!inside(realRoot, real)) throw new ArtifactAccessError('The artifact is outside this session folder.', 403);\n const expected = await fs.lstat(real);\n const handle = await openContained(realRoot, segments);\n try {\n const stat = await handle.stat();\n if (!stat.isFile() || stat.dev !== expected.dev || stat.ino !== expected.ino)\n throw new ArtifactAccessError('The artifact changed while it was opened. Try again.', 403);\n return { handle, stat, path: requested, mimeType: artifactMimeType(requested) };\n } catch (error) {\n await handle.close();\n throw error;\n }\n } catch (error) {\n if (error instanceof ArtifactAccessError) throw error;\n if (codeOf(error) === 'ENOENT' || codeOf(error) === 'ENOTDIR')\n throw new ArtifactAccessError('The artifact no longer exists.', 404);\n if (codeOf(error) === 'EACCES' || codeOf(error) === 'EPERM' || codeOf(error) === 'ELOOP')\n throw new ArtifactAccessError('The artifact cannot be read.', 403);\n throw error;\n }\n}\n\n/** Bound allocation and read length even when the file grows after its metadata was read. */\nexport async function readArtifactBytes(handle: fs.FileHandle, size: number, offset = 0): Promise<Uint8Array> {\n const bytes = new Uint8Array(size);\n let read = 0;\n while (read < size) {\n const result = await handle.read(bytes, read, size - read, offset + read);\n if (result.bytesRead === 0) break;\n read += result.bytesRead;\n }\n return bytes.subarray(0, read);\n}\n\nexport async function listArtifacts(root: string): Promise<ArtifactSnapshot> {\n const items: ArtifactItem[] = [];\n try {\n const realRoot = await checkedRoot(root);\n const visit = async (directory: string): Promise<void> => {\n for (const entry of await fs.readdir(directory, { withFileTypes: true })) {\n const location = path.join(directory, entry.name);\n if (entry.isDirectory()) {\n // Recheck each directory rather than trusting a readdir entry across a rename.\n const stat = await fs.lstat(location);\n if (!stat.isDirectory() || stat.isSymbolicLink()) continue;\n const real = await fs.realpath(location);\n if (inside(realRoot, real)) await visit(real);\n } else if (entry.isFile()) {\n const relativePath = path.relative(realRoot, location).split(path.sep).join('/');\n try {\n const file = await openArtifact(root, relativePath);\n try {\n items.push({ path: relativePath, name: entry.name, size: file.stat.size, modifiedAt: file.stat.mtimeMs });\n } finally {\n await file.handle.close();\n }\n } catch (error) {\n if (error instanceof ArtifactAccessError && (error.status === 404 || error.status === 403)) continue;\n throw error;\n }\n }\n }\n };\n await visit(realRoot);\n items.sort(\n (left, right) =>\n right.modifiedAt - left.modifiedAt || (left.path < right.path ? -1 : left.path > right.path ? 1 : 0),\n );\n return { items };\n } catch (error) {\n if (codeOf(error) === 'ENOENT') return { items: [] };\n return { items: [], error: error instanceof Error ? error.message : 'The artifact folder could not be read.' };\n }\n}\n"],"mappings":"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"}
|
package/dist/artifactFiles.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_artifactFiles = require("./artifactFiles-
|
|
2
|
+
const require_artifactFiles = require("./artifactFiles-Cr2X2lhY.cjs");
|
|
3
3
|
exports.ArtifactAccessError = require_artifactFiles.ArtifactAccessError;
|
|
4
4
|
exports.artifactRoot = require_artifactFiles.artifactRoot;
|
|
5
5
|
exports.openArtifact = require_artifactFiles.openArtifact;
|
package/dist/artifactFiles.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { a as ArtifactAccessError, o as artifactRoot, r as openArtifact, i as readArtifactBytes } from "./artifactFiles-
|
|
1
|
+
export { a as ArtifactAccessError, o as artifactRoot, r as openArtifact, i as readArtifactBytes } from "./artifactFiles-8yvD8PX3.mjs";
|
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_artifactFiles = require("../artifactFiles-
|
|
5
|
+
const require_artifactFiles = require("../artifactFiles-Cr2X2lhY.cjs");
|
|
6
6
|
let node_path = require("node:path");
|
|
7
7
|
node_path = require_artifactFiles.__toESM(node_path, 1);
|
|
8
8
|
let _agimon_ai_po_core_serverFacet = require("@agimon-ai/po-core/serverFacet");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.cjs","names":["ensureArtifactRoot","ArtifactAccessError","listArtifacts","setTimeout","ARTIFACT_REFRESH_MS","artifactRoot","defineRoot","listArtifacts","artifactRoot","defineChannel","Hono","ArtifactAccessError","listArtifacts","openArtifact","readArtifactBytes","MAX_PREVIEW_BYTES","path","artifactRoot","defineRoute","defineHook","defineHook","defineServerPlugin","composeServerProviderOverrides","rootSession","channelArtifacts","composeRootHooks","apiRoute","hookBeforeAgentStart","hookToolExecutionEnd"],"sources":["../../src/types/artifacts.ts","../../src/services/artifactSession/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/services/artifactChannel/index.ts","../../src/extensions/(backend)/channel/artifacts.server.ts","../../src/types/apiRoutes.ts","../../src/services/artifactsApi/index.ts","../../src/extensions/workspaces/sessions/(backend)/api/artifacts/route.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.ts","../../generated/server.ts"],"sourcesContent":["export interface ArtifactItem {\n /** Relative to the current session's artifact directory. */\n path: string;\n name: string;\n size: number;\n modifiedAt: number;\n}\n\nexport interface ArtifactSnapshot {\n items: ArtifactItem[];\n error?: string;\n}\n\nexport interface ArtifactPreview {\n path: string;\n content: string;\n mimeType: string;\n unavailable?: boolean;\n reason?: string;\n}\n\nexport const artifactsChannelType = 'artifacts';\n","import { clearTimeout, setTimeout } from 'node:timers';\n\nimport type { DoomHeadlessActivity, DoomHeadlessExecutionContext, DoomHeadlessHook } from '@agimon-ai/po-core/headless';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { ARTIFACT_REFRESH_MS } from '../../constants/artifacts';\nimport { artifactsChannelType } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactRoot, ensureArtifactRoot, listArtifacts } from '../artifactFiles';\n\n/**\n * Creates the session artifact folder. Resolves false when the host has no artifact folder\n * (the edge build in M1 rejects with a 404); Node never rejects with that status here.\n */\nasync function prepareArtifactRoot(root: string): Promise<boolean> {\n try {\n await ensureArtifactRoot(root);\n return true;\n } catch (error) {\n if (error instanceof ArtifactAccessError && error.status === 404) return false;\n throw error;\n }\n}\n\nexport function createArtifactSession({ host }: DoomServerPluginContext) {\n const environment = Object.freeze({ ...host.context.environment });\n let active: { context: DoomHeadlessExecutionContext; root: string; fingerprint?: string } | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let pending: Promise<void> | undefined;\n let closed = false;\n\n const refresh = (): Promise<void> => {\n if (pending) return pending;\n const runtime = active;\n if (!runtime || closed) return Promise.resolve();\n pending = (async () => {\n const snapshot = await listArtifacts(runtime.root);\n if (closed || active !== runtime) return;\n const fingerprint = JSON.stringify(snapshot);\n if (fingerprint === runtime.fingerprint) return;\n runtime.fingerprint = fingerprint;\n host.context.directEvents?.publish(artifactsChannelType, runtime.context.sessionId, snapshot);\n })().finally(() => {\n pending = undefined;\n });\n return pending;\n };\n const schedule = (): void => {\n if (!active || closed) return;\n timer = setTimeout(() => {\n timer = undefined;\n void refresh().finally(schedule);\n }, ARTIFACT_REFRESH_MS);\n };\n const stop = async (): Promise<void> => {\n active = undefined;\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n await pending;\n };\n const activity: DoomHeadlessActivity = {\n name: 'artifacts',\n async start(context) {\n if (closed) throw new Error('Artifact session is closed.');\n await stop();\n const root = artifactRoot(context.sessionId, environment);\n if (!(await prepareArtifactRoot(root))) return stop;\n if (closed) throw new Error('Artifact session is closed.');\n active = { context, root };\n await refresh();\n schedule();\n return stop;\n },\n };\n const beforeAgentStart: DoomHeadlessHook = {\n event: 'before_agent_start',\n async handle(event, context) {\n const root = artifactRoot(context.sessionId, environment);\n const systemPrompt = typeof event.systemPrompt === 'string' ? event.systemPrompt : '';\n if (!(await prepareArtifactRoot(root))) return { systemPrompt };\n const declaration = `Session artifact folder: ${root}`;\n if (systemPrompt.includes(declaration)) return { systemPrompt };\n return {\n systemPrompt:\n `${systemPrompt}\\n\\n${declaration}\\nSave user-designated artifacts and important deliverable documents in this folder, using descriptive filenames. Keep ordinary repository source edits in their existing locations. Do not move existing documents or overwrite artifacts without the user's intent. This folder belongs only to the current session.`.trim(),\n };\n },\n };\n const toolExecutionEnd: DoomHeadlessHook = {\n event: 'tool_execution_end',\n handle(_event, context) {\n return active?.context.sessionId === context.sessionId ? refresh() : undefined;\n },\n };\n return {\n activities: [activity],\n beforeAgentStart,\n toolExecutionEnd,\n onDispose: async () => {\n closed = true;\n await stop();\n },\n };\n}\n","import type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createArtifactSession } from '../../../../../services/artifactSession';\n\nexport const createArtifactServerRoot = (context: DoomServerPluginContext) => {\n const runtime = createArtifactSession(context);\n return { value: runtime, activities: runtime.activities, onDispose: runtime.onDispose };\n};\nexport type ArtifactServerScope = ReturnType<typeof createArtifactServerRoot>['value'];\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\n\nimport { createArtifactServerRoot } from './_lib/serverRoot';\n\nexport default defineRoot(createArtifactServerRoot);\n","import type { DoomHubChannel } from '@agimon-ai/po-core/hubChannel';\n\nimport { artifactsChannelType, type ArtifactSnapshot } from '../../types/artifacts';\nimport { artifactRoot, listArtifacts } from '../artifactFiles';\n\nfunction snapshotOf(value: unknown): value is ArtifactSnapshot {\n if (typeof value !== 'object' || value === null) return false;\n const snapshot = value as Partial<ArtifactSnapshot>;\n return (\n Array.isArray(snapshot.items) &&\n (snapshot.error === undefined || typeof snapshot.error === 'string') &&\n snapshot.items.every(\n (item) =>\n typeof item === 'object' &&\n item !== null &&\n typeof item.path === 'string' &&\n typeof item.name === 'string' &&\n typeof item.size === 'number' &&\n Number.isFinite(item.size) &&\n typeof item.modifiedAt === 'number' &&\n Number.isFinite(item.modifiedAt),\n )\n );\n}\n\nexport function createArtifactsChannel(): DoomHubChannel {\n return {\n frameType: artifactsChannelType,\n start(host) {\n const sessions = new Map<string, { snapshot?: ArtifactSnapshot; unsubscribe: () => void }>();\n return {\n payloadFor(scope) {\n return sessions.get(scope.sessionId)?.snapshot;\n },\n sessionAdded(scope) {\n sessions.get(scope.sessionId)?.unsubscribe();\n const state: { snapshot?: ArtifactSnapshot; unsubscribe: () => void } = { unsubscribe: () => undefined };\n sessions.set(scope.sessionId, state);\n state.unsubscribe = host.directEvents.subscribe(\n artifactsChannelType,\n scope.sessionId,\n (snapshot) => {\n if (!snapshotOf(snapshot) || sessions.get(scope.sessionId) !== state) return;\n state.snapshot = snapshot;\n host.publish(scope.sessionId, snapshot);\n },\n { replayLatest: true },\n );\n void (async () => {\n let snapshot: ArtifactSnapshot;\n try {\n snapshot = await listArtifacts(artifactRoot(scope.sessionId, scope.environment ?? {}));\n } catch (error) {\n snapshot = {\n items: [],\n error: error instanceof Error ? error.message : 'The artifact folder could not be read.',\n };\n }\n // A live session event wins over the earlier seed read.\n if (sessions.get(scope.sessionId) !== state || state.snapshot !== undefined) return;\n state.snapshot = snapshot;\n host.publish(scope.sessionId, snapshot);\n })();\n },\n sessionRemoved(sessionId) {\n sessions.get(sessionId)?.unsubscribe();\n sessions.delete(sessionId);\n },\n close() {\n for (const state of sessions.values()) state.unsubscribe();\n sessions.clear();\n },\n };\n },\n };\n}\n","import { defineChannel } from '@agimon-ai/po-core/extensionFile';\n\nimport { createArtifactsChannel } from '../../../services/artifactChannel';\n\nexport default defineChannel(createArtifactsChannel);\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { ArtifactPreview, ArtifactSnapshot } from './artifacts';\n\nconst routes = {\n list: {\n method: 'GET',\n path: '/',\n response: undefined as unknown as ApiResponseOf<ArtifactSnapshot>,\n },\n preview: {\n method: 'GET',\n path: '/preview',\n query: ['path'],\n response: undefined as unknown as ApiResponseOf<ArtifactPreview>,\n },\n content: {\n method: 'GET',\n path: '/content',\n query: ['path'],\n raw: true,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","import path from 'node:path';\n\nimport type { DoomApi, DoomApiContext } from '@agimon-ai/po-core/packageApi';\nimport { Hono } from 'hono';\n\nimport { MAX_CONTENT_BYTES, MAX_PREVIEW_BYTES } from '../../constants/artifacts';\nimport routes from '../../types/apiRoutes';\nimport type { ArtifactPreview } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactRoot, listArtifacts, openArtifact, readArtifactBytes } from '../artifactFiles';\n\nexport function createArtifactsApi(root: string): Hono {\n const app = new Hono();\n app.onError((error, context) =>\n context.json(\n { error: error instanceof ArtifactAccessError ? error.message : 'The artifact could not be read.' },\n error instanceof ArtifactAccessError ? error.status : 500,\n ),\n );\n app.get(routes.list.path, async (context) => {\n const snapshot = await listArtifacts(root);\n return context.json(snapshot, snapshot.error === undefined ? 200 : 500);\n });\n app.get(routes.preview.path, async (context) => {\n const file = await openArtifact(root, context.req.query('path'));\n try {\n const preview: ArtifactPreview = { path: file.path, content: '', mimeType: file.mimeType };\n if (file.stat.size > MAX_PREVIEW_BYTES) {\n return context.json({\n ...preview,\n unavailable: true,\n reason: 'This artifact is too large for a text preview.',\n });\n }\n const bytes = await readArtifactBytes(file.handle, MAX_PREVIEW_BYTES + 1);\n if (bytes.byteLength > MAX_PREVIEW_BYTES)\n return context.json({\n ...preview,\n unavailable: true,\n reason: 'This artifact is too large for a text preview.',\n });\n if (bytes.subarray(0, 8192).includes(0))\n return context.json({ ...preview, unavailable: true, reason: 'This artifact is binary.' });\n return context.json({ ...preview, content: new TextDecoder().decode(bytes) });\n } finally {\n await file.handle.close();\n }\n });\n app.get(routes.content.path, async (context) => {\n const file = await openArtifact(root, context.req.query('path'));\n try {\n if (file.stat.size > MAX_CONTENT_BYTES)\n throw new ArtifactAccessError('This artifact is too large to download.', 413);\n const version = `\"${file.stat.dev}-${file.stat.ino}-${file.stat.size}-${file.stat.mtimeMs}-${file.stat.ctimeMs}\"`;\n const name = encodeURIComponent(path.basename(file.path)).replace(\n /[!'()*]/gu,\n (value) => `%${value.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n const headers = new Headers({\n 'content-type': file.mimeType,\n 'content-disposition': `attachment; filename*=UTF-8''${name}`,\n 'cache-control': 'no-store',\n 'x-content-type-options': 'nosniff',\n 'content-security-policy': \"default-src 'none'; sandbox\",\n 'accept-ranges': 'bytes',\n etag: version,\n });\n const expectedVersion = context.req.header('if-match');\n if (expectedVersion !== undefined && expectedVersion !== version)\n return new Response(null, { status: 412, headers });\n let start = 0;\n let end = file.stat.size - 1;\n const range = context.req.header('range');\n if (range !== undefined) {\n const match = /^bytes=(\\d*)-(\\d*)$/u.exec(range);\n if (\n !match ||\n (!match[1] && !match[2]) ||\n file.stat.size === 0 ||\n [match[1], match[2]].some((value) => value !== '' && !Number.isSafeInteger(Number(value)))\n ) {\n headers.set('content-range', `bytes */${file.stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n if (match[1] === '') {\n const suffix = Number(match[2]);\n start = suffix > 0 ? Math.max(0, file.stat.size - suffix) : file.stat.size;\n } else {\n start = Number(match[1]);\n end = match[2] === '' ? end : Math.min(end, Number(match[2]));\n }\n if (start > end || start >= file.stat.size) {\n headers.set('content-range', `bytes */${file.stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n headers.set('content-range', `bytes ${start}-${end}/${file.stat.size}`);\n }\n const bytes = await readArtifactBytes(file.handle, Math.max(0, end - start + 1), start);\n const after = await file.handle.stat();\n if (after.size !== file.stat.size || after.mtimeMs !== file.stat.mtimeMs || after.ctimeMs !== file.stat.ctimeMs)\n return new Response(null, { status: 412, headers });\n headers.set('content-length', String(bytes.byteLength));\n return new Response(bytes, { status: range === undefined ? 200 : 206, headers });\n } finally {\n await file.handle.close();\n }\n });\n return app;\n}\n\nexport const api: DoomApi = {\n basePath: 'artifacts',\n start(context: DoomApiContext) {\n if (!context.sessionId) throw new Error('Artifacts requires a session identity.');\n const app = createArtifactsApi(artifactRoot(context.sessionId, context.environment ?? {}));\n return { fetch: (request) => app.fetch(request), close: () => undefined };\n },\n};\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\n\nimport { api } from '../../../../../../services/artifactsApi';\n\nexport default defineRoute(api);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { ArtifactServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(\n (context: WithRoot<DoomServerPluginContext, ArtifactServerScope>) => context.root.beforeAgentStart,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { ArtifactServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(\n (context: WithRoot<DoomServerPluginContext, ArtifactServerScope>) => context.root.toolExecutionEnd,\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport channelArtifacts from '../src/extensions/(backend)/channel/artifacts.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/artifacts/route.server';\nimport hookBeforeAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server';\nimport hookToolExecutionEnd from '../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-artifact',\n providerOverrides: composeServerProviderOverrides(rootSession),\n global: () => ({\n get channels(): DoomServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n }),\n workspace: () => ({\n get channels(): DoomServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n }),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): DoomServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n get channels(): DoomServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n get hooks(): DoomServerSessionPlugin['hooks'] { return [via({ event: 'before_agent_start' }, at(hookBeforeAgentStart, contextSession)), via({ event: 'tool_execution_end' }, at(hookToolExecutionEnd, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.cjs","names":["ensureArtifactRoot","ArtifactAccessError","listArtifacts","setTimeout","ARTIFACT_REFRESH_MS","artifactRoot","defineRoot","listArtifacts","artifactRoot","defineChannel","Hono","ArtifactAccessError","listArtifacts","openArtifact","readArtifactBytes","MAX_PREVIEW_BYTES","path","artifactRoot","defineRoute","defineHook","defineHook","defineServerPlugin","composeServerProviderOverrides","rootSession","channelArtifacts","composeRootHooks","apiRoute","hookBeforeAgentStart","hookToolExecutionEnd"],"sources":["../../src/types/artifacts.ts","../../src/services/artifactSession/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/services/artifactChannel/index.ts","../../src/extensions/(backend)/channel/artifacts.server.ts","../../src/types/apiRoutes.ts","../../src/services/artifactsApi/index.ts","../../src/extensions/workspaces/sessions/(backend)/api/artifacts/route.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.ts","../../generated/server.ts"],"sourcesContent":["export interface ArtifactItem {\n /** Relative to the current session's artifact directory. */\n path: string;\n name: string;\n size: number;\n modifiedAt: number;\n}\n\nexport interface ArtifactSnapshot {\n items: ArtifactItem[];\n error?: string;\n}\n\nexport interface ArtifactPreview {\n path: string;\n content: string;\n mimeType: string;\n unavailable?: boolean;\n reason?: string;\n}\n\nexport const artifactsChannelType = 'artifacts';\n","import { clearTimeout, setTimeout } from 'node:timers';\n\nimport type { PoHeadlessActivity, PoHeadlessExecutionContext, PoHeadlessHook } from '@agimon-ai/po-core/headless';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { ARTIFACT_REFRESH_MS } from '../../constants/artifacts';\nimport { artifactsChannelType } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactRoot, ensureArtifactRoot, listArtifacts } from '../artifactFiles';\n\n/**\n * Creates the session artifact folder. Resolves false when the host has no artifact folder\n * (the edge build in M1 rejects with a 404); Node never rejects with that status here.\n */\nasync function prepareArtifactRoot(root: string): Promise<boolean> {\n try {\n await ensureArtifactRoot(root);\n return true;\n } catch (error) {\n if (error instanceof ArtifactAccessError && error.status === 404) return false;\n throw error;\n }\n}\n\nexport function createArtifactSession({ host }: PoServerPluginContext) {\n const environment = Object.freeze({ ...host.context.environment });\n let active: { context: PoHeadlessExecutionContext; root: string; fingerprint?: string } | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let pending: Promise<void> | undefined;\n let closed = false;\n\n const refresh = (): Promise<void> => {\n if (pending) return pending;\n const runtime = active;\n if (!runtime || closed) return Promise.resolve();\n pending = (async () => {\n const snapshot = await listArtifacts(runtime.root);\n if (closed || active !== runtime) return;\n const fingerprint = JSON.stringify(snapshot);\n if (fingerprint === runtime.fingerprint) return;\n runtime.fingerprint = fingerprint;\n host.context.directEvents?.publish(artifactsChannelType, runtime.context.sessionId, snapshot);\n })().finally(() => {\n pending = undefined;\n });\n return pending;\n };\n const schedule = (): void => {\n if (!active || closed) return;\n timer = setTimeout(() => {\n timer = undefined;\n void refresh().finally(schedule);\n }, ARTIFACT_REFRESH_MS);\n };\n const stop = async (): Promise<void> => {\n active = undefined;\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n await pending;\n };\n const activity: PoHeadlessActivity = {\n name: 'artifacts',\n async start(context) {\n if (closed) throw new Error('Artifact session is closed.');\n await stop();\n const root = artifactRoot(context.sessionId, environment);\n if (!(await prepareArtifactRoot(root))) return stop;\n if (closed) throw new Error('Artifact session is closed.');\n active = { context, root };\n await refresh();\n schedule();\n return stop;\n },\n };\n const beforeAgentStart: PoHeadlessHook = {\n event: 'before_agent_start',\n async handle(event, context) {\n const root = artifactRoot(context.sessionId, environment);\n const systemPrompt = typeof event.systemPrompt === 'string' ? event.systemPrompt : '';\n if (!(await prepareArtifactRoot(root))) return { systemPrompt };\n const declaration = `Session artifact folder: ${root}`;\n if (systemPrompt.includes(declaration)) return { systemPrompt };\n return {\n systemPrompt:\n `${systemPrompt}\\n\\n${declaration}\\nSave user-designated artifacts and important deliverable documents in this folder, using descriptive filenames. Keep ordinary repository source edits in their existing locations. Do not move existing documents or overwrite artifacts without the user's intent. This folder belongs only to the current session.`.trim(),\n };\n },\n };\n const toolExecutionEnd: PoHeadlessHook = {\n event: 'tool_execution_end',\n handle(_event, context) {\n return active?.context.sessionId === context.sessionId ? refresh() : undefined;\n },\n };\n return {\n activities: [activity],\n beforeAgentStart,\n toolExecutionEnd,\n onDispose: async () => {\n closed = true;\n await stop();\n },\n };\n}\n","import type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createArtifactSession } from '../../../../../services/artifactSession';\n\nexport const createArtifactServerRoot = (context: PoServerPluginContext) => {\n const runtime = createArtifactSession(context);\n return { value: runtime, activities: runtime.activities, onDispose: runtime.onDispose };\n};\nexport type ArtifactServerScope = ReturnType<typeof createArtifactServerRoot>['value'];\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\n\nimport { createArtifactServerRoot } from './_lib/serverRoot';\n\nexport default defineRoot(createArtifactServerRoot);\n","import type { PoHubChannel } from '@agimon-ai/po-core/hubChannel';\n\nimport { artifactsChannelType, type ArtifactSnapshot } from '../../types/artifacts';\nimport { artifactRoot, listArtifacts } from '../artifactFiles';\n\nfunction snapshotOf(value: unknown): value is ArtifactSnapshot {\n if (typeof value !== 'object' || value === null) return false;\n const snapshot = value as Partial<ArtifactSnapshot>;\n return (\n Array.isArray(snapshot.items) &&\n (snapshot.error === undefined || typeof snapshot.error === 'string') &&\n snapshot.items.every(\n (item) =>\n typeof item === 'object' &&\n item !== null &&\n typeof item.path === 'string' &&\n typeof item.name === 'string' &&\n typeof item.size === 'number' &&\n Number.isFinite(item.size) &&\n typeof item.modifiedAt === 'number' &&\n Number.isFinite(item.modifiedAt),\n )\n );\n}\n\nexport function createArtifactsChannel(): PoHubChannel {\n return {\n frameType: artifactsChannelType,\n start(host) {\n const sessions = new Map<string, { snapshot?: ArtifactSnapshot; unsubscribe: () => void }>();\n return {\n payloadFor(scope) {\n return sessions.get(scope.sessionId)?.snapshot;\n },\n sessionAdded(scope) {\n sessions.get(scope.sessionId)?.unsubscribe();\n const state: { snapshot?: ArtifactSnapshot; unsubscribe: () => void } = { unsubscribe: () => undefined };\n sessions.set(scope.sessionId, state);\n state.unsubscribe = host.directEvents.subscribe(\n artifactsChannelType,\n scope.sessionId,\n (snapshot) => {\n if (!snapshotOf(snapshot) || sessions.get(scope.sessionId) !== state) return;\n state.snapshot = snapshot;\n host.publish(scope.sessionId, snapshot);\n },\n { replayLatest: true },\n );\n void (async () => {\n let snapshot: ArtifactSnapshot;\n try {\n snapshot = await listArtifacts(artifactRoot(scope.sessionId, scope.environment ?? {}));\n } catch (error) {\n snapshot = {\n items: [],\n error: error instanceof Error ? error.message : 'The artifact folder could not be read.',\n };\n }\n // A live session event wins over the earlier seed read.\n if (sessions.get(scope.sessionId) !== state || state.snapshot !== undefined) return;\n state.snapshot = snapshot;\n host.publish(scope.sessionId, snapshot);\n })();\n },\n sessionRemoved(sessionId) {\n sessions.get(sessionId)?.unsubscribe();\n sessions.delete(sessionId);\n },\n close() {\n for (const state of sessions.values()) state.unsubscribe();\n sessions.clear();\n },\n };\n },\n };\n}\n","import { defineChannel } from '@agimon-ai/po-core/extensionFile';\n\nimport { createArtifactsChannel } from '../../../services/artifactChannel';\n\nexport default defineChannel(createArtifactsChannel);\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { ArtifactPreview, ArtifactSnapshot } from './artifacts';\n\nconst routes = {\n list: {\n method: 'GET',\n path: '/',\n response: undefined as unknown as ApiResponseOf<ArtifactSnapshot>,\n },\n preview: {\n method: 'GET',\n path: '/preview',\n query: ['path'],\n response: undefined as unknown as ApiResponseOf<ArtifactPreview>,\n },\n content: {\n method: 'GET',\n path: '/content',\n query: ['path'],\n raw: true,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","import path from 'node:path';\n\nimport type { PoApi, PoApiContext } from '@agimon-ai/po-core/packageApi';\nimport { Hono } from 'hono';\n\nimport { MAX_CONTENT_BYTES, MAX_PREVIEW_BYTES } from '../../constants/artifacts';\nimport routes from '../../types/apiRoutes';\nimport type { ArtifactPreview } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactRoot, listArtifacts, openArtifact, readArtifactBytes } from '../artifactFiles';\n\nexport function createArtifactsApi(root: string): Hono {\n const app = new Hono();\n app.onError((error, context) =>\n context.json(\n { error: error instanceof ArtifactAccessError ? error.message : 'The artifact could not be read.' },\n error instanceof ArtifactAccessError ? error.status : 500,\n ),\n );\n app.get(routes.list.path, async (context) => {\n const snapshot = await listArtifacts(root);\n return context.json(snapshot, snapshot.error === undefined ? 200 : 500);\n });\n app.get(routes.preview.path, async (context) => {\n const file = await openArtifact(root, context.req.query('path'));\n try {\n const preview: ArtifactPreview = { path: file.path, content: '', mimeType: file.mimeType };\n if (file.stat.size > MAX_PREVIEW_BYTES) {\n return context.json({\n ...preview,\n unavailable: true,\n reason: 'This artifact is too large for a text preview.',\n });\n }\n const bytes = await readArtifactBytes(file.handle, MAX_PREVIEW_BYTES + 1);\n if (bytes.byteLength > MAX_PREVIEW_BYTES)\n return context.json({\n ...preview,\n unavailable: true,\n reason: 'This artifact is too large for a text preview.',\n });\n if (bytes.subarray(0, 8192).includes(0))\n return context.json({ ...preview, unavailable: true, reason: 'This artifact is binary.' });\n return context.json({ ...preview, content: new TextDecoder().decode(bytes) });\n } finally {\n await file.handle.close();\n }\n });\n app.get(routes.content.path, async (context) => {\n const file = await openArtifact(root, context.req.query('path'));\n try {\n if (file.stat.size > MAX_CONTENT_BYTES)\n throw new ArtifactAccessError('This artifact is too large to download.', 413);\n const version = `\"${file.stat.dev}-${file.stat.ino}-${file.stat.size}-${file.stat.mtimeMs}-${file.stat.ctimeMs}\"`;\n const name = encodeURIComponent(path.basename(file.path)).replace(\n /[!'()*]/gu,\n (value) => `%${value.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n const headers = new Headers({\n 'content-type': file.mimeType,\n 'content-disposition': `attachment; filename*=UTF-8''${name}`,\n 'cache-control': 'no-store',\n 'x-content-type-options': 'nosniff',\n 'content-security-policy': \"default-src 'none'; sandbox\",\n 'accept-ranges': 'bytes',\n etag: version,\n });\n const expectedVersion = context.req.header('if-match');\n if (expectedVersion !== undefined && expectedVersion !== version)\n return new Response(null, { status: 412, headers });\n let start = 0;\n let end = file.stat.size - 1;\n const range = context.req.header('range');\n if (range !== undefined) {\n const match = /^bytes=(\\d*)-(\\d*)$/u.exec(range);\n if (\n !match ||\n (!match[1] && !match[2]) ||\n file.stat.size === 0 ||\n [match[1], match[2]].some((value) => value !== '' && !Number.isSafeInteger(Number(value)))\n ) {\n headers.set('content-range', `bytes */${file.stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n if (match[1] === '') {\n const suffix = Number(match[2]);\n start = suffix > 0 ? Math.max(0, file.stat.size - suffix) : file.stat.size;\n } else {\n start = Number(match[1]);\n end = match[2] === '' ? end : Math.min(end, Number(match[2]));\n }\n if (start > end || start >= file.stat.size) {\n headers.set('content-range', `bytes */${file.stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n headers.set('content-range', `bytes ${start}-${end}/${file.stat.size}`);\n }\n const bytes = await readArtifactBytes(file.handle, Math.max(0, end - start + 1), start);\n const after = await file.handle.stat();\n if (after.size !== file.stat.size || after.mtimeMs !== file.stat.mtimeMs || after.ctimeMs !== file.stat.ctimeMs)\n return new Response(null, { status: 412, headers });\n headers.set('content-length', String(bytes.byteLength));\n return new Response(bytes, { status: range === undefined ? 200 : 206, headers });\n } finally {\n await file.handle.close();\n }\n });\n return app;\n}\n\nexport const api: PoApi = {\n basePath: 'artifacts',\n start(context: PoApiContext) {\n if (!context.sessionId) throw new Error('Artifacts requires a session identity.');\n const app = createArtifactsApi(artifactRoot(context.sessionId, context.environment ?? {}));\n return { fetch: (request) => app.fetch(request), close: () => undefined };\n },\n};\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\n\nimport { api } from '../../../../../../services/artifactsApi';\n\nexport default defineRoute(api);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { ArtifactServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(\n (context: WithRoot<PoServerPluginContext, ArtifactServerScope>) => context.root.beforeAgentStart,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { ArtifactServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(\n (context: WithRoot<PoServerPluginContext, ArtifactServerScope>) => context.root.toolExecutionEnd,\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport channelArtifacts from '../src/extensions/(backend)/channel/artifacts.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/artifacts/route.server';\nimport hookBeforeAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server';\nimport hookToolExecutionEnd from '../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-artifact',\n providerOverrides: composeServerProviderOverrides(rootSession),\n global: () => ({\n get channels(): PoServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n }),\n workspace: () => ({\n get channels(): PoServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n }),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): PoServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n get channels(): PoServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n get hooks(): PoServerSessionPlugin['hooks'] { return [via({ event: 'before_agent_start' }, at(hookBeforeAgentStart, contextSession)), via({ event: 'tool_execution_end' }, at(hookToolExecutionEnd, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region generated/server.d.ts
|
|
2
|
-
export declare const facet: import("@agimon-ai/po-core/serverFacet").
|
|
2
|
+
export declare const facet: import("@agimon-ai/po-core/serverFacet").PoServerFacet;
|
|
3
3
|
//#endregion
|
|
4
4
|
export { facet as default };
|
|
5
5
|
//# sourceMappingURL=server.d.mts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ArtifactAccessError, i as readArtifactBytes, l as MAX_PREVIEW_BYTES, n as listArtifacts, o as artifactRoot, r as openArtifact, s as ARTIFACT_REFRESH_MS, t as ensureArtifactRoot } from "../artifactFiles-
|
|
1
|
+
import { a as ArtifactAccessError, i as readArtifactBytes, l as MAX_PREVIEW_BYTES, n as listArtifacts, o as artifactRoot, r as openArtifact, s as ARTIFACT_REFRESH_MS, t as ensureArtifactRoot } from "../artifactFiles-8yvD8PX3.mjs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { composeServerProviderOverrides, defineServerPlugin } from "@agimon-ai/po-core/serverFacet";
|
|
4
4
|
import { composeRootHooks, defineChannel, defineHook, defineRoot, defineRoute } from "@agimon-ai/po-core/extensionFile";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.mjs","names":["rootSession","channelArtifacts","apiRoute","hookBeforeAgentStart","hookToolExecutionEnd"],"sources":["../../src/types/artifacts.ts","../../src/services/artifactSession/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/services/artifactChannel/index.ts","../../src/extensions/(backend)/channel/artifacts.server.ts","../../src/types/apiRoutes.ts","../../src/services/artifactsApi/index.ts","../../src/extensions/workspaces/sessions/(backend)/api/artifacts/route.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.ts","../../generated/server.ts"],"sourcesContent":["export interface ArtifactItem {\n /** Relative to the current session's artifact directory. */\n path: string;\n name: string;\n size: number;\n modifiedAt: number;\n}\n\nexport interface ArtifactSnapshot {\n items: ArtifactItem[];\n error?: string;\n}\n\nexport interface ArtifactPreview {\n path: string;\n content: string;\n mimeType: string;\n unavailable?: boolean;\n reason?: string;\n}\n\nexport const artifactsChannelType = 'artifacts';\n","import { clearTimeout, setTimeout } from 'node:timers';\n\nimport type { DoomHeadlessActivity, DoomHeadlessExecutionContext, DoomHeadlessHook } from '@agimon-ai/po-core/headless';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { ARTIFACT_REFRESH_MS } from '../../constants/artifacts';\nimport { artifactsChannelType } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactRoot, ensureArtifactRoot, listArtifacts } from '../artifactFiles';\n\n/**\n * Creates the session artifact folder. Resolves false when the host has no artifact folder\n * (the edge build in M1 rejects with a 404); Node never rejects with that status here.\n */\nasync function prepareArtifactRoot(root: string): Promise<boolean> {\n try {\n await ensureArtifactRoot(root);\n return true;\n } catch (error) {\n if (error instanceof ArtifactAccessError && error.status === 404) return false;\n throw error;\n }\n}\n\nexport function createArtifactSession({ host }: DoomServerPluginContext) {\n const environment = Object.freeze({ ...host.context.environment });\n let active: { context: DoomHeadlessExecutionContext; root: string; fingerprint?: string } | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let pending: Promise<void> | undefined;\n let closed = false;\n\n const refresh = (): Promise<void> => {\n if (pending) return pending;\n const runtime = active;\n if (!runtime || closed) return Promise.resolve();\n pending = (async () => {\n const snapshot = await listArtifacts(runtime.root);\n if (closed || active !== runtime) return;\n const fingerprint = JSON.stringify(snapshot);\n if (fingerprint === runtime.fingerprint) return;\n runtime.fingerprint = fingerprint;\n host.context.directEvents?.publish(artifactsChannelType, runtime.context.sessionId, snapshot);\n })().finally(() => {\n pending = undefined;\n });\n return pending;\n };\n const schedule = (): void => {\n if (!active || closed) return;\n timer = setTimeout(() => {\n timer = undefined;\n void refresh().finally(schedule);\n }, ARTIFACT_REFRESH_MS);\n };\n const stop = async (): Promise<void> => {\n active = undefined;\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n await pending;\n };\n const activity: DoomHeadlessActivity = {\n name: 'artifacts',\n async start(context) {\n if (closed) throw new Error('Artifact session is closed.');\n await stop();\n const root = artifactRoot(context.sessionId, environment);\n if (!(await prepareArtifactRoot(root))) return stop;\n if (closed) throw new Error('Artifact session is closed.');\n active = { context, root };\n await refresh();\n schedule();\n return stop;\n },\n };\n const beforeAgentStart: DoomHeadlessHook = {\n event: 'before_agent_start',\n async handle(event, context) {\n const root = artifactRoot(context.sessionId, environment);\n const systemPrompt = typeof event.systemPrompt === 'string' ? event.systemPrompt : '';\n if (!(await prepareArtifactRoot(root))) return { systemPrompt };\n const declaration = `Session artifact folder: ${root}`;\n if (systemPrompt.includes(declaration)) return { systemPrompt };\n return {\n systemPrompt:\n `${systemPrompt}\\n\\n${declaration}\\nSave user-designated artifacts and important deliverable documents in this folder, using descriptive filenames. Keep ordinary repository source edits in their existing locations. Do not move existing documents or overwrite artifacts without the user's intent. This folder belongs only to the current session.`.trim(),\n };\n },\n };\n const toolExecutionEnd: DoomHeadlessHook = {\n event: 'tool_execution_end',\n handle(_event, context) {\n return active?.context.sessionId === context.sessionId ? refresh() : undefined;\n },\n };\n return {\n activities: [activity],\n beforeAgentStart,\n toolExecutionEnd,\n onDispose: async () => {\n closed = true;\n await stop();\n },\n };\n}\n","import type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createArtifactSession } from '../../../../../services/artifactSession';\n\nexport const createArtifactServerRoot = (context: DoomServerPluginContext) => {\n const runtime = createArtifactSession(context);\n return { value: runtime, activities: runtime.activities, onDispose: runtime.onDispose };\n};\nexport type ArtifactServerScope = ReturnType<typeof createArtifactServerRoot>['value'];\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\n\nimport { createArtifactServerRoot } from './_lib/serverRoot';\n\nexport default defineRoot(createArtifactServerRoot);\n","import type { DoomHubChannel } from '@agimon-ai/po-core/hubChannel';\n\nimport { artifactsChannelType, type ArtifactSnapshot } from '../../types/artifacts';\nimport { artifactRoot, listArtifacts } from '../artifactFiles';\n\nfunction snapshotOf(value: unknown): value is ArtifactSnapshot {\n if (typeof value !== 'object' || value === null) return false;\n const snapshot = value as Partial<ArtifactSnapshot>;\n return (\n Array.isArray(snapshot.items) &&\n (snapshot.error === undefined || typeof snapshot.error === 'string') &&\n snapshot.items.every(\n (item) =>\n typeof item === 'object' &&\n item !== null &&\n typeof item.path === 'string' &&\n typeof item.name === 'string' &&\n typeof item.size === 'number' &&\n Number.isFinite(item.size) &&\n typeof item.modifiedAt === 'number' &&\n Number.isFinite(item.modifiedAt),\n )\n );\n}\n\nexport function createArtifactsChannel(): DoomHubChannel {\n return {\n frameType: artifactsChannelType,\n start(host) {\n const sessions = new Map<string, { snapshot?: ArtifactSnapshot; unsubscribe: () => void }>();\n return {\n payloadFor(scope) {\n return sessions.get(scope.sessionId)?.snapshot;\n },\n sessionAdded(scope) {\n sessions.get(scope.sessionId)?.unsubscribe();\n const state: { snapshot?: ArtifactSnapshot; unsubscribe: () => void } = { unsubscribe: () => undefined };\n sessions.set(scope.sessionId, state);\n state.unsubscribe = host.directEvents.subscribe(\n artifactsChannelType,\n scope.sessionId,\n (snapshot) => {\n if (!snapshotOf(snapshot) || sessions.get(scope.sessionId) !== state) return;\n state.snapshot = snapshot;\n host.publish(scope.sessionId, snapshot);\n },\n { replayLatest: true },\n );\n void (async () => {\n let snapshot: ArtifactSnapshot;\n try {\n snapshot = await listArtifacts(artifactRoot(scope.sessionId, scope.environment ?? {}));\n } catch (error) {\n snapshot = {\n items: [],\n error: error instanceof Error ? error.message : 'The artifact folder could not be read.',\n };\n }\n // A live session event wins over the earlier seed read.\n if (sessions.get(scope.sessionId) !== state || state.snapshot !== undefined) return;\n state.snapshot = snapshot;\n host.publish(scope.sessionId, snapshot);\n })();\n },\n sessionRemoved(sessionId) {\n sessions.get(sessionId)?.unsubscribe();\n sessions.delete(sessionId);\n },\n close() {\n for (const state of sessions.values()) state.unsubscribe();\n sessions.clear();\n },\n };\n },\n };\n}\n","import { defineChannel } from '@agimon-ai/po-core/extensionFile';\n\nimport { createArtifactsChannel } from '../../../services/artifactChannel';\n\nexport default defineChannel(createArtifactsChannel);\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { ArtifactPreview, ArtifactSnapshot } from './artifacts';\n\nconst routes = {\n list: {\n method: 'GET',\n path: '/',\n response: undefined as unknown as ApiResponseOf<ArtifactSnapshot>,\n },\n preview: {\n method: 'GET',\n path: '/preview',\n query: ['path'],\n response: undefined as unknown as ApiResponseOf<ArtifactPreview>,\n },\n content: {\n method: 'GET',\n path: '/content',\n query: ['path'],\n raw: true,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","import path from 'node:path';\n\nimport type { DoomApi, DoomApiContext } from '@agimon-ai/po-core/packageApi';\nimport { Hono } from 'hono';\n\nimport { MAX_CONTENT_BYTES, MAX_PREVIEW_BYTES } from '../../constants/artifacts';\nimport routes from '../../types/apiRoutes';\nimport type { ArtifactPreview } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactRoot, listArtifacts, openArtifact, readArtifactBytes } from '../artifactFiles';\n\nexport function createArtifactsApi(root: string): Hono {\n const app = new Hono();\n app.onError((error, context) =>\n context.json(\n { error: error instanceof ArtifactAccessError ? error.message : 'The artifact could not be read.' },\n error instanceof ArtifactAccessError ? error.status : 500,\n ),\n );\n app.get(routes.list.path, async (context) => {\n const snapshot = await listArtifacts(root);\n return context.json(snapshot, snapshot.error === undefined ? 200 : 500);\n });\n app.get(routes.preview.path, async (context) => {\n const file = await openArtifact(root, context.req.query('path'));\n try {\n const preview: ArtifactPreview = { path: file.path, content: '', mimeType: file.mimeType };\n if (file.stat.size > MAX_PREVIEW_BYTES) {\n return context.json({\n ...preview,\n unavailable: true,\n reason: 'This artifact is too large for a text preview.',\n });\n }\n const bytes = await readArtifactBytes(file.handle, MAX_PREVIEW_BYTES + 1);\n if (bytes.byteLength > MAX_PREVIEW_BYTES)\n return context.json({\n ...preview,\n unavailable: true,\n reason: 'This artifact is too large for a text preview.',\n });\n if (bytes.subarray(0, 8192).includes(0))\n return context.json({ ...preview, unavailable: true, reason: 'This artifact is binary.' });\n return context.json({ ...preview, content: new TextDecoder().decode(bytes) });\n } finally {\n await file.handle.close();\n }\n });\n app.get(routes.content.path, async (context) => {\n const file = await openArtifact(root, context.req.query('path'));\n try {\n if (file.stat.size > MAX_CONTENT_BYTES)\n throw new ArtifactAccessError('This artifact is too large to download.', 413);\n const version = `\"${file.stat.dev}-${file.stat.ino}-${file.stat.size}-${file.stat.mtimeMs}-${file.stat.ctimeMs}\"`;\n const name = encodeURIComponent(path.basename(file.path)).replace(\n /[!'()*]/gu,\n (value) => `%${value.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n const headers = new Headers({\n 'content-type': file.mimeType,\n 'content-disposition': `attachment; filename*=UTF-8''${name}`,\n 'cache-control': 'no-store',\n 'x-content-type-options': 'nosniff',\n 'content-security-policy': \"default-src 'none'; sandbox\",\n 'accept-ranges': 'bytes',\n etag: version,\n });\n const expectedVersion = context.req.header('if-match');\n if (expectedVersion !== undefined && expectedVersion !== version)\n return new Response(null, { status: 412, headers });\n let start = 0;\n let end = file.stat.size - 1;\n const range = context.req.header('range');\n if (range !== undefined) {\n const match = /^bytes=(\\d*)-(\\d*)$/u.exec(range);\n if (\n !match ||\n (!match[1] && !match[2]) ||\n file.stat.size === 0 ||\n [match[1], match[2]].some((value) => value !== '' && !Number.isSafeInteger(Number(value)))\n ) {\n headers.set('content-range', `bytes */${file.stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n if (match[1] === '') {\n const suffix = Number(match[2]);\n start = suffix > 0 ? Math.max(0, file.stat.size - suffix) : file.stat.size;\n } else {\n start = Number(match[1]);\n end = match[2] === '' ? end : Math.min(end, Number(match[2]));\n }\n if (start > end || start >= file.stat.size) {\n headers.set('content-range', `bytes */${file.stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n headers.set('content-range', `bytes ${start}-${end}/${file.stat.size}`);\n }\n const bytes = await readArtifactBytes(file.handle, Math.max(0, end - start + 1), start);\n const after = await file.handle.stat();\n if (after.size !== file.stat.size || after.mtimeMs !== file.stat.mtimeMs || after.ctimeMs !== file.stat.ctimeMs)\n return new Response(null, { status: 412, headers });\n headers.set('content-length', String(bytes.byteLength));\n return new Response(bytes, { status: range === undefined ? 200 : 206, headers });\n } finally {\n await file.handle.close();\n }\n });\n return app;\n}\n\nexport const api: DoomApi = {\n basePath: 'artifacts',\n start(context: DoomApiContext) {\n if (!context.sessionId) throw new Error('Artifacts requires a session identity.');\n const app = createArtifactsApi(artifactRoot(context.sessionId, context.environment ?? {}));\n return { fetch: (request) => app.fetch(request), close: () => undefined };\n },\n};\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\n\nimport { api } from '../../../../../../services/artifactsApi';\n\nexport default defineRoute(api);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { ArtifactServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(\n (context: WithRoot<DoomServerPluginContext, ArtifactServerScope>) => context.root.beforeAgentStart,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { ArtifactServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(\n (context: WithRoot<DoomServerPluginContext, ArtifactServerScope>) => context.root.toolExecutionEnd,\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport channelArtifacts from '../src/extensions/(backend)/channel/artifacts.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/artifacts/route.server';\nimport hookBeforeAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server';\nimport hookToolExecutionEnd from '../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-artifact',\n providerOverrides: composeServerProviderOverrides(rootSession),\n global: () => ({\n get channels(): DoomServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n }),\n workspace: () => ({\n get channels(): DoomServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n }),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): DoomServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n get channels(): DoomServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n get hooks(): DoomServerSessionPlugin['hooks'] { return [via({ event: 'before_agent_start' }, at(hookBeforeAgentStart, contextSession)), via({ event: 'tool_execution_end' }, at(hookToolExecutionEnd, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.mjs","names":["rootSession","channelArtifacts","apiRoute","hookBeforeAgentStart","hookToolExecutionEnd"],"sources":["../../src/types/artifacts.ts","../../src/services/artifactSession/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/services/artifactChannel/index.ts","../../src/extensions/(backend)/channel/artifacts.server.ts","../../src/types/apiRoutes.ts","../../src/services/artifactsApi/index.ts","../../src/extensions/workspaces/sessions/(backend)/api/artifacts/route.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.ts","../../generated/server.ts"],"sourcesContent":["export interface ArtifactItem {\n /** Relative to the current session's artifact directory. */\n path: string;\n name: string;\n size: number;\n modifiedAt: number;\n}\n\nexport interface ArtifactSnapshot {\n items: ArtifactItem[];\n error?: string;\n}\n\nexport interface ArtifactPreview {\n path: string;\n content: string;\n mimeType: string;\n unavailable?: boolean;\n reason?: string;\n}\n\nexport const artifactsChannelType = 'artifacts';\n","import { clearTimeout, setTimeout } from 'node:timers';\n\nimport type { PoHeadlessActivity, PoHeadlessExecutionContext, PoHeadlessHook } from '@agimon-ai/po-core/headless';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { ARTIFACT_REFRESH_MS } from '../../constants/artifacts';\nimport { artifactsChannelType } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactRoot, ensureArtifactRoot, listArtifacts } from '../artifactFiles';\n\n/**\n * Creates the session artifact folder. Resolves false when the host has no artifact folder\n * (the edge build in M1 rejects with a 404); Node never rejects with that status here.\n */\nasync function prepareArtifactRoot(root: string): Promise<boolean> {\n try {\n await ensureArtifactRoot(root);\n return true;\n } catch (error) {\n if (error instanceof ArtifactAccessError && error.status === 404) return false;\n throw error;\n }\n}\n\nexport function createArtifactSession({ host }: PoServerPluginContext) {\n const environment = Object.freeze({ ...host.context.environment });\n let active: { context: PoHeadlessExecutionContext; root: string; fingerprint?: string } | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let pending: Promise<void> | undefined;\n let closed = false;\n\n const refresh = (): Promise<void> => {\n if (pending) return pending;\n const runtime = active;\n if (!runtime || closed) return Promise.resolve();\n pending = (async () => {\n const snapshot = await listArtifacts(runtime.root);\n if (closed || active !== runtime) return;\n const fingerprint = JSON.stringify(snapshot);\n if (fingerprint === runtime.fingerprint) return;\n runtime.fingerprint = fingerprint;\n host.context.directEvents?.publish(artifactsChannelType, runtime.context.sessionId, snapshot);\n })().finally(() => {\n pending = undefined;\n });\n return pending;\n };\n const schedule = (): void => {\n if (!active || closed) return;\n timer = setTimeout(() => {\n timer = undefined;\n void refresh().finally(schedule);\n }, ARTIFACT_REFRESH_MS);\n };\n const stop = async (): Promise<void> => {\n active = undefined;\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n await pending;\n };\n const activity: PoHeadlessActivity = {\n name: 'artifacts',\n async start(context) {\n if (closed) throw new Error('Artifact session is closed.');\n await stop();\n const root = artifactRoot(context.sessionId, environment);\n if (!(await prepareArtifactRoot(root))) return stop;\n if (closed) throw new Error('Artifact session is closed.');\n active = { context, root };\n await refresh();\n schedule();\n return stop;\n },\n };\n const beforeAgentStart: PoHeadlessHook = {\n event: 'before_agent_start',\n async handle(event, context) {\n const root = artifactRoot(context.sessionId, environment);\n const systemPrompt = typeof event.systemPrompt === 'string' ? event.systemPrompt : '';\n if (!(await prepareArtifactRoot(root))) return { systemPrompt };\n const declaration = `Session artifact folder: ${root}`;\n if (systemPrompt.includes(declaration)) return { systemPrompt };\n return {\n systemPrompt:\n `${systemPrompt}\\n\\n${declaration}\\nSave user-designated artifacts and important deliverable documents in this folder, using descriptive filenames. Keep ordinary repository source edits in their existing locations. Do not move existing documents or overwrite artifacts without the user's intent. This folder belongs only to the current session.`.trim(),\n };\n },\n };\n const toolExecutionEnd: PoHeadlessHook = {\n event: 'tool_execution_end',\n handle(_event, context) {\n return active?.context.sessionId === context.sessionId ? refresh() : undefined;\n },\n };\n return {\n activities: [activity],\n beforeAgentStart,\n toolExecutionEnd,\n onDispose: async () => {\n closed = true;\n await stop();\n },\n };\n}\n","import type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createArtifactSession } from '../../../../../services/artifactSession';\n\nexport const createArtifactServerRoot = (context: PoServerPluginContext) => {\n const runtime = createArtifactSession(context);\n return { value: runtime, activities: runtime.activities, onDispose: runtime.onDispose };\n};\nexport type ArtifactServerScope = ReturnType<typeof createArtifactServerRoot>['value'];\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\n\nimport { createArtifactServerRoot } from './_lib/serverRoot';\n\nexport default defineRoot(createArtifactServerRoot);\n","import type { PoHubChannel } from '@agimon-ai/po-core/hubChannel';\n\nimport { artifactsChannelType, type ArtifactSnapshot } from '../../types/artifacts';\nimport { artifactRoot, listArtifacts } from '../artifactFiles';\n\nfunction snapshotOf(value: unknown): value is ArtifactSnapshot {\n if (typeof value !== 'object' || value === null) return false;\n const snapshot = value as Partial<ArtifactSnapshot>;\n return (\n Array.isArray(snapshot.items) &&\n (snapshot.error === undefined || typeof snapshot.error === 'string') &&\n snapshot.items.every(\n (item) =>\n typeof item === 'object' &&\n item !== null &&\n typeof item.path === 'string' &&\n typeof item.name === 'string' &&\n typeof item.size === 'number' &&\n Number.isFinite(item.size) &&\n typeof item.modifiedAt === 'number' &&\n Number.isFinite(item.modifiedAt),\n )\n );\n}\n\nexport function createArtifactsChannel(): PoHubChannel {\n return {\n frameType: artifactsChannelType,\n start(host) {\n const sessions = new Map<string, { snapshot?: ArtifactSnapshot; unsubscribe: () => void }>();\n return {\n payloadFor(scope) {\n return sessions.get(scope.sessionId)?.snapshot;\n },\n sessionAdded(scope) {\n sessions.get(scope.sessionId)?.unsubscribe();\n const state: { snapshot?: ArtifactSnapshot; unsubscribe: () => void } = { unsubscribe: () => undefined };\n sessions.set(scope.sessionId, state);\n state.unsubscribe = host.directEvents.subscribe(\n artifactsChannelType,\n scope.sessionId,\n (snapshot) => {\n if (!snapshotOf(snapshot) || sessions.get(scope.sessionId) !== state) return;\n state.snapshot = snapshot;\n host.publish(scope.sessionId, snapshot);\n },\n { replayLatest: true },\n );\n void (async () => {\n let snapshot: ArtifactSnapshot;\n try {\n snapshot = await listArtifacts(artifactRoot(scope.sessionId, scope.environment ?? {}));\n } catch (error) {\n snapshot = {\n items: [],\n error: error instanceof Error ? error.message : 'The artifact folder could not be read.',\n };\n }\n // A live session event wins over the earlier seed read.\n if (sessions.get(scope.sessionId) !== state || state.snapshot !== undefined) return;\n state.snapshot = snapshot;\n host.publish(scope.sessionId, snapshot);\n })();\n },\n sessionRemoved(sessionId) {\n sessions.get(sessionId)?.unsubscribe();\n sessions.delete(sessionId);\n },\n close() {\n for (const state of sessions.values()) state.unsubscribe();\n sessions.clear();\n },\n };\n },\n };\n}\n","import { defineChannel } from '@agimon-ai/po-core/extensionFile';\n\nimport { createArtifactsChannel } from '../../../services/artifactChannel';\n\nexport default defineChannel(createArtifactsChannel);\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { ArtifactPreview, ArtifactSnapshot } from './artifacts';\n\nconst routes = {\n list: {\n method: 'GET',\n path: '/',\n response: undefined as unknown as ApiResponseOf<ArtifactSnapshot>,\n },\n preview: {\n method: 'GET',\n path: '/preview',\n query: ['path'],\n response: undefined as unknown as ApiResponseOf<ArtifactPreview>,\n },\n content: {\n method: 'GET',\n path: '/content',\n query: ['path'],\n raw: true,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","import path from 'node:path';\n\nimport type { PoApi, PoApiContext } from '@agimon-ai/po-core/packageApi';\nimport { Hono } from 'hono';\n\nimport { MAX_CONTENT_BYTES, MAX_PREVIEW_BYTES } from '../../constants/artifacts';\nimport routes from '../../types/apiRoutes';\nimport type { ArtifactPreview } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactRoot, listArtifacts, openArtifact, readArtifactBytes } from '../artifactFiles';\n\nexport function createArtifactsApi(root: string): Hono {\n const app = new Hono();\n app.onError((error, context) =>\n context.json(\n { error: error instanceof ArtifactAccessError ? error.message : 'The artifact could not be read.' },\n error instanceof ArtifactAccessError ? error.status : 500,\n ),\n );\n app.get(routes.list.path, async (context) => {\n const snapshot = await listArtifacts(root);\n return context.json(snapshot, snapshot.error === undefined ? 200 : 500);\n });\n app.get(routes.preview.path, async (context) => {\n const file = await openArtifact(root, context.req.query('path'));\n try {\n const preview: ArtifactPreview = { path: file.path, content: '', mimeType: file.mimeType };\n if (file.stat.size > MAX_PREVIEW_BYTES) {\n return context.json({\n ...preview,\n unavailable: true,\n reason: 'This artifact is too large for a text preview.',\n });\n }\n const bytes = await readArtifactBytes(file.handle, MAX_PREVIEW_BYTES + 1);\n if (bytes.byteLength > MAX_PREVIEW_BYTES)\n return context.json({\n ...preview,\n unavailable: true,\n reason: 'This artifact is too large for a text preview.',\n });\n if (bytes.subarray(0, 8192).includes(0))\n return context.json({ ...preview, unavailable: true, reason: 'This artifact is binary.' });\n return context.json({ ...preview, content: new TextDecoder().decode(bytes) });\n } finally {\n await file.handle.close();\n }\n });\n app.get(routes.content.path, async (context) => {\n const file = await openArtifact(root, context.req.query('path'));\n try {\n if (file.stat.size > MAX_CONTENT_BYTES)\n throw new ArtifactAccessError('This artifact is too large to download.', 413);\n const version = `\"${file.stat.dev}-${file.stat.ino}-${file.stat.size}-${file.stat.mtimeMs}-${file.stat.ctimeMs}\"`;\n const name = encodeURIComponent(path.basename(file.path)).replace(\n /[!'()*]/gu,\n (value) => `%${value.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n const headers = new Headers({\n 'content-type': file.mimeType,\n 'content-disposition': `attachment; filename*=UTF-8''${name}`,\n 'cache-control': 'no-store',\n 'x-content-type-options': 'nosniff',\n 'content-security-policy': \"default-src 'none'; sandbox\",\n 'accept-ranges': 'bytes',\n etag: version,\n });\n const expectedVersion = context.req.header('if-match');\n if (expectedVersion !== undefined && expectedVersion !== version)\n return new Response(null, { status: 412, headers });\n let start = 0;\n let end = file.stat.size - 1;\n const range = context.req.header('range');\n if (range !== undefined) {\n const match = /^bytes=(\\d*)-(\\d*)$/u.exec(range);\n if (\n !match ||\n (!match[1] && !match[2]) ||\n file.stat.size === 0 ||\n [match[1], match[2]].some((value) => value !== '' && !Number.isSafeInteger(Number(value)))\n ) {\n headers.set('content-range', `bytes */${file.stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n if (match[1] === '') {\n const suffix = Number(match[2]);\n start = suffix > 0 ? Math.max(0, file.stat.size - suffix) : file.stat.size;\n } else {\n start = Number(match[1]);\n end = match[2] === '' ? end : Math.min(end, Number(match[2]));\n }\n if (start > end || start >= file.stat.size) {\n headers.set('content-range', `bytes */${file.stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n headers.set('content-range', `bytes ${start}-${end}/${file.stat.size}`);\n }\n const bytes = await readArtifactBytes(file.handle, Math.max(0, end - start + 1), start);\n const after = await file.handle.stat();\n if (after.size !== file.stat.size || after.mtimeMs !== file.stat.mtimeMs || after.ctimeMs !== file.stat.ctimeMs)\n return new Response(null, { status: 412, headers });\n headers.set('content-length', String(bytes.byteLength));\n return new Response(bytes, { status: range === undefined ? 200 : 206, headers });\n } finally {\n await file.handle.close();\n }\n });\n return app;\n}\n\nexport const api: PoApi = {\n basePath: 'artifacts',\n start(context: PoApiContext) {\n if (!context.sessionId) throw new Error('Artifacts requires a session identity.');\n const app = createArtifactsApi(artifactRoot(context.sessionId, context.environment ?? {}));\n return { fetch: (request) => app.fetch(request), close: () => undefined };\n },\n};\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\n\nimport { api } from '../../../../../../services/artifactsApi';\n\nexport default defineRoute(api);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { ArtifactServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(\n (context: WithRoot<PoServerPluginContext, ArtifactServerScope>) => context.root.beforeAgentStart,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { ArtifactServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(\n (context: WithRoot<PoServerPluginContext, ArtifactServerScope>) => context.root.toolExecutionEnd,\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport channelArtifacts from '../src/extensions/(backend)/channel/artifacts.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/artifacts/route.server';\nimport hookBeforeAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server';\nimport hookToolExecutionEnd from '../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-artifact',\n providerOverrides: composeServerProviderOverrides(rootSession),\n global: () => ({\n get channels(): PoServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n }),\n workspace: () => ({\n get channels(): PoServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n }),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): PoServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n get channels(): PoServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n get hooks(): PoServerSessionPlugin['hooks'] { return [via({ event: 'before_agent_start' }, at(hookBeforeAgentStart, contextSession)), via({ event: 'tool_execution_end' }, at(hookToolExecutionEnd, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
|
package/dist/extensions/web.mjs
CHANGED
|
@@ -160,28 +160,28 @@ function ArtifactPreviewView({ path, preview, error, media, loadImage }) {
|
|
|
160
160
|
return /* @__PURE__ */ jsxs("div", {
|
|
161
161
|
"data-testid": "artifact-preview",
|
|
162
162
|
"data-artifact-path": path,
|
|
163
|
-
className: "flex min-h-0 flex-1 flex-col bg-
|
|
163
|
+
className: "flex min-h-0 flex-1 flex-col bg-po-bg text-po-text",
|
|
164
164
|
children: [/* @__PURE__ */ jsxs("header", {
|
|
165
|
-
className: "flex shrink-0 items-center gap-2 border-b border-
|
|
165
|
+
className: "flex shrink-0 items-center gap-2 border-b border-po-border px-4 py-3",
|
|
166
166
|
children: [/* @__PURE__ */ jsx(Breadcrumb, {
|
|
167
167
|
path,
|
|
168
168
|
className: "min-w-0 flex-1"
|
|
169
169
|
}), /* @__PURE__ */ jsx("span", {
|
|
170
|
-
className: "text-sm text-
|
|
170
|
+
className: "text-sm text-po-dim",
|
|
171
171
|
children: "Artifact"
|
|
172
172
|
})]
|
|
173
173
|
}), /* @__PURE__ */ jsx("div", {
|
|
174
174
|
className: "min-h-0 flex-1 overflow-auto p-4",
|
|
175
175
|
children: error ? /* @__PURE__ */ jsx("p", {
|
|
176
176
|
role: "alert",
|
|
177
|
-
className: "text-sm text-
|
|
177
|
+
className: "text-sm text-po-red",
|
|
178
178
|
children: error
|
|
179
179
|
}) : !preview ? /* @__PURE__ */ jsx("p", {
|
|
180
180
|
role: "status",
|
|
181
|
-
className: "text-sm text-
|
|
181
|
+
className: "text-sm text-po-dim",
|
|
182
182
|
children: "Reading this artifact..."
|
|
183
183
|
}) : mode === "media" ? media : preview.unavailable ? /* @__PURE__ */ jsx("p", {
|
|
184
|
-
className: "text-sm text-
|
|
184
|
+
className: "text-sm text-po-dim",
|
|
185
185
|
children: preview.reason ?? "This artifact cannot be previewed."
|
|
186
186
|
}) : mode === "markdown" ? /* @__PURE__ */ jsx(Markdown, {
|
|
187
187
|
text: preview.content,
|
|
@@ -190,14 +190,14 @@ function ArtifactPreviewView({ path, preview, error, media, loadImage }) {
|
|
|
190
190
|
title: path,
|
|
191
191
|
sandbox: "",
|
|
192
192
|
srcDoc: preview.content,
|
|
193
|
-
className: "h-96 w-full rounded-md border border-
|
|
193
|
+
className: "h-96 w-full rounded-md border border-po-border bg-po-deep"
|
|
194
194
|
}) : mode === "code" ? /* @__PURE__ */ jsx(CodeEditor, {
|
|
195
195
|
value: preview.content,
|
|
196
196
|
path,
|
|
197
197
|
readOnly: true,
|
|
198
|
-
className: "min-h-64 rounded-md border border-
|
|
198
|
+
className: "min-h-64 rounded-md border border-po-border"
|
|
199
199
|
}) : /* @__PURE__ */ jsx("pre", {
|
|
200
|
-
className: "whitespace-pre-wrap break-words font-mono text-sm text-
|
|
200
|
+
className: "whitespace-pre-wrap break-words font-mono text-sm text-po-text",
|
|
201
201
|
children: preview.content
|
|
202
202
|
})
|
|
203
203
|
})]
|
|
@@ -241,7 +241,7 @@ function ArtifactMedia({ sessionId, path, revision }) {
|
|
|
241
241
|
contentType: current.mimeType
|
|
242
242
|
}) : /* @__PURE__ */ jsx("p", {
|
|
243
243
|
role: current?.error ? "alert" : "status",
|
|
244
|
-
className: current?.error ? "text-sm text-
|
|
244
|
+
className: current?.error ? "text-sm text-po-red" : "text-sm text-po-dim",
|
|
245
245
|
children: current?.error ?? "Loading artifact media..."
|
|
246
246
|
});
|
|
247
247
|
}
|
|
@@ -314,16 +314,16 @@ function RecentArtifactsView({ items, loading, error, onOpen }) {
|
|
|
314
314
|
children: [
|
|
315
315
|
error ? /* @__PURE__ */ jsx("p", {
|
|
316
316
|
role: "alert",
|
|
317
|
-
className: "text-sm text-
|
|
317
|
+
className: "text-sm text-po-red",
|
|
318
318
|
children: error
|
|
319
319
|
}) : null,
|
|
320
320
|
loading ? /* @__PURE__ */ jsx("p", {
|
|
321
321
|
role: "status",
|
|
322
|
-
className: "text-sm text-
|
|
322
|
+
className: "text-sm text-po-dim",
|
|
323
323
|
children: "Loading artifacts..."
|
|
324
324
|
}) : null,
|
|
325
325
|
!loading && !error && items.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
326
|
-
className: "text-sm text-
|
|
326
|
+
className: "text-sm text-po-dim",
|
|
327
327
|
children: "No artifacts yet"
|
|
328
328
|
}) : null,
|
|
329
329
|
sortedArtifacts(items).slice(0, 3).map((item) => /* @__PURE__ */ jsxs(Button, {
|
|
@@ -333,16 +333,16 @@ function RecentArtifactsView({ items, loading, error, onOpen }) {
|
|
|
333
333
|
onClick: () => onOpen(item),
|
|
334
334
|
className: "gap-0.5 px-2 py-1.5",
|
|
335
335
|
children: [/* @__PURE__ */ jsxs("span", {
|
|
336
|
-
className: "flex min-w-0 items-center gap-2 text-base text-
|
|
336
|
+
className: "flex min-w-0 items-center gap-2 text-base text-po-text",
|
|
337
337
|
children: [/* @__PURE__ */ jsx(FileIcon, {
|
|
338
338
|
"aria-hidden": true,
|
|
339
|
-
className: "size-3.5 text-
|
|
339
|
+
className: "size-3.5 text-po-dim"
|
|
340
340
|
}), /* @__PURE__ */ jsx("span", {
|
|
341
341
|
className: "truncate",
|
|
342
342
|
children: item.name
|
|
343
343
|
})]
|
|
344
344
|
}), /* @__PURE__ */ jsx("span", {
|
|
345
|
-
className: "truncate font-mono text-sm text-
|
|
345
|
+
className: "truncate font-mono text-sm text-po-dim",
|
|
346
346
|
title: item.path,
|
|
347
347
|
children: item.path
|
|
348
348
|
})]
|
|
@@ -352,16 +352,16 @@ function RecentArtifactsView({ items, loading, error, onOpen }) {
|
|
|
352
352
|
}
|
|
353
353
|
function ArtifactGridView({ items, loading, error, onOpen }) {
|
|
354
354
|
return /* @__PURE__ */ jsxs("div", {
|
|
355
|
-
className: "flex min-h-0 flex-1 flex-col overflow-auto bg-
|
|
355
|
+
className: "flex min-h-0 flex-1 flex-col overflow-auto bg-po-bg text-po-text",
|
|
356
356
|
children: [
|
|
357
357
|
/* @__PURE__ */ jsxs("header", {
|
|
358
|
-
className: "flex shrink-0 items-center gap-3 border-b border-
|
|
358
|
+
className: "flex shrink-0 items-center gap-3 border-b border-po-border px-4 py-3",
|
|
359
359
|
children: [/* @__PURE__ */ jsx("h1", {
|
|
360
|
-
className: "text-lg font-semibold text-
|
|
360
|
+
className: "text-lg font-semibold text-po-hi",
|
|
361
361
|
children: "Artifacts"
|
|
362
362
|
}), /* @__PURE__ */ jsxs("span", {
|
|
363
363
|
"data-testid": "artifacts-count",
|
|
364
|
-
className: "text-sm text-
|
|
364
|
+
className: "text-sm text-po-dim",
|
|
365
365
|
children: [
|
|
366
366
|
items.length,
|
|
367
367
|
" ",
|
|
@@ -371,12 +371,12 @@ function ArtifactGridView({ items, loading, error, onOpen }) {
|
|
|
371
371
|
}),
|
|
372
372
|
error ? /* @__PURE__ */ jsx("p", {
|
|
373
373
|
role: "alert",
|
|
374
|
-
className: "px-4 py-3 text-sm text-
|
|
374
|
+
className: "px-4 py-3 text-sm text-po-red",
|
|
375
375
|
children: error
|
|
376
376
|
}) : null,
|
|
377
377
|
loading ? /* @__PURE__ */ jsx("p", {
|
|
378
378
|
role: "status",
|
|
379
|
-
className: "px-4 py-3 text-sm text-
|
|
379
|
+
className: "px-4 py-3 text-sm text-po-dim",
|
|
380
380
|
children: "Loading artifacts..."
|
|
381
381
|
}) : null,
|
|
382
382
|
!loading && !error && items.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {
|
|
@@ -396,20 +396,20 @@ function ArtifactGridView({ items, loading, error, onOpen }) {
|
|
|
396
396
|
children: [
|
|
397
397
|
/* @__PURE__ */ jsx(FileIcon, {
|
|
398
398
|
"aria-hidden": true,
|
|
399
|
-
className: "size-5 text-
|
|
399
|
+
className: "size-5 text-po-green"
|
|
400
400
|
}),
|
|
401
401
|
/* @__PURE__ */ jsx("span", {
|
|
402
|
-
className: "truncate text-base font-semibold text-
|
|
402
|
+
className: "truncate text-base font-semibold text-po-hi",
|
|
403
403
|
title: item.name,
|
|
404
404
|
children: item.name
|
|
405
405
|
}),
|
|
406
406
|
/* @__PURE__ */ jsx("span", {
|
|
407
|
-
className: "truncate font-mono text-sm text-
|
|
407
|
+
className: "truncate font-mono text-sm text-po-dim",
|
|
408
408
|
title: item.path,
|
|
409
409
|
children: item.path
|
|
410
410
|
}),
|
|
411
411
|
/* @__PURE__ */ jsx("span", {
|
|
412
|
-
className: "text-sm text-
|
|
412
|
+
className: "text-sm text-po-dim",
|
|
413
413
|
children: sizeLabel(item.size)
|
|
414
414
|
})
|
|
415
415
|
]
|
|
@@ -466,7 +466,7 @@ function ArtifactsPanel({ sessionId, openTransientTab }) {
|
|
|
466
466
|
var artifacts_web_default$1 = defineActivityGroup({
|
|
467
467
|
icon: FileIcon,
|
|
468
468
|
keys: "",
|
|
469
|
-
statusKey: "
|
|
469
|
+
statusKey: "po-artifacts",
|
|
470
470
|
activeSource: artifactActivitySource,
|
|
471
471
|
transientTab: artifactsTab,
|
|
472
472
|
marksBackgroundWork: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.mjs","names":["activityGroupArtifacts","channelArtifacts","fillArtifacts"],"sources":["../../src/types/apiRoutes.ts","../../generated/client.ts","../../src/types/artifacts.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/artifactView.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/artifactsStore.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/artifactsApi.ts","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactPreviewView.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactPreviewPanel.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactsView.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactsPanel.tsx","../../src/extensions/workspaces/sessions/(frontend)/activity-group/artifacts.web.ts","../../src/extensions/workspaces/sessions/(frontend)/channel/artifacts.web.ts","../../src/extensions/workspaces/sessions/(frontend)/fill/Artifacts.activity.artifacts.web.tsx","../../generated/web.ts"],"sourcesContent":["import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { ArtifactPreview, ArtifactSnapshot } from './artifacts';\n\nconst routes = {\n list: {\n method: 'GET',\n path: '/',\n response: undefined as unknown as ApiResponseOf<ArtifactSnapshot>,\n },\n preview: {\n method: 'GET',\n path: '/preview',\n query: ['path'],\n response: undefined as unknown as ApiResponseOf<ArtifactPreview>,\n },\n content: {\n method: 'GET',\n path: '/content',\n query: ['path'],\n raw: true,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { createApiClient, sessionApiAddress } from '@agimon-ai/po-core/web';\nimport { sealedTransport } from '@agimon-ai/po-web-security/browser';\n\nimport routes from '../src/types/apiRoutes';\n\n/** Scopes the tree mounts 'artifacts' at. */\nexport const apiScopes = ['session'] as const;\n\nexport const api = createApiClient(routes, {\n scopes: apiScopes,\n basePath: 'artifacts',\n transport: (input, init) => sealedTransport.fetch(input, init),\n sessionAddress: sessionApiAddress,\n});\n","export interface ArtifactItem {\n /** Relative to the current session's artifact directory. */\n path: string;\n name: string;\n size: number;\n modifiedAt: number;\n}\n\nexport interface ArtifactSnapshot {\n items: ArtifactItem[];\n error?: string;\n}\n\nexport interface ArtifactPreview {\n path: string;\n content: string;\n mimeType: string;\n unavailable?: boolean;\n reason?: string;\n}\n\nexport const artifactsChannelType = 'artifacts';\n","import type { ArtifactItem, ArtifactSnapshot } from '../../../../../types/artifacts';\n\nexport function sortedArtifacts(items: readonly ArtifactItem[]): ArtifactItem[] {\n return [...items].sort(\n (left, right) =>\n right.modifiedAt - left.modifiedAt || (left.path < right.path ? -1 : left.path > right.path ? 1 : 0),\n );\n}\n\nexport function artifactTabId(sessionId: string, path: string): string {\n return `artifact:${encodeURIComponent(sessionId)}:${encodeURIComponent(path)}`;\n}\n\nexport function artifactsTabId(sessionId: string): string {\n return `artifacts-${encodeURIComponent(sessionId)}`;\n}\n\n/** Resolves document assets within the artifact folder, never into another filesystem root. */\nexport function artifactAssetPath(documentPath: string, imagePath: string): string {\n if (!imagePath || imagePath.startsWith('/') || imagePath.includes('\\\\') || /^[a-z][a-z0-9+.-]*:/iu.test(imagePath))\n throw new Error('Artifact images must use a relative path.');\n const segments = documentPath.split('/').slice(0, -1);\n for (const segment of imagePath.split('/')) {\n if (segment === '' || segment === '.') continue;\n if (segment === '..') {\n if (segments.length === 0) throw new Error('The image is outside this artifact folder.');\n segments.pop();\n } else segments.push(segment);\n }\n if (segments.length === 0) throw new Error('The image path is empty.');\n return segments.join('/');\n}\n\nexport function artifactPreviewMode(path: string): 'markdown' | 'html' | 'media' | 'code' | 'text' {\n if (/\\.(?:png|jpe?g|gif|webp|avif|bmp|svg|ico|mp3|wav|m4a|ogg|mp4|mov|webm|pdf)$/iu.test(path)) return 'media';\n if (/\\.(?:md|markdown)$/iu.test(path)) return 'markdown';\n if (/\\.html?$/iu.test(path)) return 'html';\n if (/\\.(?:[cm]?[jt]sx?|json|ya?ml|css|scss|py|sh|bash|go|rs|rb|java|c|cpp|h|sql|xml|toml)$/iu.test(path))\n return 'code';\n return 'text';\n}\n\n/** A malformed channel frame must not replace the current session's known inventory. */\nexport function parseArtifactSnapshot(input: unknown): ArtifactSnapshot | null {\n if (typeof input !== 'object' || input === null || !('items' in input) || !Array.isArray(input.items)) return null;\n if ('error' in input && input.error !== undefined && typeof input.error !== 'string') return null;\n const items: ArtifactItem[] = [];\n for (const item of input.items) {\n if (\n typeof item !== 'object' ||\n item === null ||\n typeof item.path !== 'string' ||\n !item.path ||\n item.path.startsWith('/') ||\n item.path.includes('\\\\') ||\n item.path.split('/').some((part: string) => part === '..' || part === '.') ||\n typeof item.name !== 'string' ||\n !item.name ||\n typeof item.size !== 'number' ||\n !Number.isFinite(item.size) ||\n item.size < 0 ||\n typeof item.modifiedAt !== 'number' ||\n !Number.isFinite(item.modifiedAt) ||\n item.modifiedAt < 0\n )\n return null;\n items.push({ path: item.path, name: item.name, size: item.size, modifiedAt: item.modifiedAt });\n }\n return {\n items: sortedArtifacts(items),\n ...('error' in input && typeof input.error === 'string' ? { error: input.error } : {}),\n };\n}\n","import { defineSessionStore } from '@agimon-ai/po-core/web';\n\nimport { artifactsChannelType, type ArtifactSnapshot } from '../../../../../types/artifacts';\nimport { parseArtifactSnapshot } from './artifactView';\n\nexport const artifacts = defineSessionStore<ArtifactSnapshot & { loaded: boolean }>({ items: [], loaded: false });\n\nexport const artifactsChannel = artifacts.channel<ArtifactSnapshot>({\n channel: artifactsChannelType,\n parse: parseArtifactSnapshot,\n reduce: (_current, snapshot) => ({ ...snapshot, loaded: true }),\n});\n\n/** Artifact documents are outcomes, not background work. The source also keeps the empty launcher visible. */\nexport const artifactActivitySource = {\n subscribe(listener: () => void) {\n const subscription = artifacts.store.subscribe(listener);\n return () => subscription.unsubscribe();\n },\n isActive: () => false,\n};\n","import { api } from '../../../../../../generated/client';\nimport type { ArtifactPreview } from '../../../../../types/artifacts';\nimport { artifacts } from './artifactsStore';\nimport { artifactAssetPath, parseArtifactSnapshot } from './artifactView';\n\nexport async function refreshArtifacts(sessionId: string, signal: AbortSignal): Promise<void> {\n const before = artifacts.select(artifacts.store.state, sessionId);\n try {\n const result = await api.session(sessionId).list({ signal });\n if (signal.aborted || artifacts.select(artifacts.store.state, sessionId) !== before) return;\n const snapshot = result.ok ? parseArtifactSnapshot(result.data) : null;\n artifacts.update(sessionId, () =>\n snapshot\n ? { ...snapshot, loaded: true }\n : {\n ...before,\n loaded: true,\n error: result.ok\n ? 'The session answered with no artifact list.'\n : result.error || 'Artifacts could not be loaded.',\n },\n );\n } catch (error) {\n if (!signal.aborted && artifacts.select(artifacts.store.state, sessionId) === before)\n artifacts.update(sessionId, () => ({\n ...before,\n loaded: true,\n error: error instanceof Error ? error.message : String(error),\n }));\n }\n}\n\nexport async function fetchArtifactPreview(\n sessionId: string,\n path: string,\n signal: AbortSignal,\n): Promise<ArtifactPreview> {\n const result = await api.session(sessionId).preview({ query: { path }, signal });\n if (!result.ok) throw new Error(result.error || 'This artifact could not be read.');\n const preview = result.data;\n if (\n !preview ||\n typeof preview.path !== 'string' ||\n typeof preview.content !== 'string' ||\n typeof preview.mimeType !== 'string'\n )\n throw new Error('The session answered with no artifact.');\n return preview;\n}\n\n/** Generated raw calls keep media and Markdown asset reads on the sealed artifact transport. */\nexport async function artifactBlob(sessionId: string, path: string, signal?: AbortSignal): Promise<Blob> {\n const result = await api.session(sessionId).content({ query: { path }, ...(signal ? { signal } : {}) });\n if (!result.ok) throw new Error(result.error || 'This artifact could not be loaded.');\n return result.response.blob();\n}\n\nexport async function loadArtifactImage(sessionId: string, documentPath: string, imagePath: string) {\n const blob = await artifactBlob(sessionId, artifactAssetPath(documentPath, imagePath));\n if (!blob.type.startsWith('image/')) throw new Error('This artifact asset is not an image.');\n const url = URL.createObjectURL(blob);\n return { url, dispose: () => URL.revokeObjectURL(url) };\n}\n","import { Breadcrumb, CodeEditor, Markdown } from '@agimon-ai/po-web-components';\nimport type { ReactNode } from 'react';\n\nimport type { ArtifactPreview } from '../../../../../types/artifacts';\nimport { artifactPreviewMode } from '../_lib/artifactView';\n\ninterface ArtifactPreviewViewProps {\n path: string;\n preview?: ArtifactPreview;\n error?: string;\n media?: ReactNode;\n loadImage?: (path: string) => Promise<{ url: string; dispose: () => void }>;\n}\n\nexport function ArtifactPreviewView({ path, preview, error, media, loadImage }: ArtifactPreviewViewProps) {\n const mode = artifactPreviewMode(path);\n return (\n <div\n data-testid=\"artifact-preview\"\n data-artifact-path={path}\n className=\"flex min-h-0 flex-1 flex-col bg-doom-bg text-doom-text\"\n >\n <header className=\"flex shrink-0 items-center gap-2 border-b border-doom-border px-4 py-3\">\n <Breadcrumb path={path} className=\"min-w-0 flex-1\" />\n <span className=\"text-sm text-doom-dim\">Artifact</span>\n </header>\n <div className=\"min-h-0 flex-1 overflow-auto p-4\">\n {error ? (\n <p role=\"alert\" className=\"text-sm text-doom-red\">\n {error}\n </p>\n ) : !preview ? (\n <p role=\"status\" className=\"text-sm text-doom-dim\">\n Reading this artifact...\n </p>\n ) : mode === 'media' ? (\n media\n ) : preview.unavailable ? (\n <p className=\"text-sm text-doom-dim\">{preview.reason ?? 'This artifact cannot be previewed.'}</p>\n ) : mode === 'markdown' ? (\n <Markdown text={preview.content} loadImage={loadImage} />\n ) : mode === 'html' ? (\n <iframe\n title={path}\n sandbox=\"\"\n srcDoc={preview.content}\n className=\"h-96 w-full rounded-md border border-doom-border bg-doom-deep\"\n />\n ) : mode === 'code' ? (\n <CodeEditor\n value={preview.content}\n path={path}\n readOnly\n className=\"min-h-64 rounded-md border border-doom-border\"\n />\n ) : (\n <pre className=\"whitespace-pre-wrap break-words font-mono text-sm text-doom-text\">{preview.content}</pre>\n )}\n </div>\n </div>\n );\n}\n","import type { TransientTab, WebPluginSlotProps } from '@agimon-ai/po-core/web';\nimport { MediaPreview } from '@agimon-ai/po-web-components';\nimport { useStore } from '@tanstack/react-store';\nimport { useCallback, useEffect, useState } from 'react';\n\nimport type { ArtifactPreview } from '../../../../../types/artifacts';\nimport { artifactBlob, fetchArtifactPreview, loadArtifactImage } from '../_lib/artifactsApi';\nimport { artifacts } from '../_lib/artifactsStore';\nimport { artifactTabId } from '../_lib/artifactView';\nimport { ArtifactPreviewView } from './ArtifactPreviewView';\n\nfunction ArtifactMedia({ sessionId, path, revision }: { sessionId: string; path: string; revision: string }) {\n const [loaded, setLoaded] = useState<{ source: string; url?: string; mimeType?: string; error?: string }>();\n const source = `${sessionId}:${path}:${revision}`;\n useEffect(() => {\n const controller = new AbortController();\n let url: string | undefined;\n void artifactBlob(sessionId, path, controller.signal)\n .then((blob) => {\n if (controller.signal.aborted) return;\n url = URL.createObjectURL(blob);\n setLoaded({ source, url, mimeType: blob.type });\n })\n .catch((error: unknown) => {\n if (!controller.signal.aborted)\n setLoaded({ source, error: error instanceof Error ? error.message : String(error) });\n });\n return () => {\n controller.abort();\n if (url !== undefined) URL.revokeObjectURL(url);\n };\n }, [sessionId, path, source]);\n const current = loaded?.source === source ? loaded : undefined;\n return current?.url ? (\n <MediaPreview src={current.url} path={path} contentType={current.mimeType} />\n ) : (\n <p\n role={current?.error ? 'alert' : 'status'}\n className={current?.error ? 'text-sm text-doom-red' : 'text-sm text-doom-dim'}\n >\n {current?.error ?? 'Loading artifact media...'}\n </p>\n );\n}\n\nexport function ArtifactPreviewPanel({ path, sessionId }: WebPluginSlotProps & { path: string }) {\n const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));\n const item = snapshot.items.find((candidate) => candidate.path === path);\n const revision = item ? `${item.modifiedAt}:${item.size}` : snapshot.loaded ? 'missing' : 'initial';\n const [loaded, setLoaded] = useState<{\n sessionId: string;\n path: string;\n revision: string;\n preview?: ArtifactPreview;\n error?: string;\n }>();\n useEffect(() => {\n if (sessionId === null || revision === 'missing') return;\n const controller = new AbortController();\n void fetchArtifactPreview(sessionId, path, controller.signal)\n .then((preview) => {\n if (!controller.signal.aborted) setLoaded({ sessionId, path, revision, preview });\n })\n .catch((error: unknown) => {\n if (!controller.signal.aborted)\n setLoaded({ sessionId, path, revision, error: error instanceof Error ? error.message : String(error) });\n });\n return () => controller.abort();\n }, [sessionId, path, revision]);\n const current =\n loaded?.sessionId === sessionId && loaded.path === path && loaded.revision === revision ? loaded : undefined;\n const loadImage = useCallback(\n (imagePath: string) => {\n if (sessionId === null) return Promise.reject(new Error('The session is unavailable.'));\n return loadArtifactImage(sessionId, path, imagePath);\n },\n [sessionId, path],\n );\n return (\n <ArtifactPreviewView\n path={path}\n preview={current?.preview}\n error={\n sessionId === null\n ? 'The session is unavailable.'\n : revision === 'missing'\n ? 'This artifact no longer exists.'\n : current?.error\n }\n loadImage={loadImage}\n media={sessionId === null ? undefined : <ArtifactMedia sessionId={sessionId} path={path} revision={revision} />}\n />\n );\n}\n\nexport function artifactTab(sessionId: string, path: string): TransientTab {\n return {\n id: artifactTabId(sessionId, path),\n label: path.split('/').at(-1) ?? path,\n panel: (props) => <ArtifactPreviewPanel {...props} sessionId={sessionId} path={path} />,\n };\n}\n","import { Button, EmptyState, FileIcon } from '@agimon-ai/po-web-components';\n\nimport type { ArtifactItem } from '../../../../../types/artifacts';\nimport { sortedArtifacts } from '../_lib/artifactView';\n\ninterface ArtifactsViewProps {\n items: readonly ArtifactItem[];\n loading?: boolean;\n error?: string;\n onOpen: (item: ArtifactItem) => void;\n}\n\nfunction sizeLabel(size: number): string {\n return size < 1024\n ? `${size} B`\n : size < 1024 * 1024\n ? `${Math.ceil(size / 1024)} KB`\n : `${(size / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nexport function RecentArtifactsView({ items, loading, error, onOpen }: ArtifactsViewProps) {\n return (\n <div data-testid=\"artifacts-recent-items\" className=\"flex flex-col gap-1\">\n {error ? (\n <p role=\"alert\" className=\"text-sm text-doom-red\">\n {error}\n </p>\n ) : null}\n {loading ? (\n <p role=\"status\" className=\"text-sm text-doom-dim\">\n Loading artifacts...\n </p>\n ) : null}\n {!loading && !error && items.length === 0 ? <p className=\"text-sm text-doom-dim\">No artifacts yet</p> : null}\n {sortedArtifacts(items)\n .slice(0, 3)\n .map((item) => (\n <Button\n key={item.path}\n variant=\"ghost\"\n size=\"card\"\n data-artifact-path={item.path}\n onClick={() => onOpen(item)}\n className=\"gap-0.5 px-2 py-1.5\"\n >\n <span className=\"flex min-w-0 items-center gap-2 text-base text-doom-text\">\n <FileIcon aria-hidden className=\"size-3.5 text-doom-dim\" />\n <span className=\"truncate\">{item.name}</span>\n </span>\n <span className=\"truncate font-mono text-sm text-doom-dim\" title={item.path}>\n {item.path}\n </span>\n </Button>\n ))}\n </div>\n );\n}\n\nexport function ArtifactGridView({ items, loading, error, onOpen }: ArtifactsViewProps) {\n return (\n <div className=\"flex min-h-0 flex-1 flex-col overflow-auto bg-doom-bg text-doom-text\">\n <header className=\"flex shrink-0 items-center gap-3 border-b border-doom-border px-4 py-3\">\n <h1 className=\"text-lg font-semibold text-doom-hi\">Artifacts</h1>\n <span data-testid=\"artifacts-count\" className=\"text-sm text-doom-dim\">\n {items.length} {items.length === 1 ? 'artifact' : 'artifacts'}\n </span>\n </header>\n {error ? (\n <p role=\"alert\" className=\"px-4 py-3 text-sm text-doom-red\">\n {error}\n </p>\n ) : null}\n {loading ? (\n <p role=\"status\" className=\"px-4 py-3 text-sm text-doom-dim\">\n Loading artifacts...\n </p>\n ) : null}\n {!loading && !error && items.length === 0 ? (\n <EmptyState\n title=\"No artifacts yet\"\n description=\"Documents Po prepares for this session will appear here.\"\n className=\"px-4 py-8\"\n />\n ) : null}\n <div data-testid=\"artifacts-grid\" className=\"grid grid-cols-1 gap-3 p-4 sm:grid-cols-2 xl:grid-cols-3\">\n {sortedArtifacts(items).map((item) => (\n <Button\n key={item.path}\n variant=\"outline\"\n size=\"card\"\n data-artifact-path={item.path}\n onClick={() => onOpen(item)}\n className=\"min-w-0 gap-2 p-4\"\n >\n <FileIcon aria-hidden className=\"size-5 text-doom-green\" />\n <span className=\"truncate text-base font-semibold text-doom-hi\" title={item.name}>\n {item.name}\n </span>\n <span className=\"truncate font-mono text-sm text-doom-dim\" title={item.path}>\n {item.path}\n </span>\n <span className=\"text-sm text-doom-dim\">{sizeLabel(item.size)}</span>\n </Button>\n ))}\n </div>\n </div>\n );\n}\n","import type { TransientTab, WebPluginSlotProps } from '@agimon-ai/po-core/web';\nimport { useStore } from '@tanstack/react-store';\nimport { useEffect } from 'react';\n\nimport { refreshArtifacts } from '../_lib/artifactsApi';\nimport { artifacts } from '../_lib/artifactsStore';\nimport { artifactTab } from './ArtifactPreviewPanel';\nimport { ArtifactGridView, RecentArtifactsView } from './ArtifactsView';\n\nexport function artifactsTab(): TransientTab {\n // The host stores transient tabs per session, including this heading factory's fixed id.\n return { id: 'artifacts-grid', label: 'Artifacts', panel: ArtifactsPanel };\n}\n\nexport function ArtifactsActivitySection({ sessionId, openTransientTab }: WebPluginSlotProps) {\n const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));\n useEffect(() => {\n if (sessionId === null || snapshot.loaded) return;\n const controller = new AbortController();\n void refreshArtifacts(sessionId, controller.signal);\n return () => controller.abort();\n }, [sessionId, snapshot.loaded]);\n return (\n <RecentArtifactsView\n items={snapshot.items}\n loading={!snapshot.loaded}\n error={snapshot.error}\n onOpen={(item) => {\n if (sessionId !== null) openTransientTab(artifactTab(sessionId, item.path));\n }}\n />\n );\n}\n\nexport function ArtifactsPanel({ sessionId, openTransientTab }: WebPluginSlotProps) {\n const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));\n useEffect(() => {\n if (sessionId === null) return;\n const controller = new AbortController();\n void refreshArtifacts(sessionId, controller.signal);\n return () => controller.abort();\n }, [sessionId]);\n return (\n <ArtifactGridView\n items={snapshot.items}\n loading={!snapshot.loaded && sessionId !== null}\n error={sessionId === null ? 'The session is unavailable.' : snapshot.error}\n onOpen={(item) => {\n if (sessionId !== null) openTransientTab(artifactTab(sessionId, item.path));\n }}\n />\n );\n}\n","import { defineActivityGroup } from '@agimon-ai/po-core/web';\nimport { FileIcon } from '@agimon-ai/po-web-components';\n\nimport { artifactsTab } from '../_components/ArtifactsPanel';\nimport { artifactActivitySource } from '../_lib/artifactsStore';\nexport default defineActivityGroup({\n icon: FileIcon,\n keys: '',\n statusKey: 'doom-artifacts',\n activeSource: artifactActivitySource,\n transientTab: artifactsTab,\n marksBackgroundWork: false,\n order: 15,\n});\n","import { defineChannelFile } from '@agimon-ai/po-core/web';\n\nimport { artifactsChannel } from '../_lib/artifactsStore';\nexport default defineChannelFile(artifactsChannel);\n","import { defineFill } from '@agimon-ai/po-core/web';\n\nimport { ArtifactsActivitySection } from '../_components/ArtifactsPanel';\nexport default defineFill({ component: ArtifactsActivitySection });\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/po-core/web';\n\nimport activityGroupArtifacts from '../src/extensions/workspaces/sessions/(frontend)/activity-group/artifacts.web';\nimport channelArtifacts from '../src/extensions/workspaces/sessions/(frontend)/channel/artifacts.web';\nimport fillArtifacts from '../src/extensions/workspaces/sessions/(frontend)/fill/Artifacts.activity.artifacts.web';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const webPlugin = defineWebPlugin({\n id: 'artifacts',\n session: {\n get activityGroups(): WebPluginContributions['activityGroups'] { return [via({ name: 'artifacts' }, activityGroupArtifacts)]; },\n get channels(): WebPluginContributions['channels'] { return [via({ channel: 'artifacts' }, channelArtifacts)]; },\n get fills(): WebPluginContributions['fills'] { return [via({ slot: 'activity.artifacts', id: 'artifacts' }, fillArtifacts)]; },\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"web.mjs","names":["activityGroupArtifacts","channelArtifacts","fillArtifacts"],"sources":["../../src/types/apiRoutes.ts","../../generated/client.ts","../../src/types/artifacts.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/artifactView.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/artifactsStore.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/artifactsApi.ts","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactPreviewView.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactPreviewPanel.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactsView.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactsPanel.tsx","../../src/extensions/workspaces/sessions/(frontend)/activity-group/artifacts.web.ts","../../src/extensions/workspaces/sessions/(frontend)/channel/artifacts.web.ts","../../src/extensions/workspaces/sessions/(frontend)/fill/Artifacts.activity.artifacts.web.tsx","../../generated/web.ts"],"sourcesContent":["import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { ArtifactPreview, ArtifactSnapshot } from './artifacts';\n\nconst routes = {\n list: {\n method: 'GET',\n path: '/',\n response: undefined as unknown as ApiResponseOf<ArtifactSnapshot>,\n },\n preview: {\n method: 'GET',\n path: '/preview',\n query: ['path'],\n response: undefined as unknown as ApiResponseOf<ArtifactPreview>,\n },\n content: {\n method: 'GET',\n path: '/content',\n query: ['path'],\n raw: true,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { createApiClient, sessionApiAddress } from '@agimon-ai/po-core/web';\nimport { sealedTransport } from '@agimon-ai/po-web-security/browser';\n\nimport routes from '../src/types/apiRoutes';\n\n/** Scopes the tree mounts 'artifacts' at. */\nexport const apiScopes = ['session'] as const;\n\nexport const api = createApiClient(routes, {\n scopes: apiScopes,\n basePath: 'artifacts',\n transport: (input, init) => sealedTransport.fetch(input, init),\n sessionAddress: sessionApiAddress,\n});\n","export interface ArtifactItem {\n /** Relative to the current session's artifact directory. */\n path: string;\n name: string;\n size: number;\n modifiedAt: number;\n}\n\nexport interface ArtifactSnapshot {\n items: ArtifactItem[];\n error?: string;\n}\n\nexport interface ArtifactPreview {\n path: string;\n content: string;\n mimeType: string;\n unavailable?: boolean;\n reason?: string;\n}\n\nexport const artifactsChannelType = 'artifacts';\n","import type { ArtifactItem, ArtifactSnapshot } from '../../../../../types/artifacts';\n\nexport function sortedArtifacts(items: readonly ArtifactItem[]): ArtifactItem[] {\n return [...items].sort(\n (left, right) =>\n right.modifiedAt - left.modifiedAt || (left.path < right.path ? -1 : left.path > right.path ? 1 : 0),\n );\n}\n\nexport function artifactTabId(sessionId: string, path: string): string {\n return `artifact:${encodeURIComponent(sessionId)}:${encodeURIComponent(path)}`;\n}\n\nexport function artifactsTabId(sessionId: string): string {\n return `artifacts-${encodeURIComponent(sessionId)}`;\n}\n\n/** Resolves document assets within the artifact folder, never into another filesystem root. */\nexport function artifactAssetPath(documentPath: string, imagePath: string): string {\n if (!imagePath || imagePath.startsWith('/') || imagePath.includes('\\\\') || /^[a-z][a-z0-9+.-]*:/iu.test(imagePath))\n throw new Error('Artifact images must use a relative path.');\n const segments = documentPath.split('/').slice(0, -1);\n for (const segment of imagePath.split('/')) {\n if (segment === '' || segment === '.') continue;\n if (segment === '..') {\n if (segments.length === 0) throw new Error('The image is outside this artifact folder.');\n segments.pop();\n } else segments.push(segment);\n }\n if (segments.length === 0) throw new Error('The image path is empty.');\n return segments.join('/');\n}\n\nexport function artifactPreviewMode(path: string): 'markdown' | 'html' | 'media' | 'code' | 'text' {\n if (/\\.(?:png|jpe?g|gif|webp|avif|bmp|svg|ico|mp3|wav|m4a|ogg|mp4|mov|webm|pdf)$/iu.test(path)) return 'media';\n if (/\\.(?:md|markdown)$/iu.test(path)) return 'markdown';\n if (/\\.html?$/iu.test(path)) return 'html';\n if (/\\.(?:[cm]?[jt]sx?|json|ya?ml|css|scss|py|sh|bash|go|rs|rb|java|c|cpp|h|sql|xml|toml)$/iu.test(path))\n return 'code';\n return 'text';\n}\n\n/** A malformed channel frame must not replace the current session's known inventory. */\nexport function parseArtifactSnapshot(input: unknown): ArtifactSnapshot | null {\n if (typeof input !== 'object' || input === null || !('items' in input) || !Array.isArray(input.items)) return null;\n if ('error' in input && input.error !== undefined && typeof input.error !== 'string') return null;\n const items: ArtifactItem[] = [];\n for (const item of input.items) {\n if (\n typeof item !== 'object' ||\n item === null ||\n typeof item.path !== 'string' ||\n !item.path ||\n item.path.startsWith('/') ||\n item.path.includes('\\\\') ||\n item.path.split('/').some((part: string) => part === '..' || part === '.') ||\n typeof item.name !== 'string' ||\n !item.name ||\n typeof item.size !== 'number' ||\n !Number.isFinite(item.size) ||\n item.size < 0 ||\n typeof item.modifiedAt !== 'number' ||\n !Number.isFinite(item.modifiedAt) ||\n item.modifiedAt < 0\n )\n return null;\n items.push({ path: item.path, name: item.name, size: item.size, modifiedAt: item.modifiedAt });\n }\n return {\n items: sortedArtifacts(items),\n ...('error' in input && typeof input.error === 'string' ? { error: input.error } : {}),\n };\n}\n","import { defineSessionStore } from '@agimon-ai/po-core/web';\n\nimport { artifactsChannelType, type ArtifactSnapshot } from '../../../../../types/artifacts';\nimport { parseArtifactSnapshot } from './artifactView';\n\nexport const artifacts = defineSessionStore<ArtifactSnapshot & { loaded: boolean }>({ items: [], loaded: false });\n\nexport const artifactsChannel = artifacts.channel<ArtifactSnapshot>({\n channel: artifactsChannelType,\n parse: parseArtifactSnapshot,\n reduce: (_current, snapshot) => ({ ...snapshot, loaded: true }),\n});\n\n/** Artifact documents are outcomes, not background work. The source also keeps the empty launcher visible. */\nexport const artifactActivitySource = {\n subscribe(listener: () => void) {\n const subscription = artifacts.store.subscribe(listener);\n return () => subscription.unsubscribe();\n },\n isActive: () => false,\n};\n","import { api } from '../../../../../../generated/client';\nimport type { ArtifactPreview } from '../../../../../types/artifacts';\nimport { artifacts } from './artifactsStore';\nimport { artifactAssetPath, parseArtifactSnapshot } from './artifactView';\n\nexport async function refreshArtifacts(sessionId: string, signal: AbortSignal): Promise<void> {\n const before = artifacts.select(artifacts.store.state, sessionId);\n try {\n const result = await api.session(sessionId).list({ signal });\n if (signal.aborted || artifacts.select(artifacts.store.state, sessionId) !== before) return;\n const snapshot = result.ok ? parseArtifactSnapshot(result.data) : null;\n artifacts.update(sessionId, () =>\n snapshot\n ? { ...snapshot, loaded: true }\n : {\n ...before,\n loaded: true,\n error: result.ok\n ? 'The session answered with no artifact list.'\n : result.error || 'Artifacts could not be loaded.',\n },\n );\n } catch (error) {\n if (!signal.aborted && artifacts.select(artifacts.store.state, sessionId) === before)\n artifacts.update(sessionId, () => ({\n ...before,\n loaded: true,\n error: error instanceof Error ? error.message : String(error),\n }));\n }\n}\n\nexport async function fetchArtifactPreview(\n sessionId: string,\n path: string,\n signal: AbortSignal,\n): Promise<ArtifactPreview> {\n const result = await api.session(sessionId).preview({ query: { path }, signal });\n if (!result.ok) throw new Error(result.error || 'This artifact could not be read.');\n const preview = result.data;\n if (\n !preview ||\n typeof preview.path !== 'string' ||\n typeof preview.content !== 'string' ||\n typeof preview.mimeType !== 'string'\n )\n throw new Error('The session answered with no artifact.');\n return preview;\n}\n\n/** Generated raw calls keep media and Markdown asset reads on the sealed artifact transport. */\nexport async function artifactBlob(sessionId: string, path: string, signal?: AbortSignal): Promise<Blob> {\n const result = await api.session(sessionId).content({ query: { path }, ...(signal ? { signal } : {}) });\n if (!result.ok) throw new Error(result.error || 'This artifact could not be loaded.');\n return result.response.blob();\n}\n\nexport async function loadArtifactImage(sessionId: string, documentPath: string, imagePath: string) {\n const blob = await artifactBlob(sessionId, artifactAssetPath(documentPath, imagePath));\n if (!blob.type.startsWith('image/')) throw new Error('This artifact asset is not an image.');\n const url = URL.createObjectURL(blob);\n return { url, dispose: () => URL.revokeObjectURL(url) };\n}\n","import { Breadcrumb, CodeEditor, Markdown } from '@agimon-ai/po-web-components';\nimport type { ReactNode } from 'react';\n\nimport type { ArtifactPreview } from '../../../../../types/artifacts';\nimport { artifactPreviewMode } from '../_lib/artifactView';\n\ninterface ArtifactPreviewViewProps {\n path: string;\n preview?: ArtifactPreview;\n error?: string;\n media?: ReactNode;\n loadImage?: (path: string) => Promise<{ url: string; dispose: () => void }>;\n}\n\nexport function ArtifactPreviewView({ path, preview, error, media, loadImage }: ArtifactPreviewViewProps) {\n const mode = artifactPreviewMode(path);\n return (\n <div\n data-testid=\"artifact-preview\"\n data-artifact-path={path}\n className=\"flex min-h-0 flex-1 flex-col bg-po-bg text-po-text\"\n >\n <header className=\"flex shrink-0 items-center gap-2 border-b border-po-border px-4 py-3\">\n <Breadcrumb path={path} className=\"min-w-0 flex-1\" />\n <span className=\"text-sm text-po-dim\">Artifact</span>\n </header>\n <div className=\"min-h-0 flex-1 overflow-auto p-4\">\n {error ? (\n <p role=\"alert\" className=\"text-sm text-po-red\">\n {error}\n </p>\n ) : !preview ? (\n <p role=\"status\" className=\"text-sm text-po-dim\">\n Reading this artifact...\n </p>\n ) : mode === 'media' ? (\n media\n ) : preview.unavailable ? (\n <p className=\"text-sm text-po-dim\">{preview.reason ?? 'This artifact cannot be previewed.'}</p>\n ) : mode === 'markdown' ? (\n <Markdown text={preview.content} loadImage={loadImage} />\n ) : mode === 'html' ? (\n <iframe\n title={path}\n sandbox=\"\"\n srcDoc={preview.content}\n className=\"h-96 w-full rounded-md border border-po-border bg-po-deep\"\n />\n ) : mode === 'code' ? (\n <CodeEditor\n value={preview.content}\n path={path}\n readOnly\n className=\"min-h-64 rounded-md border border-po-border\"\n />\n ) : (\n <pre className=\"whitespace-pre-wrap break-words font-mono text-sm text-po-text\">{preview.content}</pre>\n )}\n </div>\n </div>\n );\n}\n","import type { TransientTab, WebPluginSlotProps } from '@agimon-ai/po-core/web';\nimport { MediaPreview } from '@agimon-ai/po-web-components';\nimport { useStore } from '@tanstack/react-store';\nimport { useCallback, useEffect, useState } from 'react';\n\nimport type { ArtifactPreview } from '../../../../../types/artifacts';\nimport { artifactBlob, fetchArtifactPreview, loadArtifactImage } from '../_lib/artifactsApi';\nimport { artifacts } from '../_lib/artifactsStore';\nimport { artifactTabId } from '../_lib/artifactView';\nimport { ArtifactPreviewView } from './ArtifactPreviewView';\n\nfunction ArtifactMedia({ sessionId, path, revision }: { sessionId: string; path: string; revision: string }) {\n const [loaded, setLoaded] = useState<{ source: string; url?: string; mimeType?: string; error?: string }>();\n const source = `${sessionId}:${path}:${revision}`;\n useEffect(() => {\n const controller = new AbortController();\n let url: string | undefined;\n void artifactBlob(sessionId, path, controller.signal)\n .then((blob) => {\n if (controller.signal.aborted) return;\n url = URL.createObjectURL(blob);\n setLoaded({ source, url, mimeType: blob.type });\n })\n .catch((error: unknown) => {\n if (!controller.signal.aborted)\n setLoaded({ source, error: error instanceof Error ? error.message : String(error) });\n });\n return () => {\n controller.abort();\n if (url !== undefined) URL.revokeObjectURL(url);\n };\n }, [sessionId, path, source]);\n const current = loaded?.source === source ? loaded : undefined;\n return current?.url ? (\n <MediaPreview src={current.url} path={path} contentType={current.mimeType} />\n ) : (\n <p\n role={current?.error ? 'alert' : 'status'}\n className={current?.error ? 'text-sm text-po-red' : 'text-sm text-po-dim'}\n >\n {current?.error ?? 'Loading artifact media...'}\n </p>\n );\n}\n\nexport function ArtifactPreviewPanel({ path, sessionId }: WebPluginSlotProps & { path: string }) {\n const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));\n const item = snapshot.items.find((candidate) => candidate.path === path);\n const revision = item ? `${item.modifiedAt}:${item.size}` : snapshot.loaded ? 'missing' : 'initial';\n const [loaded, setLoaded] = useState<{\n sessionId: string;\n path: string;\n revision: string;\n preview?: ArtifactPreview;\n error?: string;\n }>();\n useEffect(() => {\n if (sessionId === null || revision === 'missing') return;\n const controller = new AbortController();\n void fetchArtifactPreview(sessionId, path, controller.signal)\n .then((preview) => {\n if (!controller.signal.aborted) setLoaded({ sessionId, path, revision, preview });\n })\n .catch((error: unknown) => {\n if (!controller.signal.aborted)\n setLoaded({ sessionId, path, revision, error: error instanceof Error ? error.message : String(error) });\n });\n return () => controller.abort();\n }, [sessionId, path, revision]);\n const current =\n loaded?.sessionId === sessionId && loaded.path === path && loaded.revision === revision ? loaded : undefined;\n const loadImage = useCallback(\n (imagePath: string) => {\n if (sessionId === null) return Promise.reject(new Error('The session is unavailable.'));\n return loadArtifactImage(sessionId, path, imagePath);\n },\n [sessionId, path],\n );\n return (\n <ArtifactPreviewView\n path={path}\n preview={current?.preview}\n error={\n sessionId === null\n ? 'The session is unavailable.'\n : revision === 'missing'\n ? 'This artifact no longer exists.'\n : current?.error\n }\n loadImage={loadImage}\n media={sessionId === null ? undefined : <ArtifactMedia sessionId={sessionId} path={path} revision={revision} />}\n />\n );\n}\n\nexport function artifactTab(sessionId: string, path: string): TransientTab {\n return {\n id: artifactTabId(sessionId, path),\n label: path.split('/').at(-1) ?? path,\n panel: (props) => <ArtifactPreviewPanel {...props} sessionId={sessionId} path={path} />,\n };\n}\n","import { Button, EmptyState, FileIcon } from '@agimon-ai/po-web-components';\n\nimport type { ArtifactItem } from '../../../../../types/artifacts';\nimport { sortedArtifacts } from '../_lib/artifactView';\n\ninterface ArtifactsViewProps {\n items: readonly ArtifactItem[];\n loading?: boolean;\n error?: string;\n onOpen: (item: ArtifactItem) => void;\n}\n\nfunction sizeLabel(size: number): string {\n return size < 1024\n ? `${size} B`\n : size < 1024 * 1024\n ? `${Math.ceil(size / 1024)} KB`\n : `${(size / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nexport function RecentArtifactsView({ items, loading, error, onOpen }: ArtifactsViewProps) {\n return (\n <div data-testid=\"artifacts-recent-items\" className=\"flex flex-col gap-1\">\n {error ? (\n <p role=\"alert\" className=\"text-sm text-po-red\">\n {error}\n </p>\n ) : null}\n {loading ? (\n <p role=\"status\" className=\"text-sm text-po-dim\">\n Loading artifacts...\n </p>\n ) : null}\n {!loading && !error && items.length === 0 ? <p className=\"text-sm text-po-dim\">No artifacts yet</p> : null}\n {sortedArtifacts(items)\n .slice(0, 3)\n .map((item) => (\n <Button\n key={item.path}\n variant=\"ghost\"\n size=\"card\"\n data-artifact-path={item.path}\n onClick={() => onOpen(item)}\n className=\"gap-0.5 px-2 py-1.5\"\n >\n <span className=\"flex min-w-0 items-center gap-2 text-base text-po-text\">\n <FileIcon aria-hidden className=\"size-3.5 text-po-dim\" />\n <span className=\"truncate\">{item.name}</span>\n </span>\n <span className=\"truncate font-mono text-sm text-po-dim\" title={item.path}>\n {item.path}\n </span>\n </Button>\n ))}\n </div>\n );\n}\n\nexport function ArtifactGridView({ items, loading, error, onOpen }: ArtifactsViewProps) {\n return (\n <div className=\"flex min-h-0 flex-1 flex-col overflow-auto bg-po-bg text-po-text\">\n <header className=\"flex shrink-0 items-center gap-3 border-b border-po-border px-4 py-3\">\n <h1 className=\"text-lg font-semibold text-po-hi\">Artifacts</h1>\n <span data-testid=\"artifacts-count\" className=\"text-sm text-po-dim\">\n {items.length} {items.length === 1 ? 'artifact' : 'artifacts'}\n </span>\n </header>\n {error ? (\n <p role=\"alert\" className=\"px-4 py-3 text-sm text-po-red\">\n {error}\n </p>\n ) : null}\n {loading ? (\n <p role=\"status\" className=\"px-4 py-3 text-sm text-po-dim\">\n Loading artifacts...\n </p>\n ) : null}\n {!loading && !error && items.length === 0 ? (\n <EmptyState\n title=\"No artifacts yet\"\n description=\"Documents Po prepares for this session will appear here.\"\n className=\"px-4 py-8\"\n />\n ) : null}\n <div data-testid=\"artifacts-grid\" className=\"grid grid-cols-1 gap-3 p-4 sm:grid-cols-2 xl:grid-cols-3\">\n {sortedArtifacts(items).map((item) => (\n <Button\n key={item.path}\n variant=\"outline\"\n size=\"card\"\n data-artifact-path={item.path}\n onClick={() => onOpen(item)}\n className=\"min-w-0 gap-2 p-4\"\n >\n <FileIcon aria-hidden className=\"size-5 text-po-green\" />\n <span className=\"truncate text-base font-semibold text-po-hi\" title={item.name}>\n {item.name}\n </span>\n <span className=\"truncate font-mono text-sm text-po-dim\" title={item.path}>\n {item.path}\n </span>\n <span className=\"text-sm text-po-dim\">{sizeLabel(item.size)}</span>\n </Button>\n ))}\n </div>\n </div>\n );\n}\n","import type { TransientTab, WebPluginSlotProps } from '@agimon-ai/po-core/web';\nimport { useStore } from '@tanstack/react-store';\nimport { useEffect } from 'react';\n\nimport { refreshArtifacts } from '../_lib/artifactsApi';\nimport { artifacts } from '../_lib/artifactsStore';\nimport { artifactTab } from './ArtifactPreviewPanel';\nimport { ArtifactGridView, RecentArtifactsView } from './ArtifactsView';\n\nexport function artifactsTab(): TransientTab {\n // The host stores transient tabs per session, including this heading factory's fixed id.\n return { id: 'artifacts-grid', label: 'Artifacts', panel: ArtifactsPanel };\n}\n\nexport function ArtifactsActivitySection({ sessionId, openTransientTab }: WebPluginSlotProps) {\n const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));\n useEffect(() => {\n if (sessionId === null || snapshot.loaded) return;\n const controller = new AbortController();\n void refreshArtifacts(sessionId, controller.signal);\n return () => controller.abort();\n }, [sessionId, snapshot.loaded]);\n return (\n <RecentArtifactsView\n items={snapshot.items}\n loading={!snapshot.loaded}\n error={snapshot.error}\n onOpen={(item) => {\n if (sessionId !== null) openTransientTab(artifactTab(sessionId, item.path));\n }}\n />\n );\n}\n\nexport function ArtifactsPanel({ sessionId, openTransientTab }: WebPluginSlotProps) {\n const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));\n useEffect(() => {\n if (sessionId === null) return;\n const controller = new AbortController();\n void refreshArtifacts(sessionId, controller.signal);\n return () => controller.abort();\n }, [sessionId]);\n return (\n <ArtifactGridView\n items={snapshot.items}\n loading={!snapshot.loaded && sessionId !== null}\n error={sessionId === null ? 'The session is unavailable.' : snapshot.error}\n onOpen={(item) => {\n if (sessionId !== null) openTransientTab(artifactTab(sessionId, item.path));\n }}\n />\n );\n}\n","import { defineActivityGroup } from '@agimon-ai/po-core/web';\nimport { FileIcon } from '@agimon-ai/po-web-components';\n\nimport { artifactsTab } from '../_components/ArtifactsPanel';\nimport { artifactActivitySource } from '../_lib/artifactsStore';\nexport default defineActivityGroup({\n icon: FileIcon,\n keys: '',\n statusKey: 'po-artifacts',\n activeSource: artifactActivitySource,\n transientTab: artifactsTab,\n marksBackgroundWork: false,\n order: 15,\n});\n","import { defineChannelFile } from '@agimon-ai/po-core/web';\n\nimport { artifactsChannel } from '../_lib/artifactsStore';\nexport default defineChannelFile(artifactsChannel);\n","import { defineFill } from '@agimon-ai/po-core/web';\n\nimport { ArtifactsActivitySection } from '../_components/ArtifactsPanel';\nexport default defineFill({ component: ArtifactsActivitySection });\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/po-core/web';\n\nimport activityGroupArtifacts from '../src/extensions/workspaces/sessions/(frontend)/activity-group/artifacts.web';\nimport channelArtifacts from '../src/extensions/workspaces/sessions/(frontend)/channel/artifacts.web';\nimport fillArtifacts from '../src/extensions/workspaces/sessions/(frontend)/fill/Artifacts.activity.artifacts.web';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const webPlugin = defineWebPlugin({\n id: 'artifacts',\n session: {\n get activityGroups(): WebPluginContributions['activityGroups'] { return [via({ name: 'artifacts' }, activityGroupArtifacts)]; },\n get channels(): WebPluginContributions['channels'] { return [via({ channel: 'artifacts' }, channelArtifacts)]; },\n get fills(): WebPluginContributions['fills'] { return [via({ slot: 'activity.artifacts', id: 'artifacts' }, fillArtifacts)]; },\n },\n});\n"],"mappings":";;;;;;ACSA,MAAa,MAAM,gBAAgB;CDJjC,MAAM;EACJ,QAAQ;EACR,MAAM;EACN,UAAU,KAAA;CACZ;CACA,SAAS;EACP,QAAQ;EACR,MAAM;EACN,OAAO,CAAC,MAAM;EACd,UAAU,KAAA;CACZ;CACA,SAAS;EACP,QAAQ;EACR,MAAM;EACN,OAAO,CAAC,MAAM;EACd,KAAK;CACP;ACZiC,GAAQ;CACzC,QAAQ,CAHgB,SAGhB;CACR,UAAU;CACV,YAAY,OAAO,SAAS,gBAAgB,MAAM,OAAO,IAAI;CAC7D,gBAAgB;AAClB,CAAC;;;ACOD,MAAa,uBAAuB;;;ACnBpC,SAAgB,gBAAgB,OAAgD;CAC9E,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,MACf,MAAM,UACL,MAAM,aAAa,KAAK,eAAe,KAAK,OAAO,MAAM,OAAO,KAAK,KAAK,OAAO,MAAM,OAAO,IAAI,EACtG;AACF;AAEA,SAAgB,cAAc,WAAmB,MAAsB;CACrE,OAAO,YAAY,mBAAmB,SAAS,EAAE,GAAG,mBAAmB,IAAI;AAC7E;;AAOA,SAAgB,kBAAkB,cAAsB,WAA2B;CACjF,IAAI,CAAC,aAAa,UAAU,WAAW,GAAG,KAAK,UAAU,SAAS,IAAI,KAAK,wBAAwB,KAAK,SAAS,GAC/G,MAAM,IAAI,MAAM,2CAA2C;CAC7D,MAAM,WAAW,aAAa,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG,EAAE;CACpD,KAAK,MAAM,WAAW,UAAU,MAAM,GAAG,GAAG;EAC1C,IAAI,YAAY,MAAM,YAAY,KAAK;EACvC,IAAI,YAAY,MAAM;GACpB,IAAI,SAAS,WAAW,GAAG,MAAM,IAAI,MAAM,4CAA4C;GACvF,SAAS,IAAI;EACf,OAAO,SAAS,KAAK,OAAO;CAC9B;CACA,IAAI,SAAS,WAAW,GAAG,MAAM,IAAI,MAAM,0BAA0B;CACrE,OAAO,SAAS,KAAK,GAAG;AAC1B;AAEA,SAAgB,oBAAoB,MAA+D;CACjG,IAAI,gFAAgF,KAAK,IAAI,GAAG,OAAO;CACvG,IAAI,uBAAuB,KAAK,IAAI,GAAG,OAAO;CAC9C,IAAI,aAAa,KAAK,IAAI,GAAG,OAAO;CACpC,IAAI,0FAA0F,KAAK,IAAI,GACrG,OAAO;CACT,OAAO;AACT;;AAGA,SAAgB,sBAAsB,OAAyC;CAC7E,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,EAAE,WAAW,UAAU,CAAC,MAAM,QAAQ,MAAM,KAAK,GAAG,OAAO;CAC9G,IAAI,WAAW,SAAS,MAAM,UAAU,KAAA,KAAa,OAAO,MAAM,UAAU,UAAU,OAAO;CAC7F,MAAM,QAAwB,CAAC;CAC/B,KAAK,MAAM,QAAQ,MAAM,OAAO;EAC9B,IACE,OAAO,SAAS,YAChB,SAAS,QACT,OAAO,KAAK,SAAS,YACrB,CAAC,KAAK,QACN,KAAK,KAAK,WAAW,GAAG,KACxB,KAAK,KAAK,SAAS,IAAI,KACvB,KAAK,KAAK,MAAM,GAAG,CAAC,CAAC,MAAM,SAAiB,SAAS,QAAQ,SAAS,GAAG,KACzE,OAAO,KAAK,SAAS,YACrB,CAAC,KAAK,QACN,OAAO,KAAK,SAAS,YACrB,CAAC,OAAO,SAAS,KAAK,IAAI,KAC1B,KAAK,OAAO,KACZ,OAAO,KAAK,eAAe,YAC3B,CAAC,OAAO,SAAS,KAAK,UAAU,KAChC,KAAK,aAAa,GAElB,OAAO;EACT,MAAM,KAAK;GAAE,MAAM,KAAK;GAAM,MAAM,KAAK;GAAM,MAAM,KAAK;GAAM,YAAY,KAAK;EAAW,CAAC;CAC/F;CACA,OAAO;EACL,OAAO,gBAAgB,KAAK;EAC5B,GAAI,WAAW,SAAS,OAAO,MAAM,UAAU,WAAW,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;CACtF;AACF;;;ACnEA,MAAa,YAAY,mBAA2D;CAAE,OAAO,CAAC;CAAG,QAAQ;AAAM,CAAC;AAEhH,MAAa,mBAAmB,UAAU,QAA0B;CAClE,SAAS;CACT,OAAO;CACP,SAAS,UAAU,cAAc;EAAE,GAAG;EAAU,QAAQ;CAAK;AAC/D,CAAC;;AAGD,MAAa,yBAAyB;CACpC,UAAU,UAAsB;EAC9B,MAAM,eAAe,UAAU,MAAM,UAAU,QAAQ;EACvD,aAAa,aAAa,YAAY;CACxC;CACA,gBAAgB;AAClB;;;ACfA,eAAsB,iBAAiB,WAAmB,QAAoC;CAC5F,MAAM,SAAS,UAAU,OAAO,UAAU,MAAM,OAAO,SAAS;CAChE,IAAI;EACF,MAAM,SAAS,MAAM,IAAI,QAAQ,SAAS,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC;EAC3D,IAAI,OAAO,WAAW,UAAU,OAAO,UAAU,MAAM,OAAO,SAAS,MAAM,QAAQ;EACrF,MAAM,WAAW,OAAO,KAAK,sBAAsB,OAAO,IAAI,IAAI;EAClE,UAAU,OAAO,iBACf,WACI;GAAE,GAAG;GAAU,QAAQ;EAAK,IAC5B;GACE,GAAG;GACH,QAAQ;GACR,OAAO,OAAO,KACV,gDACA,OAAO,SAAS;EACtB,CACN;CACF,SAAS,OAAO;EACd,IAAI,CAAC,OAAO,WAAW,UAAU,OAAO,UAAU,MAAM,OAAO,SAAS,MAAM,QAC5E,UAAU,OAAO,kBAAkB;GACjC,GAAG;GACH,QAAQ;GACR,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAC9D,EAAE;CACN;AACF;AAEA,eAAsB,qBACpB,WACA,MACA,QAC0B;CAC1B,MAAM,SAAS,MAAM,IAAI,QAAQ,SAAS,CAAC,CAAC,QAAQ;EAAE,OAAO,EAAE,KAAK;EAAG;CAAO,CAAC;CAC/E,IAAI,CAAC,OAAO,IAAI,MAAM,IAAI,MAAM,OAAO,SAAS,kCAAkC;CAClF,MAAM,UAAU,OAAO;CACvB,IACE,CAAC,WACD,OAAO,QAAQ,SAAS,YACxB,OAAO,QAAQ,YAAY,YAC3B,OAAO,QAAQ,aAAa,UAE5B,MAAM,IAAI,MAAM,wCAAwC;CAC1D,OAAO;AACT;;AAGA,eAAsB,aAAa,WAAmB,MAAc,QAAqC;CACvG,MAAM,SAAS,MAAM,IAAI,QAAQ,SAAS,CAAC,CAAC,QAAQ;EAAE,OAAO,EAAE,KAAK;EAAG,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;CAAG,CAAC;CACtG,IAAI,CAAC,OAAO,IAAI,MAAM,IAAI,MAAM,OAAO,SAAS,oCAAoC;CACpF,OAAO,OAAO,SAAS,KAAK;AAC9B;AAEA,eAAsB,kBAAkB,WAAmB,cAAsB,WAAmB;CAClG,MAAM,OAAO,MAAM,aAAa,WAAW,kBAAkB,cAAc,SAAS,CAAC;CACrF,IAAI,CAAC,KAAK,KAAK,WAAW,QAAQ,GAAG,MAAM,IAAI,MAAM,sCAAsC;CAC3F,MAAM,MAAM,IAAI,gBAAgB,IAAI;CACpC,OAAO;EAAE;EAAK,eAAe,IAAI,gBAAgB,GAAG;CAAE;AACxD;;;AChDA,SAAgB,oBAAoB,EAAE,MAAM,SAAS,OAAO,OAAO,aAAuC;CACxG,MAAM,OAAO,oBAAoB,IAAI;CACrC,OACE,qBAAC,OAAD;EACE,eAAY;EACZ,sBAAoB;EACpB,WAAU;EAHZ,UAAA,CAKE,qBAAC,UAAD;GAAQ,WAAU;GAAlB,UAAA,CACE,oBAAC,YAAD;IAAkB;IAAM,WAAU;GAAkB,CAAA,GACpD,oBAAC,QAAD;IAAM,WAAU;IAAsB,UAAA;GAAc,CAAA,CAC9C;EACR,CAAA,GAAA,oBAAC,OAAD;GAAK,WAAU;GACZ,UAAA,QACC,oBAAC,KAAD;IAAG,MAAK;IAAQ,WAAU;IACvB,UAAA;GACA,CAAA,IACD,CAAC,UACH,oBAAC,KAAD;IAAG,MAAK;IAAS,WAAU;IAAsB,UAAA;GAE9C,CAAA,IACD,SAAS,UACX,QACE,QAAQ,cACV,oBAAC,KAAD;IAAG,WAAU;IAAuB,UAAA,QAAQ,UAAU;GAAwC,CAAA,IAC5F,SAAS,aACX,oBAAC,UAAD;IAAU,MAAM,QAAQ;IAAoB;GAAY,CAAA,IACtD,SAAS,SACX,oBAAC,UAAD;IACE,OAAO;IACP,SAAQ;IACR,QAAQ,QAAQ;IAChB,WAAU;GACX,CAAA,IACC,SAAS,SACX,oBAAC,YAAD;IACE,OAAO,QAAQ;IACT;IACN,UAAA;IACA,WAAU;GACX,CAAA,IAED,oBAAC,OAAD;IAAK,WAAU;IAAkE,UAAA,QAAQ;GAAa,CAAA;EAErG,CAAA,CACF;;AAET;;;AClDA,SAAS,cAAc,EAAE,WAAW,MAAM,YAAmE;CAC3G,MAAM,CAAC,QAAQ,aAAa,SAA8E;CAC1G,MAAM,SAAS,GAAG,UAAU,GAAG,KAAK,GAAG;CACvC,gBAAgB;EACd,MAAM,aAAa,IAAI,gBAAgB;EACvC,IAAI;EACJ,aAAkB,WAAW,MAAM,WAAW,MAAM,CAAC,CAClD,MAAM,SAAS;GACd,IAAI,WAAW,OAAO,SAAS;GAC/B,MAAM,IAAI,gBAAgB,IAAI;GAC9B,UAAU;IAAE;IAAQ;IAAK,UAAU,KAAK;GAAK,CAAC;EAChD,CAAC,CAAC,CACD,OAAO,UAAmB;GACzB,IAAI,CAAC,WAAW,OAAO,SACrB,UAAU;IAAE;IAAQ,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAAE,CAAC;EACvF,CAAC;EACH,aAAa;GACX,WAAW,MAAM;GACjB,IAAI,QAAQ,KAAA,GAAW,IAAI,gBAAgB,GAAG;EAChD;CACF,GAAG;EAAC;EAAW;EAAM;CAAM,CAAC;CAC5B,MAAM,UAAU,QAAQ,WAAW,SAAS,SAAS,KAAA;CACrD,OAAO,SAAS,MACd,oBAAC,cAAD;EAAc,KAAK,QAAQ;EAAW;EAAM,aAAa,QAAQ;CAAW,CAAA,IAE5E,oBAAC,KAAD;EACE,MAAM,SAAS,QAAQ,UAAU;EACjC,WAAW,SAAS,QAAQ,wBAAwB;EAEnD,UAAA,SAAS,SAAS;CAClB,CAAA;AAEP;AAEA,SAAgB,qBAAqB,EAAE,MAAM,aAAoD;CAC/F,MAAM,WAAW,SAAS,UAAU,QAAQ,UAAU,UAAU,OAAO,OAAO,SAAS,CAAC;CACxF,MAAM,OAAO,SAAS,MAAM,MAAM,cAAc,UAAU,SAAS,IAAI;CACvE,MAAM,WAAW,OAAO,GAAG,KAAK,WAAW,GAAG,KAAK,SAAS,SAAS,SAAS,YAAY;CAC1F,MAAM,CAAC,QAAQ,aAAa,SAMzB;CACH,gBAAgB;EACd,IAAI,cAAc,QAAQ,aAAa,WAAW;EAClD,MAAM,aAAa,IAAI,gBAAgB;EACvC,qBAA0B,WAAW,MAAM,WAAW,MAAM,CAAC,CAC1D,MAAM,YAAY;GACjB,IAAI,CAAC,WAAW,OAAO,SAAS,UAAU;IAAE;IAAW;IAAM;IAAU;GAAQ,CAAC;EAClF,CAAC,CAAC,CACD,OAAO,UAAmB;GACzB,IAAI,CAAC,WAAW,OAAO,SACrB,UAAU;IAAE;IAAW;IAAM;IAAU,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAAE,CAAC;EAC1G,CAAC;EACH,aAAa,WAAW,MAAM;CAChC,GAAG;EAAC;EAAW;EAAM;CAAQ,CAAC;CAC9B,MAAM,UACJ,QAAQ,cAAc,aAAa,OAAO,SAAS,QAAQ,OAAO,aAAa,WAAW,SAAS,KAAA;CACrG,MAAM,YAAY,aACf,cAAsB;EACrB,IAAI,cAAc,MAAM,OAAO,QAAQ,uBAAO,IAAI,MAAM,6BAA6B,CAAC;EACtF,OAAO,kBAAkB,WAAW,MAAM,SAAS;CACrD,GACA,CAAC,WAAW,IAAI,CAClB;CACA,OACE,oBAAC,qBAAD;EACQ;EACN,SAAS,SAAS;EAClB,OACE,cAAc,OACV,gCACA,aAAa,YACX,oCACA,SAAS;EAEN;EACX,OAAO,cAAc,OAAO,KAAA,IAAY,oBAAC,eAAD;GAA0B;GAAiB;GAAgB;EAAW,CAAA;CAC/G,CAAA;AAEL;AAEA,SAAgB,YAAY,WAAmB,MAA4B;CACzE,OAAO;EACL,IAAI,cAAc,WAAW,IAAI;EACjC,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK;EACjC,QAAQ,UAAU,oBAAC,sBAAD;GAAsB,GAAI;GAAkB;GAAiB;EAAO,CAAA;CACxF;AACF;;;ACzFA,SAAS,UAAU,MAAsB;CACvC,OAAO,OAAO,OACV,GAAG,KAAK,MACR,OAAO,UACL,GAAG,KAAK,KAAK,OAAO,IAAI,EAAE,OAC1B,IAAI,OAAQ,QAAA,CAAc,QAAQ,CAAC,EAAE;AAC7C;AAEA,SAAgB,oBAAoB,EAAE,OAAO,SAAS,OAAO,UAA8B;CACzF,OACE,qBAAC,OAAD;EAAK,eAAY;EAAyB,WAAU;EAApD,UAAA;GACG,QACC,oBAAC,KAAD;IAAG,MAAK;IAAQ,WAAU;IACvB,UAAA;GACA,CAAA,IACD;GACH,UACC,oBAAC,KAAD;IAAG,MAAK;IAAS,WAAU;IAAsB,UAAA;GAE9C,CAAA,IACD;GACH,CAAC,WAAW,CAAC,SAAS,MAAM,WAAW,IAAI,oBAAC,KAAD;IAAG,WAAU;IAAsB,UAAA;GAAmB,CAAA,IAAI;GACrG,gBAAgB,KAAK,CAAC,CACpB,MAAM,GAAG,CAAC,CAAC,CACX,KAAK,SACJ,qBAAC,QAAD;IAEE,SAAQ;IACR,MAAK;IACL,sBAAoB,KAAK;IACzB,eAAe,OAAO,IAAI;IAC1B,WAAU;IANZ,UAAA,CAQE,qBAAC,QAAD;KAAM,WAAU;KAAhB,UAAA,CACE,oBAAC,UAAD;MAAU,eAAA;MAAY,WAAU;KAAwB,CAAA,GACxD,oBAAC,QAAD;MAAM,WAAU;MAAY,UAAA,KAAK;KAAW,CAAA,CACxC;IACN,CAAA,GAAA,oBAAC,QAAD;KAAM,WAAU;KAAyC,OAAO,KAAK;KAClE,UAAA,KAAK;IACF,CAAA,CACA;GAdD,GAAA,KAAK,IAcJ,CACT;EACA;;AAET;AAEA,SAAgB,iBAAiB,EAAE,OAAO,SAAS,OAAO,UAA8B;CACtF,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACE,qBAAC,UAAD;IAAQ,WAAU;IAAlB,UAAA,CACE,oBAAC,MAAD;KAAI,WAAU;KAAmC,UAAA;IAAa,CAAA,GAC9D,qBAAC,QAAD;KAAM,eAAY;KAAkB,WAAU;KAA9C,UAAA;MACG,MAAM;MAAO;MAAE,MAAM,WAAW,IAAI,aAAa;KAC9C;IACA,CAAA,CAAA;;GACP,QACC,oBAAC,KAAD;IAAG,MAAK;IAAQ,WAAU;IACvB,UAAA;GACA,CAAA,IACD;GACH,UACC,oBAAC,KAAD;IAAG,MAAK;IAAS,WAAU;IAAgC,UAAA;GAExD,CAAA,IACD;GACH,CAAC,WAAW,CAAC,SAAS,MAAM,WAAW,IACtC,oBAAC,YAAD;IACE,OAAM;IACN,aAAY;IACZ,WAAU;GACX,CAAA,IACC;GACJ,oBAAC,OAAD;IAAK,eAAY;IAAiB,WAAU;IACzC,UAAA,gBAAgB,KAAK,CAAC,CAAC,KAAK,SAC3B,qBAAC,QAAD;KAEE,SAAQ;KACR,MAAK;KACL,sBAAoB,KAAK;KACzB,eAAe,OAAO,IAAI;KAC1B,WAAU;KANZ,UAAA;MAQE,oBAAC,UAAD;OAAU,eAAA;OAAY,WAAU;MAAwB,CAAA;MACxD,oBAAC,QAAD;OAAM,WAAU;OAA8C,OAAO,KAAK;OACvE,UAAA,KAAK;MACF,CAAA;MACN,oBAAC,QAAD;OAAM,WAAU;OAAyC,OAAO,KAAK;OAClE,UAAA,KAAK;MACF,CAAA;MACN,oBAAC,QAAD;OAAM,WAAU;OAAuB,UAAA,UAAU,KAAK,IAAI;MAAQ,CAAA;KAC5D;IAfD,GAAA,KAAK,IAeJ,CACT;GACE,CAAA;EACF;;AAET;;;AClGA,SAAgB,eAA6B;CAE3C,OAAO;EAAE,IAAI;EAAkB,OAAO;EAAa,OAAO;CAAe;AAC3E;AAEA,SAAgB,yBAAyB,EAAE,WAAW,oBAAwC;CAC5F,MAAM,WAAW,SAAS,UAAU,QAAQ,UAAU,UAAU,OAAO,OAAO,SAAS,CAAC;CACxF,gBAAgB;EACd,IAAI,cAAc,QAAQ,SAAS,QAAQ;EAC3C,MAAM,aAAa,IAAI,gBAAgB;EACvC,iBAAsB,WAAW,WAAW,MAAM;EAClD,aAAa,WAAW,MAAM;CAChC,GAAG,CAAC,WAAW,SAAS,MAAM,CAAC;CAC/B,OACE,oBAAC,qBAAD;EACE,OAAO,SAAS;EAChB,SAAS,CAAC,SAAS;EACnB,OAAO,SAAS;EAChB,SAAS,SAAS;GAChB,IAAI,cAAc,MAAM,iBAAiB,YAAY,WAAW,KAAK,IAAI,CAAC;EAC5E;CACD,CAAA;AAEL;AAEA,SAAgB,eAAe,EAAE,WAAW,oBAAwC;CAClF,MAAM,WAAW,SAAS,UAAU,QAAQ,UAAU,UAAU,OAAO,OAAO,SAAS,CAAC;CACxF,gBAAgB;EACd,IAAI,cAAc,MAAM;EACxB,MAAM,aAAa,IAAI,gBAAgB;EACvC,iBAAsB,WAAW,WAAW,MAAM;EAClD,aAAa,WAAW,MAAM;CAChC,GAAG,CAAC,SAAS,CAAC;CACd,OACE,oBAAC,kBAAD;EACE,OAAO,SAAS;EAChB,SAAS,CAAC,SAAS,UAAU,cAAc;EAC3C,OAAO,cAAc,OAAO,gCAAgC,SAAS;EACrE,SAAS,SAAS;GAChB,IAAI,cAAc,MAAM,iBAAiB,YAAY,WAAW,KAAK,IAAI,CAAC;EAC5E;CACD,CAAA;AAEL;;;AC/CA,IAAA,0BAAe,oBAAoB;CACjC,MAAM;CACN,MAAM;CACN,WAAW;CACX,cAAc;CACd,cAAc;CACd,qBAAqB;CACrB,OAAO;AACT,CAAC;;;ACVD,IAAA,wBAAe,kBAAkB,gBAAgB;;;ACAjD,IAAA,2CAAe,WAAW,EAAE,WAAW,yBAAyB,CAAC;;;ACIjE,MAAM,OAAU,UAAmC,UAChD,UAAU,KAAA,IAAY,KAAA,IAAY;CAAE,GAAG;CAAU,GAAI;AAAiB;AAEzE,MAAa,YAAY,gBAAgB;CACvC,IAAI;CACJ,SAAS;EACP,IAAI,iBAA2D;GAAE,OAAO,CAAC,IAAI,EAAE,MAAM,YAAY,GAAGA,uBAAsB,CAAC;EAAG;EAC9H,IAAI,WAA+C;GAAE,OAAO,CAAC,IAAI,EAAE,SAAS,YAAY,GAAGC,qBAAgB,CAAC;EAAG;EAC/G,IAAI,QAAyC;GAAE,OAAO,CAAC,IAAI;IAAE,MAAM;IAAsB,IAAI;GAAY,GAAGC,wCAAa,CAAC;EAAG;CAC/H;AACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/po-artifact",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.3",
|
|
4
4
|
"description": "Durable session artifacts, prompt guidance, and web/native artifact views.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -65,37 +65,37 @@
|
|
|
65
65
|
"registry": "https://registry.npmjs.org/"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@agimon-ai/po-core": "0.0.0-alpha.
|
|
69
|
-
"@agimon-ai/po-web-components": "0.0.0-alpha.
|
|
70
|
-
"@agimon-ai/po-web-security": "0.0.0-alpha.
|
|
68
|
+
"@agimon-ai/po-core": "0.0.0-alpha.4",
|
|
69
|
+
"@agimon-ai/po-web-components": "0.0.0-alpha.4",
|
|
70
|
+
"@agimon-ai/po-web-security": "0.0.0-alpha.4",
|
|
71
71
|
"@deepseek-ai/cordis": "4.0.4",
|
|
72
72
|
"hono": "4.13.13",
|
|
73
|
-
"typebox": "1.3.
|
|
73
|
+
"typebox": "1.3.37"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
|
-
"@agimon-ai/po-build": "0.0.0-alpha.
|
|
77
|
-
"@agimon-ai/po-native-components": "0.0.0-alpha.
|
|
78
|
-
"@agimon-ai/vibe-lint-plugin-
|
|
76
|
+
"@agimon-ai/po-build": "0.0.0-alpha.4",
|
|
77
|
+
"@agimon-ai/po-native-components": "0.0.0-alpha.4",
|
|
78
|
+
"@agimon-ai/vibe-lint-plugin-po-extension": "0.0.0-alpha.4",
|
|
79
79
|
"@earendil-works/pi-coding-agent": "1.2.0",
|
|
80
80
|
"@tanstack/react-store": "0.11.2",
|
|
81
81
|
"@tanstack/store": "0.11.2",
|
|
82
|
-
"@types/node": "
|
|
82
|
+
"@types/node": "26.6.5",
|
|
83
83
|
"@types/react": "19.3.0",
|
|
84
84
|
"@types/react-dom": "19.3.0",
|
|
85
|
-
"@vitest/coverage-v8": "5.0.
|
|
85
|
+
"@vitest/coverage-v8": "5.0.4",
|
|
86
86
|
"react": "19.3.0",
|
|
87
87
|
"react-dom": "19.3.0",
|
|
88
|
-
"react-native": "0.
|
|
89
|
-
"react-native-unistyles": "3.5.
|
|
88
|
+
"react-native": "0.86.3",
|
|
89
|
+
"react-native-unistyles": "3.5.3",
|
|
90
90
|
"tsdown": "0.23.1",
|
|
91
91
|
"typescript": "7.0.2",
|
|
92
|
-
"vitest": "5.0.
|
|
92
|
+
"vitest": "5.0.4"
|
|
93
93
|
},
|
|
94
94
|
"peerDependencies": {
|
|
95
|
-
"@agimon-ai/po-native-components": "0.0.0-alpha.
|
|
95
|
+
"@agimon-ai/po-native-components": "0.0.0-alpha.4",
|
|
96
96
|
"@earendil-works/pi-coding-agent": "1.2.0",
|
|
97
|
-
"react-native": "0.
|
|
98
|
-
"react-native-unistyles": "3.5.
|
|
97
|
+
"react-native": "0.86.3",
|
|
98
|
+
"react-native-unistyles": "^3.5.3"
|
|
99
99
|
},
|
|
100
100
|
"peerDependenciesMeta": {
|
|
101
101
|
"@earendil-works/pi-coding-agent": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"artifactFiles-B8w_HQXv.mjs","names":[],"sources":["../src/constants/artifacts.ts","../src/services/artifactFiles/artifactPaths.ts","../src/services/artifactFiles/index.ts"],"sourcesContent":["export const ARTIFACT_DIRECTORY = 'doom-artifacts';\nexport const MAX_PREVIEW_BYTES = 1024 * 1024;\nexport const MAX_CONTENT_BYTES = 25 * 1024 * 1024;\nexport const ARTIFACT_REFRESH_MS = 2000;\n\nexport const ARTIFACT_CONTENT_TYPES: Readonly<Record<string, string>> = {\n '.avif': 'image/avif',\n '.bmp': 'image/bmp',\n '.gif': 'image/gif',\n '.jpeg': 'image/jpeg',\n '.jpg': 'image/jpeg',\n '.png': 'image/png',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.ico': 'image/vnd.microsoft.icon',\n '.mp3': 'audio/mpeg',\n '.wav': 'audio/wav',\n '.m4a': 'audio/mp4',\n '.ogg': 'audio/ogg',\n '.m4v': 'video/mp4',\n '.mov': 'video/quicktime',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.pdf': 'application/pdf',\n '.md': 'text/markdown; charset=utf-8',\n '.markdown': 'text/markdown; charset=utf-8',\n '.txt': 'text/plain; charset=utf-8',\n '.html': 'text/html; charset=utf-8',\n '.htm': 'text/html; charset=utf-8',\n};\n","import path from 'node:path';\n\nimport { piAgentDirectory } from '@agimon-ai/po-core/piSettings';\n\nimport { ARTIFACT_CONTENT_TYPES, ARTIFACT_DIRECTORY } from '../../constants/artifacts';\n\nexport class ArtifactAccessError extends Error {\n constructor(\n message: string,\n readonly status: 400 | 403 | 404 | 413,\n ) {\n super(message);\n }\n}\n\nexport function artifactRoot(sessionId: string, environment: Readonly<Record<string, string | undefined>>): string {\n if (!/^[a-zA-Z0-9_-]+$/u.test(sessionId)) throw new ArtifactAccessError('Invalid artifact session identity.', 400);\n return path.join(piAgentDirectory(environment, environment.HOME), ARTIFACT_DIRECTORY, sessionId);\n}\n\nexport function artifactMimeType(relativePath: string): string {\n return ARTIFACT_CONTENT_TYPES[path.extname(relativePath).toLowerCase()] ?? 'application/octet-stream';\n}\n","import { constants } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport type { ArtifactItem, ArtifactSnapshot } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactMimeType } from './artifactPaths';\n\nexport { ArtifactAccessError, artifactMimeType, artifactRoot } from './artifactPaths';\n\nfunction inside(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);\n}\n\nfunction codeOf(error: unknown): string | undefined {\n return error instanceof Error && 'code' in error ? String(error.code) : undefined;\n}\n\n/** The owned directories must not redirect artifact access through a symlink. */\nasync function checkedRoot(root: string): Promise<string> {\n const base = path.dirname(root);\n for (const directory of [base, root]) {\n const stat = await fs.lstat(directory);\n if (!stat.isDirectory() || stat.isSymbolicLink())\n throw new ArtifactAccessError('The artifact folder is unavailable.', 403);\n }\n const realBase = await fs.realpath(base);\n const realRoot = await fs.realpath(root);\n if (!inside(realBase, realRoot))\n throw new ArtifactAccessError('The artifact folder is outside its storage directory.', 403);\n return realRoot;\n}\n\nexport async function ensureArtifactRoot(root: string): Promise<void> {\n await fs.mkdir(path.dirname(root), { recursive: true, mode: 0o700 });\n await fs.mkdir(root, { recursive: true, mode: 0o700 });\n await checkedRoot(root);\n}\n\n/** Open without following mutable ancestors. Node exposes no portable openat API. */\nasync function openContained(root: string, segments: string[]): Promise<fs.FileHandle> {\n if (process.platform === 'darwin') {\n // Darwin O_NOFOLLOW_ANY checks every component atomically. Do not combine it with O_NOFOLLOW.\n return fs.open(path.join(root, ...segments), constants.O_RDONLY | constants.O_NONBLOCK | 0x20000000);\n }\n if (process.platform !== 'linux') throw new ArtifactAccessError('Secure artifact reads require macOS or Linux.', 403);\n const flags = constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW;\n let directory = await fs.open(path.dirname(root), flags);\n try {\n for (const segment of [path.basename(root), ...segments.slice(0, -1)]) {\n const child = await fs.open(`/proc/self/fd/${directory.fd}/${segment}`, flags);\n await directory.close();\n directory = child;\n }\n return await fs.open(\n `/proc/self/fd/${directory.fd}/${segments.at(-1)!}`,\n constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK,\n );\n } finally {\n await directory.close();\n }\n}\n\n/** One guarded open shared by metadata, text and byte reads. The caller closes the handle. */\nexport async function openArtifact(root: string, requested: string | undefined) {\n if (\n !requested ||\n requested.includes('\\0') ||\n requested.includes('\\\\') ||\n path.isAbsolute(requested) ||\n requested.split('/').some((part) => part === '..' || part === '.' || part === '')\n )\n throw new ArtifactAccessError('Invalid artifact path.', 400);\n try {\n const realRoot = await checkedRoot(root);\n const candidate = path.resolve(realRoot, requested);\n if (!inside(realRoot, candidate))\n throw new ArtifactAccessError('The artifact is outside this session folder.', 403);\n let current = realRoot;\n const segments = requested.split('/');\n for (let index = 0; index < segments.length; index += 1) {\n current = path.join(current, segments[index]!);\n const stat = await fs.lstat(current);\n if (stat.isSymbolicLink()) throw new ArtifactAccessError('Artifact symlinks cannot be read.', 403);\n if (index < segments.length - 1 ? !stat.isDirectory() : !stat.isFile())\n throw new ArtifactAccessError('The artifact is not a regular file.', 404);\n }\n const real = await fs.realpath(candidate);\n if (!inside(realRoot, real)) throw new ArtifactAccessError('The artifact is outside this session folder.', 403);\n const expected = await fs.lstat(real);\n const handle = await openContained(realRoot, segments);\n try {\n const stat = await handle.stat();\n if (!stat.isFile() || stat.dev !== expected.dev || stat.ino !== expected.ino)\n throw new ArtifactAccessError('The artifact changed while it was opened. Try again.', 403);\n return { handle, stat, path: requested, mimeType: artifactMimeType(requested) };\n } catch (error) {\n await handle.close();\n throw error;\n }\n } catch (error) {\n if (error instanceof ArtifactAccessError) throw error;\n if (codeOf(error) === 'ENOENT' || codeOf(error) === 'ENOTDIR')\n throw new ArtifactAccessError('The artifact no longer exists.', 404);\n if (codeOf(error) === 'EACCES' || codeOf(error) === 'EPERM' || codeOf(error) === 'ELOOP')\n throw new ArtifactAccessError('The artifact cannot be read.', 403);\n throw error;\n }\n}\n\n/** Bound allocation and read length even when the file grows after its metadata was read. */\nexport async function readArtifactBytes(handle: fs.FileHandle, size: number, offset = 0): Promise<Uint8Array> {\n const bytes = new Uint8Array(size);\n let read = 0;\n while (read < size) {\n const result = await handle.read(bytes, read, size - read, offset + read);\n if (result.bytesRead === 0) break;\n read += result.bytesRead;\n }\n return bytes.subarray(0, read);\n}\n\nexport async function listArtifacts(root: string): Promise<ArtifactSnapshot> {\n const items: ArtifactItem[] = [];\n try {\n const realRoot = await checkedRoot(root);\n const visit = async (directory: string): Promise<void> => {\n for (const entry of await fs.readdir(directory, { withFileTypes: true })) {\n const location = path.join(directory, entry.name);\n if (entry.isDirectory()) {\n // Recheck each directory rather than trusting a readdir entry across a rename.\n const stat = await fs.lstat(location);\n if (!stat.isDirectory() || stat.isSymbolicLink()) continue;\n const real = await fs.realpath(location);\n if (inside(realRoot, real)) await visit(real);\n } else if (entry.isFile()) {\n const relativePath = path.relative(realRoot, location).split(path.sep).join('/');\n try {\n const file = await openArtifact(root, relativePath);\n try {\n items.push({ path: relativePath, name: entry.name, size: file.stat.size, modifiedAt: file.stat.mtimeMs });\n } finally {\n await file.handle.close();\n }\n } catch (error) {\n if (error instanceof ArtifactAccessError && (error.status === 404 || error.status === 403)) continue;\n throw error;\n }\n }\n }\n };\n await visit(realRoot);\n items.sort(\n (left, right) =>\n right.modifiedAt - left.modifiedAt || (left.path < right.path ? -1 : left.path > right.path ? 1 : 0),\n );\n return { items };\n } catch (error) {\n if (codeOf(error) === 'ENOENT') return { items: [] };\n return { items: [], error: error instanceof Error ? error.message : 'The artifact folder could not be read.' };\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"artifactFiles-Bo5Mhwq0.cjs","names":["path","piAgentDirectory","path","fs","constants"],"sources":["../src/constants/artifacts.ts","../src/services/artifactFiles/artifactPaths.ts","../src/services/artifactFiles/index.ts"],"sourcesContent":["export const ARTIFACT_DIRECTORY = 'doom-artifacts';\nexport const MAX_PREVIEW_BYTES = 1024 * 1024;\nexport const MAX_CONTENT_BYTES = 25 * 1024 * 1024;\nexport const ARTIFACT_REFRESH_MS = 2000;\n\nexport const ARTIFACT_CONTENT_TYPES: Readonly<Record<string, string>> = {\n '.avif': 'image/avif',\n '.bmp': 'image/bmp',\n '.gif': 'image/gif',\n '.jpeg': 'image/jpeg',\n '.jpg': 'image/jpeg',\n '.png': 'image/png',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.ico': 'image/vnd.microsoft.icon',\n '.mp3': 'audio/mpeg',\n '.wav': 'audio/wav',\n '.m4a': 'audio/mp4',\n '.ogg': 'audio/ogg',\n '.m4v': 'video/mp4',\n '.mov': 'video/quicktime',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.pdf': 'application/pdf',\n '.md': 'text/markdown; charset=utf-8',\n '.markdown': 'text/markdown; charset=utf-8',\n '.txt': 'text/plain; charset=utf-8',\n '.html': 'text/html; charset=utf-8',\n '.htm': 'text/html; charset=utf-8',\n};\n","import path from 'node:path';\n\nimport { piAgentDirectory } from '@agimon-ai/po-core/piSettings';\n\nimport { ARTIFACT_CONTENT_TYPES, ARTIFACT_DIRECTORY } from '../../constants/artifacts';\n\nexport class ArtifactAccessError extends Error {\n constructor(\n message: string,\n readonly status: 400 | 403 | 404 | 413,\n ) {\n super(message);\n }\n}\n\nexport function artifactRoot(sessionId: string, environment: Readonly<Record<string, string | undefined>>): string {\n if (!/^[a-zA-Z0-9_-]+$/u.test(sessionId)) throw new ArtifactAccessError('Invalid artifact session identity.', 400);\n return path.join(piAgentDirectory(environment, environment.HOME), ARTIFACT_DIRECTORY, sessionId);\n}\n\nexport function artifactMimeType(relativePath: string): string {\n return ARTIFACT_CONTENT_TYPES[path.extname(relativePath).toLowerCase()] ?? 'application/octet-stream';\n}\n","import { constants } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport type { ArtifactItem, ArtifactSnapshot } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactMimeType } from './artifactPaths';\n\nexport { ArtifactAccessError, artifactMimeType, artifactRoot } from './artifactPaths';\n\nfunction inside(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);\n}\n\nfunction codeOf(error: unknown): string | undefined {\n return error instanceof Error && 'code' in error ? String(error.code) : undefined;\n}\n\n/** The owned directories must not redirect artifact access through a symlink. */\nasync function checkedRoot(root: string): Promise<string> {\n const base = path.dirname(root);\n for (const directory of [base, root]) {\n const stat = await fs.lstat(directory);\n if (!stat.isDirectory() || stat.isSymbolicLink())\n throw new ArtifactAccessError('The artifact folder is unavailable.', 403);\n }\n const realBase = await fs.realpath(base);\n const realRoot = await fs.realpath(root);\n if (!inside(realBase, realRoot))\n throw new ArtifactAccessError('The artifact folder is outside its storage directory.', 403);\n return realRoot;\n}\n\nexport async function ensureArtifactRoot(root: string): Promise<void> {\n await fs.mkdir(path.dirname(root), { recursive: true, mode: 0o700 });\n await fs.mkdir(root, { recursive: true, mode: 0o700 });\n await checkedRoot(root);\n}\n\n/** Open without following mutable ancestors. Node exposes no portable openat API. */\nasync function openContained(root: string, segments: string[]): Promise<fs.FileHandle> {\n if (process.platform === 'darwin') {\n // Darwin O_NOFOLLOW_ANY checks every component atomically. Do not combine it with O_NOFOLLOW.\n return fs.open(path.join(root, ...segments), constants.O_RDONLY | constants.O_NONBLOCK | 0x20000000);\n }\n if (process.platform !== 'linux') throw new ArtifactAccessError('Secure artifact reads require macOS or Linux.', 403);\n const flags = constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW;\n let directory = await fs.open(path.dirname(root), flags);\n try {\n for (const segment of [path.basename(root), ...segments.slice(0, -1)]) {\n const child = await fs.open(`/proc/self/fd/${directory.fd}/${segment}`, flags);\n await directory.close();\n directory = child;\n }\n return await fs.open(\n `/proc/self/fd/${directory.fd}/${segments.at(-1)!}`,\n constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK,\n );\n } finally {\n await directory.close();\n }\n}\n\n/** One guarded open shared by metadata, text and byte reads. The caller closes the handle. */\nexport async function openArtifact(root: string, requested: string | undefined) {\n if (\n !requested ||\n requested.includes('\\0') ||\n requested.includes('\\\\') ||\n path.isAbsolute(requested) ||\n requested.split('/').some((part) => part === '..' || part === '.' || part === '')\n )\n throw new ArtifactAccessError('Invalid artifact path.', 400);\n try {\n const realRoot = await checkedRoot(root);\n const candidate = path.resolve(realRoot, requested);\n if (!inside(realRoot, candidate))\n throw new ArtifactAccessError('The artifact is outside this session folder.', 403);\n let current = realRoot;\n const segments = requested.split('/');\n for (let index = 0; index < segments.length; index += 1) {\n current = path.join(current, segments[index]!);\n const stat = await fs.lstat(current);\n if (stat.isSymbolicLink()) throw new ArtifactAccessError('Artifact symlinks cannot be read.', 403);\n if (index < segments.length - 1 ? !stat.isDirectory() : !stat.isFile())\n throw new ArtifactAccessError('The artifact is not a regular file.', 404);\n }\n const real = await fs.realpath(candidate);\n if (!inside(realRoot, real)) throw new ArtifactAccessError('The artifact is outside this session folder.', 403);\n const expected = await fs.lstat(real);\n const handle = await openContained(realRoot, segments);\n try {\n const stat = await handle.stat();\n if (!stat.isFile() || stat.dev !== expected.dev || stat.ino !== expected.ino)\n throw new ArtifactAccessError('The artifact changed while it was opened. Try again.', 403);\n return { handle, stat, path: requested, mimeType: artifactMimeType(requested) };\n } catch (error) {\n await handle.close();\n throw error;\n }\n } catch (error) {\n if (error instanceof ArtifactAccessError) throw error;\n if (codeOf(error) === 'ENOENT' || codeOf(error) === 'ENOTDIR')\n throw new ArtifactAccessError('The artifact no longer exists.', 404);\n if (codeOf(error) === 'EACCES' || codeOf(error) === 'EPERM' || codeOf(error) === 'ELOOP')\n throw new ArtifactAccessError('The artifact cannot be read.', 403);\n throw error;\n }\n}\n\n/** Bound allocation and read length even when the file grows after its metadata was read. */\nexport async function readArtifactBytes(handle: fs.FileHandle, size: number, offset = 0): Promise<Uint8Array> {\n const bytes = new Uint8Array(size);\n let read = 0;\n while (read < size) {\n const result = await handle.read(bytes, read, size - read, offset + read);\n if (result.bytesRead === 0) break;\n read += result.bytesRead;\n }\n return bytes.subarray(0, read);\n}\n\nexport async function listArtifacts(root: string): Promise<ArtifactSnapshot> {\n const items: ArtifactItem[] = [];\n try {\n const realRoot = await checkedRoot(root);\n const visit = async (directory: string): Promise<void> => {\n for (const entry of await fs.readdir(directory, { withFileTypes: true })) {\n const location = path.join(directory, entry.name);\n if (entry.isDirectory()) {\n // Recheck each directory rather than trusting a readdir entry across a rename.\n const stat = await fs.lstat(location);\n if (!stat.isDirectory() || stat.isSymbolicLink()) continue;\n const real = await fs.realpath(location);\n if (inside(realRoot, real)) await visit(real);\n } else if (entry.isFile()) {\n const relativePath = path.relative(realRoot, location).split(path.sep).join('/');\n try {\n const file = await openArtifact(root, relativePath);\n try {\n items.push({ path: relativePath, name: entry.name, size: file.stat.size, modifiedAt: file.stat.mtimeMs });\n } finally {\n await file.handle.close();\n }\n } catch (error) {\n if (error instanceof ArtifactAccessError && (error.status === 404 || error.status === 403)) continue;\n throw error;\n }\n }\n }\n };\n await visit(realRoot);\n items.sort(\n (left, right) =>\n right.modifiedAt - left.modifiedAt || (left.path < right.path ? -1 : left.path > right.path ? 1 : 0),\n );\n return { items };\n } catch (error) {\n if (codeOf(error) === 'ENOENT') return { items: [] };\n return { items: [], error: error instanceof Error ? error.message : 'The artifact folder could not be read.' };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,MAAa,qBAAqB;AAClC,MAAa,oBAAoB;AACjC,MAAa,oBAAoB;AACjC,MAAa,sBAAsB;AAEnC,MAAa,yBAA2D;CACtE,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;CACT,QAAQ;AACV;;;ACvBA,IAAa,sBAAb,cAAyC,MAAM;CAGlC;CAFX,YACE,SACA,QACA;EACA,MAAM,OAAO;EAFJ,KAAA,SAAA;CAGX;AACF;AAEA,SAAgB,aAAa,WAAmB,aAAmE;CACjH,IAAI,CAAC,oBAAoB,KAAK,SAAS,GAAG,MAAM,IAAI,oBAAoB,sCAAsC,GAAG;CACjH,OAAOA,UAAAA,QAAK,MAAA,GAAKC,8BAAAA,iBAAAA,CAAiB,aAAa,YAAY,IAAI,GAAG,oBAAoB,SAAS;AACjG;AAEA,SAAgB,iBAAiB,cAA8B;CAC7D,OAAO,uBAAuBD,UAAAA,QAAK,QAAQ,YAAY,CAAC,CAAC,YAAY,MAAM;AAC7E;;;ACbA,SAAS,OAAO,MAAc,WAA4B;CACxD,MAAM,WAAWE,UAAAA,QAAK,SAAS,MAAM,SAAS;CAC9C,OAAO,aAAa,QAAQ,CAAC,SAAS,WAAW,KAAKA,UAAAA,QAAK,KAAK,KAAK,CAACA,UAAAA,QAAK,WAAW,QAAQ;AAChG;AAEA,SAAS,OAAO,OAAoC;CAClD,OAAO,iBAAiB,SAAS,UAAU,QAAQ,OAAO,MAAM,IAAI,IAAI,KAAA;AAC1E;;AAGA,eAAe,YAAY,MAA+B;CACxD,MAAM,OAAOA,UAAAA,QAAK,QAAQ,IAAI;CAC9B,KAAK,MAAM,aAAa,CAAC,MAAM,IAAI,GAAG;EACpC,MAAM,OAAO,MAAMC,iBAAAA,QAAG,MAAM,SAAS;EACrC,IAAI,CAAC,KAAK,YAAY,KAAK,KAAK,eAAe,GAC7C,MAAM,IAAI,oBAAoB,uCAAuC,GAAG;CAC5E;CACA,MAAM,WAAW,MAAMA,iBAAAA,QAAG,SAAS,IAAI;CACvC,MAAM,WAAW,MAAMA,iBAAAA,QAAG,SAAS,IAAI;CACvC,IAAI,CAAC,OAAO,UAAU,QAAQ,GAC5B,MAAM,IAAI,oBAAoB,yDAAyD,GAAG;CAC5F,OAAO;AACT;AAEA,eAAsB,mBAAmB,MAA6B;CACpE,MAAMA,iBAAAA,QAAG,MAAMD,UAAAA,QAAK,QAAQ,IAAI,GAAG;EAAE,WAAW;EAAM,MAAM;CAAM,CAAC;CACnE,MAAMC,iBAAAA,QAAG,MAAM,MAAM;EAAE,WAAW;EAAM,MAAM;CAAM,CAAC;CACrD,MAAM,YAAY,IAAI;AACxB;;AAGA,eAAe,cAAc,MAAc,UAA4C;CACrF,IAAI,QAAQ,aAAa,UAEvB,OAAOA,iBAAAA,QAAG,KAAKD,UAAAA,QAAK,KAAK,MAAM,GAAG,QAAQ,GAAGE,QAAAA,UAAU,WAAWA,QAAAA,UAAU,aAAa,SAAU;CAErG,IAAI,QAAQ,aAAa,SAAS,MAAM,IAAI,oBAAoB,iDAAiD,GAAG;CACpH,MAAM,QAAQA,QAAAA,UAAU,WAAWA,QAAAA,UAAU,cAAcA,QAAAA,UAAU;CACrE,IAAI,YAAY,MAAMD,iBAAAA,QAAG,KAAKD,UAAAA,QAAK,QAAQ,IAAI,GAAG,KAAK;CACvD,IAAI;EACF,KAAK,MAAM,WAAW,CAACA,UAAAA,QAAK,SAAS,IAAI,GAAG,GAAG,SAAS,MAAM,GAAG,EAAE,CAAC,GAAG;GACrE,MAAM,QAAQ,MAAMC,iBAAAA,QAAG,KAAK,iBAAiB,UAAU,GAAG,GAAG,WAAW,KAAK;GAC7E,MAAM,UAAU,MAAM;GACtB,YAAY;EACd;EACA,OAAO,MAAMA,iBAAAA,QAAG,KACd,iBAAiB,UAAU,GAAG,GAAG,SAAS,GAAG,EAAE,KAC/CC,QAAAA,UAAU,WAAWA,QAAAA,UAAU,aAAaA,QAAAA,UAAU,UACxD;CACF,UAAU;EACR,MAAM,UAAU,MAAM;CACxB;AACF;;AAGA,eAAsB,aAAa,MAAc,WAA+B;CAC9E,IACE,CAAC,aACD,UAAU,SAAS,IAAI,KACvB,UAAU,SAAS,IAAI,KACvBF,UAAAA,QAAK,WAAW,SAAS,KACzB,UAAU,MAAM,GAAG,CAAC,CAAC,MAAM,SAAS,SAAS,QAAQ,SAAS,OAAO,SAAS,EAAE,GAEhF,MAAM,IAAI,oBAAoB,0BAA0B,GAAG;CAC7D,IAAI;EACF,MAAM,WAAW,MAAM,YAAY,IAAI;EACvC,MAAM,YAAYA,UAAAA,QAAK,QAAQ,UAAU,SAAS;EAClD,IAAI,CAAC,OAAO,UAAU,SAAS,GAC7B,MAAM,IAAI,oBAAoB,gDAAgD,GAAG;EACnF,IAAI,UAAU;EACd,MAAM,WAAW,UAAU,MAAM,GAAG;EACpC,KAAK,IAAI,QAAQ,GAAG,QAAQ,SAAS,QAAQ,SAAS,GAAG;GACvD,UAAUA,UAAAA,QAAK,KAAK,SAAS,SAAS,MAAO;GAC7C,MAAM,OAAO,MAAMC,iBAAAA,QAAG,MAAM,OAAO;GACnC,IAAI,KAAK,eAAe,GAAG,MAAM,IAAI,oBAAoB,qCAAqC,GAAG;GACjG,IAAI,QAAQ,SAAS,SAAS,IAAI,CAAC,KAAK,YAAY,IAAI,CAAC,KAAK,OAAO,GACnE,MAAM,IAAI,oBAAoB,uCAAuC,GAAG;EAC5E;EACA,MAAM,OAAO,MAAMA,iBAAAA,QAAG,SAAS,SAAS;EACxC,IAAI,CAAC,OAAO,UAAU,IAAI,GAAG,MAAM,IAAI,oBAAoB,gDAAgD,GAAG;EAC9G,MAAM,WAAW,MAAMA,iBAAAA,QAAG,MAAM,IAAI;EACpC,MAAM,SAAS,MAAM,cAAc,UAAU,QAAQ;EACrD,IAAI;GACF,MAAM,OAAO,MAAM,OAAO,KAAK;GAC/B,IAAI,CAAC,KAAK,OAAO,KAAK,KAAK,QAAQ,SAAS,OAAO,KAAK,QAAQ,SAAS,KACvE,MAAM,IAAI,oBAAoB,wDAAwD,GAAG;GAC3F,OAAO;IAAE;IAAQ;IAAM,MAAM;IAAW,UAAU,iBAAiB,SAAS;GAAE;EAChF,SAAS,OAAO;GACd,MAAM,OAAO,MAAM;GACnB,MAAM;EACR;CACF,SAAS,OAAO;EACd,IAAI,iBAAiB,qBAAqB,MAAM;EAChD,IAAI,OAAO,KAAK,MAAM,YAAY,OAAO,KAAK,MAAM,WAClD,MAAM,IAAI,oBAAoB,kCAAkC,GAAG;EACrE,IAAI,OAAO,KAAK,MAAM,YAAY,OAAO,KAAK,MAAM,WAAW,OAAO,KAAK,MAAM,SAC/E,MAAM,IAAI,oBAAoB,gCAAgC,GAAG;EACnE,MAAM;CACR;AACF;;AAGA,eAAsB,kBAAkB,QAAuB,MAAc,SAAS,GAAwB;CAC5G,MAAM,QAAQ,IAAI,WAAW,IAAI;CACjC,IAAI,OAAO;CACX,OAAO,OAAO,MAAM;EAClB,MAAM,SAAS,MAAM,OAAO,KAAK,OAAO,MAAM,OAAO,MAAM,SAAS,IAAI;EACxE,IAAI,OAAO,cAAc,GAAG;EAC5B,QAAQ,OAAO;CACjB;CACA,OAAO,MAAM,SAAS,GAAG,IAAI;AAC/B;AAEA,eAAsB,cAAc,MAAyC;CAC3E,MAAM,QAAwB,CAAC;CAC/B,IAAI;EACF,MAAM,WAAW,MAAM,YAAY,IAAI;EACvC,MAAM,QAAQ,OAAO,cAAqC;GACxD,KAAK,MAAM,SAAS,MAAMA,iBAAAA,QAAG,QAAQ,WAAW,EAAE,eAAe,KAAK,CAAC,GAAG;IACxE,MAAM,WAAWD,UAAAA,QAAK,KAAK,WAAW,MAAM,IAAI;IAChD,IAAI,MAAM,YAAY,GAAG;KAEvB,MAAM,OAAO,MAAMC,iBAAAA,QAAG,MAAM,QAAQ;KACpC,IAAI,CAAC,KAAK,YAAY,KAAK,KAAK,eAAe,GAAG;KAClD,MAAM,OAAO,MAAMA,iBAAAA,QAAG,SAAS,QAAQ;KACvC,IAAI,OAAO,UAAU,IAAI,GAAG,MAAM,MAAM,IAAI;IAC9C,OAAO,IAAI,MAAM,OAAO,GAAG;KACzB,MAAM,eAAeD,UAAAA,QAAK,SAAS,UAAU,QAAQ,CAAC,CAAC,MAAMA,UAAAA,QAAK,GAAG,CAAC,CAAC,KAAK,GAAG;KAC/E,IAAI;MACF,MAAM,OAAO,MAAM,aAAa,MAAM,YAAY;MAClD,IAAI;OACF,MAAM,KAAK;QAAE,MAAM;QAAc,MAAM,MAAM;QAAM,MAAM,KAAK,KAAK;QAAM,YAAY,KAAK,KAAK;OAAQ,CAAC;MAC1G,UAAU;OACR,MAAM,KAAK,OAAO,MAAM;MAC1B;KACF,SAAS,OAAO;MACd,IAAI,iBAAiB,wBAAwB,MAAM,WAAW,OAAO,MAAM,WAAW,MAAM;MAC5F,MAAM;KACR;IACF;GACF;EACF;EACA,MAAM,MAAM,QAAQ;EACpB,MAAM,MACH,MAAM,UACL,MAAM,aAAa,KAAK,eAAe,KAAK,OAAO,MAAM,OAAO,KAAK,KAAK,OAAO,MAAM,OAAO,IAAI,EACtG;EACA,OAAO,EAAE,MAAM;CACjB,SAAS,OAAO;EACd,IAAI,OAAO,KAAK,MAAM,UAAU,OAAO,EAAE,OAAO,CAAC,EAAE;EACnD,OAAO;GAAE,OAAO,CAAC;GAAG,OAAO,iBAAiB,QAAQ,MAAM,UAAU;EAAyC;CAC/G;AACF"}
|