Skip to content

software factory: replace create realm logic with boxel-cli equivalent #4399

Draft
jurgenwerk wants to merge 9 commits intomainfrom
cs-10642-boxel-cli-owns-full-auth-lifecycle-realm-server-tokens-per-2
Draft

software factory: replace create realm logic with boxel-cli equivalent #4399
jurgenwerk wants to merge 9 commits intomainfrom
cs-10642-boxel-cli-owns-full-auth-lifecycle-realm-server-tokens-per-2

Conversation

@jurgenwerk
Copy link
Copy Markdown
Contributor

@jurgenwerk jurgenwerk commented Apr 14, 2026

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.

jurgenwerk and others added 8 commits April 14, 2026 12:15
- 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>
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 14, 2026

Realm Server Test Results

  1 files  ±0    1 suites  ±0   14m 33s ⏱️ +46s
844 tests ±0  844 ✅ ±0  0 💤 ±0  0 ❌ ±0 
915 runs  ±0  915 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit 02f46d5. ± Comparison against base commit 72d1f10.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 14, 2026

Host Test Results

2 194 tests  ±0   2 179 ✅ ±0   2h 15m 48s ⏱️ - 17m 54s
    1 suites ±0      15 💤 ±0 
    1 files   ±0       0 ❌ ±0 

Results for commit 02f46d5. ± Comparison against base commit 72d1f10.

♻️ This comment has been updated with latest results.

Clearer naming that matches the core createRealm signature.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jurgenwerk jurgenwerk changed the title in software factory replace create realm logic with boxel-cli methods software factory: replace create realm logic with boxel-cli methods Apr 14, 2026
@jurgenwerk jurgenwerk changed the title software factory: replace create realm logic with boxel-cli methods software factory: replace create realm logic with boxel-cli equivalent Apr 14, 2026
@jurgenwerk jurgenwerk marked this pull request as ready for review April 14, 2026 13:21
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-cli as a dependency for packages/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 create command to expose a programmatic createRealm() that returns structured results (URL/token/created), and adjust integration tests to set REALM_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.

Comment on lines 224 to 230
env: {
...process.env,
MATRIX_USERNAME: 'hassan',
MATRIX_PASSWORD: 'secret',
MATRIX_URL: origin,
REALM_SERVER_URL: `${origin}/`,
},
Comment on lines +25 to 27
await BoxelCLIClient.ensureProfile();

let options = parseFactoryEntrypointArgs(process.argv.slice(2));
Comment on lines +71 to 72
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) {
Comment on lines +52 to +55
return {
realmUrl: result.realmUrl,
created: result.created,
authorization: result.realmToken ?? '',
Comment on lines 123 to +146
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,
};
Comment on lines 87 to 95
cwd: packageRoot,
env: {
...process.env,
MATRIX_USERNAME: targetUsername,
MATRIX_PASSWORD: targetPassword,
MATRIX_URL: matrixURL,
REALM_SERVER_URL: realmServerURL,
},
timeoutMs: 120_000,
@jurgenwerk jurgenwerk marked this pull request as draft April 14, 2026 13:37
@jurgenwerk
Copy link
Copy Markdown
Contributor Author

@habdelra here is the PR I mentioned - can you check the PR description and let me know if this approach makes sense?

@habdelra
Copy link
Copy Markdown
Contributor

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?

@habdelra
Copy link
Copy Markdown
Contributor

habdelra commented Apr 14, 2026

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

@habdelra
Copy link
Copy Markdown
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants