-
Notifications
You must be signed in to change notification settings - Fork 26
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
Closed
Closed
Changes from all commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
71501c2
Revamp agents panel profiles
klopez4212 facace3
Delete persona-backed agents from profile
klopez4212 74fd4a5
Address agents panel review feedback
klopez4212 8e27a1d
Update agent start smoke expectation
klopez4212 3305f87
Address profile persona review feedback
klopez4212 9c35cf2
Sync desktop smoke tests with agent cards
klopez4212 5d0c428
Update persona env vars e2e entry point
klopez4212 4fe66cf
Address remaining agent profile review feedback
klopez4212 430ecb9
Show persona avatars on agent cards
klopez4212 cb37bca
Keep secondary persona agents reachable
klopez4212 6148347
Use live agent profile avatars
klopez4212 372201c
Preserve persona model without runtime
klopez4212 650400a
Left align agents panel content
klopez4212 bee5314
Sync persona profile edits to agents
klopez4212 9107401
Preserve imported persona provider
klopez4212 17622ce
Restore persona env var editing
klopez4212 844e6ca
Preserve cleared agent avatars
klopez4212 b38729a
Keep managed agent types under size limit
klopez4212 37c1012
Sync persona runtime edits to agents
klopez4212 1f406ef
Preserve provider in batch persona imports
klopez4212 b25d074
Preserve imported persona model on provider select
klopez4212 690781f
Fix profile agent delete and subview routes
klopez4212 47eefb1
Preserve imported avatar URL refs
klopez4212 c421b2a
Reset reused agent respond-to defaults
klopez4212 c25fa65
Fix agent panel desktop e2e expectations
klopez4212 7bd453b
Address remaining agent profile review feedback
klopez4212 71efb2b
Address agent import review feedback
klopez4212 941257a
Address profile panel review feedback
klopez4212 c5cdd4c
Address profile panel PR feedback
klopez4212 9c740c0
Fix agent avatars in cards and teams
klopez4212 8d28ee1
Prevent runtime logos from replacing persona avatars
klopez4212 49aa2c3
Restore built-in Fizz avatar
klopez4212 0673fd6
Update agents page loading skeletons
klopez4212 a666f00
Make create identity cards transparent by default
klopez4212 05f136f
Honor persona agent overrides and provider edits
klopez4212 b965437
Stabilize welcome onboarding e2e assertion
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.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't have block internal stuff in our OSS PRs.