Skip to content

refactor: replace built-in env list with PHOTON_API_HOST runtime var#14

Merged
citron (lcandy2) merged 4 commits into
mainfrom
refactor/api-host-via-env-var
Apr 30, 2026
Merged

refactor: replace built-in env list with PHOTON_API_HOST runtime var#14
citron (lcandy2) merged 4 commits into
mainfrom
refactor/api-host-via-env-var

Conversation

@lcandy2
Copy link
Copy Markdown
Member

@lcandy2 citron (lcandy2) commented Apr 30, 2026

Why

Until now src/lib/env.ts hardcoded production, staging, and dev URLs into the bundle. That means our internal staging URL shipped to npm and to every standalone binary in every release. This is the kind of thing security review eventually catches — better to fix it before that.

What changes

The CLI now has a single runtime knob for backend selection:

# default (built-in) — production
photon ping
# →  https://app.photon.codes

# override per-shell
export PHOTON_API_HOST=https://staging-app.photon.codes
photon login
photon projects ls

# override per-command (wins over the env var)
photon ping --api-host https://your.backend.tld

The whole env list / use / add / remove tree goes away — redundant with the env var. env current stays as a debug helper showing the resolved host.

Credentials + links keyed by sanitized hostname

Previously: ~/.config/photon/credentials/{production,staging,dev}.json

Now: ~/.config/photon/credentials/<key>.json where the key is derived from the URL — production keeps the literal name production for back-compat (so existing creds files survive the upgrade), other URLs get sanitized hostnames like staging-app-photon-codes, localhost-3001, api-example-com-8080. Same idea for links/<key>.json.

This means you can be logged into multiple backends simultaneously — there's no "current env" persisted on disk, just whichever PHOTON_API_HOST resolves at command time.

Flag rename: -e, --env <name>--api-host <url>

Across all 13 commands that took the old flag. The rename is breaking, but the old flag was meaningless once env names were no longer a thing — --env staging had no defined value to override.

Removed

  • BUILTIN_ENVS constant
  • BuiltinEnvName, DEFAULT_ENV, isBuiltin
  • customEnvs / currentEnv from persisted config
  • setCurrentEnv / addCustomEnv / removeCustomEnv / listEnvs
  • UnknownEnvError (no more enumerable env list to be unknown of)
  • env list / use / add / remove subcommands

Bundle audit

$ strings dist/photon.js | grep -c app.photon.codes        →  1   # only the production default
$ strings dist/photon.js | grep -c staging                  →  0
$ strings dist/photon.js | grep -c localhost:3001           →  0

The public bundle now contains zero references to anything other than the production URL.

Diff

23 files touched, -468 / +309 lines (refactor net-shrinks the codebase).

Test plan

  • bunx tsc --noEmit clean
  • bun run build produces dist/photon.js (~0.38 MB)
  • photon env current defaults to production
  • PHOTON_API_HOST=https://x.tld photon env current shows the override
  • photon ping --api-host https://y.tld (flag wins over env var)
  • PHOTON_API_HOST=not-a-url photon env current surfaces a clean URL-validation error
  • Bundle has zero internal URL leaks (audit above)
  • After merge: existing production.json creds keep working (verified by hostKey special-case logic)
  • After merge: bun add -g @photon-ai/photon + PHOTON_API_HOST=https://staging-app.photon.codes photon login writes creds to staging-app-photon-codes.json

Migration notes

Users with credentials files for old custom envs (staging.json, dev.json, etc.) keep them on disk but they become orphaned — the CLI no longer resolves a "staging" name. Re-login with PHOTON_API_HOST set to recreate creds under the new hostname-derived key. Production users are unaffected (the special case keeps production.json working).

🤖 Generated with Claude Code

Made with Orca 🐋

Summary by CodeRabbit

  • New Features

    • Added standalone binary install option and documented a new "pho" alias for installed CLI.
  • Changed

    • CLI selection moved from --env to --api-host and PHOTON_API_HOST-driven backends.
    • Terminology updated from "environments"/"envs" to "backends"/"keys"; auth/status and whoami outputs reflect this.
    • Many commands (login, logout, billing, projects, profile, link, ping, spectrum, etc.) now accept --api-host.
  • Removed

    • Interactive local env management (add/remove/list/use); env command now only shows current backend.
  • Documentation

    • README expanded with three install flows and updated backend/command examples.

citron (lcandy2) and others added 2 commits April 30, 2026 13:35
Restructure the README's top half around the three actually-supported
install paths so first-time readers can pick the right one without
hunting:

  1. one-off via npx / bunx — no install (unpinned now works as of 0.1.1)
  2. global install via bun add -g — daily use
  3. standalone binary downloaded from a GitHub Release — no runtime

