software factory: replace create realm logic with boxel-cli equivalent #4399
software factory: replace create realm logic with boxel-cli equivalent #4399jurgenwerk wants to merge 9 commits intomainfrom
Conversation
…realm-server-tokens-per-2
- Extract core createRealm logic from CLI command (no process.exit, returns data, handles "already exists" gracefully) - Rename createRealmCommand to executeCreateRealmCommand - Add static BoxelCLIClient.ensureProfile() for one-time env var migration at process startup - Call ensureProfile() in factory CLI entrypoint so env-var-only CI environments get a stored profile before realm operations Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
migrateFromEnv() requires REALM_SERVER_URL to create a profile from env vars. The test was missing this env var for the spawned child process. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Same fix as the qunit integration test — migrateFromEnv() needs REALM_SERVER_URL to create a profile from env vars. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Import from @cardstack/boxel-cli/src/lib/boxel-cli-client directly, same pattern as runtime-common deep imports. The barrel added no value. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
migrateFromEnv() only sets a profile as active when no active profile exists. When tests run sequentially and share ~/.boxel-cli/profiles.json, a stale active profile from a prior test would persist. ensureProfile() now explicitly switches to the migrated profile so env vars always win. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Clearer naming that matches the core createRealm signature. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 02f46d587d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return; | ||
| } | ||
|
|
||
| await BoxelCLIClient.ensureProfile(); |
There was a problem hiding this comment.
Parse args before profile migration
factory-entrypoint now calls BoxelCLIClient.ensureProfile() before parsing CLI flags, but ensureProfile() can only read REALM_SERVER_URL from env. That means a user with no active profile who follows the documented flow (MATRIX_URL, MATRIX_USERNAME, MATRIX_PASSWORD, plus --realm-server-url) will still fail during bootstrap with “No active profile” unless they redundantly set REALM_SERVER_URL in the environment, which is a behavior regression from the previous path that accepted the flag alone.
Useful? React with 👍 / 👎.
| ); | ||
| } | ||
| let client = new BoxelCLIClient(); | ||
| let result = await client.createRealm({ realmName, displayName: realmName }); |
There was a problem hiding this comment.
Respect resolved server URL when creating realms
The new factory createRealm path ignores resolution.serverUrl and delegates directly to BoxelCLIClient.createRealm, which uses the active profile’s realmServerUrl instead. If the active profile points to a different origin than --realm-server-url, realm creation happens on the wrong server while later wiring still uses resolution.serverUrl, causing server/token mismatches in subsequent operations.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
This PR migrates software-factory’s “create target realm” path to delegate realm creation/auth to boxel-cli’s profile manager, as a first step toward moving auth management out of software-factory.
Changes:
- Add
@cardstack/boxel-clias a dependency forpackages/software-factory. - Replace software-factory realm creation flow with a
BoxelCLIClient.createRealm(...)delegation and ensure a CLI profile exists at startup. - Refactor boxel-cli’s
realm createcommand to expose a programmaticcreateRealm()that returns structured results (URL/token/created), and adjust integration tests to setREALM_SERVER_URL.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Adds workspace link for @cardstack/boxel-cli in software-factory. |
| packages/software-factory/package.json | Adds @cardstack/boxel-cli devDependency. |
| packages/software-factory/src/factory-target-realm.ts | Replaces internal HTTP/auth realm creation with BoxelCLIClient delegation. |
| packages/software-factory/src/cli/factory-entrypoint.ts | Calls BoxelCLIClient.ensureProfile() at process startup. |
| packages/software-factory/tests/factory-target-realm.test.ts | Removes detailed HTTP-flow unit tests; keeps delegation-focused behavior tests. |
| packages/software-factory/tests/factory-target-realm.spec.ts | Sets REALM_SERVER_URL in end-to-end factory bootstrap test env. |
| packages/software-factory/tests/factory-entrypoint.integration.test.ts | Sets REALM_SERVER_URL in integration env. |
| packages/boxel-cli/src/lib/boxel-cli-client.ts | Introduces BoxelCLIClient wrapper + ensureProfile() helper. |
| packages/boxel-cli/src/commands/realm/create.ts | Splits CLI wrapper from core creation; returns structured result and supports readiness wait. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| env: { | ||
| ...process.env, | ||
| MATRIX_USERNAME: 'hassan', | ||
| MATRIX_PASSWORD: 'secret', | ||
| MATRIX_URL: origin, | ||
| REALM_SERVER_URL: `${origin}/`, | ||
| }, |
| await BoxelCLIClient.ensureProfile(); | ||
|
|
||
| let options = parseFactoryEntrypointArgs(process.argv.slice(2)); |
| let result = await client.createRealm({ realmName, displayName: realmName }); | ||
|
|
| @@ -1,21 +1,7 @@ | |||
| import { BoxelCLIClient } from '@cardstack/boxel-cli/src/lib/boxel-cli-client'; | |||
| if (urlMatch) { | ||
| return ensureTrailingSlash(urlMatch[1]); | ||
| } | ||
| return ensureTrailingSlash(`${realmServerUrl}/${endpoint}`); |
| // This should be first | ||
| import '../setup-logger'; | ||
|
|
||
| import { BoxelCLIClient } from '@cardstack/boxel-cli/src/lib/boxel-cli-client'; |
| static async ensureProfile(): Promise<void> { | ||
| let pm = getProfileManager(); | ||
| let result = await pm.migrateFromEnv(); | ||
| if (result?.created) { |
| return { | ||
| realmUrl: result.realmUrl, | ||
| created: result.created, | ||
| authorization: result.realmToken ?? '', |
| let result = await response.json(); | ||
| let realmUrl = result?.data?.id; | ||
| let normalizedRealmUrl = realmUrl ? ensureTrailingSlash(realmUrl) : undefined; | ||
|
|
||
| let realmToken: string | undefined; | ||
| if (normalizedRealmUrl) { | ||
| realmToken = await fetchRealmToken(pm, normalizedRealmUrl); | ||
|
|
||
| try { | ||
| let serverToken = await pm.getOrRefreshServerToken(); | ||
| let token = await pm.fetchAndStoreRealmToken( | ||
| normalizedRealmUrl, | ||
| serverToken, | ||
| ); | ||
| if (!token) { | ||
| console.error( | ||
| `${DIM}Warning: realm created but JWT not found in auth response.${RESET}`, | ||
| ); | ||
| } | ||
| await pm.addToUserRealms(normalizedRealmUrl); | ||
| } catch { | ||
| console.error( | ||
| `${DIM}Warning: realm created but could not obtain realm JWT.${RESET}`, | ||
| ); | ||
| // Non-critical — realm still works without dashboard registration | ||
| } | ||
| } | ||
|
|
||
| if (options.waitForReady && normalizedRealmUrl && realmToken) { | ||
| await waitForRealmReady(normalizedRealmUrl, realmToken); | ||
| } | ||
|
|
||
| return { | ||
| realmUrl: normalizedRealmUrl ?? realmName, | ||
| created: true, | ||
| realmToken, | ||
| }; |
| cwd: packageRoot, | ||
| env: { | ||
| ...process.env, | ||
| MATRIX_USERNAME: targetUsername, | ||
| MATRIX_PASSWORD: targetPassword, | ||
| MATRIX_URL: matrixURL, | ||
| REALM_SERVER_URL: realmServerURL, | ||
| }, | ||
| timeoutMs: 120_000, |
|
@habdelra here is the PR I mentioned - can you check the PR description and let me know if this approach makes sense? |
|
so the central place you'll need to work thru is software-factory/src/lib/realm-operations. I've centralized all the realm operations that need to be replaced by boxel-cli into this module. I would expect that you are removing the createRealm logic here and replacing with calls to the boxel-cli. Right now, for the sfotware factory, we don't want the agent to create realms. rather, our deterministic orchestration makes the realm. so i think we should update the createRealm() implementation in the lib/realm-operations to just call the boxel-cli. For other things where we want the agent to actually do the thing, (like push and pull files), it makes more sense to literally remove this function from the lib/realm-operations.ts and just teach the agent via a skill how to push and pull. This make sense? |
|
It looks like your approach right now is to update all teh call sites that need to make a realm to just use the boxel-cli to do that. I'm totally ok with that approach too. although, I think you'll find the call sites more easily if you remove the createRealm() function from lib/realm-operations.ts and look at all the type errors |
|
Also I think you are correct in the tests that you are removing. any tests that are overlapping with tests that exist in boxel-cli are not necessary. |
This is a PR that aims to complete the first step of a larger task, which is moving auth management out of the software factory and rely solely on the boxel cli to take care of that.
In this PR, the only part being replaced is the "create realm" functionality - it removes authentication logic that gets the server token and uses it in fetch calls to perform realm creation. All this has been replaced with just a single call:
let client = new BoxelCLIClient(); let result = await client.createRealm({ realmName, displayName: realmName });.Client is imported from boxel cli and under the hood it makes use of the profile manager which maintains the server token and realm tokens for the currently active profile in a persisted state.
There are other functionalities to replace, such as "push", "pull", "sync", and "search", but at the time of this writing the CLI doesn't have those commands yet (the migration is still in process). I decided to defer that to subsequent PRs so that we can first verify this approach and make PRs smaller and easier to review.