Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
03e4058
Implement tool registry and executor for agent actions (CS-10479)
habdelra Mar 22, 2026
740e9de
Address PR review feedback: realm API contracts, safety, and validation
habdelra Mar 22, 2026
34e21ed
Add auth header propagation tests for all realm-api tools
habdelra Mar 22, 2026
cf1a379
Add integration tests: realm-api request building and safety constraints
habdelra Mar 22, 2026
e4eca7a
Add realm-auth tool, realm-create defaults, and live integration tests
habdelra Mar 22, 2026
1a0cf67
Simplify live tests: use harness JWTs, run in pnpm test:node
habdelra Mar 22, 2026
cb12707
Live tests fail-hard when harness not running, separate test:live script
habdelra Mar 22, 2026
fbb5f9c
Add test:live to CI workflow for software-factory
habdelra Mar 22, 2026
1fcdeee
Fix live tests: use harness JWT seeds, remove realm-create (CS-10472)
habdelra Mar 22, 2026
3c97e4e
Document boxel-icons server prerequisite for cache:prepare
habdelra Mar 22, 2026
b02753a
Address review: validate all URL args, fix SSRF gap, remove realm-rei…
habdelra Mar 22, 2026
b334c36
Merge remote-tracking branch 'origin/main' into cs-10479-implement-to…
habdelra Mar 23, 2026
4a6b6b2
Merge remote-tracking branch 'origin/main' into cs-10479-implement-to…
habdelra Mar 24, 2026
966e5f8
Move iconURLFor/getRandomBackgroundURL to @cardstack/runtime-common
habdelra Mar 24, 2026
74a8254
Move live tests to Playwright spec, remove test:live script
habdelra Mar 24, 2026
f6e8cd7
Merge remote-tracking branch 'origin/main' into cs-10479-implement-to…
habdelra Mar 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 4 additions & 91 deletions packages/host/app/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import {
devSkillLocalPath,
envSkillLocalPath,
} from '@cardstack/runtime-common';
export {
iconURLFor,
getRandomBackgroundURL,
} from '@cardstack/runtime-common/realm-display-defaults';

import ENV from '@cardstack/host/config/environment';

Expand Down Expand Up @@ -47,97 +51,6 @@ export function cleanseString(value: string) {
.replace(/[^a-z0-9]$/, '');
}

export function iconURLFor(word: string) {
if (!word) {
return undefined;
}
let cleansedWord = cleanseString(word).replace(/^[0-9]+/, '');
return iconURLs[cleansedWord.charAt(0)];
}

export function getRandomBackgroundURL() {
const index = Math.floor(Math.random() * backgroundURLs.length);
return backgroundURLs[index];
}