Also lifts the pho-alias note into its own subsection under Quickstart
with the explicit caveat that npx/bunx ephemeral runs don't get pho
(it's only created when running through an installed photon binary).

The previous version led with `bun add -g` and buried npx as an
afterthought in the Quickstart, which understated what the CLI now
supports.

Co-authored-by: Orca <help@stably.ai>
Previously BUILTIN_ENVS hardcoded production / staging / dev URLs into
the public bundle, which meant our internal staging URL shipped to npm
and to every standalone binary in every release. This is the kind of
thing security review eventually catches — better to fix it before
that.

The new model:

  - The only URL baked in is production (https://app.photon.codes).
  - Every other backend is reached via the PHOTON_API_HOST env var
    (or `--api-host <url>` per command).
  - Credentials and project links are now keyed by sanitized hostname
    instead of by env name. Production keeps the "production" key for
    back-compat, so existing creds files survive the upgrade. Other
    URLs get keys like `staging-app-photon-codes` or `localhost-3001`.
  - The whole `env list / use / add / remove` tree goes away —
    redundant with the env var. `env current` stays as a debug helper.

Net result: 23 files touched, -468/+309 lines, public bundle has zero
references to anything other than the production URL.

Verified locally:
  $ photon env current
  production (https://app.photon.codes)
  $ PHOTON_API_HOST=https://staging-app.photon.codes photon env current
  staging-app-photon-codes (https://staging-app.photon.codes)
  $ photon ping --api-host https://wins.tld   # flag overrides env var
  → https://wins.tld
  $ PHOTON_API_HOST=not-a-url photon env current
  ✗ Invalid API host URL: "not-a-url". Must include scheme — e.g. https://your.host.tld.

Bundle audit:
  $ strings dist/photon.js | grep -c app.photon.codes        → 1
  $ strings dist/photon.js | grep -c staging                  → 0
  $ strings dist/photon.js | grep -c localhost:3001           → 0

Co-authored-by: Orca <help@stably.ai>
Copilot AI review requested due to automatic review settings April 30, 2026 06:30
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1847bf9c-ae4a-4303-a066-92517cb579ac

📥 Commits

Reviewing files that changed from the base of the PR and between 57e1c35 and 2429635.

📒 Files selected for processing (2)
  • src/lib/api.ts
  • src/lib/env.ts

📝 Walkthrough

Walkthrough

Replaces env-name selection with API-host-driven backends: introduces PHOTON_API_HOST/--api-host, host-derived credential keys and storage, removes local env CRUD, updates command flags/messages/docs, and threads apiHost/url through resolve/getApi across CLI commands and libs.

Changes

Cohort / File(s) Summary
Documentation
README.md
Expanded install options, documented pho alias behavior, and switched terminology/examples from env/--env to backend/PHOTON_API_HOST/--api-host.
Auth & Status
src/commands/auth.ts
Status now reports backend name/url via resolveEnv() and loadCredentials(name); added AuthRow and updated messaging to reference --api-host.
Billing & Payments
src/commands/billing.ts
Replaced --env with --api-host; passes opts.apiHost to resolveProject and uses resolved.url for API client.
Config & Env command
src/commands/config.ts, src/commands/env.ts
config show / env current use resolveEnv() for active backend; removed available-env listing and renamed UI labels to backend/keys.
Linking & Project Resolution
src/commands/link.ts, src/lib/api-context.ts
link/unlink and resolveProject accept --api-host/apiHost; error/hint text emits --api-host.
Auth Flow Commands
src/commands/login.ts, src/commands/logout.ts, src/commands/whoami.ts
CLI options switched from --env/-e to --api-host; option interfaces and internal resolution now use apiHost and resolveEnv(apiHost).
Diagnostics & Profile
src/commands/ping.ts, src/commands/profile.ts
Replaced --env with --api-host; updated option interfaces and getApi calls to use apiHost/resolved url.
Projects & Spectrum Subcommands
src/commands/projects.ts, src/commands/spectrum/...
src/commands/spectrum/avatar.ts, src/commands/spectrum/lines.ts, src/commands/spectrum/platforms.ts, src/commands/spectrum/profile.ts, src/commands/spectrum/users.ts
Uniformly replaced --env/envOverride with --api-host/apiHost; resolveProject/getApi now use resolved backend url.
Core API client
src/lib/api.ts
ApiOptions now has apiHost?: string (removed envName); getApi resolves active env via host when needed.
Config module removed features
src/lib/config.ts
Removed local dashboard-config CRUD and env-management exports; resolveEnv now delegates to resolveActiveEnv.
Environment/Host utilities
src/lib/env.ts
Removed builtin envs; added PRODUCTION_URL, resolveApiHost, normalizeOrigin, resolveActiveEnv, hostKey, and credentialsDir/path keyed by host-derived key; ResolvedEnv now has name (key) and normalized url.
Errors & top-level hints
src/lib/errors.ts, src/index.ts
Removed UnknownEnvError; adjusted NotAuthenticated/SessionExpired messages and top-level hints to reference backend/--api-host/PHOTON_API_HOST.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant CLI as "photon CLI"
  participant Resolver as "resolveApiHost / resolveActiveEnv"
  participant API as "getApi (API client)"
  participant FS as "credentialsDir / credentialsPath"

  CLI->>Resolver: receive --api-host or PHOTON_API_HOST
  Resolver-->>CLI: return ResolvedEnv{name (hostKey), url}
  CLI->>API: getApi({ apiHost: resolved.url, ... })
  API->>FS: loadCredentials(resolved.name)
  FS-->>API: credentials (or none / corrupt)
  API-->>CLI: authenticated client or auth-required response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I hop from names to hosts at dawn,
Keys from URLs are neatly drawn,
A tiny "pho" symlink, soft and spry,
Backends found beneath the sky—
Rabbit refactor, snack and song, hop on!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main refactoring: replacing the built-in environment list with PHOTON_API_HOST as the runtime backend selector, which is the central theme across all 23 files in this changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Warning

Review ran into problems

🔥 Problems

These MCP integrations need to be re-authenticated in the Integrations settings: Linear


Review rate limit: 3/5 reviews remaining, refill in 17 minutes and 26 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

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

Refactors the CLI’s backend/environment selection so only the production API URL is bundled, while all other backends are selected at runtime via PHOTON_API_HOST or --api-host <url>. This removes the previous built-in env list and config-persisted “current env”, and re-keys credentials/links storage by a hostname-derived key.

Changes:

  • Replace built-in environment list + photon env list/use/add/remove workflow with runtime backend resolution (PHOTON_API_HOST / --api-host), keeping photon env current as a debug helper.
  • Update API/env resolution plumbing across commands and libraries to use URL-based backend selection and host-keyed credentials/links.
  • Update CLI help/errors and README documentation to reflect the new backend host model and flag rename.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/lib/errors.ts Updates auth/session error wording to “backend” and removes UnknownEnvError.
src/lib/env.ts Introduces production-only baked URL, runtime host resolution, host-key derivation, and key validation/paths.
src/lib/config.ts Removes persisted env config; resolveEnv() now resolves the active backend via env var/flag/production.
src/lib/api.ts Switches API client creation and credential lookup to apiHost and host-keyed env names.
src/lib/api-context.ts Updates project resolution docs + --api-host wiring for link lookup and hints.
src/index.ts Removes unknown-env handling; updates top-level error hints for new backend selection model.
src/commands/whoami.ts Renames --env to --api-host and updates output wording.
src/commands/spectrum/users.ts Ports Spectrum users subcommands to --api-host and URL-based backend selection.
src/commands/spectrum/profile.ts Ports Spectrum profile subcommands to --api-host.
src/commands/spectrum/platforms.ts Ports Spectrum platforms subcommands (and helper opts typing) to --api-host.
src/commands/spectrum/lines.ts Ports Spectrum lines subcommands to --api-host.
src/commands/spectrum/avatar.ts Ports avatar upload flow to --api-host; recovery command now emits --api-host when non-production.
src/commands/projects.ts Ports projects commands to --api-host and URL-based env resolution.
src/commands/profile.ts Ports profile commands to --api-host.
src/commands/ping.ts Ports ping to --api-host while retaining --url bypass.
src/commands/logout.ts Ports logout to --api-host.
src/commands/login.ts Ports login to --api-host and logs the resolved backend key + URL.
src/commands/link.ts Ports link/unlink to --api-host and updates wording (“backend”).
src/commands/env.ts Collapses env management to env current and updates description text.
src/commands/config.ts Removes env list/current-env persistence from config output; focuses on active backend + keys.
src/commands/billing.ts Ports billing commands to --api-host.
src/commands/auth.ts Reworks auth status to show authenticated backend keys (and active backend) without relying on env config.
README.md Updates install/usage docs and replaces “environments” concept with “backend host” (PHOTON_API_HOST / --api-host).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/lib/env.ts Outdated
Comment thread src/lib/env.ts Outdated
Comment thread src/commands/env.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/commands/spectrum/avatar.ts (1)

103-107: Normalize URL before production comparison in recovery command.

Direct string comparison can treat equivalent production URLs as different (for example, https://app.photon.codes/), causing unnecessary --api-host in hints.

Suggested refinement
-  if (opts.apiHost !== PRODUCTION_URL) {
+  const resolvedOrigin = new URL(opts.apiHost).origin;
+  const productionOrigin = new URL(PRODUCTION_URL).origin;
+  if (resolvedOrigin !== productionOrigin) {
     parts.push(`--api-host ${shellQuote(opts.apiHost)}`);
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/spectrum/avatar.ts` around lines 103 - 107, Normalize the API
host before comparing to PRODUCTION_URL so equivalent URLs (e.g.
"https://app.photon.codes/") don't trigger an unnecessary --api-host; inside the
block that builds parts (reference opts.apiHost, PRODUCTION_URL, parts,
shellQuote in avatar.ts) canonicalize both values by trimming any trailing slash
and lowercasing the host (or otherwise normalizing the URL string) and then
compare the normalized strings—only push `--api-host ${shellQuote(...)}` when
the normalized opts.apiHost differs from the normalized PRODUCTION_URL.
src/commands/projects.ts (1)

36-37: Consider extracting a shared addApiHostOption() helper.

The same option signature/description is repeated across many subcommands; centralizing it will reduce drift and simplify future CLI flag updates.

Also applies to: 81-82, 154-155, 304-305, 399-400, 457-458, 507-508, 527-528

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/projects.ts` around lines 36 - 37, Extract the repeated
.option("--api-host <url>", "API host URL (defaults to PHOTON_API_HOST or
built-in production)") calls into a small helper function addApiHostOption(cmd)
that accepts a Commander command/command-builder and returns the command after
calling .option(...); replace each inline .option(...) occurrence in
src/commands/projects.ts (and the other command files/command builders at the
listed locations) with addApiHostOption(myCommand) so all commands share a
single source of truth; name the helper addApiHostOption and export it from a
shared CLI utilities module (e.g., cliOptions.ts) and update imports where used.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/commands/env.ts`:
- Around line 8-17: The help text for the CLI advertises an unsupported
"--api-host" flag; update the description strings to remove the "--api-host"
mention. Specifically, edit the top-level .description("show the active backend
(set via PHOTON_API_HOST or --api-host)") and any help text associated with
env.command("current") so they only reference PHOTON_API_HOST (or add a proper
--api-host option if you prefer), ensuring env.command("current")'s .description
no longer claims support for the nonexistent flag.

In `@src/commands/whoami.ts`:
- Around line 9-10: The whoami command prints inconsistent wording for target
selection: update the token-only output that currently says "on env ..." to use
"on backend ..." (or the same "backend" phrasing used elsewhere) so both
branches are consistent; locate the string literal in the whoami command handler
(the token-auth/token-only branch around the existing message at the lines
referenced, e.g., the branch that emits "on env ...") and replace it with the
backend wording and ensure the alternate branch output at the other occurrence
(around line 41) matches exactly.

In `@src/lib/env.ts`:
- Around line 57-59: hostKey currently only returns "production" when the input
exactly equals PRODUCTION_URL, which breaks for equivalent forms (e.g., trailing
slash); update hostKey to normalize/compare origins instead of raw strings:
parse the input url and PRODUCTION_URL with the URL API (or fallback to trimming
trailing slashes) and return "production" when their origins (or normalized
forms) match, handling invalid URLs safely; apply the same
normalization/comparison logic to the other hostKey branches referenced in this
diff so equivalent URLs produce the same key.

---

Nitpick comments:
In `@src/commands/projects.ts`:
- Around line 36-37: Extract the repeated .option("--api-host <url>", "API host
URL (defaults to PHOTON_API_HOST or built-in production)") calls into a small
helper function addApiHostOption(cmd) that accepts a Commander
command/command-builder and returns the command after calling .option(...);
replace each inline .option(...) occurrence in src/commands/projects.ts (and the
other command files/command builders at the listed locations) with
addApiHostOption(myCommand) so all commands share a single source of truth; name
the helper addApiHostOption and export it from a shared CLI utilities module
(e.g., cliOptions.ts) and update imports where used.

In `@src/commands/spectrum/avatar.ts`:
- Around line 103-107: Normalize the API host before comparing to PRODUCTION_URL
so equivalent URLs (e.g. "https://app.photon.codes/") don't trigger an
unnecessary --api-host; inside the block that builds parts (reference
opts.apiHost, PRODUCTION_URL, parts, shellQuote in avatar.ts) canonicalize both
values by trimming any trailing slash and lowercasing the host (or otherwise
normalizing the URL string) and then compare the normalized strings—only push
`--api-host ${shellQuote(...)}` when the normalized opts.apiHost differs from
the normalized PRODUCTION_URL.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2db77ae3-14c0-429b-8f6b-adb74cd6e548

📥 Commits

Reviewing files that changed from the base of the PR and between dec16a2 and 2a82356.

📒 Files selected for processing (23)
  • README.md
  • src/commands/auth.ts
  • src/commands/billing.ts
  • src/commands/config.ts
  • src/commands/env.ts
  • src/commands/link.ts
  • src/commands/login.ts
  • src/commands/logout.ts
  • src/commands/ping.ts
  • src/commands/profile.ts
  • src/commands/projects.ts
  • src/commands/spectrum/avatar.ts
  • src/commands/spectrum/lines.ts
  • src/commands/spectrum/platforms.ts
  • src/commands/spectrum/profile.ts
  • src/commands/spectrum/users.ts
  • src/commands/whoami.ts
  • src/index.ts
  • src/lib/api-context.ts
  • src/lib/api.ts
  • src/lib/config.ts
  • src/lib/env.ts
  • src/lib/errors.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> or ts-node <file>
Bun automatically loads .env, so don't use dotenv
Use Bun.serve() with WebSockets, HTTPS, and routes instead of express
Use bun:sqlite for SQLite instead of better-sqlite3
Use Bun.redis for Redis instead of ioredis
Use Bun.sql for Postgres instead of pg or postgres.js
Use built-in WebSocket instead of ws package
Prefer Bun.file over node:fs's readFile/writeFile for file operations
Use Bun.$ template literal for shell commands instead of execa
Use bun --hot to run server files with hot module reloading

Files:

  • src/commands/logout.ts
  • src/commands/ping.ts
  • src/commands/whoami.ts
  • src/lib/api.ts
  • src/commands/link.ts
  • src/commands/login.ts
  • src/lib/api-context.ts
  • src/index.ts
  • src/commands/env.ts
  • src/commands/spectrum/profile.ts
  • src/commands/spectrum/avatar.ts
  • src/lib/config.ts
  • src/lib/errors.ts
  • src/commands/spectrum/platforms.ts
  • src/commands/spectrum/users.ts
  • src/commands/profile.ts
  • src/lib/env.ts
  • src/commands/config.ts
  • src/commands/spectrum/lines.ts
  • src/commands/auth.ts
  • src/commands/billing.ts
  • src/commands/projects.ts
**/*.{html,ts,tsx,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Use bun build <file.html|file.ts|file.css> instead of webpack or esbuild

Files:

  • src/commands/logout.ts
  • src/commands/ping.ts
  • src/commands/whoami.ts
  • src/lib/api.ts
  • src/commands/link.ts
  • src/commands/login.ts
  • src/lib/api-context.ts
  • src/index.ts
  • src/commands/env.ts
  • src/commands/spectrum/profile.ts
  • src/commands/spectrum/avatar.ts
  • src/lib/config.ts
  • src/lib/errors.ts
  • src/commands/spectrum/platforms.ts
  • src/commands/spectrum/users.ts
  • src/commands/profile.ts
  • src/lib/env.ts
  • src/commands/config.ts
  • src/commands/spectrum/lines.ts
  • src/commands/auth.ts
  • src/commands/billing.ts
  • src/commands/projects.ts
**/*.{html,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use HTML imports with Bun.serve() for frontend instead of Vite

Files:

  • src/commands/logout.ts
  • src/commands/ping.ts
  • src/commands/whoami.ts
  • src/lib/api.ts
  • src/commands/link.ts
  • src/commands/login.ts
  • src/lib/api-context.ts
  • src/index.ts
  • src/commands/env.ts
  • src/commands/spectrum/profile.ts
  • src/commands/spectrum/avatar.ts
  • src/lib/config.ts
  • src/lib/errors.ts
  • src/commands/spectrum/platforms.ts
  • src/commands/spectrum/users.ts
  • src/commands/profile.ts
  • src/lib/env.ts
  • src/commands/config.ts
  • src/commands/spectrum/lines.ts
  • src/commands/auth.ts
  • src/commands/billing.ts
  • src/commands/projects.ts
🧠 Learnings (20)
📚 Learning: 2026-04-21T18:03:31.894Z
Learnt from: lcandy2
Repo: photon-hq/dashboard PR: 40
File: apps/web/src/app/dashboard/[projectId]/layout.tsx:40-40
Timestamp: 2026-04-21T18:03:31.894Z
Learning: In `apps/web/src/app/dashboard/[projectId]/layout.tsx` (Next.js, TypeScript), the Eden `treaty<App>` client from `elysiajs/eden` returns union types for API responses (data/error union), requiring `as any` casts or explicit type narrowing when accessing response properties like `project.name`. The proper fix is to type `getProjectById` in `apps/web/src/lib/api/projects.ts` to return `ProjectRecord | null` (from `~/shared`) so the inferred type is usable without casting.

Applied to files:

  • src/lib/api-context.ts
📚 Learning: 2026-04-21T18:03:17.985Z
Learnt from: lcandy2
Repo: photon-hq/dashboard PR: 40
File: apps/web/next.config.ts:6-8
Timestamp: 2026-04-21T18:03:17.985Z
Learning: In the photon-hq/dashboard repo (`apps/web/next.config.ts`), `typescript.ignoreBuildErrors: true` is intentional. Eden's `import type { App }` (cross-workspace type import from `photon-dashboard/api`) triggers unresolvable type resolution issues during `next build` in the Turborepo monorepo. Runtime behavior is correct and types resolve at dev time via the separate `typecheck` turbo task. Do not flag this as an issue.

Applied to files:

  • src/index.ts
  • src/lib/config.ts
📚 Learning: 2026-04-20T03:54:45.839Z
Learnt from: CR
Repo: photon-hq/imessage-kit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-20T03:54:45.839Z
Learning: Applies to src/**/*.ts : Use `SendError(msg)` factory function to return `IMessageError` instead of using `new SendError()` constructor directly; use `instanceof IMessageError` in catch blocks

Applied to files:

  • src/index.ts
  • src/lib/errors.ts
📚 Learning: 2026-04-01T08:17:58.254Z
Learnt from: CR
Repo: photon-hq/advanced-imessage-ts PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-04-01T08:17:58.254Z
Learning: Applies to **/*.{js,ts,tsx} : Always handle errors explicitly with try-catch blocks or error callbacks, never silently fail

Applied to files:

  • src/index.ts
📚 Learning: 2026-04-18T01:54:11.728Z
Learnt from: CR
Repo: photon-hq/cosmos PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-18T01:54:11.728Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Throw `Error` objects with descriptive messages, not strings or other values

Applied to files:

  • src/lib/errors.ts
📚 Learning: 2026-03-11T19:21:45.922Z
Learnt from: CR
Repo: photon-hq/webhook PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-11T19:21:45.922Z
Learning: Applies to **/*.{js,ts,jsx,tsx} : Throw `Error` objects with descriptive messages, not strings or other values

Applied to files:

  • src/lib/errors.ts
📚 Learning: 2026-04-13T23:00:20.897Z
Learnt from: CR
Repo: photon-hq/spectrum-ts PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-13T23:00:20.897Z
Learning: Applies to **/*.{js,ts,jsx,tsx} : Throw `Error` objects with descriptive messages, not strings or other values in JavaScript/TypeScript

Applied to files:

  • src/lib/errors.ts
📚 Learning: 2026-04-15T02:30:10.124Z
Learnt from: CR
Repo: photon-hq/whatsapp-business-ts PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-15T02:30:10.124Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Throw `Error` objects with descriptive messages, not strings or other values

Applied to files:

  • src/lib/errors.ts
📚 Learning: 2026-04-21T18:03:24.694Z
Learnt from: lcandy2
Repo: photon-hq/dashboard PR: 40
File: apps/web/src/lib/api/projects.ts:152-158
Timestamp: 2026-04-21T18:03:24.694Z
Learning: In `apps/web/src/lib/api/projects.ts`, `getEnabledPlatforms` intentionally returns `{}` on API error as a safe default (all platform toggles shown as off in the UI). This matches the original pattern where empty object means "unable to determine." Throwing is avoided because platforms are a non-critical feature and an error would crash the entire Spectrum page.

Applied to files:

  • src/commands/spectrum/platforms.ts
📚 Learning: 2026-04-28T03:08:36.661Z
Learnt from: CR
Repo: photon-hq/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-28T03:08:36.661Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Bun automatically loads .env, so don't use dotenv

Applied to files:

  • src/commands/config.ts
📚 Learning: 2026-03-10T22:49:19.048Z
Learnt from: CR
Repo: photon-hq/advanced-imessage-kit PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-03-10T22:49:19.048Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Bun automatically loads .env, so don't use dotenv library

Applied to files:

  • src/commands/config.ts
📚 Learning: 2026-04-04T19:01:14.591Z
Learnt from: CR
Repo: photon-hq/explore-send-imessage-app PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-04-04T19:01:14.591Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `Bun.$` for shell command execution instead of `execa`

Applied to files:

  • src/commands/config.ts
📚 Learning: 2026-04-28T03:08:36.661Z
Learnt from: CR
Repo: photon-hq/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-28T03:08:36.661Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `Bun.$` template literal for shell commands instead of execa

Applied to files:

  • src/commands/config.ts
📚 Learning: 2026-04-04T19:01:14.591Z
Learnt from: CR
Repo: photon-hq/explore-send-imessage-app PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-04-04T19:01:14.591Z
Learning: Applies to package.json : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install`

Applied to files:

  • README.md
📚 Learning: 2026-04-28T03:08:36.661Z
Learnt from: CR
Repo: photon-hq/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-28T03:08:36.661Z
Learning: Applies to package.json : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install` in package.json

Applied to files:

  • README.md
📚 Learning: 2026-03-10T22:49:19.048Z
Learnt from: CR
Repo: photon-hq/advanced-imessage-kit PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-03-10T22:49:19.048Z
Learning: Applies to package.json : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install` in package.json scripts

Applied to files:

  • README.md
📚 Learning: 2026-04-28T03:08:36.661Z
Learnt from: CR
Repo: photon-hq/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-28T03:08:36.661Z
Learning: Applies to package.json : Use `bun run <script>` instead of `npm run <script>`, `yarn run <script>`, or `pnpm run <script>`

Applied to files:

  • README.md
📚 Learning: 2026-03-10T22:49:19.048Z
Learnt from: CR
Repo: photon-hq/advanced-imessage-kit PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-03-10T22:49:19.048Z
Learning: Applies to package.json : Use `bun run <script>` instead of `npm run <script>`, `yarn run <script>`, or `pnpm run <script>` for running scripts

Applied to files:

  • README.md
📚 Learning: 2026-04-29T19:00:22.283Z
Learnt from: lcandy2
Repo: photon-hq/cli PR: 11
File: package.json:14-23
Timestamp: 2026-04-29T19:00:22.283Z
Learning: In photon-hq/cli, the `LICENSE` file was intentionally removed (commit 9c451da) per project direction. The `license` field is deliberately absent from `package.json`, and `LICENSE` is not included in the `files` array. Do not flag the missing `license` field or absent LICENSE file as issues in this repository.

Applied to files:

  • README.md
📚 Learning: 2026-04-21T18:03:26.523Z
Learnt from: lcandy2
Repo: photon-hq/dashboard PR: 40
File: apps/web/src/lib/api/projects.ts:104-109
Timestamp: 2026-04-21T18:03:26.523Z
Learning: In `apps/web/src/lib/api/projects.ts` (photon-hq/dashboard), `checkPhoneAvailability` intentionally returns `{ available: true }` on error as a deliberate fail-open degradation. This avoids blocking the user flow when the availability service is down. Phone number uniqueness is enforced server-side on project creation regardless. Do not flag this behavior as a bug.

Applied to files:

  • src/commands/projects.ts
🔇 Additional comments (19)
src/lib/errors.ts (1)

9-12: Backend-oriented error messaging is clear and consistent.

These message/doc updates correctly reflect host-key/backend semantics and keep central error handling behavior intact.

Also applies to: 31-33

README.md (1)

12-124: Docs migration to backend host model is thorough and consistent.

Install guidance, precedence rules, command reference, and examples all line up with the new PHOTON_API_HOST/--api-host behavior.

Also applies to: 154-203

src/commands/ping.ts (1)

9-13: ping host override migration is correctly wired.

The command option and getApi call now consistently use --api-host without altering runtime behavior.

src/commands/billing.ts (1)

29-35: Billing subcommands were migrated consistently to --api-host.

Resolution and API wiring remain coherent across plans/show/checkout/manage paths.

Also applies to: 71-81, 120-136, 193-204

src/commands/logout.ts (1)

10-13: logout now targets the active backend correctly.

The new option and resolver usage are aligned with the refactor and keep the local-clear + best-effort server revoke flow unchanged.

src/commands/spectrum/profile.ts (1)

16-26: Spectrum profile commands are cleanly migrated to --api-host.

Both show and update keep their existing behavior while using the new backend resolution path.

Also applies to: 62-82

src/commands/spectrum/avatar.ts (1)

16-30: Avatar command host-context migration looks solid.

Option parsing, project resolution, API client setup, and recovery command context are all aligned with the new backend model.

Also applies to: 74-80

src/commands/spectrum/lines.ts (1)

18-28: Spectrum lines commands are consistently updated for backend-host targeting.

The CLI flags and API resolution flow are aligned across list, add, and remove.

Also applies to: 60-75, 103-113

src/commands/spectrum/users.ts (1)

20-30: --api-host migration is correctly wired for Spectrum users commands.

Option parsing, project resolution, and API client construction are consistent across list, add, and remove.

Also applies to: 66-77, 114-124

src/commands/profile.ts (1)

31-37: API-host migration for profile commands looks solid.

show, init, and update all consistently pass opts.apiHost through getApi.

Also applies to: 94-117, 341-378

src/index.ts (1)

66-71: Top-level auth/network hints align with the new backend selection model.

The updated guidance for --api-host / PHOTON_API_HOST is clear and consistent with the refactor.

Also applies to: 85-86

src/commands/spectrum/platforms.ts (1)

17-27: Spectrum platforms commands are consistently migrated to --api-host.

The option wiring and resolveProject/getApi flow are coherent across list/enable/disable paths.

Also applies to: 54-66, 76-84

src/lib/api-context.ts (1)

20-30: resolveProject host-based resolution update looks correct.

The precedence and no-link guidance are aligned with the new backend model.

Also applies to: 45-47

src/lib/api.ts (1)

50-53: Good env-resolution split between raw URL and resolved backend.

This keeps direct URL calls explicit while preserving normal --api-host / env-var resolution for credentialed flows.

src/commands/link.ts (1)

14-18: --api-host plumbing is consistent in both link and unlink paths.

The resolution path and persisted key usage are aligned end-to-end (resolveEnv/getApienv.name).

Also applies to: 61-65

src/commands/config.ts (1)

18-26: Config view now cleanly reflects the resolved active backend.

Nice simplification: active backend, linked project, and “other links” are now all keyed off the same resolved backend key.

Also applies to: 35-36, 50-67

src/commands/login.ts (1)

14-15: Login command migration to --api-host is correctly applied.

Resolution, auth flow, and credential persistence stay coherent with the new backend model.

Also applies to: 22-23, 36-38

src/commands/auth.ts (1)

25-33: Auth status handling for missing vs corrupt credentials is solid.

The row model and fallback behavior avoid false “logged in” states while still surfacing active backend context.

Also applies to: 38-47, 62-83

src/lib/config.ts (1)

5-16: resolveEnv() as a compatibility wrapper is a good simplification.

Delegating to resolveActiveEnv() centralizes resolution logic without breaking async call sites.

Comment thread src/commands/env.ts
Comment thread src/commands/whoami.ts
Comment thread src/lib/env.ts Outdated
Address PR #14 review (Copilot + CodeRabbit, 4 distinct findings):

- resolveActiveEnv now normalizes via URL.origin so trailing slashes,
  paths, queries, and fragments don't break the production back-compat
  fallback. `https://app.photon.codes/` and `https://app.photon.codes`
  both resolve to key "production". Persisted creds.apiUrl is now
  always canonical.

- hostKey switches the dot replacement from `-` to `_` so distinct
  hosts can never collide on disk. `a-b.com` → `a-b_com`, `a.b-com` →
  `a_b-com`. Also strips IPv6 brackets and replaces `:` / `%` with
  `_`, and surfaces a clear error if the resulting key would exceed
  64 chars (instead of letting it crash deep inside credentialsPath).
  SAFE_KEY_RE now allows a leading `_` since IPv6 hostnames can
  produce keys like `_3000` after `::` collapses.

- env current now declares `--api-host <url>` so the help text matches
  reality. Previously it advertised the flag in its description but
  didn't accept it.

- whoami's token-only branch said "on env" — updated to "on backend"
  for consistency with everywhere else post-refactor.

api.ts also routes the `opts.url` branch through resolveActiveEnv so
URL-mode requests get the same normalization.

Verified locally:

  $ PHOTON_API_HOST=https://app.photon.codes/ photon env current
  production (https://app.photon.codes)
  $ PHOTON_API_HOST=https://a-b.com photon env current
  a-b_com (https://a-b.com)
  $ PHOTON_API_HOST=https://a.b-com photon env current
  a_b-com (https://a.b-com)            # distinct from a-b.com
  $ PHOTON_API_HOST='http://[::1]:3000' photon whoami
  ✗ Not authenticated for backend "__1_3000".  # downstream regex no longer trips
  $ photon env current --api-host https://wins.tld
  wins_tld (https://wins.tld)

Co-authored-by: Orca <help@stably.ai>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/lib/api.ts`:
- Around line 50-54: The URL branch currently calls resolveActiveEnv(opts.url)
which triggers hostKey() and can throw for long valid hostnames; change the URL
path to only normalize the origin without computing host keys by introducing and
using a helper like normalizeApiOrigin(raw: string) that returns new
URL(raw).origin (throwing a clear "Invalid API host URL" error on parse failure)
and set env accordingly instead of calling resolveActiveEnv; keep
resolveEnv(opts.apiHost) for the non-URL branch and leave hostKey() usage only
in the credential/host lookup flow (e.g., functions resolveEnv or wherever
hostKey is intended).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 16cd8699-a3a9-4fd4-a617-c10f49ddf986

📥 Commits

Reviewing files that changed from the base of the PR and between 2a82356 and 57e1c35.

📒 Files selected for processing (4)
  • src/commands/env.ts
  • src/commands/whoami.ts
  • src/lib/api.ts
  • src/lib/env.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/commands/whoami.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> or ts-node <file>
Bun automatically loads .env, so don't use dotenv
Use Bun.serve() with WebSockets, HTTPS, and routes instead of express
Use bun:sqlite for SQLite instead of better-sqlite3
Use Bun.redis for Redis instead of ioredis
Use Bun.sql for Postgres instead of pg or postgres.js
Use built-in WebSocket instead of ws package
Prefer Bun.file over node:fs's readFile/writeFile for file operations
Use Bun.$ template literal for shell commands instead of execa
Use bun --hot to run server files with hot module reloading

Files:

  • src/lib/api.ts
  • src/lib/env.ts
  • src/commands/env.ts
**/*.{html,ts,tsx,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Use bun build <file.html|file.ts|file.css> instead of webpack or esbuild

Files:

  • src/lib/api.ts
  • src/lib/env.ts
  • src/commands/env.ts
**/*.{html,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use HTML imports with Bun.serve() for frontend instead of Vite

Files:

  • src/lib/api.ts
  • src/lib/env.ts
  • src/commands/env.ts
🧠 Learnings (1)
📚 Learning: 2026-04-04T02:06:26.185Z
Learnt from: lcandy2
Repo: photon-hq/cosmos-cli PR: 25
File: Tests/CosmosAPITests.swift:46-46
Timestamp: 2026-04-04T02:06:26.185Z
Learning: In `Tests/CosmosAPITests.swift` (PR `#25`), the hardcoded API key `2f129b0dcff0565013d2713f05df8e0192c6aac7f98123275064431d4e45a51f` at line 46 (`private static let key`) is intentionally committed. It is a testing-environment-only key for `http://testing.cosmos.internal`, an internal service that is not internet-routable and has no access to production data. Do not flag it as a critical credential leak in future reviews; a low-severity/informational note about env-var best practice is acceptable but not required.

Applied to files:

  • src/lib/env.ts
🔍 Remote MCP Linear

Based on the searches, I was unable to locate the specific Linear issue for PR #14 in the available Linear workspace. Let me provide a comprehensive summary based on the context you've provided:

Summary of Review Context for PR #14

Based on the AI-generated analysis and PR objectives provided, here is the relevant context for reviewing this pull request:

Core Refactoring Objective

This PR implements a significant architectural shift in the Photon CLI from a hardcoded environment list model to a runtime backend URL selection model. The key changes are:

  1. Replaced Mechanism:

    • Old: Built-in environment names (production, staging, dev) with persisted "current env" state
    • New: Dynamic backend selection via PHOTON_API_HOST environment variable and per-command --api-host <url> flag
  2. Credentials & Link Storage:

    • Old: Credentials keyed by environment name
    • New: Credentials keyed by sanitized hostname derived from URL (e.g., ~/.config/photon/credentials/<key>.json)
    • Production URL gets back-compat treatment with literal key "production"
  3. API Resolution:

    • New resolveApiHost() function handles override/environment variable/production fallback
    • New resolveActiveEnv() normalizes URLs (using URL.origin for consistent canonicalization) and derives filesystem-safe host keys
    • New hostKey() function sanitizes hostnames for filesystem storage (underscore-safe, 64-char limit, IPv6-aware)
  4. CLI Interface Changes:

    • Removed: -e, --env <name> option across all commands (auth, billing, config, login, logout, ping, profile, projects, whoami, and spectrum subcommands)
    • Added: --api-host <url> option in its place
    • Removed: env subcommand tree (env list, env use, env add, env remove)
    • Retained: env current as a debug helper showing resolved backend
  5. Error Handling Updates:

    • Removed: UnknownEnvError class (no longer needed without env list)
    • Updated error messages: "environment" → "backend" terminology
    • Updated login/session failure hints to suggest --api-host <url> or PHOTON_API_HOST

Scope of Changes

Lines Changed: -468 / +309 across 23 files including:

  • Core config/env resolution (src/lib/config.ts, src/lib/env.ts, src/lib/api.ts, src/lib/api-context.ts)
  • Command implementations (login, logout, auth, billing, config, projects, profile, whoami, ping, link)
  • Spectrum subcommands (avatar, lines, platforms, profile, users)
  • Error handling (src/lib/errors.ts)
  • README documentation with three install paths and alias explanation

Key Design Decisions

  • URL Normalization: Uses URL.origin to canonicalize variants (trailing slashes, paths, queries)
  • Host Key Generation: Strips IPv6 brackets, replaces dots/colons with underscores, enforces 64-char limit, production back-compat mapping
  • Bundle Security: Public bundle includes only production URL; bundle audit confirms no internal URL leaks
  • No Persisted "Current Env": State is runtime-only (no config file changes)

Test Status

Per PR objectives:

  • Typecheck/build: ✓ Pass
  • Runtime checks: Listed (several post-merge verification items remain)

Related PRs

  • PR #13: Overlaps on README install/quickstart updates
  • PR #1: Main refactor introducing the env-name/config model being replaced
  • PR #3: Modifies same CLI config resolution code (potential conflicts)
  • PR #8: Modifies same spectrum command modules (addition context)
🔇 Additional comments (2)
src/commands/env.ts (1)

13-21: Nice fix: help text and behavior are now aligned.

env current now actually accepts --api-host and resolves through resolveEnv(opts.apiHost), so UX and command behavior are consistent.

src/lib/env.ts (1)

67-98: Host-key normalization and production back-compat look good.

Parsing first, comparing on canonical origin for production, and sanitizing host/port into filesystem-safe keys is a strong implementation.

Comment thread src/lib/api.ts Outdated
PR #14 review (CodeRabbit, 1 finding on the previous fix commit):

The earlier consolidation routed `getApi({ url })` through
`resolveActiveEnv` so URL-mode and resolve-mode shared normalization.
But `resolveActiveEnv` also calls `hostKey()`, which enforces a 64-char
ceiling (and other constraints) appropriate for filesystem-safe
credential keys — not for picking an arbitrary HTTP target. The result:
URL-mode (used by `ping --url <host>` for unauth health checks)
suddenly rejected long-but-valid hostnames.

Split the concerns:

  - Extract `normalizeOrigin(raw)` from resolveActiveEnv. It's the URL
    parse + .origin step, no key derivation. Exported.
  - URL-mode in api.ts now uses `normalizeOrigin(opts.url)` and a fixed
    `name: "custom"` — no hostKey, no key-limit enforcement, since no
    credential lookup happens on this path anyway.
  - resolveActiveEnv keeps using normalizeOrigin internally, so the
    canonical-URL behavior in resolve-mode is unchanged.

Verified locally: a 91-char hostname now succeeds via `ping --url …`
(URL-mode) and still surfaces the clear "too long" error via
`PHOTON_API_HOST=… env current` (resolve-mode).

Co-authored-by: Orca <help@stably.ai>
Copilot AI review requested due to automatic review settings April 30, 2026 06:48
@lcandy2 citron (lcandy2) added the release Just as it is label Apr 30, 2026
@lcandy2 citron (lcandy2) merged commit 6f65284 into main Apr 30, 2026
2 of 3 checks passed
Copy link
Copy Markdown

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

Copilot reviewed 23 out of 23 changed files in this pull request and generated 9 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/lib/api-context.ts
Comment on lines +45 to 47
const flag = opts.apiHost ? ` --api-host ${opts.apiHost}` : "";
die(`No project linked for backend "${env.name}".`, {
hint: `Run \`photon link <id>${flag}\`, or pass \`--project <id>\`.`,
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

The hint builds a copy/pastable command by interpolating opts.apiHost without any quoting/escaping. Since this value is a URL, it can contain shell-significant characters (&, ?, ;, spaces, etc.), which can break the command or lead to accidental shell injection when users paste it. Please shell-quote/escape the URL in the hint (or reuse a shared quoting helper).

Copilot uses AI. Check for mistakes.
Comment thread src/lib/api-context.ts
@@ -17,16 +17,16 @@ export interface ResolvedProject {
* 3. `~/.config/photon/links/<active-env>.json` written by `photon link`
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

This comment still refers to links/<active-env>.json, but links are now keyed by the resolved host key (same key used for credentials). Updating this to links/<key>.json (or similar) would keep the docs accurate and consistent with the new backend selection model.

Suggested change
* 3. `~/.config/photon/links/<active-env>.json` written by `photon link`
* 3. `~/.config/photon/links/<key>.json` written by `photon link`, where
* `<key>` is the resolved host key for the selected backend

Copilot uses AI. Check for mistakes.
Comment thread src/lib/errors.ts
Comment on lines +9 to 10
/** Host key (e.g. "production" or "staging-app-photon-codes"). */
constructor(public envName: string) {
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

The example host key staging-app-photon-codes doesn’t match the current hostKey() encoding (which replaces . and : with _). Please update this example to reflect the actual key format (e.g. staging-app_photon_codes).

Copilot uses AI. Check for mistakes.
Comment thread src/lib/errors.ts
Comment on lines +31 to 32
/** Host key (e.g. "production" or "staging-app-photon-codes"). */
constructor(public envName: string) {
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

Same as above: the example host key format here should match hostKey() (underscores for ./:), otherwise it’s misleading when debugging auth/session issues.

Copilot uses AI. Check for mistakes.
Comment thread README.md
$ photon env current
production (https://app.photon.codes)
$ PHOTON_API_HOST=http://localhost:3000 photon env current
localhost-3000 (http://localhost:3000)
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

The hostKey() implementation replaces : with _, so for PHOTON_API_HOST=http://localhost:3000 the key printed by photon env current will be localhost_3000, not localhost-3000. Please update this example output to match actual behavior.

Suggested change
localhost-3000 (http://localhost:3000)
localhost_3000 (http://localhost:3000)

Copilot uses AI. Check for mistakes.
Comment thread src/lib/env.ts
* underscore. Length 1-64.
*/
const SAFE_ENV_NAME_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/;
// First char allows `_` because hostKey() can produce keys like `_3000`
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

This comment’s example (_3000) doesn’t seem achievable with the current hostKey() logic for IPv6 hosts (it typically yields multiple underscores, e.g. __1_3000). Please adjust the example to match the actual output so the rationale for allowing a leading _ is accurate.

Suggested change
// First char allows `_` because hostKey() can produce keys like `_3000`
// First char allows `_` because hostKey() can produce keys like `__1_3000`

Copilot uses AI. Check for mistakes.
Comment thread src/commands/link.ts
Comment on lines +85 to 88
.description("show currently linked project(s) across backends")
.option("--json", "output JSON")
.action(async (opts) => {
const links = await listLinks();
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

This command is now described as operating across “backends”, but the link:status output below still uses “env” terminology (e.g., the hint and table header later in this action). Consider updating those strings/headers as well so user-facing terminology is consistent.

Copilot uses AI. Check for mistakes.
Comment thread README.md
Comment on lines +112 to 113
Credentials are stored **per host** (`$PHOTON_CONFIG_DIR/credentials/<key>.json` by default — see [config dir](#config-dir) below — mode 600), so you can be logged into multiple backends simultaneously. The `<key>` is derived from the URL — production keeps the literal name `production` for back-compat; other hosts get a sanitized hostname (e.g. `staging-app-photon-codes`, `localhost-3000`).

Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

The documented <key> examples use hyphens (e.g. staging-app-photon-codes, localhost-3000), but hostKey() currently derives keys by replacing ., :, and % with underscores (e.g. staging-app_photon_codes, localhost_3000). Please align the README examples/description with the actual key derivation to avoid confusing users when locating credential/link files.

Copilot uses AI. Check for mistakes.
Comment thread src/commands/link.ts
const env = await resolveEnv(opts.apiHost);
const existing = await loadLink(env.name);
if (!existing) {
console.log(c.dim(`No link to clear for env ${c.bold(env.name)}.`));
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

This user-facing message still says “env”, but the CLI has switched terminology to “backend”/host keys. Updating the string (and keeping terminology consistent across commands) would reduce confusion.

Suggested change
console.log(c.dim(`No link to clear for env ${c.bold(env.name)}.`));
console.log(c.dim(`No link to clear for backend ${c.bold(env.name)}.`));

Copilot uses AI. Check for mistakes.
citron (lcandy2) added a commit that referenced this pull request Apr 30, 2026
Stale from PR #14's review fix that switched the dot replacement from
`-` to `_`. README still showed pre-fix output:
- staging-app-photon-codes -> staging-app_photon_codes
- localhost-3000           -> localhost_3000

Also added a brief note about the `_` substitution rationale (avoids
`a-b.com` vs `a.b-com` collisions) since users seeing the unusual
underscore separator will reasonably wonder why.

Co-authored-by: Orca <help@stably.ai>
citron (lcandy2) added a commit that referenced this pull request May 13, 2026
* docs: fix hostKey examples (now uses _ for collision-safety)

Stale from PR #14's review fix that switched the dot replacement from
`-` to `_`. README still showed pre-fix output:
- staging-app-photon-codes -> staging-app_photon_codes
- localhost-3000           -> localhost_3000

Also added a brief note about the `_` substitution rationale (avoids
`a-b.com` vs `a.b-com` collisions) since users seeing the unusual
underscore separator will reasonably wonder why.

Co-authored-by: Orca <help@stably.ai>

* copy: reposition tagline to 'bring your agents to any interface'

Was: 'Photon CLI — replaces the dashboard web UI for end-user
interaction' (positioned as a web replacement).

Now: 'Photon CLI — bring your agents to any interface' (positioned
as the integration surface for agents reaching Photon services).

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release Just as it is

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants