-
Notifications
You must be signed in to change notification settings - Fork 22
Revamp agents panel profiles #1132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
klopez4212
wants to merge
23
commits into
main
Choose a base branch
from
kennylopez-agents-panel
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
ca267bc
Revamp agents panel profiles
klopez4212 531a242
Delete persona-backed agents from profile
klopez4212 2066c00
Address agents panel review feedback
klopez4212 3bc59ec
Update agent start smoke expectation
klopez4212 9f6539e
Address profile persona review feedback
klopez4212 ec0d366
Sync desktop smoke tests with agent cards
klopez4212 656e9fb
Update persona env vars e2e entry point
klopez4212 b952a4b
Address remaining agent profile review feedback
klopez4212 d0d3a3e
Show persona avatars on agent cards
klopez4212 2f4e08a
Keep secondary persona agents reachable
klopez4212 f920e4f
Use live agent profile avatars
klopez4212 a825124
Preserve persona model without runtime
klopez4212 0e0740b
Left align agents panel content
klopez4212 92a639d
Restore agents page gutter
klopez4212 0b9acb8
Sync persona profile edits to agents
klopez4212 c2a79d7
Merge remote-tracking branch 'origin/main' into kennylopez-agents-panel
klopez4212 b046d17
Preserve imported persona provider
klopez4212 458e266
Restore persona env var editing
klopez4212 0066e99
Preserve cleared agent avatars
klopez4212 1834451
Keep managed agent types under size limit
klopez4212 5c9b795
Sync persona runtime edits to agents
klopez4212 c7fd8fc
Preserve provider in batch persona imports
klopez4212 b733c33
Preserve imported persona model on provider select
klopez4212 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,175 @@ | ||
| #!/usr/bin/env node | ||
| import { spawn } from "node:child_process"; | ||
| import { mkdir, readFile, stat, writeFile } from "node:fs/promises"; | ||
| import { dirname, join } from "node:path"; | ||
| import process from "node:process"; | ||
| import { fileURLToPath } from "node:url"; | ||
|
|
||
| const ARTIFACTORY_BASE = | ||
| "https://global.block-artifacts.com/artifactory/goose-internal/avatars"; | ||
| const LATEST_URL = `${ARTIFACTORY_BASE}/latest.json`; | ||
|
|
||
| const scriptDir = dirname(fileURLToPath(import.meta.url)); | ||
| const desktopRoot = dirname(scriptDir); | ||
| const outputRoot = join(desktopRoot, "src/shared/assets/goose-avatars"); | ||
| const catalogPath = join(outputRoot, "catalog.json"); | ||
|
|
||
| const FORMATS = ["webm", "hevc"]; | ||
|
|
||
| function variantOutputPath(asset, format) { | ||
| const extension = format === "hevc" ? "mp4" : "webm"; | ||
| return join( | ||
| outputRoot, | ||
| format, | ||
| asset.collectionId, | ||
| `${asset.id}.${extension}`, | ||
| ); | ||
| } | ||
|
|
||
| function posterOutputPath(asset) { | ||
| return join(outputRoot, "posters", asset.collectionId, `${asset.id}.png`); | ||
| } | ||
|
|
||
| async function fetchJson(url) { | ||
| const response = await fetch(url); | ||
| if (!response.ok) { | ||
| throw new Error(`Failed to fetch ${url}: ${response.status}`); | ||
| } | ||
| return response.json(); | ||
| } | ||
|
|
||
| async function fileExistsWithSize(path, byteSize) { | ||
| try { | ||
| const info = await stat(path); | ||
| return info.size === byteSize; | ||
| } catch { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| async function downloadFile(url, path, byteSize) { | ||
| if (await fileExistsWithSize(path, byteSize)) { | ||
| return "skipped"; | ||
| } | ||
|
|
||
| const response = await fetch(url); | ||
| if (!response.ok) { | ||
| throw new Error(`Failed to download ${url}: ${response.status}`); | ||
| } | ||
|
|
||
| const bytes = new Uint8Array(await response.arrayBuffer()); | ||
| if (bytes.byteLength !== byteSize) { | ||
| throw new Error( | ||
| `Downloaded ${url} with ${bytes.byteLength} bytes, expected ${byteSize}.`, | ||
| ); | ||
| } | ||
|
|
||
| await mkdir(dirname(path), { recursive: true }); | ||
| await writeFile(path, bytes); | ||
| return "downloaded"; | ||
| } | ||
|
|
||
| async function runFfmpeg(args) { | ||
| await new Promise((resolve, reject) => { | ||
| const child = spawn("ffmpeg", args, { | ||
| stdio: ["ignore", "ignore", "pipe"], | ||
| }); | ||
| let stderr = ""; | ||
| child.stderr.on("data", (chunk) => { | ||
| stderr += chunk.toString(); | ||
| }); | ||
| child.on("error", reject); | ||
| child.on("close", (code) => { | ||
| if (code === 0) { | ||
| resolve(); | ||
| } else { | ||
| reject( | ||
| new Error( | ||
| `ffmpeg exited with ${code}: ${stderr.trim() || "no stderr"}`, | ||
| ), | ||
| ); | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| async function ensurePoster(asset) { | ||
| const posterPath = posterOutputPath(asset); | ||
| try { | ||
| await stat(posterPath); | ||
| return "skipped"; | ||
| } catch { | ||
| // Generate it below. | ||
| } | ||
|
|
||
| await mkdir(dirname(posterPath), { recursive: true }); | ||
| await runFfmpeg([ | ||
| "-hide_banner", | ||
| "-loglevel", | ||
| "error", | ||
| "-y", | ||
| "-i", | ||
| variantOutputPath(asset, "webm"), | ||
| "-frames:v", | ||
| "1", | ||
| posterPath, | ||
| ]); | ||
| return "generated"; | ||
| } | ||
|
|
||
| async function main() { | ||
| const latest = await fetchJson(LATEST_URL); | ||
| const manifest = await fetchJson( | ||
| `${ARTIFACTORY_BASE}/${latest.manifestPath}`, | ||
| ); | ||
| const versionRoot = `${ARTIFACTORY_BASE}/${manifest.catalogVersion}`; | ||
|
|
||
| await mkdir(outputRoot, { recursive: true }); | ||
| await writeFile(catalogPath, `${JSON.stringify(manifest, null, 2)}\n`); | ||
|
|
||
| const totals = { | ||
| downloaded: 0, | ||
| skipped: 0, | ||
| postersGenerated: 0, | ||
| postersSkipped: 0, | ||
| }; | ||
|
|
||
| for (const [index, asset] of manifest.assets.entries()) { | ||
| for (const format of FORMATS) { | ||
| const variant = asset.variants[format]; | ||
| const sourceUrl = `${versionRoot}/${variant.path}`; | ||
| const result = await downloadFile( | ||
| sourceUrl, | ||
| variantOutputPath(asset, format), | ||
| variant.byteSize, | ||
| ); | ||
| totals[result] += 1; | ||
| } | ||
|
|
||
| const posterResult = await ensurePoster(asset); | ||
| if (posterResult === "generated") { | ||
| totals.postersGenerated += 1; | ||
| } else { | ||
| totals.postersSkipped += 1; | ||
| } | ||
|
|
||
| const completed = index + 1; | ||
| if (completed % 5 === 0 || completed === manifest.assets.length) { | ||
| console.log( | ||
| `Synced ${completed}/${manifest.assets.length} Goose avatars...`, | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| const catalogBytes = await readFile(catalogPath, "utf8"); | ||
| JSON.parse(catalogBytes); | ||
|
|
||
| console.log( | ||
| `Done. Downloaded ${totals.downloaded}, skipped ${totals.skipped}, generated ${totals.postersGenerated} posters, skipped ${totals.postersSkipped} posters.`, | ||
| ); | ||
| } | ||
|
|
||
| main().catch((error) => { | ||
| console.error(error); | ||
| process.exit(1); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.