const iconURLs: { [letter: string]: string } = Object.freeze({
a: 'https://boxel-images.boxel.ai/icons/Letter-a.png',
b: 'https://boxel-images.boxel.ai/icons/Letter-b.png',
c: 'https://boxel-images.boxel.ai/icons/Letter-c.png',
d: 'https://boxel-images.boxel.ai/icons/Letter-d.png',
e: 'https://boxel-images.boxel.ai/icons/Letter-e.png',
f: 'https://boxel-images.boxel.ai/icons/Letter-f.png',
g: 'https://boxel-images.boxel.ai/icons/Letter-g.png',
h: 'https://boxel-images.boxel.ai/icons/Letter-h.png',
i: 'https://boxel-images.boxel.ai/icons/Letter-i.png',
j: 'https://boxel-images.boxel.ai/icons/Letter-j.png',
k: 'https://boxel-images.boxel.ai/icons/Letter-k.png',
l: 'https://boxel-images.boxel.ai/icons/Letter-l.png',
m: 'https://boxel-images.boxel.ai/icons/Letter-m.png',
n: 'https://boxel-images.boxel.ai/icons/Letter-n.png',
o: 'https://boxel-images.boxel.ai/icons/Letter-o.png',
p: 'https://boxel-images.boxel.ai/icons/Letter-p.png',
q: 'https://boxel-images.boxel.ai/icons/Letter-q.png',
r: 'https://boxel-images.boxel.ai/icons/Letter-r.png',
s: 'https://boxel-images.boxel.ai/icons/Letter-s.png',
t: 'https://boxel-images.boxel.ai/icons/Letter-t.png',
u: 'https://boxel-images.boxel.ai/icons/Letter-u.png',
v: 'https://boxel-images.boxel.ai/icons/Letter-v.png',
w: 'https://boxel-images.boxel.ai/icons/Letter-w.png',
x: 'https://boxel-images.boxel.ai/icons/Letter-x.png',
y: 'https://boxel-images.boxel.ai/icons/Letter-y.png',
z: 'https://boxel-images.boxel.ai/icons/letter-z.png',
});
const backgroundURLs: readonly string[] = Object.freeze([
'https://boxel-images.boxel.ai/background-images/4k-arabic-teal.jpg',
'https://boxel-images.boxel.ai/background-images/4k-arrow-weave.jpg',
'https://boxel-images.boxel.ai/background-images/4k-atmosphere-curvature.jpg',
'https://boxel-images.boxel.ai/background-images/4k-brushed-slabs.jpg',
'https://boxel-images.boxel.ai/background-images/4k-coral-reefs.jpg',
'https://boxel-images.boxel.ai/background-images/4k-crescent-lake.jpg',
'https://boxel-images.boxel.ai/background-images/4k-curvilinear-stairs.jpg',
'https://boxel-images.boxel.ai/background-images/4k-desert-dunes.jpg',
'https://boxel-images.boxel.ai/background-images/4k-doodle-board.jpg',
'https://boxel-images.boxel.ai/background-images/4k-fallen-leaves.jpg',
'https://boxel-images.boxel.ai/background-images/4k-flowing-mesh.jpg',
'https://boxel-images.boxel.ai/background-images/4k-glass-reflection.jpg',
'https://boxel-images.boxel.ai/background-images/4k-glow-cells.jpg',
'https://boxel-images.boxel.ai/background-images/4k-granite-peaks.jpg',
'https://boxel-images.boxel.ai/background-images/4k-green-wormhole.jpg',
'https://boxel-images.boxel.ai/background-images/4k-joshua-dawn.jpg',
'https://boxel-images.boxel.ai/background-images/4k-lava-river.jpg',
'https://boxel-images.boxel.ai/background-images/4k-leaves-moss.jpg',
'https://boxel-images.boxel.ai/background-images/4k-light-streaks.jpg',
'https://boxel-images.boxel.ai/background-images/4k-lowres-glitch.jpg',
'https://boxel-images.boxel.ai/background-images/4k-marble-shimmer.jpg',
'https://boxel-images.boxel.ai/background-images/4k-metallic-leather.jpg',
'https://boxel-images.boxel.ai/background-images/4k-microscopic-crystals.jpg',
'https://boxel-images.boxel.ai/background-images/4k-moon-face.jpg',
'https://boxel-images.boxel.ai/background-images/4k-mountain-runway.jpg',
'https://boxel-images.boxel.ai/background-images/4k-origami-flock.jpg',
'https://boxel-images.boxel.ai/background-images/4k-paint-swirl.jpg',
'https://boxel-images.boxel.ai/background-images/4k-pastel-triangles.jpg',
'https://boxel-images.boxel.ai/background-images/4k-perforated-sheet.jpg',
'https://boxel-images.boxel.ai/background-images/4k-plastic-ripples.jpg',
'https://boxel-images.boxel.ai/background-images/4k-powder-puff.jpg',
'https://boxel-images.boxel.ai/background-images/4k-radiant-crystal.jpg',
'https://boxel-images.boxel.ai/background-images/4k-redrock-canyon.jpg',
'https://boxel-images.boxel.ai/background-images/4k-rock-portal.jpg',
'https://boxel-images.boxel.ai/background-images/4k-rolling-hills.jpg',
'https://boxel-images.boxel.ai/background-images/4k-sand-stone.jpg',
'https://boxel-images.boxel.ai/background-images/4k-silver-fur.jpg',
'https://boxel-images.boxel.ai/background-images/4k-spa-pool.jpg',
'https://boxel-images.boxel.ai/background-images/4k-stained-glass.jpg',
'https://boxel-images.boxel.ai/background-images/4k-stone-veins.jpg',
'https://boxel-images.boxel.ai/background-images/4k-tangerine-plains.jpg',
'https://boxel-images.boxel.ai/background-images/4k-techno-floor.jpg',
'https://boxel-images.boxel.ai/background-images/4k-thick-frost.jpg',
'https://boxel-images.boxel.ai/background-images/4k-water-surface.jpg',
'https://boxel-images.boxel.ai/background-images/4k-watercolor-splashes.jpg',
'https://boxel-images.boxel.ai/background-images/4k-wildflower-field.jpg',
'https://boxel-images.boxel.ai/background-images/4k-wood-grain.jpg',
]);

