Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 13 additions & 2 deletions docs/aura/FORK.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ and after every upstream merge.
| `packages/coding-agent/src/modes/setup-wizard/wizard-overlay.ts` | one symbol: the wizard scene header renders `AURA_LOGO` |
| `packages/coding-agent/src/tools/renderers.ts` | one import + one `...runtimeToolRenderers` spread at the head of `toolRenderers`, registering the nine runtime tool renderers. The spread is first so a future upstream entry with the same key would win rather than be silently shadowed; all renderer logic lives in the fork-owned `tools/runtime-renderer.ts`, so this row stays a two-line change through any merge |
| `packages/coding-agent/src/cli/gallery-fixtures/index.ts` | one import + one `...runtimeFixtures` spread, adding the runtime tool family's `omp gallery` sample data (the fixtures themselves are the fork-owned sibling module `gallery-fixtures/runtime.ts`). Without it the coverage test still passes — unfixtured tools fall back to a generic sample — but the runtime rows render as placeholder args |
| `packages/coding-agent/src/config/settings-schema.ts` | `theme.dark` default = `aura` (was `titanium`) and `theme.light` default = `aura-light` (was `light`), so the fork's terminal-background auto light/dark switching stays on-brand in both directions; `runtime.*` settings (`runtime.enabled`, `runtime.adapter` with process default and explicit-embedded no-fallback, `runtime.autoDownload`, `runtime.path`, `runtime.version`, `runtime.embeddedPath`) added to the `tools` tab; Python capability hierarchy: parent `python.enabled` and embedded-runtime child `python.embedded` default on, local snake-action child `python.shell` defaults off (the `$` action rides the embedded runtime; this key only adds a subprocess-interpreter fallback for hosts without it); `DEFAULT_BASH_INTERCEPTOR_RULES` retains only the ordinary user-controlled dedicated-tool nudges |
| `packages/coding-agent/src/config/settings-schema.ts` | `theme.dark` default = `aura` (was `titanium`) and `theme.light` default = `aura-light` (was `light`), so the fork's terminal-background auto light/dark switching stays on-brand in both directions; `runtime.*` settings (`runtime.enabled`, `runtime.adapter` with process default and explicit-embedded no-fallback, `runtime.autoDownload`, `runtime.path`, `runtime.version`, `runtime.embeddedPath`) added to the `tools` tab; Python capability hierarchy: parent `python.enabled` and embedded-runtime child `python.embedded` default on, local snake-action child `python.shell` defaults off (the `$` action rides the embedded runtime; this key only adds a subprocess-interpreter fallback for hosts without it); `DEFAULT_BASH_INTERCEPTOR_RULES` retains only the ordinary user-controlled dedicated-tool nudges; `cloud.stats.enabled` (default off, same reasoning as `cloud.telemetry.enabled`) gates pushing local `stats` records to Aura's hosted observability panel and re-pointing `aura stats` at it |
| `packages/coding-agent/src/tools/report-tool-issue.ts`, `src/cli/grievances-cli.ts`, `test/tools/report-tool-issue.test.ts` | no default Auto-QA collector: `dev.autoqaPush.endpoint` ships empty, so consent enables local recording only and grievances stay queued until an operator configures an endpoint (setting, `AURA_QA_URL`/`PI_AUTO_QA_PUSH_URL`, or `AURA_DOMAIN`). Tests pin the empty default and preserve explicit setting / `PI_AUTO_QA_PUSH_URL` precedence. Keep upstream batching, consent, local retention, and push behavior unchanged when resolving merges |
| `packages/coding-agent/src/tools/bash.ts` | selects configured rules through `activeBashInterceptorRules(getBashInterceptorRules(), settings.get("bashInterceptor.enabled"))`; the toggle gates every rule, and direct runtime-binary commands are not intercepted |
| `packages/coding-agent/src/tools/index.ts` | `ToolSession.getRuntimeService?: () => RuntimeService \| undefined` accessor added beside `getMnemopiSessionState`, plus the root-owned `runtimeServiceScope` propagated into every subagent executor; registers engine-aware `run` and validation-only `check` as essential, `insights`/`profile` plus four specialized JVM tools (`jvm_disassemble`, `jvm_format`, `jvm_jar`, `jvm_deps`) and hub-supervised `serve` as discoverable, all on the `runtime.enabled` gate |
Expand All @@ -47,7 +47,7 @@ and after every upstream merge.
| `packages/coding-agent/src/cli/flag-tables.ts` | `--prepend-system-prompt <text-or-file>` and `--runtime <path>` added to `STRING_SETTERS` (the single source of truth for string-valued launch flags, so the profile bootstrap and subcommand resolver pick it up automatically) |
| `packages/coding-agent/src/main.ts` | `discoverPrependSystemPromptFile` (`PREPEND_SYSTEM.md`, project then global, mirroring `discoverAppendSystemPromptFile`), resolved in `buildSessionOptions` and applied through a fourth `applyResolvedSystemPromptInputs` parameter; `--prepend-system-prompt` also joins the fork-cache-shape check; the `--version` launch-flag path prints `RUNTIME_PROTOCOL_LINE` under `VERSION`, matching the top-level interception in `cli.ts`; `--runtime <path>` applied as an ephemeral `Settings.override("runtime.path", …)` alongside the other CLI-flag overrides, so every `settings.get("runtime.path")` site (innate-tool endpoint resolution included) observes it |
| `packages/coding-agent/src/commands/launch.ts` | `prepend-system-prompt: Flags.string(...)` and `runtime: Flags.string(...)` declared for oclif's generated `--help`; the real parse lives in `cli/args.ts` (same pattern as `--auto-approve` / `--approval-mode`) |
| `packages/coding-agent/src/cli-commands.ts` | register runtime and doctor commands |
| `packages/coding-agent/src/cli-commands.ts` | register runtime and doctor commands; registers `account` (Aura account login/logout/status — see `cli/account-cli.ts` below) |
| `packages/coding-agent/src/sdk.ts` | `CreateAgentSessionOptions.prependSystemPrompt`, forwarded to `buildSystemPrompt` as `resolvedPrependSystemPrompt` and included in the fork-cache-shape check; wires selected/composite runtime settings onto the lazy `toolSession.getRuntimeService` accessor; creates a root-owned runtime cache/config scope, exposes the canonical settings snapshot reader for private top-level session factories, and propagates that scope into every descendant; every top-level session sharing that scope acquires an idempotent lease while subagents never acquire or release one; startup failure releases the lease; last release asynchronously evicts/closes only that scope |
| `packages/coding-agent/src/prompts/system/system-prompt.md`, `src/discovery/claude-plugins.ts` | promotes runtime selection and the universal engineering method into the inherent system layer; routes standalone Java/Kotlin through runtime/JVM tools and project builds through declared project commands; canonical Superpowers workflow skills are filtered only within the canonical plugin provider, while domain skills and same-named user/project skills remain discoverable |
| `packages/coding-agent/src/telemetry/{events,metrics,sink-otlp}.ts` | adds bounded `runtime.call.completed` events, `aura.runtime.calls` and `aura.runtime.duration` instruments, and structured OTLP logs without source, arguments, output, paths, or exception messages |
Expand Down Expand Up @@ -95,6 +95,10 @@ and after every upstream merge.
| `AGENTS.md` | appended the `## Aura fork conventions` section (points contributors at this file, states the runtime naming rule, locates specs/plans) |
| `biome.json` | excludes checked-in `capnp-es` runtime protocol bindings from source formatting/lint; generated output is verified byte-for-byte by `scripts/sync-embedded-runtime-protocol.ts --check` instead |
| `bun.lock` | `aura` bin entry plus the exact `capnp-es@0.0.14` runtime dependency and coding-agent-local `typescript@5.9.3` generator peer. Regenerate with `bun install` rather than resolving a merge conflict by hand |
| `packages/coding-agent/src/cloud/token-manager.ts` | fixed `verifyAuraToken`'s JWT claim names to match elide-cloud's real issuer, discovered when this module — built and unit-tested but never exercised against a real server — got its first real consumer: reads `org`/`act`/`realm` (were `org_id`/`account_id`/`realm_id`) and `scope` as one RFC-6749 space-delimited string (was a `scopes` JSON array), matching `workers/auth/tokens.ts` / `packages/worker-auth/src/principal.ts` in elide-dev/cloud exactly. `test/cloud/aura-auth-fixture.ts`'s `userClaims`/`apiKeyClaims` and every override in `token-manager.test.ts`/`auth.test.ts` updated to match |
| `packages/coding-agent/src/cloud/deployment.ts` | adds the `stats` `AuraServiceSurface` (`statsIngestBaseUrl`, derived as `https://stats-ingest.${AURA_DOMAIN}`, matching elide-dev/cloud's `workers/stats`) and `resolveStatsPanelUrl` (derives `https://stats.${AURA_DOMAIN}`, matching `workers/stats-panel` — deliberately not a full surface, since a browser-navigation target carries no bearer/override tiers); `stats` added to `CloudConsumer`/`CLOUD_CONSUMER_SETTINGS`/`CLOUD_SWITCH_DEFAULTS` (off by default) and `LONGEST_DERIVED_PREFIX` updated since `stats-ingest.` is now the longest prefix |
| `packages/coding-agent/src/cli/stats-cli.ts` | after the existing local sync, `tryOpenHostedPanel()` checks `cloud.stats.enabled`; when on and signed in (`aura account login`) it pushes the most recent local records to the Aura ingest endpoint and opens the hosted panel (`stats.${AURA_DOMAIN}`) instead of starting the local server. Falls back to the unchanged local flow for every reason this might not apply — switch off (the default), Aura not configured, not signed in, or the push failing — none of those are hard errors. `omp`'s `stats` is untouched |
| `packages/stats/src/index.ts` | exports the pre-existing `getRecentRequests` (was internal to `aggregator.ts`) so `stats-cli.ts` can read the batch to push without a new aggregator function |

### Upstream tests de-hardcoded for the rebrand

Expand Down Expand Up @@ -146,6 +150,13 @@ changes the template sources.

## Fork-added files and directories (additive, no merge risk)

- `packages/coding-agent/src/commands/account.ts`, `src/cli/account-cli.ts` —
`aura account login|logout|status`: runs the (pre-existing but previously
unwired — see the `cloud/token-manager.ts` row above) `AuraAuthClient`
device flow to completion, so an Elide account can be signed in for
cloud-backed surfaces. Registered in `cli-commands.ts`. `omp` has no
equivalent command

- `packages/utils/src/distribution.ts` — single source of truth for aura's
distribution coordinates (release repo `elide-dev/aura`, update channel
`github`, install URL, brew/mise/npm coordinates), re-exported from the
Expand Down
1 change: 1 addition & 0 deletions packages/coding-agent/src/cli-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { flagConsumesValue } from "./cli/flag-tables";
export const commands: CommandEntry[] = [
{ name: "launch", load: () => import("./commands/launch").then(m => m.default) },
{ name: "acp", load: () => import("./commands/acp").then(m => m.default) },
{ name: "account", load: () => import("./commands/account").then(m => m.default) },
{ name: "auth-broker", load: () => import("./commands/auth-broker").then(m => m.default) },
{ name: "auth-gateway", load: () => import("./commands/auth-gateway").then(m => m.default) },
{ name: "agents", load: () => import("./commands/agents").then(m => m.default) },
Expand Down
149 changes: 149 additions & 0 deletions packages/coding-agent/src/cli/account-cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/**
* CLI handler for `aura account` — sign in to (or out of) the Aura account
* used by cloud-backed surfaces (currently: the hosted observability panel).
*
* This is aura-only: `omp` carries none of this. See docs/aura/FORK.md.
*/
import * as readline from "node:readline";
import chalk from "chalk";
import { AuraAuthClient, type AuraLoginPresenter, type AuraOrganizationOption } from "../cloud/auth";
import {
auraDeploymentFor,
readCloudSwitches,
resolveAuraDeployment,
resolveServiceEndpoint,
} from "../cloud/deployment";
import { isAuraCloudError } from "../cloud/errors";
import { AuraTokenStore } from "../cloud/token-store";
import { Settings } from "../config/settings";
import { openPath } from "../utils/open";

export type AccountAction = "login" | "logout" | "status";
export const ACCOUNT_ACTIONS = ["login", "logout", "status"] as const;

export interface AccountCommandArgs {
action: AccountAction;
flags: {
json?: boolean;
label?: string;
open?: boolean;
force?: boolean;
};
}

/** Resolve the configured auth origin, or `undefined` when Aura is not configured at all. */
function resolveAuthOrigin(settings: Settings): string | undefined {
const deployment = resolveAuraDeployment({ env: process.env });
const switches = readCloudSwitches(settings);
const narrowed = auraDeploymentFor("account", deployment, switches);
return resolveServiceEndpoint("auth", { deployment: narrowed })?.url;
}

async function openStore(): Promise<AuraTokenStore> {
return await AuraTokenStore.open();
}

function cliPresenter(openBrowser: boolean): AuraLoginPresenter {
return {
present(approval) {
console.log(chalk.bold("\nSign in to Aura"));
console.log(` Go to: ${chalk.cyan(approval.verificationUri)}`);
console.log(` Enter code: ${chalk.bold(approval.userCode)}`);
if (openBrowser) console.log(chalk.dim(" (opening your browser to the pre-filled link...)"));
console.log(chalk.dim("Waiting for approval...\n"));
},
open(url) {
openPath(url);
},
async selectOrganization(options: readonly AuraOrganizationOption[]): Promise<string | undefined> {
console.log(chalk.bold("\nMultiple organizations are available:"));
options.forEach((option, index) => {
console.log(` ${index + 1}. ${option.name ?? option.id}`);
});
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
try {
const answer = await new Promise<string>(resolve => rl.question("Select a number: ", resolve));
const index = Number.parseInt(answer.trim(), 10) - 1;
return options[index]?.id;
} finally {
rl.close();
}
},
};
}

function explainAuthError(error: unknown): string {
if (isAuraCloudError(error)) {
switch (error.code) {
case "login_required":
return "Not signed in. Run `aura account login` first.";
case "relogin_required":
return "Your Aura session has expired. Run `aura account login` again.";
case "access_denied":
return "Sign-in was denied.";
case "expired":
return "The sign-in code expired before it was approved.";
case "invalid_configuration":
return "AURA_DOMAIN (or an explicit AURA_AUTH_URL) is not configured.";
default:
return `Aura request failed: ${error.code}`;
}
}
return error instanceof Error ? error.message : String(error);
}

export async function runAccountCommand(cmd: AccountCommandArgs): Promise<void> {
const settings = await Settings.init();
const authOrigin = resolveAuthOrigin(settings);
if (!authOrigin) {
const message = "Aura is not configured — set AURA_DOMAIN (or AURA_AUTH_URL) first.";
if (cmd.flags.json) console.log(JSON.stringify({ error: message }));
else console.error(chalk.red(message));
process.exitCode = 1;
return;
}

const store = await openStore();
const client = new AuraAuthClient({ authOrigin, store });

try {
switch (cmd.action) {
case "login": {
const result = await client.login({
label: cmd.flags.label,
open: cmd.flags.open ?? true,
presenter: cliPresenter(cmd.flags.open ?? true),
});
if (cmd.flags.json) {
console.log(JSON.stringify({ signedIn: true, ...result }));
} else {
console.log(chalk.green(`Signed in (account ${result.identity.accountId}).`));
}
return;
}
case "logout": {
const result = await client.logout({ force: cmd.flags.force });
if (cmd.flags.json) console.log(JSON.stringify(result));
else
console.log(chalk.green(result.revoked ? "Signed out." : "Signed out locally (server logout failed)."));
return;
}
case "status": {
const status = client.status();
if (cmd.flags.json) {
console.log(JSON.stringify(status));
} else if (!status.signedIn) {
console.log(chalk.dim("Not signed in to Aura."));
} else {
console.log(chalk.green(`Signed in as account ${status.identity?.accountId} (${status.issuer}).`));
}
return;
}
}
} catch (error) {
const message = explainAuthError(error);
if (cmd.flags.json) console.log(JSON.stringify({ error: message }));
else console.error(chalk.red(message));
process.exitCode = 1;
}
}
Loading
Loading