export function urlForRealmLookup(card: CardDef) {
let urlForRealmLookup = card.id ?? card[realmURL]?.href;
if (!urlForRealmLookup) {
Expand Down
108 changes: 108 additions & 0 deletions packages/runtime-common/realm-display-defaults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* Default icon and background URL generators for new realms.
*
* Shared by host, software-factory, and any other package that creates realms.
* Pure JS — no external dependencies.
*/

const ICON_URLS: { [letter: string]: string } = Object.freeze({
a: 'https://boxel-images.boxel.ai/icons/Letter-a.png',
b: 'https://boxel-images.boxel.ai/icons/Letter-b.png',
c: 'https://boxel-images.boxel.ai/icons/Letter-c.png',
d: 'https://boxel-images.boxel.ai/icons/Letter-d.png',
e: 'https://boxel-images.boxel.ai/icons/Letter-e.png',
f: 'https://boxel-images.boxel.ai/icons/Letter-f.png',
g: 'https://boxel-images.boxel.ai/icons/Letter-g.png',
h: 'https://boxel-images.boxel.ai/icons/Letter-h.png',
i: 'https://boxel-images.boxel.ai/icons/Letter-i.png',
j: 'https://boxel-images.boxel.ai/icons/Letter-j.png',
k: 'https://boxel-images.boxel.ai/icons/Letter-k.png',
l: 'https://boxel-images.boxel.ai/icons/Letter-l.png',
m: 'https://boxel-images.boxel.ai/icons/Letter-m.png',
n: 'https://boxel-images.boxel.ai/icons/Letter-n.png',
o: 'https://boxel-images.boxel.ai/icons/Letter-o.png',
p: 'https://boxel-images.boxel.ai/icons/Letter-p.png',
q: 'https://boxel-images.boxel.ai/icons/Letter-q.png',
r: 'https://boxel-images.boxel.ai/icons/Letter-r.png',
s: 'https://boxel-images.boxel.ai/icons/Letter-s.png',
t: 'https://boxel-images.boxel.ai/icons/Letter-t.png',
u: 'https://boxel-images.boxel.ai/icons/Letter-u.png',
v: 'https://boxel-images.boxel.ai/icons/Letter-v.png',
w: 'https://boxel-images.boxel.ai/icons/Letter-w.png',
x: 'https://boxel-images.boxel.ai/icons/Letter-x.png',
y: 'https://boxel-images.boxel.ai/icons/Letter-y.png',
z: 'https://boxel-images.boxel.ai/icons/letter-z.png',
});

const BACKGROUND_URLS: readonly string[] = Object.freeze([
'https://boxel-images.boxel.ai/background-images/4k-arabic-teal.jpg',
'https://boxel-images.boxel.ai/background-images/4k-arrow-weave.jpg',
'https://boxel-images.boxel.ai/background-images/4k-atmosphere-curvature.jpg',
'https://boxel-images.boxel.ai/background-images/4k-brushed-slabs.jpg',
'https://boxel-images.boxel.ai/background-images/4k-coral-reefs.jpg',
'https://boxel-images.boxel.ai/background-images/4k-crescent-lake.jpg',
'https://boxel-images.boxel.ai/background-images/4k-curvilinear-stairs.jpg',
'https://boxel-images.boxel.ai/background-images/4k-desert-dunes.jpg',
'https://boxel-images.boxel.ai/background-images/4k-doodle-board.jpg',
'https://boxel-images.boxel.ai/background-images/4k-fallen-leaves.jpg',
'https://boxel-images.boxel.ai/background-images/4k-flowing-mesh.jpg',
'https://boxel-images.boxel.ai/background-images/4k-glass-reflection.jpg',
'https://boxel-images.boxel.ai/background-images/4k-glow-cells.jpg',
'https://boxel-images.boxel.ai/background-images/4k-granite-peaks.jpg',
'https://boxel-images.boxel.ai/background-images/4k-green-wormhole.jpg',
'https://boxel-images.boxel.ai/background-images/4k-joshua-dawn.jpg',
'https://boxel-images.boxel.ai/background-images/4k-lava-river.jpg',
'https://boxel-images.boxel.ai/background-images/4k-leaves-moss.jpg',
'https://boxel-images.boxel.ai/background-images/4k-light-streaks.jpg',
'https://boxel-images.boxel.ai/background-images/4k-lowres-glitch.jpg',
'https://boxel-images.boxel.ai/background-images/4k-marble-shimmer.jpg',
'https://boxel-images.boxel.ai/background-images/4k-metallic-leather.jpg',
'https://boxel-images.boxel.ai/background-images/4k-microscopic-crystals.jpg',
'https://boxel-images.boxel.ai/background-images/4k-moon-face.jpg',
'https://boxel-images.boxel.ai/background-images/4k-mountain-runway.jpg',
'https://boxel-images.boxel.ai/background-images/4k-origami-flock.jpg',
'https://boxel-images.boxel.ai/background-images/4k-paint-swirl.jpg',
'https://boxel-images.boxel.ai/background-images/4k-pastel-triangles.jpg',
'https://boxel-images.boxel.ai/background-images/4k-perforated-sheet.jpg',
'https://boxel-images.boxel.ai/background-images/4k-plastic-ripples.jpg',
'https://boxel-images.boxel.ai/background-images/4k-powder-puff.jpg',
'https://boxel-images.boxel.ai/background-images/4k-radiant-crystal.jpg',
'https://boxel-images.boxel.ai/background-images/4k-redrock-canyon.jpg',
'https://boxel-images.boxel.ai/background-images/4k-rock-portal.jpg',
'https://boxel-images.boxel.ai/background-images/4k-rolling-hills.jpg',
'https://boxel-images.boxel.ai/background-images/4k-sand-stone.jpg',
'https://boxel-images.boxel.ai/background-images/4k-silver-fur.jpg',
'https://boxel-images.boxel.ai/background-images/4k-spa-pool.jpg',
'https://boxel-images.boxel.ai/background-images/4k-stained-glass.jpg',
'https://boxel-images.boxel.ai/background-images/4k-stone-veins.jpg',
'https://boxel-images.boxel.ai/background-images/4k-tangerine-plains.jpg',
'https://boxel-images.boxel.ai/background-images/4k-techno-floor.jpg',
'https://boxel-images.boxel.ai/background-images/4k-thick-frost.jpg',
'https://boxel-images.boxel.ai/background-images/4k-water-surface.jpg',
'https://boxel-images.boxel.ai/background-images/4k-watercolor-splashes.jpg',
'https://boxel-images.boxel.ai/background-images/4k-wildflower-field.jpg',
'https://boxel-images.boxel.ai/background-images/4k-wood-grain.jpg',
]);

/**
* Generate a letter-based icon URL from a realm/workspace name.
* Returns undefined if the name has no alphabetic characters.
*/
export function iconURLFor(word: string): string | undefined {
if (!word) {
return undefined;
}
let cleansed = word
.toLowerCase()
.replace(/[^a-z0-9]/g, '')
.replace(/^[0-9]+/, '');
return ICON_URLS[cleansed.charAt(0)];
}

/**
* Pick a random background image URL from the predefined set.
*/
export function getRandomBackgroundURL(): string {
let index = Math.floor(Math.random() * BACKGROUND_URLS.length);
return BACKGROUND_URLS[index];
}
5 changes: 5 additions & 0 deletions packages/software-factory/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ the repo. Package linting currently runs `glint`, `eslint`, and `prettier`.
- Docker running
- Host app assets available at `http://localhost:4200/`
- use `cd packages/host && pnpm serve:dist`
- Boxel icons server available at `http://localhost:4206/`
- use `cd packages/boxel-icons && pnpm serve`
- in a worktree where boxel-icons hasn't been built, symlink the dist from the main checkout:
`ln -s /path/to/boxel/packages/boxel-icons/dist packages/boxel-icons/dist`
- required before `cache:prepare` — the harness indexes cards that reference icon modules

The harness starts its own seeded test Postgres, Synapse, prerender server, and
isolated realm server. By default it serves the test realm and base realm from
Expand Down
1 change: 1 addition & 0 deletions packages/software-factory/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"factory:agent-smoke": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/factory-agent-smoke.ts",
"factory:go": "NODE_NO_WARNINGS=1 ts-node --transpileOnly src/cli/factory-entrypoint.ts",
"factory:skill-smoke": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/factory-skill-smoke.ts",
"factory:tools-smoke": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/factory-tools-smoke.ts",
"lint": "concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\"",
"lint:fix": "concurrently \"pnpm:lint:*:fix\" --names \"fix:\"",
"lint:js": "eslint . --report-unused-disable-directives --cache",
Expand Down
Loading
Loading